タイトル画像
琴葉茜

さて、一応CPUの動きも出来たことだし、次はスコアの管理と表示をしていこうか!

琴葉葵

スコアの管理も変数を使えば良いんだね!

琴葉茜

ちなみに、ホッケーゲームの点数はどういう時に得点になるかな??

琴葉葵

んー今回の場合だと、左端に当たった場合は「CPU」の得点で、右端に当たった場合は「プレイヤー」の得点だよね

琴葉茜

そうだね!ということで、プレイヤーの得点を「self.score_player」、CPUの得点を「self.score_cpu」として得点の管理をしていこうか!

# 関数「__init__」の「pyxel.run()」の上の行に追加
# スコア初期化
self.score_player = 0
self.score_cpu = 0
琴葉葵

おっけー!それぞれの得点用の変数は準備できたよ!

琴葉茜

次は、得点を表示するための「self.text()」を関数「draw」に追加しよう!

# 関数「draw」の最後に追加
pyxel.text(20, 4, f"YOU: {self.score_player:04}", 11)
pyxel.text(100, 4, f"CPU: {self.score_cpu:04}", 14)
解説画像1
琴葉葵

良い感じ!!ところでちょっと気になったんだけど、Pyxelでは日本語表示はできないの?

琴葉茜

残念ながら非対応なんだよね
だけど、「PyxelUniversalFont」っていう拡張用の外部ライブラリを使えば対応するみたいだから、興味があれば調べてみてね!

琴葉葵

了解!

琴葉茜

そしたら、ここからは得点が増える仕組みを考えよう!まずは、関数「update」の「左右の端にぶつかったときに跳ね返す」プログラムの部分に注目してね

# 関数「update」内
# ウィンドウの端に当たったら反射(跳ね返る)
if self.x <= self.r or self.x >= pyxel.width - self.r: # ここの2行に注目!
    self.vx *= -1
if self.y <= self.r or self.y >= pyxel.height - self.r:
    self.vy *= -1
琴葉葵

...(じーーーーーー)

琴葉茜

前回までは、左右の端にぶつかったら跳ね返すようにしてたから左右の区別なしでif文を作ってたよね

琴葉葵

あ!だけど、今回は左と右で「得点が入る対象」が違うから条件を分けないといけないのか!

琴葉茜

そういうこと!つまり、こんな感じの変更になるよ

# ウィンドウの端に当たったら反射(跳ね返る)
if self.x <= self.r: # 左側が当たった場合はCPUの得点
    self.score_cpu += 1
    self.vx *= -1
elif self.x >= pyxel.width - self.r:
    self.score_player += 1 # 右側が当たった場合はプレイヤーの得点
    self.vx *= -1
if self.y <= self.r or self.y >= pyxel.height - self.r:
    self.vy *= -1
琴葉葵

まぁそんな感じだよね!イメージ通り!

琴葉茜

これで一応得点の管理は完了だけど、せっかくだからもう少しホッケーゲームを改良してみよう!

琴葉茜

まずは得点が入った時にホッケーが真ん中にリセットされるようにしようか

琴葉葵

そうだね!ついでに、リセットのたびに飛んでいく方向が変わると面白いかも!

琴葉茜

オッケー!せっかくだし、リセットの部分を関数にして、プログラムを起動した時の初期化にも対応させよう

琴葉茜

まず、ホッケーをリセットするための関数「reset_hockey」を関数「update」より上の辺りに用意しよう

# ホッケーの初期化
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
琴葉葵

ホッケーの速度「self.vx」「self.vy」は飛んでいく方向をランダムにしたんだね!

琴葉茜

次に、関数「reset_hockey」を適切な箇所に使っていこうか

琴葉葵

関数「__init__」の最初の方のホッケーを初期化している場所と...

def __init__(self):
    pyxel.init(160, 120, title="ホッケーゲーム")
    # ホッケーの座標、速度、半径
    self.x = pyxel.width // 2
    self.y = pyxel.height // 2
    self.vx = 1
    self.vy = 1
    self.r = 4 

↓↓ 変更後 ↓↓

def __init__(self):
    pyxel.init(160, 120, title="ホッケーゲーム")
    # ホッケーの初期化
    self.reset_hockey()
琴葉葵

関数「update」の中の前回作った左右の端に来たら跳ね返す場所だね!

# ウィンドウの端に当たったら反射(跳ね返る)
if self.x <= self.r: # 左側が当たった場合はCPUの得点
    self.score_cpu += 1
    self.vx *= -1
elif self.x >= pyxel.width - self.r:
    self.score_player += 1 # 右側が当たった場合はプレイヤーの得点
    self.vx *= -1
if self.y <= self.r or self.y >= pyxel.height - self.r:
    self.vy *= -1

↓↓ 変更後 ↓↓

# ウィンドウの端に当たったら反射(跳ね返る)
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
琴葉茜

バッチリ!早速動かしてみようか!

琴葉葵

うん、ちゃんと左右の端に当たった時にホッケーが真ん中に戻ってるし、最初の動きもランダムになってるね!

琴葉葵

ところでおねーちゃん!リセットを作る時に思いついたんだけど、ゲームをもう少し面白くするために、「ホッケーをバーで跳ね返す度に速くしていく」ってどうかな??

琴葉茜

お!確かに、試合が長引くと難しくなっていく仕組みは緊張感が出てくるし面白そうだね!

琴葉葵

ふふん!じゃあ早速自分で変更してみる!!

琴葉葵

関数「update」の衝突判定をするif文の中の「abs(self.vx)」が「1」増えるようにすれば良いから、こうかな??

# 関数「update」の中
# プレイヤーバーの衝突判定
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) # ←ココを変更
琴葉葵

「1」はさすがに速すぎたかも...!!

琴葉茜

増やす量は丁度良い感じに修正すれば良いとして、バッチリだね!!
そんな感じで、自分なりにどうしたら面白くなるか考えて試してみることが大事だから、どんどんチャレンジしてみようね!!

この時点での全体のプログラム

import pyxel
import random

class App:
    def __init__(self):
        pyxel.init(160, 120, title="ホッケーゲーム")
        # ホッケーの初期化
        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

        pyxel.run(self.update, self.draw)

    # ホッケーの初期化
    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):
        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.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)

    def draw(self):
        pyxel.cls(0)
        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)

App()