前回の記事ではゴール判定と得点表示を追加しました。
今回はそのコードにスタート画面、反射処理、フィールドのを追加したので記載します。
動作デモ
追加・変更点
-
スタート画面の追加:
ゲームが開始されていない状態でのスタート画面を追加しました。
プレイヤーはEnterキーを押すことでゲームを開始できます。 -
ボールの反射処理の追加:
ボールが壁やパドルに衝突したときの反射処理を追加しました。 -
フィールドの追加:
ゲームの中央にフィールド(線)を追加しました。
1. スタート画面の追加
ゲームの状態を管理するGameState
型を定義し、Starting
状態を追加してゲームが開始されていないときの振る舞いを制御します。
type GameState int
const (
Starting GameState = iota // ゲームが開始されていない状態
Playing // ゲームが進行中の状態
Resetting // ボールがゴールした後、リセット中の状態
)
ゲームのデータを管理するGame
構造体に、スタート画面に関する変数を追加します。
type Game struct {
state GameState // ゲームの現在の状態
textBlinkTicker int // スタートメッセージの点滅用タイマー
showStartMessage bool // スタートメッセージを表示するかのフラグ
}
ゲームのロジックを更新するUpdate
メソッドに、ゲームがStarting
状態のときの処理を追加します。この処理では、スタートメッセージが点滅し、プレイヤーがEnterキーを押すとゲームが開始されます。
case Starting:
// ゲーム開始前の処理
g.textBlinkTicker++
if g.textBlinkTicker >= 30 {
// スタートメッセージの点滅を切り替え
g.showStartMessage = !g.showStartMessage
g.textBlinkTicker = 0
}
if ebiten.IsKeyPressed(ebiten.KeyEnter) {
// Enterキーが押されたらゲームを開始
g.state = Playing
g.ballPositionX = screenWidth / 2
g.ballPositionY = screenHeight / 2
}
2. ボールの反射処理の追加
ボールが壁やパドルに衝突したときに、ボールが反射する処理を追加します。
// ボールが左の壁に触れた場合の処理
if g.ballPositionX < 0 {
g.ballDX = -g.ballDX
g.ballPositionX = 0
g.player2Score++
g.state = Resetting
} else if g.ballPositionX > screenWidth-ballSize {
// ボールが右の壁に触れた場合の処理
g.ballDX = -g.ballDX
g.ballPositionX = screenWidth - ballSize
g.player1Score++
g.state = Resetting
}
// ボールが上または下の壁に触れた場合の処理
if g.ballPositionY < 0 {
g.ballDY = -g.ballDY
g.ballPositionY = 0
} else if g.ballPositionY > screenHeight-ballSize {
g.ballDY = -g.ballDY
g.ballPositionY = screenHeight - ballSize
}
3. フィールドの追加
ゲームの中央にフィールド(線)を追加しました。
// 画面の中央に縦線を描画
for i := 0; i < screenHeight; i += 30 {
ebitenutil.DrawRect(screen, screenWidth/2-1, float64(i), 2, 20, color.White)
}
まとめ
これらの変更により、Pongに近づいてきて楽しくなってきました。
ここからも小さな箇所でもアップデートを続けていこうと思います。
前回