おねーちゃん!ホッケーゲームは良い感じになってきてるけど、プログラムを起動すると突然ゲームが始まるのは良くないと思うんだよね
ほとんどのゲームはちゃんとタイトル画面があって、そこから自分の好きなタイミングでゲームを始められるって感じだし
たしかにそうだね!
そしたら今回は「タイトル画面」を作って、画面を切り替える処理を作っていこうか!
よろしくお願いします!
まずは、「タイトル画面」「ゲーム画面」の切り替えを管理するための変数「self.mode」を用意しよう
ついでに関数「__init__」内で処理していたホッケーやバーの初期化をまとめて関数「reset_game」にしておくと管理が楽になるからオススメだよ!
class App:
def __init__(self):
pyxel.init(160, 120, title="ホッケーゲーム")
self.mode = "TITLE" # "PLAY" と切り替える
self.reset_game()
pyxel.run(self.update, self.draw)
# ゲームの初期化
def reset_game(self):
# ホッケーの初期化
self.reset_hockey()
# プレイヤー(左側)のバー
self.bar_height = 20 # バーの高さ
self.bar_width = 5 # バーの幅
self.bar_x = 10 # プレイヤーのx座標
self.bar_y = (pyxel.height - self.bar_height) // 2 # プレイヤーのy座標
# CPU(右側)のバー
self.cpu_x = pyxel.width - 10 - self.bar_width # CPUのx座標
self.cpu_y = (pyxel.height - self.bar_height) // 2 # CPUのy座標
# スコア用
self.score_player = 0
self.score_cpu = 0
関数「__init__」の中がスッキリしたね!
変数「self.mode」の中身については、タイトル画面「"TITLE"」とプレイ画面「"PLAY"」で管理するって覚えておいてね
次に、関数「update」と「draw」内で作ってきたプログラムを、「self.mode」が「"PLAY"」の場合だけ動くようにしよう
def update(self):
if self.mode == "PLAY": # if文の追加
self.x += self.vx
self.y += self.vy
# 以下略
def draw(self):
pyxel.cls(0)
if self.mode == "PLAY": # if文の追加
pyxel.circ(self.x, self.y, self.r, 8)
pyxel.rect(self.bar_x, self.bar_y, self.bar_width, self.bar_height, 11)
pyxel.rect(self.cpu_x, self.cpu_y, self.bar_width, self.bar_height, 14)
pyxel.text(20, 4, f"YOU: {self.score_player:04}", 11)
pyxel.text(100, 4, f"CPU: {self.score_cpu:04}", 14)
インデントの処理を間違えないように注意しないとだね!!
この変更の後にそのまま動かすと、背景だけのウィンドウになるはずだよ
まだ「"TITLE"」の時の処理を作っていないもんね!
ということで次は、タイトル画面を作っていこうか!
今回はタイトル画面中に「スペースキー」を押すとプレイ画面に切り替わるようにしよう
まずは関数「draw」の方から
シンプルに真ん中に文字が表示されるようにしよう
# 関数「draw」の最後に追加
elif self.mode == "TITLE":
pyxel.text(40, 40, "--- HOCKEY GAME ---", 7)
pyxel.text(40, 80, "PRESS SPACE TO START", 6)
ちゃんと文字が表示されてる!
次に、関数「update」の方でスペースキーを押したら「self.mode」が「"PLAY"」に変化するようにしよう
# 関数「update」の最後に追加
elif self.mode == "TITLE":
# スペースで開始
if pyxel.btnp(pyxel.KEY_SPACE):
self.reset_game()
self.mode = "PLAY"
あれ?よく見たらキーボード入力のプログラムが「pyxel.btn」じゃなくて「pyxel.btnp」になってる
よく観察してるね!実はキーボード入力に関するPyxelの関数には「pyxel.btn()」「pyxel.btnp()」「pyxel.btnr()」の3種類があるんだよね
| 関数名 | 意味 | 主な使い方 | 使う場面の例 |
|---|---|---|---|
pyxel.btn(k) |
キーが押されている間ずっと (「押しっぱなし」に反応) |
連続して反応させたいとき | 上下キーでプレイヤーを移動 |
pyxel.btnp(k) |
キーが押された瞬間だけ (「1回押したとき」に反応) |
一度きりの処理 | スタート画面からゲーム開始、選択切り替え |
pyxel.btnr(k) |
キーが離された瞬間だけ (「指を離したとき」に反応) |
特殊・上級向け | 連打防止、チャージ攻撃など |
ほうほう、使い分けは意識しとかないとだね
これでタイトル画面からプレイ画面に切り替え出来るようになったと思うけど、どうかな??
うん!ちゃんとスペースキーを押したら切り替わったよ!
ここまで出来たら、ゲームの終わり方も作りたくなるね
いいね!あとは、ゲームの終了条件とその時の画面も作って、3種類の画面が切り替わるようにしようか!
3つ目の画面と機能もタイトル画面を作った時と同じで「self.mode」の中身がどうなってるかで作れば良いのかな
そのとおり!今回は「self.mode」が「"GAMEOVER"」の時にどちらが勝ったか表示されるようにしようか
ゲームの終了条件と勝敗はひとまず、「どちらかの得点が5点になったらゲーム終了」「得点が5点になった方が勝ち」ってことにしよう
シンプルな条件で良いね!
最初に、終了条件を関数「update」の条件「self.mode == "PLAY"」の時の処理に追加しよう
ゴール判定の処理後に追加するのが無難かな
elif self.mode == "PLAY":
# 途中省略
# ウィンドウの端に当たったらリセット
if self.x <= self.r: # 左側が当たった場合はCPUの得点
self.score_cpu += 1
self.reset_hockey()
elif self.x >= pyxel.width - self.r:
self.score_player += 1 # 右側が当たった場合はプレイヤーの得点
self.reset_hockey()
if self.y <= self.r or self.y >= pyxel.height - self.r:
self.vy *= -1
# 勝利条件チェック
if self.score_player >= 5 or self.score_cpu >= 5:
self.mode = "GAMEOVER"
関数「update」内を変更したついでに、「self.mode」が「"GAMEOVER"」の時にスペースキーを押すとタイトル画面に戻るようにしよう
# 関数「update」の最後に追加
elif self.mode == "GAMEOVER":
if pyxel.btnp(pyxel.KEY_SPACE):
self.mode = "TITLE"
ここの追加はタイトル画面の時と同じだね!
あとはゲーム終了時の文字を表示すればOK
どちらが勝ったかは、得点の大きさで判断すれば良いよ
# 関数「draw」の最後に追加
elif self.mode == "GAMEOVER": # 終了画面
if self.score_player > self.score_cpu:
winner = "YOU"
else:
winner = "CPU"
pyxel.text(60, 40, f"{winner} WIN!!", 10)
pyxel.text(20, 70, "PRESS SPACE TO RETURN TO TITLE", 7)
おおー、いい感じ!
ひとまず、これで画面の移動はバッチリだね!
この時点での全体のプログラム
import pyxel
import random
class App:
def __init__(self):
pyxel.init(160, 120, title="ホッケーゲーム")
self.mode = "TITLE"
self.reset_game()
pyxel.run(self.update, self.draw)
def reset_game(self):
# ホッケーの初期化
self.reset_hockey()
# プレイヤー(左側)のバー
self.bar_height = 20 # バーの高さ
self.bar_width = 5 # バーの幅
self.bar_x = 10 # プレイヤーのx座標
self.bar_y = (pyxel.height - self.bar_height) // 2 # プレイヤーのy座標
# CPU(右側)のバー
self.cpu_x = pyxel.width - 10 - self.bar_width # CPUのx座標
self.cpu_y = (pyxel.height - self.bar_height) // 2 # CPUのy座標
# スコア用
self.score_player = 0
self.score_cpu = 0
# ホッケーの初期化
def reset_hockey(self):
self.x = pyxel.width // 2
self.y = pyxel.height // 2
self.vx = random.choice([-1, 1])
self.vy = random.choice([-1, 1])
self.r = 4
def update(self):
if self.mode == "PLAY": # プレイ画面
self.x += self.vx
self.y += self.vy
# プレイヤーバーの操作(上下キー)
if pyxel.btn(pyxel.KEY_UP):
self.bar_y -= 2
if pyxel.btn(pyxel.KEY_DOWN):
self.bar_y += 2
self.bar_y = max(0, min(pyxel.height - self.bar_height, self.bar_y))
# CPUバーの自動追尾
if random.random() > 0.2: # 80%の確率で追尾
if self.y > self.cpu_y + self.bar_height // 2:
self.cpu_y += 1.2
elif self.y < self.cpu_y + self.bar_height // 2:
self.cpu_y -= 1.2
self.cpu_y = max(0, min(pyxel.height - self.bar_height, self.cpu_y))
# ウィンドウの端に当たったらリセット
if self.x <= self.r: # 左側が当たった場合はCPUの得点
self.score_cpu += 1
self.reset_hockey()
elif self.x >= pyxel.width - self.r:
self.score_player += 1 # 右側が当たった場合はプレイヤーの得点
self.reset_hockey()
if self.y <= self.r or self.y >= pyxel.height - self.r:
self.vy *= -1
# 勝利条件チェック
if self.score_player >= 5 or self.score_cpu >= 5:
self.mode = "GAMEOVER"
# プレイヤーバーの衝突判定
if (
self.x + self.r >= self.bar_x and
self.x - self.r <= self.bar_x + self.bar_width and
self.y + self.r >= self.bar_y and
self.y - self.r <= self.bar_y + self.bar_height
):
self.vx = abs(self.vx) + 1
# CPUの衝突判定
if (
self.x - self.r <= self.cpu_x + self.bar_width and
self.x + self.r >= self.cpu_x and
self.y + self.r >= self.cpu_y and
self.y - self.r <= self.cpu_y + self.bar_height
):
self.vx = -(abs(self.vx) + 1)
elif self.mode == "TITLE": # タイトル画面
# スペースで開始
if pyxel.btnp(pyxel.KEY_SPACE):
self.reset_game()
self.mode = "PLAY"
elif self.mode == "GAMEOVER": # 終了画面
if pyxel.btnp(pyxel.KEY_SPACE):
self.mode = "TITLE"
def draw(self):
pyxel.cls(0)
if self.mode == "PLAY": # プレイ画面
pyxel.circ(self.x, self.y, self.r, 8)
pyxel.rect(self.bar_x, self.bar_y, self.bar_width, self.bar_height, 11)
pyxel.rect(self.cpu_x, self.cpu_y, self.bar_width, self.bar_height, 14)
pyxel.text(20, 4, f"YOU: {self.score_player:04}", 11)
pyxel.text(100, 4, f"CPU: {self.score_cpu:04}", 14)
elif self.mode == "TITLE": # タイトル画面
pyxel.text(40, 40, "--- HOCKEY GAME ---", 7)
pyxel.text(40, 80, "PRESS SPACE TO START", 6)
elif self.mode == "GAMEOVER": # 終了画面
if self.score_player > self.score_cpu:
winner = "YOU"
else:
winner = "CPU"
pyxel.text(60, 40, f"{winner} WIN!!", 10)
pyxel.text(20, 70, "PRESS SPACE TO RETURN TO TITLE", 7)
App()
