1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PyxelとPymunkのサンプルプログラム その2 (6ファイル)

Last updated at Posted at 2025-11-01

以下、少しずつ、いろいろ変更を加えていくことで、変化を検証してみてください。

1.地面にボールが落下する物理シミュレーション

設定

  • 200,150 画面サイズ
  • 地面、ボール、ボールに回転を示す線(中心から半径方向)
  • 画面中央上から、鉛直落下
  • ボールに角速度を付加
  • スペースキーでリセットする
pyxel_pymunk_ball2.py
pyxel_pymunk_ball2.py
import math
import pyxel
import pymunk

class App:
    def __init__(self):
        pyxel.init(200, 150, title="Pyxel + pymunk: Rotating Ball")
        self.space = pymunk.Space()
        self.space.gravity = (0, 400)

        # 床
        floor = pymunk.Segment(self.space.static_body, (0, 130), (200, 130), 0)
        floor.elasticity = 0.9
        self.space.add(floor)

        # ボール
        mass, radius = 1, 8
        moment = pymunk.moment_for_circle(mass, 0, radius)
        self.ball_body = pymunk.Body(mass, moment)
        self.ball_body.position = (100, 50)
        self.ball_body.angular_velocity = 4.0 # ← 初期角速度を設定!
        self.ball_shape = pymunk.Circle(self.ball_body, radius)
        self.ball_shape.elasticity = 0.8
        self.space.add(self.ball_body, self.ball_shape)

        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_SPACE):
            self.ball_body.position = (100, 50)
            self.ball_body.angle = 0  # 角度リセット
            self.ball_body.velocity = (0, 0)
            self.ball_body.angular_velocity = 4.0
        self.space.step(1/60)

    def draw(self):
        pyxel.cls(0)
        pyxel.rect(0, 130, 200, 20, 5)

        # ボール位置と角度を取得
        x, y = self.ball_body.position
        r = 8
        angle = self.ball_body.angle

        # 半径方向の端点を計算
        rx = x + math.cos(angle) * r
        ry = y + math.sin(angle) * r

        # ボール本体
        pyxel.circ(int(x), int(y), r, 9)
        # 回転を示す線(中心から半径方向)
        pyxel.line(int(x), int(y), int(rx), int(ry), 8)

        pyxel.text(5, 5, f"angle={angle:.2f}", 7)

App()

2.ボールに横方向の初速度と角速度をつけて、落下する物理シミュレーション

設定

  • 200,150 画面サイズ
  • 地面、ボール、ボールに回転を示す線(中心から半径方向)
  • 画面中央上から、ボールに角速度、初速度、落下
  • スペースキーでリセットする
pyxel_pymunk_ball3.py
pyxel_pymunk_ball3.py
# pyxel_pymunk_ball3.py
# pip install pyxel pymunk

import math
import pyxel
import pymunk


class App:
    def __init__(self):
        pyxel.init(200, 150, title="Pyxel + pymunk: Rolling Ball")
        self.space = pymunk.Space()
        self.space.gravity = (0, 400)

        # 床
        floor = pymunk.Segment(self.space.static_body, (0, 130), (200, 130), 0)
        floor.elasticity = 0.8
        floor.friction = 0.9   # ← 床の摩擦を設定
        self.space.add(floor)

        # ボール
        mass, radius = 1, 8
        moment = pymunk.moment_for_circle(mass, 0, radius)
        self.ball_body = pymunk.Body(mass, moment)
        self.ball_body.position = (100, 50)
        self.ball_body.velocity = (1, 0)        # ← 横方向の初速度
        self.ball_body.angular_velocity = 4.0    # ← 初期角速度(スピン)
        self.ball_shape = pymunk.Circle(self.ball_body, radius)
        self.ball_shape.elasticity = 0.8
        self.ball_shape.friction = 0.9           # ← ボール側にも摩擦を設定
        self.space.add(self.ball_body, self.ball_shape)

        pyxel.run(self.update, self.draw)

    def update(self):
        # スペースキーでリセット
        if pyxel.btnp(pyxel.KEY_SPACE):
            self.ball_body.position = (100, 50)
            self.ball_body.angle = 0
            self.ball_body.velocity = (2, 0)
            self.ball_body.angular_velocity = 4.0
        self.space.step(1 / 60)

    def draw(self):
        pyxel.cls(0)
        pyxel.rect(0, 130, 200, 20, 5)

        # ボール位置と角度を取得
        x, y = self.ball_body.position
        r = 8
        angle = self.ball_body.angle

        # 半径方向の端点を計算(回転表示用)
        rx = x + math.cos(angle) * r
        ry = y + math.sin(angle) * r

        # ボール本体
        pyxel.circ(int(x), int(y), r, 9)
        # 回転を示す線(スピン方向を視覚化)
        pyxel.line(int(x), int(y), int(rx), int(ry), 8)

        # 情報表示
        vx, vy = self.ball_body.velocity
        pyxel.text(5, 5, f"v=({vx:.1f},{vy:.1f})", 7)  
        pyxel.text(5, 12, f"kakusoku={self.ball_body.angular_velocity:.2f}", 7)


App()

3.地面にボールが落下する。画面640 x 480

設定

  • 640,480 画面サイズ
  • 地面、ボール
  • 画面中央上から、鉛直落下
  • リセットなし
02redball_elastic.py
02redball_elastic.py
import pyxel
import pymunk

class App:
    def __init__(self):
        # 画面サイズ
        self.WIDTH = 640
        self.HEIGHT = 480

        # Pyxel の初期化(caption→title に変更)
        pyxel.init(self.WIDTH, self.HEIGHT, title="Pyxel + Pymunk Demo")
        pyxel.mouse(True)

        # Pymunk のスペースを作成し、重力を設定(y軸下向きの重力を -900)
        self.space = pymunk.Space()
        self.space.gravity = (0.0, -900.0)

        # 地面のセグメントを作成
        ground_y = 100
        body = pymunk.Body(body_type=pymunk.Body.STATIC)
        shape = pymunk.Segment(body, (0, ground_y), (self.WIDTH, ground_y), 1)
        shape.friction = 0.8
        shape.elasticity = 0.9   # ← ここを追加!
        self.space.add(body, shape)

        # 落下する赤いボール(動的円形オブジェクト)
        mass = 1
        radius = 20
        inertia = pymunk.moment_for_circle(mass, 0, radius)
        self.ball_body = pymunk.Body(mass, inertia)
        self.ball_body.position = (self.WIDTH // 2, 400)
        self.ball_shape = pymunk.Circle(self.ball_body, radius)
        self.ball_shape.elasticity = 0.9
        self.ball_shape.friction = 0.6
        self.space.add(self.ball_body, self.ball_shape)

        pyxel.run(self.update, self.draw)

    def update(self):
        dt = 1.0 / 60.0
        self.space.step(dt)
        if pyxel.btnp(pyxel.KEY_ESCAPE):
            pyxel.quit()

    def draw(self):
        pyxel.cls(0)
        # 地面の描画
        ground_screen_y = self.HEIGHT - 100
        pyxel.line(0, ground_screen_y, self.WIDTH, ground_screen_y, 7)
        # ボールの描画
        x, y_phys = self.ball_body.position
        y = self.HEIGHT - y_phys
        pyxel.circ(x, y, int(self.ball_shape.radius), 8)


if __name__ == "__main__":
    App()

4.ボールに角速度と初速度を加え、落下する。画面640 x 480

設定

  • 640,480 画面サイズ
  • 地面、ボール
  • 画面中央上から、角速度と初速度、落下
  • リセットなし
03ball_angular.py
03ball_angular.py
import pyxel
import pymunk


class App:
    def __init__(self):
        self.WIDTH = 640
        self.HEIGHT = 480

        # title に変更
        pyxel.init(self.WIDTH, self.HEIGHT, title="Pyxel + Pymunk Demo",fps=60)
        pyxel.mouse(True)

        # Pymunk SPACE と重力設定
        self.space = pymunk.Space()
        self.space.gravity = (0.0, -900.0)

        # 地面のセグメント
        ground_y = 100
        ground_body = pymunk.Body(body_type=pymunk.Body.STATIC)
        ground_shape = pymunk.Segment(ground_body, (0, ground_y), (self.WIDTH, ground_y), 1)
        ground_shape.friction = 0.8
        ground_shape.elasticity = 0.9  # 大きくバウンド
        self.space.add(ground_body, ground_shape)

        # 動的な赤いボール
        mass = 1
        radius = 20
        inertia = pymunk.moment_for_circle(mass, 0, radius)
        self.ball_body = pymunk.Body(mass, inertia)
        # 初期位置
        self.ball_body.position = (self.WIDTH // 2, 400)

        # ここで横方向の速度と角速度を設定
        self.ball_body.velocity = (5.0, 0.0)      # 右方向に初速度
        self.ball_body.angular_velocity = -10.0   # 時計回りに回転させる(負の符号は Pymunk の座標系依存)

        self.ball_shape = pymunk.Circle(self.ball_body, radius)
        self.ball_shape.elasticity = 0.9
        self.ball_shape.friction = 0.6
        self.space.add(self.ball_body, self.ball_shape)

        pyxel.run(self.update, self.draw)

    def update(self):
        dt = 1.0 / 60.0
        self.space.step(dt)

        if pyxel.btnp(pyxel.KEY_ESCAPE):
            pyxel.quit()

    def draw(self):
        pyxel.cls(0)

        # 地面描画
        ground_screen_y = self.HEIGHT - 100
        pyxel.line(0, ground_screen_y, self.WIDTH, ground_screen_y, 7)

        # ボール描画
        x, y_phys = self.ball_body.position
        y = self.HEIGHT - y_phys
        pyxel.circ(x, y, int(self.ball_shape.radius), 8)

        # デバッグ: 速度と角速度(任意)
        vel = self.ball_body.velocity
        ang = self.ball_body.angular_velocity
        pyxel.text(5, 5, f"V=({vel.x:.1f},{vel.y:.1f})  w={ang:.1f}", 7)

if __name__ == "__main__":
    App()

5.ボールに角速度と初速度を加え、落下する。ボールに回転を示す線を入れる

設定

  • 640,480 画面サイズ
  • 地面、ボール、ボールに回転を示す線(中心から半径方向)
  • 画面中央上から、角速度と初速度、落下
  • リセットなし
04redball_radiusline2.py
04redball_radiusline2.py
import pyxel
import pymunk
import math


class App:
    def __init__(self):
        self.WIDTH = 640
        self.HEIGHT = 480

        # Pyxel の初期化:タイトルと FPS を指定
        pyxel.init(self.WIDTH, self.HEIGHT,
                   title="Pyxel + Pymunk Demo",
                   fps=60)

        # Pymunk のスペース設定は、pyxel.run の前に済ませる
        self.space = pymunk.Space()
        self.space.gravity = (0.0, -900.0)

        # 地面の作成
        ground_y = 100
        ground_body = pymunk.Body(body_type=pymunk.Body.STATIC)
        ground_shape = pymunk.Segment(
            ground_body,
            (0, ground_y),
            (self.WIDTH, ground_y),
            1
        )
        ground_shape.friction = 0.8
        ground_shape.elasticity = 0.9
        self.space.add(ground_body, ground_shape)

        # ボールの作成
        mass = 1
        radius = 20
        inertia = pymunk.moment_for_circle(mass, 0, radius)
        self.ball_body = pymunk.Body(mass, inertia)
        self.ball_body.position = (self.WIDTH // 2, 400)
        self.ball_body.velocity = (5.0, 0.0)
        self.ball_body.angular_velocity = -10.0

        self.ball_shape = pymunk.Circle(self.ball_body, radius)
        self.ball_shape.elasticity = 0.9
        self.ball_shape.friction = 0.6
        self.space.add(self.ball_body, self.ball_shape)

        # メインループ開始(update → draw を FPS=60 で呼び出し)
        pyxel.run(self.update, self.draw)

    def update(self):
        # 1/60秒だけ物理演算を進める
        dt = 1.0 / 60.0
        self.space.step(dt)

        # ESC で終了
        if pyxel.btnp(pyxel.KEY_ESCAPE):
            pyxel.quit()

    def draw(self):
        pyxel.cls(0)

        # 地面を描画
        ground_screen_y = self.HEIGHT - 100
        pyxel.line(0, ground_screen_y,
                   self.WIDTH, ground_screen_y, 7)

        # ボール位置を画面座標に変換
        x_phys, y_phys = self.ball_body.position
        x = x_phys
        y = self.HEIGHT - y_phys
        r = self.ball_shape.radius

        # 1) ボール本体(赤色で塗りつぶし)
        pyxel.circ(x, y, int(r), 8)

        # 2) 回転確認用の半径線(白色)
        angle = self.ball_body.angle
        dx = r * math.cos(angle)
        dy = r * math.sin(angle)
        x2 = x + dx
        y2 = y - dy
        pyxel.line(x, y, x2, y2, 7)


if __name__ == "__main__":
    App()

6.ボールに角速度と初速度を加え、落下する。リセットキーを入れる

設定

  • 640,480 画面サイズ
  • 地面、ボール、ボールに回転を示す線(中心から半径方向)
  • 画面中央上から、角速度と初速度、落下
  • スペースキーでリセットする
05redball_resetkey.py
05redball_resetkey.py
import pyxel
import pymunk
import math


class App:
    def __init__(self):
        self.WIDTH = 640
        self.HEIGHT = 480

        # Pyxel 初期化:タイトルと FPS を指定
        pyxel.init(self.WIDTH, self.HEIGHT,
                   title="Pyxel + Pymunk Demo",
                   fps=60)
        
        pyxel.mouse(True)

        # Pymunk のスペースとオブジェクトを作成
        self._setup_space_and_objects()

        # メインループ開始
        pyxel.run(self.update, self.draw)

    def _setup_space_and_objects(self):
        # スペース
        self.space = pymunk.Space()
        self.space.gravity = (0.0, -900.0)

        # 地面
        ground_y = 100
        ground_body = pymunk.Body(body_type=pymunk.Body.STATIC)
        ground_shape = pymunk.Segment(
            ground_body,
            (0, ground_y),
            (self.WIDTH, ground_y),
            1
        )
        ground_shape.friction = 0.8
        ground_shape.elasticity = 0.9
        self.space.add(ground_body, ground_shape)

        # ボール(共通設定)
        mass = 1
        radius = 20
        inertia = pymunk.moment_for_circle(mass, 0, radius)

        # Body と Shape を属性に保存
        self.ball_body = pymunk.Body(mass, inertia)
        self.ball_shape = pymunk.Circle(self.ball_body, radius)
        self.ball_shape.friction = 0.6
        self.ball_shape.elasticity = 0.9

        # 最初の初期化
        self.reset_ball()

        # スペースへ追加
        self.space.add(self.ball_body, self.ball_shape)

    def reset_ball(self):
        """ボールを初期位置・初速度・角速度にリセットする"""
        # 初期位置
        self.ball_body.position = (self.WIDTH // 2, 400)
        # 初速度(右方向へ)
        self.ball_body.velocity = (5.0, 0.0)
        # 角速度(負で右回転)
        self.ball_body.angular_velocity = -10.0
        # 回転角度も 0 に戻しておく
        self.ball_body.angle = 0

    def update(self):
        # Rキーでリセット
        if pyxel.btnp(pyxel.KEY_R):
            self.reset_ball()

        # 物理演算を 1/60 秒進める
        dt = 1.0 / 60.0
        self.space.step(dt)

        # ESCキーで終了
        if pyxel.btnp(pyxel.KEY_ESCAPE):
            pyxel.quit()

    def draw(self):
        pyxel.cls(0)

        # 地面を描画
        ground_screen_y = self.HEIGHT - 100
        pyxel.line(0, ground_screen_y,
                   self.WIDTH, ground_screen_y, 7)

        # ボールを描画
        x_phys, y_phys = self.ball_body.position
        x = x_phys
        y = self.HEIGHT - y_phys
        r = self.ball_shape.radius

        # 本体(色8=赤)
        pyxel.circ(x, y, int(r), 8)

        # 半径線(色7=白)
        angle = self.ball_body.angle
        dx = r * math.cos(angle)
        dy = r * math.sin(angle)
        x2 = x + dx
        y2 = y - dy
        pyxel.line(x, y, x2, y2, 7)
        
        # デバッグ: 速度と角速度(任意)
        vel = self.ball_body.velocity
        ang = self.ball_body.angular_velocity
        pyxel.text(5, 5, f"V=({vel.x:.1f},{vel.y:.1f})  w={ang:.1f}", 7)


if __name__ == "__main__":
    App()

pyxel-20251101-212401.gif

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?