0
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で物理シミュレーションをやろう その4

Last updated at Posted at 2025-11-03

画面中央上部から赤い長方形が落下する基本的なサンプルプログラムを、pyxel.rect()で描画

rect_t1.py
import pyxel
import pymunk

COLOR_RED = 8
COLOR_BLUE = 5

class PhysicsSimulation:
    def __init__(self):
        self.width = 640
        self.height = 480
        pyxel.init(self.width, self.height, title="2D Physics Simulation")
        pyxel.mouse(True)
        self.reset()
        pyxel.run(self.update, self.draw)

    def reset(self):
        self.space = pymunk.Space()
        self.space.gravity = (0, -900)

        # 地面の作成 (青い長方形)
        ground_body = pymunk.Body(body_type=pymunk.Body.STATIC)
        ground_body.position = (self.width / 2, 20)
        ground_shape = pymunk.Poly.create_box(ground_body, (self.width, 40))
        ground_shape.friction = 0.8
        ground_shape.elasticity = 0.4
        self.space.add(ground_body, ground_shape)
        self.ground_shape = ground_shape

        # 落下する赤い長方形(画面中央上部)
        size = (60, 30)
        mass = 5
        moment = pymunk.moment_for_box(mass, size)
        self.rect_body = pymunk.Body(mass, moment)
        # y座標は画面上部なのでheight - 少し下げた位置に設定
        self.rect_body.position = (self.width / 2, self.height - 50)
        rect_shape = pymunk.Poly.create_box(self.rect_body, size)
        rect_shape.friction = 0.5
        rect_shape.elasticity = 0.6
        self.space.add(self.rect_body, rect_shape)
        self.rect_shape = rect_shape

    def update(self):
        if pyxel.btnp(pyxel.KEY_R):
            self.reset()
        
        dt = 1 / 60.0
        self.space.step(dt)

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

        # 地面描画(青色)
        ground_bb = self.ground_shape.bb
        pyxel.rect(
            int(ground_bb.left),
            int(self.height - ground_bb.top),
            int(ground_bb.right - ground_bb.left),
            int(ground_bb.top - ground_bb.bottom),
            COLOR_BLUE
        )

        # 赤い長方形の描画(回転なしなのでrectでOK)
        pos_x, pos_y = self.rect_body.position
        width, height_rect = (60,30) 
        x_draw_pos= int(pos_x - width/2) 
        y_draw_pos= int(self.height - pos_y - height_rect/2) 

        pyxel.rect(x_draw_pos,y_draw_pos,width,height_rect,COLOR_RED)

if __name__ == "__main__":
    PhysicsSimulation()
  • サイズ640x480
  • タイトルはtitle引数で指定
  • マウスカーソル表示あり
  • 重力は下向き(y軸負方向)に設定
  • 青色の地面を設置し弾性係数・摩擦係数を設定
  • 赤い長方形が画面中央上部から落下し、地面に衝突する挙動を表現
  • Rキー押下でリセット

地面無し、赤い長方形が落下し続ける。pyxel.tri()を用いて描画

rect_t0.py
import pyxel
import pymunk

COLOR_RED = 8

class PhysicsSimulation:
    def __init__(self):
        self.width = 640
        self.height = 480
        pyxel.init(self.width, self.height, title="2D Physics Simulation")
        self.reset()
        pyxel.run(self.update, self.draw)

    def reset(self):
        # 物理空間の初期化と重力設定(下向き)
        self.space = pymunk.Space()
        self.space.gravity = (0, -900)

        # 赤い長方形の作成(画面中央上部)
        size = (60, 30)
        mass = 5
        moment = pymunk.moment_for_box(mass, size)
        self.rect_body = pymunk.Body(mass, moment)
        # y座標はpyxelの座標系に合わせて高めに設定(pymunkは上向きy軸)
        self.rect_body.position = (self.width / 2, self.height - 50)
        rect_shape = pymunk.Poly.create_box(self.rect_body, size)
        rect_shape.friction = 0.5
        rect_shape.elasticity = 0.6
        self.space.add(self.rect_body, rect_shape)
        self.rect_shape = rect_shape

    def update(self):
        dt = 1 / 60.0
        # ステップ実行で物理演算を進める
        self.space.step(dt)

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

        # pymunkのPolyの頂点(ローカル座標)をワールド座標に変換
        verts = [self.rect_body.local_to_world(v) for v in self.rect_shape.get_vertices()]

        # pymunkのy軸は上向き、pyxelは下向きなのでyを変換
        verts_pyxel = [(int(x), int(self.height - y)) for x, y in verts]

        # 四角形の頂点は順序通りなので三角形2つに分割して描画
        v0, v1, v2, v3 = verts_pyxel

        # 三角形1: v0,v1,v2 を赤色で描画
        pyxel.tri(v0[0], v0[1], v1[0], v1[1], v2[0], v2[1], COLOR_RED)
        
        # 三角形2: v2,v3,v0 を赤色で描画
        pyxel.tri(v2[0], v2[1], v3[0], v3[1], v0[0], v0[1], COLOR_RED)

if __name__ == "__main__":
    PhysicsSimulation()

長方形に角速度、初速度を付加し、pyxel.tri()を用いて、回転描画する

rect_t2.py
import pyxel
import pymunk

COLOR_RED = 8
COLOR_BLUE = 5

class PhysicsSimulation:
    def __init__(self):
        self.width = 640
        self.height = 480
        pyxel.init(self.width, self.height, title="2D Physics Simulation")
        pyxel.mouse(True)
        self.reset()
        pyxel.run(self.update, self.draw)

    def reset(self):
        self.space = pymunk.Space()
        self.space.gravity = (0, -900)

        # 地面の作成 (青い長方形)
        ground_body = pymunk.Body(body_type=pymunk.Body.STATIC)
        ground_body.position = (self.width / 2, 20)
        ground_shape = pymunk.Poly.create_box(ground_body, (self.width, 40))
        ground_shape.friction = 0.8
        ground_shape.elasticity = 0.7
        self.space.add(ground_body, ground_shape)
        self.ground_shape = ground_shape

        # 落下する赤い長方形(画面中央上部)
        size = (60, 30)
        mass = 5
        moment = pymunk.moment_for_box(mass, size)
        self.rect_body = pymunk.Body(mass, moment)
        
        # 初期位置は画面中央上部(yは高い位置)
        self.rect_body.position = (self.width / 2, self.height - 50)

        # 初速度(右方向へ100ピクセル/秒)
        self.rect_body.velocity = (100, 0)

        # 初期角速度(ラジアン/秒)
        self.rect_body.angular_velocity = 3.14 * 6  # 約180度*6/秒

        rect_shape = pymunk.Poly.create_box(self.rect_body, size)
        
        rect_shape.friction = 0.5
        rect_shape.elasticity = 0.7
        
        self.space.add(self.rect_body, rect_shape)
        
        self.rect_shape = rect_shape

    def update(self):
        if pyxel.btnp(pyxel.KEY_R):
            self.reset()

        
        dt = 1 / 60.0
        
        # シミュレーションステップ進行
        
        # pymunkではy軸正方向が上なので、
        # 重力もそれに合わせています。
        
        # dt秒だけ物理演算を進める
        
        self.space.step(dt)

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

        # 地面描画(青色)
        
        ground_bb=self.ground_shape.bb
        
        pyxel.rect(
           int(ground_bb.left),
           int(self.height - ground_bb.top),
           int(ground_bb.right - ground_bb.left),
           int(ground_bb.top - ground_bb.bottom),
           COLOR_BLUE,
        )

        # 回転する長方形の描画
        
        verts=[self.rect_body.local_to_world(v) for v in self.rect_shape.get_vertices()]
        
        verts_pyxel=[(int(x), int(self.height - y)) for x,y in verts]

        v0,v1,v2,v3=verts_pyxel

        # 三角形1: v0,v1,v2
        
        pyxel.tri(v0[0],v0[1],v1[0],v1[1],v2[0],v2[1],COLOR_RED)

        # 三角形2: v2,v3,v0
        
        pyxel.tri(v2[0],v2[1],v3[0],v3[1],v0[0],v0[1],COLOR_RED)


if __name__=="__main__":
    PhysicsSimulation()
  • 長方形の初速度(線速度)と角速度を設定
  • pymunkのローカル頂点をワールド座標に変換し、pyxel座標系に合わせてy軸反転
  • 頂点4つを2つの三角形に分けてpyxel.tri()で描画
    このコードでは、初速と角速度が付いた赤い長方形が回転しながら落下し、地面で跳ね返ります。
    Rキーでリセットして同じ状態から再スタート可能です。
    ありがとうございます。

0
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
0
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?