メインコンテンツへスキップ
見出し画像

【pyxel 1.5】pyxelで2Dアクションを作ってみる(1)

    Pythonのレトロゲームエンジン「pyxel」で2Dアクションゲームを作ってみます。
    今回は手始めに「壁・床」の当たり判定の設置、ジャンプアクション、攻撃アクションの作成を行いました。コードには結構長くなってしまったので要所要所のみご紹介して、画像メインで進めたいと思います。

    画像
    ▲基本動作です。
    横に動いてジャンプ。右の燃料ゲージがある間はブーストできます。
    画像
    ▲燃料ゲージが空になるとブーストはできません。
    ゲージは徐々に回復していきます。
    画像
    ▲ビームで攻撃。
    固定砲台風の適当な敵を設置しています。

    結構いい感じなんじゃないでしょうか?
    この時点での操作は左右キーで移動、スペースキーでブースト、Vキーで攻撃です。
    プレイヤーはなんとなくロボットに。ブーストを実装したかっただけで他に意味はありません。
    画面は128*128のメインウィンドウの右側に情報ウィンドウをくっつけています。今は燃料ゲージとプレイヤーの座標を表示。文字色をオレンジにしたらアーマードコア3みたいで楽しくなりました。
    攻撃は謎のビーム。燃料ゲージがブーストと共通なので、このあたりの管理が難しい。飛びながら撃ってるとすぐ燃料切れになります。

    さて、ここからは少しコードの紹介を。

    今回最も面倒くさかったのは当たり判定の実装でした。最終的には自作の外部モジュールファイルとしてまとめています。

    # -*- coding: utf-8 -*-
    import pyxel
    
    def check_move(player, x, c):
        
        """
        Player coordinates
             
               2        pm1
      (x, y).--.----------.----
            |                 |
          7.|                 |.8
            |       P         |        
          5.|                 |.6
            |                 |
            ---.----------.----
               4          3
            
        """
          
        #Position Set
        px = int((player.p_x + c[0] + 2) // 8)
        py = int((player.p_y + c[1]) // 8)
          
        px2 = int((player.p_x + c[0] + 8) // 8)
        py2 = int((player.p_y + c[1] + 8) // 8)
          
        px3 = int((player.p_x + c[0] + 6) // 8)
        py3 = int((player.p_y + c[1] + 6) // 8)
          
        px4 = int((player.p_x + c[0]) // 8)
        py4 = int((player.p_y + c[1] + 2) // 8)
          
        pm1 = pyxel.tilemap(0).pget(px3, py)
        pm2 = pyxel.tilemap(0).pget(px, py)
        pm3 = pyxel.tilemap(0).pget(px3, py2)
        pm4 = pyxel.tilemap(0).pget(px, py2)
          
        pm5 = pyxel.tilemap(0).pget(px4, py3)
        pm6 = pyxel.tilemap(0).pget(px2, py3)
        pm7 = pyxel.tilemap(0).pget(px4, py4)
        pm8 = pyxel.tilemap(0).pget(px2, py4)
         
        #collision detection
        #Move UP
        if x == 1:
            if pm1[1] > 5 and pm2[1] > 5:
                return True
    
        #Move DOWN
        elif x == 3:
            if pm3[1] > 5 and pm4[1] > 5:
                return True
            else:
                return False
                
        #Move RIGHT
        elif x == 2:
            if pm6[1] > 5 and pm8[1] > 5:
                return True
        #Move LEFT
        elif x == 4:
            if pm5[1] > 5 and pm7[1] > 5:
                return True
        else:
            return False
    
    

    色々書いていますが、プレイヤーの移動先のタイルマップを見て、移動可能ならTrueを、ダメならFalseを返す関数です。
    そしてその判定をするためにこれまた色々と座標を取得しています。これは左右の移動と上下の移動の当たり判定を分けるためです。簡単に図示します。

    画像

    このプログラムではプレイヤー本来の描画範囲(黒枠)と縦移動の当たり判定(赤枠)、横移動の当たり判定(緑枠)が別の範囲になっています。当初は黒枠のみ考え、枠の四隅を判定基準にしていたのですが、これだと壁にくっついたときに上下移動ができなくなったり、ジャンプで天井に頭が付いたときに左右に移動できなくなったりとうまく動きませんでした。
    どういうことかというと…

    画像

    具体的には「右に進むには2と4の点が移動可能ならいい」というルールと「上に進むには1と2の点が移動可能ならいい」というルールがありました。この状態で壁まで行くと2と4の点が進行不可になり止まります。ここまではOKです。
    次にジャンプしようとすると、すでに2の点が進行不可になっているので飛べなくなってしまいました。進行方向が違うので何とかなりそうなもんなのですが、ここがどうにも直せなくて思いついた苦肉の策が今回の当たり判定になります。

    ※オブジェクトの中心点同士の距離をとったりするともっとスマートに解決できるらしい。ちょっと難しいので、これはまた今度。

    コード全体は長くなるので、GitHubのリポジトリをご参照ください。

    そんなこんなで2Dアクションの基本的な部分はできました。次は敵にも攻撃させてもっとゲームらしくしていこうと思います。

    ここまで読んでいただきありがとうございました。

    この記事が参加している募集

     
     
    出版社のしがない社内SE。 ここでは主に趣味のプログラミングやゲーム制作について書いています。作ったゲームは https://koidemizu.github.io/ で公開しているのでよかったら遊んでください。 ゲーム大好きインドア派。

    あなたへのおすすめ