見出し画像

【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アクションの基本的な部分はできました。次は敵にも攻撃させてもっとゲームらしくしていこうと思います。

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

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

つくってみた

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