見出し画像

pyxelゲーム制作TIPS_シューティングゲーム風_前編

はじめに

このnoteはpythonのレトロゲームエンジン「pyxel」でのゲーム制作に役立つかもしれない小さなサンプルプログラムのTIPSです。
以下の環境で作成・動作させています。
・OS:Windows11
・開発環境:Visual Studio Code(Ver.1.88.1)
・pyxelのバージョン:2.0.12

記事内のコードはご自由にお使いください。イメージ等もファイルは添付していませんが、模写(?)して使っていただいて構いません。
また、pyxelの動作環境や各関数の詳細な説明はpyxel公式GitHubをご参照ください。

シューティングゲームといえば

縦スクロール、横スクロール、弾幕系…いろいろあります。長く続いているシリーズも多いですし、ゲームセンター文化の功労者ともいえるでしょう。今回はあまり難しいことはせず、シンプルな画面固定のシューティングゲームを作ってみます。

作っていきましょう

それでは始めましょう。

まずプレイヤーキャラクターの画像を作成します。ちょっと変わり種という事で海賊船をモチーフにしてみましょう。

▲16*16ドットで海賊船を作成。
隣には移動時に描画する波しぶきが立っているバージョンも作っておきます。

イメージができたら、早速プレイヤーの移動までのコードを作成します。

import pyxel


class App:
    def __init__(self):
        #ここで起動時の処理をします                                
        pyxel.init(160, 250)        
        pyxel.load('./sample03.pyxres')    
        self.player_pos = [5, 200]         
        self.speed = 2
        self.moving_flag = False
        pyxel.run(self.update, self.draw)

    def update(self):
        #ここで毎フレームの更新作業をします
        #コントロール部分###################
        if pyxel.btn(pyxel.KEY_RIGHT):
            if self.player_pos[0] + self.speed < 160 - 16:
                self.player_pos[0] += self.speed
                self.moving_flag = True
            
        elif pyxel.btn(pyxel.KEY_LEFT):
            if self.player_pos[0] - self.speed > 0:
                self.player_pos[0] -= self.speed
                self.moving_flag = True

        else:
            self.moving_flag = False



    def draw(self):
        #ここで毎フレームの描画作業をします
        pyxel.cls(1)        
        if self.moving_flag == True:
            pyxel.blt(self.player_pos[0], self.player_pos[1], 0, 16, 0, 16, 16, 14)
        else:
            pyxel.blt(self.player_pos[0], self.player_pos[1], 0, 0, 0, 16, 16, 14)


App()

変数として、プレイヤー座標のplayer_pos、移動速度のspeed、プレイヤーが移動中か判定するmoving_flagを作成しています。
update関数では右キーが押されたらspeed分プレイヤーのx座標を右に動かし、左キーが押されたら同様に座標を左に動かしています。また、右の時は「< 160 - 16」、左の時は「> 0」の条件を挟むことで、プレイヤーが画面外に行かないようにしています。また、ボタンが押されている間はmoving_flag をTrueにし、移動中であることが判定できるようにしています。
draw関数ではプレイヤーの描画を行っています。moving_flagがTrueの場合は移動中ですので、移動用のイメージを、そうでない場合は静止用のイメージを描画します。

▲ここまでで実行した画面。
移動時にはきちんとイメージが変わっています。

さて、次はシューティングゲームなのですからプレイヤーが弾を撃てなければいけません。今回は新規にクラスを作ることで弾を実装してみます。
※クラスとはいわば設計図のようなもので、それを基にしてオブジェクトが作られます。弾のクラス作成→攻撃ボタンを押したら弾のオブジェクト作成、と進めていきます。

#プレイヤーの弾クラス
class Bullet:
    def __init__(self, x, y):
        #ここでオブジェクト作成時の処理をします   
        self.x = x
        self.y = y
        self.w = 3
        self.h = 3
        self.speed = 3

    def update(self):
        #各オブジェクトが毎フレーム行う更新処理です
        self.y -= self.speed

    def draw(self):
        #各オブジェクトが毎フレーム行う描画処理です
        pyxel.rect(self.x, self.y, self.w, self.h, 13)
        pyxel.rectb(self.x, self.y, self.w, self.h, 0)

プレイヤー弾となるBulletクラスを作成しました。
クラスの中には関数としてupdateとdrawを作っています。これはそれぞれ座標の更新と描画を担当する関数です。
update関数では弾のy座標をspeed分マイナスしています。pyxelではy軸は上が0なので、これは弾が下から上に動いていく機能です。
draw関数ではpyxelのrect関数を使って弾のドットを、rectb関数を使って枠線を描画しています。

さて、このBulletクラスを使って弾を撃たせるわけですが、せっかくなのでより船っぽい感じにしましょう。

    def __init__(self):
        #ここで起動時の処理をします                                
        pyxel.init(160, 250)        
        pyxel.load('./sample03.pyxres')    
        self.player_pos = [5, 200]         
        self.speed = 2
        self.moving_flag = False
        self.bullets = []
        self.magazine = 5
        self.magazine_count = 0
        pyxel.run(self.update, self.draw)

新しくいくつか変数を作成しました。bulletsはBulletクラスで作ったインスタンスを格納するためのもの。magazinemagazine_countは自機が弾を撃つ際の挙動制御用です。具体的にはむやみに連射できないようにして「船っぽさ」を出そうという試みです。船の大砲があんまりにも連射できるのは、ちょっと雰囲気出ないですからね。

    def update(self):
        #ここで毎フレームの更新作業をします
        #弾の装填部分######################
        self.magazine_count += 1
        if self.magazine_count > 30:
            self.magazine_count = 0
            if self.magazine < 5:
                self.magazine += 1


        #コントロール部分###################
        #移動部分
        if pyxel.btn(pyxel.KEY_RIGHT):
            if self.player_pos[0] + self.speed < 160 - 16:
                self.player_pos[0] += self.speed
                self.moving_flag = True
            
        elif pyxel.btn(pyxel.KEY_LEFT):
            if self.player_pos[0] - self.speed > 0:
                self.player_pos[0] -= self.speed
                self.moving_flag = True

        else:
            self.moving_flag = False

        #攻撃部分
        if pyxel.btnp(pyxel.KEY_SPACE):
            #斬弾がある場合は発射
            if self.magazine > 0:
                self.magazine -= 1
                self.bullets.append(Bullet(self.player_pos[0] + 8, self.player_pos[1]))
 
        #プレイヤーの弾の更新部分#########################
        for b in self.bullets:
            b.update()
            if b.y < 0:
                self.bullets.remove(b)

それでは先ほど作った変数を利用してupdate関数を修正していきましょう。
まずは、弾の装填部分を追加。ここでは毎フレームmagazine_countを+1して>30の条件に入ったら、弾を装填します。弾の最大数は5です。処理終わりにはmagazine_countをリセットしておきます。
次にコントロール部分に攻撃ボタンを追加。今回はスペースキーを割り当てます。スペースキーが押下された際に、装填された弾があれば(magazineが1以上ならば)bulletsにBulletクラスから作成したインスタンスを格納します。
作成したBulletインスタンスはfor文で取り出してupdateのメソッドを呼び出します。画面の上部まで到達したら(Bulletインスタンスのy座標が<0になったら)removeで削除します。

    def draw(self):
        #ここで毎フレームの描画作業をします
        pyxel.cls(1)        
        #プレイヤーの描画
        if self.moving_flag == True:
            pyxel.blt(self.player_pos[0], self.player_pos[1], 0, 16, 0, 16, 16, 14)
        else:
            pyxel.blt(self.player_pos[0], self.player_pos[1], 0, 0, 0, 16, 16, 14)

        #残弾表記部分の描画
        pyxel.rect(0, 228, 160, 22, 0)
        pyxel.rectb(0, 228, 160, 22, 7)
        for i in range(5):
            if i+1 > self.magazine:
                pyxel.blt(5 + 8*i, 232, 0, 0, 16, 8, 16, 14)
            else:
                pyxel.blt(5 + 8*i, 232, 0, 8, 16, 8, 16, 14)

        #プレイヤーの弾の描画
        for b in self.bullets:
            b.draw()

せっかく残弾の概念を作ったのでそれが分かるようなアイコンを表示してみます。

▲大砲のアイコン。
発射可能な場合は口を赤くした方を表示させます。

また、update関数で作成したBulletインスタンスについてfor文で取り出してdrawのメソッドを呼び出します。

ここまでを動かしてみましょう。

▲プレイヤーが弾を撃てるところまではできました。

さあ、あとちょっとで完成です。
長くなってきたので続きは後編のnoteに分けようと思います。
後編では「敵の作成」「敵の移動ロジック」「敵の攻撃ロジック」「当たり判定」を作成し、シューティングゲームとして遊べるようにしようと思います。

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

※有料エリアですが、特に何もありません。設定しているだけですので、お気に召しましたら購入いただけると嬉しいです。

ここから先は

28字

¥ 100

期間限定!Amazon Payで支払うと抽選で
Amazonギフトカード5,000円分が当たる

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

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