Pyxel タイトル画面とゲーム画面を作る

ゲームの進行管理 開始から終了まで

 ゲームに「タイトル画面 → ゲームのプレイ → ステージクリアもしくはゲームオーバー」という一連の流れを組み込む例を紹介します。

 題材は以前の記事で作成したアクションゲームを流用します。
 ※一連の記事は Python向けレトロゲームエンジン「Pyxel(ピクセル)」でのゲーム制作を通したプログラミングの練習を目的としています。

作成したい内容

 開始から終了までで必要になりそうな項目や,やりたいことを挙げてみます。
(A)タイトル画面とクリア画面に読み込んだ画像を表示する。
(B)Pyxelのタイルマップを使ってステージにゴールを設定し,ゴール到着でステージのクリアとする。
(C)用意された複数のステージをクリアしたらゲームクリアとする。
(D)ステージ上に触れてはいけないタイルを配置して,接触したらゲームオーバーとする。

 図に描いて全体の流れを考えてみましょう。
 

 右側の吹き出しは「ゲームプレイ」の中での流れです。(D)のゲームオーバー時にはリトライを選択できるようにしてステージの最初に戻すことにします。
 

実装方法の検討

 以下にユーザに見える振る舞いを図示しました。(図中の●は開始を表しています。状態遷移と画面遷移が混ざったような書き方ですが,雰囲気はわかるかと思います)
 

 図から「タイトル画面」や「ステージクリア表示」など6個の状態があることがわかります。Pyxelアプリケーションでは,update()関数とdraw()関数が繰り返し実行されるので,今どの状態かがわかれば処理内容を分岐させることができそうです。

 以下の表のように各状態に「シーン番号」をつけて,それぞれの状態での更新(update()関数)と描画(draw()関数)の内容を検討します。
 
 ※Escキーを押したときの終了動作は,Pyxelアプリケーションのデフォルト機能で行えるので作成不要です。

リソースファイルの作成

 イメージバンク(0)に,キャラクターと各タイルを作成しました。(前回記事で作成した忍者のリソースと位置が同じなので,そのリソースをベースにしてもよいです)
 

 タイルマップエディタでステージを3つ作成しました。
・タイルマップ(0)にステージ2つ(ステージが上下に接しないように離して作成しました) 

・タイルマップ(1)に3つ目のステージを作成 

・タイルマップの0と1の編集切り替えはエディタ下部のボタンから行います。
  

タイトル画面と終了画面用の画像ファイル作成

 EDGE「高機能ドット絵エディタ EDGE | TAKABO SOFTWindows用ソフト)を使用して画像を作成しました。
 
 256×256のサイズで,左側上半分をタイトル画面,下半分を終了画面に使用します。

 

ソースコードの例

 ソースコードの内容はこちらのGoogleドキュメントを参照してください。
 Pyxel_アクションゲーム - Google ドライブ
 list07_01 ~ list07_04 が本記事の内容です。
 リソースファイルは penguin.pyxres,penguin.png です。

シーンの切り替え

 シーンの切り替え処理の枠組みを作成してみます。タイトル画面でスペースキーを押すとステージが表示され,上キーを押すとゲームオーバー,下キーを押すとステージクリアで終了画面へと遷移するテストプログラムです。

 【ソースコード】はこちらのリンクから確認してください(Googleドキュメント)
list07_01.py 抜粋

# シーン番号の定義
SNO_TITLE    = 0
SNO_STAGESET = 10
SNO_PLAY     = 11
SNO_SFINISH  = 12
SNO_GAMEOVER = 13
SNO_END      = 20

scene = SNO_TITLE   # ゲームの進行を管理する変数
tmr = 0             # シーン内でカウントするタイマー変数

def update():
    global scene,tmr,stage
    tmr = tmr + 1           # タイマー変数更新
    
    if scene == SNO_TITLE:
        # 初期化処理
        stage = 0
        if pyxel.btnp(pyxel.KEY_SPACE):
            scene = SNO_STAGESET
            tmr = 0
    elif scene == SNO_STAGESET:
        # ステージの初期化処理
        if tmr == 1
            scene = SNO_PLAY
            tmr = 0
    elif scene == SNO_PLAY:
        # ゲームプレイのメインの処理 ユーザー操作
    elif scene == SNO_SFINISH:
         # ステージクリア
    elif scene == SNO_GAMEOVER:
        # リトライ確認
    else:
        # ゲームクリア画面
        
    return

def draw():

    if scene == SNO_TITLE:
        # タイトル用の画像を表示
    elif scene == SNO_PLAY or scene == SNO_SFINISH or scene == SNO_GAMEOVER:
        # リソースファイルのタイルマップを表示
    elif scene == SNO_END:
        # エンド用の画像を表示

    # シーン確認用コード
    pyxel.text(1,1,"SCENE="+str(scene),7)
    return

実行結果
 
・シーン番号を変数に代入して判別しやすくする(定数として扱う)
 コード例中の SNO_TITLE = 0 や SNO_STAGESET = 10 の記述についての説明です。
 6個のシーン番号は今回のゲームに合わせて数値に意味を持たせていますが,10とか11のように数値のままだとどのシーンのことか判別しにくくなります。そこで,プログラムを作る人にわかりやすくするため変数に代入して,変数名でコードを記述できるようにしています。
 処理中に変更しない値のことを「定数(ていすう)」とよび,プログラミング言語によっては定数を定義する文法が用意されていることもあります。Pythonでは特に言語のルールとしてはありませんが,一般的には変数名を大文字で記述しておくと「これは定数ですよ」とコードを読む人に伝わります。
 繰り返しになりますが,ソースコードに数値が直接記述されるよりも,変数名で書かれていると何を処理しているのか理解しやすくなります。プログラム作成の際は意識してみてください。

・ゲームの進行を管理する変数 scene
 変数 scene に現在のシーン番号を代入します。scene の値をif文で判定して処理を分岐させています。それぞれのシーンの処理で scene の値を更新することで次のシーンへ切り替えができます。

・タイマー変数 tmr
 シーン切り替え時に0にして,update()関数のはじめに値を1ずつ増やしています。シーンの中で最初に一度だけ処理をしたいときや,シーンが始まって何秒後かに処理をしたいことがある場合に利用する仕組みです。BGM再生開始などもtmr==1の条件式内で処理をすれば初回のみ行うことができます。

 
<「pyxel.colors[3] = 0x19C5FA # 背景塗りつぶしに使う薄い青色」について>
 Pyxelでは色の指定を表示色パレットの色番号で指定しますが,この処理はその表示色を任意の色に変更するためのものです。
 詳細はPyxel カラーパレットの変更 - 勉強ボックス管理者ブログを参照してください。

 

ステージの切り替え

 タイトル画面でスペースキーを押すとステージが表示され,上キーを押すとゲームオーバー,下キーを押すとステージクリア,3ステージクリアでエンド画面と遷移するテストプログラムです。

 【ソースコード】はこちらのリンクから確認してください(Googleドキュメント)
list07_02.py 抜粋

# ステージデータ 下記タプルのリスト
#               0 タイルマップ番号(0-7)
#               1 ステージ左上 x座標
#               2 ステージ左上 y座標
#               3 ステージ幅
#               4 ステージ高さ
stagedata = [(0, 0,    0, 128*1,128),
             (0, 0,128*2, 128*2,128),
             (1, 0,    0, 128*3,128)]
stage = 0   # 現在のステージ番号(ステージデータ配列のインデックス)

・ステージデータ
 ステージごとのタイルマップの番号,座標,サイズをリスト(配列)にして,配列を参照するときのインデックスを変更することでステージを切り替えます。
 (必要があればプレイヤーキャラクターのスタート座標もデータに加えてください)

 

    elif scene == SNO_SFINISH:
        # 3秒経過後にステージ更新(1秒30フレームなので3倍待つ)
        if 30*3 < tmr :
            stage += 1      # 次のステージに変更 
            if stage < len(stagedata):
                scene = SNO_STAGESET
                tmr = 0
            else:
                # 最後のステージをクリアしたのでエンド画面へ
                scene = SNO_END
                tmr = 0

・ステージクリアの表示を3秒間行う制御
 tmr 変数を使って時間判定を行っている箇所です。Pyxelの既定値では1秒間に30回update()関数が呼ばれるので,0で初期化したtmrが30になれば1秒経過と考えて90を超えたときに3秒経過と判断しています。(厳密に時間を計りたいときはPythonのtimeモジュール等を使用するとよい)

・stage += 1
 stage = stage + 1 と同じ結果が得られます。(stage変数の値に1を加えた値を,stage変数に代入する)
 Pythonの代入演算子の書き方で,一見奇妙ですがすっきりと記述できるのでプログラミングにおいてよく使われます。

演算子意味
+= a += b a = a + b
-= a -= b a = a - b
*= a *= b a = a * b
/= a /= b a = a / b
 ※ ループカウンタなどで「 i += 1 」で i を1ずつ増やす記述が特によく出てくるので慣れておきましょう。

 

ゲーム初期化,ステージ初期化の処理追加

 各グローバル変数の初期化と,テキスト表示位置を画面スクロールに対応させます。
 【ソースコード】はこちらのリンクから確認してください(Googleドキュメント)
list07_03.py 抜粋

def setstage():
    global x,y,scroll_x,scroll_y,stage_tm,stage_x,stage_y,stage_width,stage_height,score_tmp

    stage_tm = stagedata[stage][0]
    scroll_x = stagedata[stage][1]
    scroll_y = stagedata[stage][2]
    x = scroll_x + 8
    y = scroll_y + 100
    stage_x = scroll_x
    stage_y = scroll_y
    stage_width = scroll_x + stagedata[stage][3]
    stage_height = scroll_y + stagedata[stage][4]
    score_tmp = 0

    return

・setstage()関数で,前の手順で用意したステージデータをグローバル変数に代入しています。
x,yがキャラクターの座標なので,例では毎ステージ画面左下あたりからスタートします。

 

キャラクター移動とスクロール処理の移植

 ゲーム画面でのユーザー操作によるアクション部分の処理を,前回記事のソースコード list06_02.py から移植します。
 【ソースコード】はこちらのリンクから確認してください(Googleドキュメント)
list07_04.py 抜粋

def restore_tile():
    global item_list
    # 得点アイテムタイルを復帰
    for xi,yi,tile in item_list:
        pyxel.tilemap(stage_tm).pset(xi,yi,tile)
    item_list.clear()  # リストを空にする
    return

setstage()関数ステージクリア/リトライ/リタイア時に追加した「得点アイテムの復帰処理」は,プレイ中に消去したアイテムのタイルマップをリトライ時に再表示するためのものです。(ステージのタイルマップ全体を取り出しておいてリトライ時に復帰させる方法でも良いかと思います)

 ※2022-12-25 20:00 追記 記事作成時に公開したlist07_04.pyのコードにバグがありました。(全ステージクリア後に二周目を開始すると一周目で消したアイテムが消えたままになる) ステージクリア/リトライ/リタイア時に復帰処理を呼び出すようにコード例を修正しました。


 以上でアクションゲームに一連の流れを組み込むコードの完成です。

  

Webブラウザでのデモ画面

 (遊び方)スペースキーを押してゲーム開始。左右方向キーで移動。スペースキーでジャンプ。黄色い旗のゴールを目指します。
 ゲームオーバー時は Yキーでリトライ,Nキーでタイトル画面に戻ります。
 http://benkyoubox.starfree.jp/100_game/10_pyxel/penguin.html

 


【参考文献】
 廣瀬 豪(2022)『7大ゲームの作り方を完全マスター! ゲームアルゴリズムまるごと図鑑』 技術評論社

 本記事は上記書籍のゲーム進行管理の内容を参考に作成しました。

【使用ツール】
 記事中の遷移図は Diagram Software and Flowchart Maker で作成しました。
 使い方はアプリ上部メニュー「ヘルプ」-「クイックスタートビデオ」を参照してください。

 

関連記事

 kinutani.hateblo.jp