Phaser4入門【Phaser4基礎編①】生成AIへの指示
A子
それじゃ、記念すべき最初のゲームは何にしようか?
C菜
オーソドックスに「ブロック崩し」とかで良いんじゃないでしょうか~?
A子
あ、でも一口に「生成AI」と言っても、色々あるよね
どれを使えば良いんだろ?
B美
|
・OpenAIの「ChatGPT」
・Googleの「Gemini」 ・Microsoftの「Copilot」 ・Anthropicの「Claude」 |
…ってところが有名どころかしら
(画像生成AIの分野だったらもっといっぱいあるけど…)
C菜
B美
まぁ、プログラミングに関して最も良いのは、(有料の)「Claude Code」という噂だけどね
(使ったことがないから、よく分からん(苦笑))
A子
(もちろん、無料版だけどさ)
B美
あ、「Claude」では無料会員登録するときにWindowsアプリを一緒にインストールすると便利よ
(わざわざログインする手間が省けるからね)
C菜
それでは使えるように準備します~
C菜
それではさっそく「Claude」に対して、以下のプロンプトを入れてみますね~
|
Phaser4を使ってブロック崩しのゲームを作りたい。
プロジェクト作成ツールを起動して、すでにひな形は作っている(「Web Bundler」→「vite」→「Complete」→「TypeScript」)。 「MainMenu.ts」や「Game.ts」に記述する内容を教えてください。 |
A子
(早っ!)
B美
まず新規のプロジェクトを作ることからね
C菜
コマンドプロンプトを開いてから、以下のコードを入力しますね~
|
> cd /d d:\phaser[Enter]
> npm create @phaserjs/game@latest[Enter] |
| 項目 | 値 |
|---|---|
| プロジェクト名 | block |
| 1番目の選択肢 | Web Bundler |
| 2番目の選択肢 | vite |
| 3番目の選択肢 | Complete |
| 4番目の選択肢 | TypeScript |
上記はAIに指示した内容と一致させてます~
|
> cd block[Enter]
> npm install[Enter] |
で完了です~
A子
んじゃ、AIに教えてもらった通り、コード修正をやっていこう
まずは「D:\phaser\block\src\game\main.ts」だね
|
const config: Phaser.Types.Core.GameConfig = {
type: AUTO, width: 1024, height: 768, parent: 'game-container', backgroundColor: '#028af8', scene: [ Boot, Preloader, MainMenu, MainGame, GameOver ] }; |
の部分を
|
const config: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO, width: 1024, height: 768, parent: 'game-container', backgroundColor: '#028af8', scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }, physics: { default: 'arcade', arcade: { debug: false } }, scene: [Boot, Preloader, MainMenu, MainGame, GameOver] }; |
に変えたよ
(赤字部分が追加した箇所)
C菜
ここは丸ごと書き換えちゃいますね~
|
import { Scene } from 'phaser';
export class MainMenu extends Scene { constructor() { super('MainMenu'); } create() { this.cameras.main.setBackgroundColor(0x111827); this.add.text(512, 280, 'BLOCK BREAKER', { fontFamily: 'Arial Black', fontSize: 72, color: '#ffffff' }).setOrigin(0.5); const guide = this.add.text(512, 450, 'クリック または スペースキーでスタート', { fontFamily: 'sans-serif', fontSize: 28, color: '#cbd5e1' }).setOrigin(0.5); // 点滅演出 this.tweens.add({ targets: guide, alpha: 0.2, duration: 700, yoyo: true, repeat: -1 }); this.input.once('pointerdown', () => this.scene.start('Game')); this.input.keyboard?.once('keydown-SPACE', () => this.scene.start('Game')); } } |
教えてもらったコードをコピペしただけですけどね~
A子
|
import { Scene } from 'phaser';
const WIDTH = 1024; const HEIGHT = 768; const PADDLE_W = 140; const PADDLE_H = 20; const BALL_R = 10; const BALL_SPEED = 450; export class Game extends Scene { private paddle!: Phaser.GameObjects.Rectangle; private ball!: Phaser.GameObjects.Arc; private ballBody!: Phaser.Physics.Arcade.Body; private bricks!: Phaser.Physics.Arcade.StaticGroup; private cursors?: Phaser.Types.Input.Keyboard.CursorKeys; private scoreText!: Phaser.GameObjects.Text; private livesText!: Phaser.GameObjects.Text; private score = 0; private lives = 3; private launched = false; private gameEnded = false; constructor() { super('Game'); } create() { // シーン再開時の状態リセット this.score = 0; this.lives = 3; this.launched = false; this.gameEnded = false; this.cameras.main.setBackgroundColor(0x111827); // 下の壁だけ衝突なし(落下判定のため) this.physics.world.setBoundsCollision(true, true, true, false); // パドル this.paddle = this.add.rectangle(WIDTH / 2, HEIGHT - 60, PADDLE_W, PADDLE_H, 0xffffff); this.physics.add.existing(this.paddle); const paddleBody = this.paddle.body as Phaser.Physics.Arcade.Body; paddleBody.setImmovable(true); paddleBody.setCollideWorldBounds(true); // ボール this.ball = this.add.circle(WIDTH / 2, HEIGHT - 60 - PADDLE_H / 2 - BALL_R, BALL_R, 0xffd54a); this.physics.add.existing(this.ball); this.ballBody = this.ball.body as Phaser.Physics.Arcade.Body; this.ballBody.setCircle(BALL_R); this.ballBody.setBounce(1, 1); this.ballBody.setCollideWorldBounds(true); // ブロック this.createBricks(); // 衝突 this.physics.add.collider(this.ball, this.paddle, () => this.hitPaddle()); this.physics.add.collider(this.ball, this.bricks, (_ball, brick) => this.hitBrick(brick as Phaser.GameObjects.Rectangle) ); // UI this.scoreText = this.add.text(20, 16, 'SCORE: 0', { fontFamily: 'Arial', fontSize: 24, color: '#ffffff' }); this.livesText = this.add.text(WIDTH - 20, 16, 'LIVES: 3', { fontFamily: 'Arial', fontSize: 24, color: '#ffffff' }).setOrigin(1, 0); // 入力 this.cursors = this.input.keyboard?.createCursorKeys(); this.input.on('pointermove', (p: Phaser.Input.Pointer) => { if (!this.gameEnded) this.movePaddleTo(p.x); }); this.input.on('pointerdown', () => this.launchBall()); this.input.keyboard?.on('keydown-SPACE', () => this.launchBall()); } update(_time: number, delta: number) { if (this.gameEnded) return; // キーボード操作 if (this.cursors?.left.isDown) { this.movePaddleTo(this.paddle.x - 0.6 * delta); } else if (this.cursors?.right.isDown) { this.movePaddleTo(this.paddle.x + 0.6 * delta); } // 発射前はボールをパドルに追従 if (!this.launched) { this.ball.x = this.paddle.x; this.ball.y = this.paddle.y - PADDLE_H / 2 - BALL_R; this.ballBody.reset(this.ball.x, this.ball.y); } // 落下判定 if (this.launched && this.ball.y > HEIGHT + BALL_R) { this.loseLife(); } } private createBricks() { this.bricks = this.physics.add.staticGroup(); const cols = 10; const rows = 6; const w = 88; const h = 28; const gap = 8; const totalW = cols * w + (cols - 1) * gap; const startX = (WIDTH - totalW) / 2 + w / 2; const startY = 100; const colors = [0xef4444, 0xf97316, 0xeab308, 0x22c55e, 0x3b82f6, 0xa855f7]; for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { const x = startX + c * (w + gap); const y = startY + r * (h + gap); const brick = this.add.rectangle(x, y, w, h, colors[r]); this.bricks.add(brick); } } } private movePaddleTo(x: number) { const half = PADDLE_W / 2; this.paddle.x = Phaser.Math.Clamp(x, half, WIDTH - half); } private launchBall() { if (this.launched || this.gameEnded) return; this.launched = true; this.ballBody.setVelocity(Phaser.Math.Between(-150, 150), -BALL_SPEED); } // パドルのどこに当たったかで反射角を変える private hitPaddle() { const diff = (this.ball.x - this.paddle.x) / (PADDLE_W / 2); // -1 ~ 1 const angle = Phaser.Math.Clamp(diff, -1, 1) * Phaser.Math.DegToRad(60); this.ballBody.setVelocity(Math.sin(angle) * BALL_SPEED, -Math.cos(angle) * BALL_SPEED); } private hitBrick(brick: Phaser.GameObjects.Rectangle) { brick.destroy(); this.score += 10; this.scoreText.setText(`SCORE: ${this.score}`); if (this.bricks.countActive() === 0) { this.endGame(true); } } private loseLife() { this.lives--; this.livesText.setText(`LIVES: ${this.lives}`); if (this.lives <= 0) { this.endGame(false); return; } this.launched = false; this.ballBody.setVelocity(0, 0); } private endGame(cleared: boolean) { this.gameEnded = true; this.ballBody.setVelocity(0, 0); this.add.text(WIDTH / 2, HEIGHT / 2 - 20, cleared ? 'CLEAR!' : 'GAME OVER', { fontFamily: 'Arial Black', fontSize: 72, color: '#ffffff' }).setOrigin(0.5); this.add.text(WIDTH / 2, HEIGHT / 2 + 50, 'クリックでタイトルへ', { fontFamily: 'sans-serif', fontSize: 28, color: '#cbd5e1' }).setOrigin(0.5); // 連打で即遷移しないよう少し待つ this.time.delayedCall(500, () => { this.input.once('pointerdown', () => this.scene.start('MainMenu')); }); } } |
めっちゃ長いけど、全部コピペね(苦笑)
(「Claude」さんの提示したコードそのまま)
C菜
まずは「管理者モード」でコマンドプロンプトを開いて~
(Windowsの検索欄に以下を入力)
| cmd[Ctrl]+[Shift]+[Enter] |
以下のコマンドを打ち込んだら
|
> cd /d d:\phaser\block[Enter]
> npm run dev[Enter] |
ブラウザで確認してみます~
| http://localhost:8080 |
↓
A子
画面が真っ暗なまま、何も出ないよ
B美
きちんと教えてくれるから…
A子
…って言われたよ
んで、それ(エラー内容)をAIに伝えたら、すぐに解決策を教えてくれた
C菜
A子
まず「D:\phaser\block\src\game\main.ts」だけど
| 行番号 | 変更前 | 変更後 |
|---|---|---|
| 5 | import { AUTO, Game } from 'phaser'; | import { AUTO, Game, Scale, Types } from 'phaser'; |
| 11 | type: Phaser.AUTO, | type: AUTO, |
| 17 | mode: Phaser.Scale.FIT, | mode: Scale.FIT, |
| 18 | autoCenter: Phaser.Scale.CENTER_BOTH | autoCenter: Scale.CENTER_BOTH |
…って感じで「Phaser.」を取れば良いらしい
あと「D:\phaser\block\src\game\scenes\Game.ts」のほうはもっと簡単で、ファイルの先頭行に以下の一文を入れるだけだった
(本当は「main.ts」と同様に「Phaser.」を削除したほうが良いみたいだけど、この一文があれば削除しなくても良いってことみたい)
| import * as Phaser from 'phaser'; |
C菜
うわぁ、出ました~
すごいです~
メッセージが文字化けしてますけど、マウスでもキーボード(のカーソルキー)でも遊べましたよ~
↓
↓
A子
文字コードを「シフトJIS」じゃなくて「UTF-8」にしろってさ
「D:\phaser\block\src\game\scenes\MainMenu.ts」と「D:\phaser\block\src\game\scenes\Game.ts」の二つだけ「UTF-8」に変更したらうまくいったよ
(超簡単!)
C菜
| > npm run build[Enter] |
こちらをクリックしてくださいです~
(クリックすると別ウィンドウで開きます)
B美
次回は、ゲーム性を高める(より面白くする)ための改良を加えていきましょう
A子
しっかし、ほんとに簡単だったよ


