初心者でもできた!GPT4を使ってブラウザで動くシューティングゲームを作成した
先日発表されたGPT4を活用することで、全くの初心者でもブラウザで動くシューティングゲームを作成できました。本記事では、ChatGPTとやり取りしながら作成した内容をまとめています。ネットワークエラーに悩まされ少々グダった部分もありますが、そういったやり取りも含めてどのように完成まで至ったか解説します。
ChatGPTでゲーム制作
GPT4が公開
2023年3月14日、OpenAI社の言語モデルの最新バージョンであるGPT4が公開されました。同社が公開するAIチャットボットのChatGPTの有料版を使えば、GPT4が使用できるようになっています。その高機能ぶりが話題になっており、簡単な指示でさまざまな動作が可能になっているようです。ブラウザで動く簡単なゲームが作れるという話もあり、私もさっそく有料版に登録して試してみました。
ChatGPTは、自然な文章を生成するAIであり、専門知識がなくても適切な指示を出すことができます。また、プログラミングやゲーム制作に関する質問に対しても、適切な回答や解決策を提案してくれます。実際に、ChatGPTと対話しながらゲームが完成するまでのプロセスを詳しく紹介します。SNSなどでは数分でゲームができたという話もありますが、私の場合はネットワークエラーに悩まされて少々長くかかりました。詳細は不明ですが、GPT4が公開されて間もないためネットワークが混んでいたのかもしれません。それでも約1時間半で完成できました。
作成したゲーム
この記事で作成するシューティングゲームはスペースインベーダのようなイメージのものです。「プレイヤーが操作するスペースシップを使って、次々と現れるエイリアンを倒す」という設定はありますが、プレイヤーも敵敵のエイリアンも単純な四角形で表されています。敵は左右にゆらゆらと動きならが画面上から下に降りてきます。プレイヤーのスペースシップはキーボードの矢印キーで左右に動くことができ、スペースキーで弾を打ち出します。弾が敵に当たると敵は消滅し、点数が与えられます。
作成に用いたコードはHTML、CSS、JavaScriptです。私自身は本ブログの装飾に多少触ったことがある程度で、ほぼ他サイトを参考にし他コピペで済ませています。ゲーム制作についても全くの未経験です。それでもChatGPTに指示を与えるだけでほぼコーディングせずにゲームが作れました。
ゲームの基本機能の設計
はじめにゲームを作成するのにどのような情報が必要か尋ねました。ただ長文を出力すると途中でネットワークエラーが発生し、リセットされてしまうことが頻発しました。
必要な情報の確認
SNSの情報からブラウザで動くゲームが作れることは分かりましたが、どのような言語で書くのか、どのような情報が必要か分からなかったため、素直にChatGPTに質問しました。
Webブラウザ上で動くシューティングゲームを作ってください。HTMLとCSSだけで作れますか?
作成を依頼するにはどのような情報が必要ですか?
Webブラウザ上で動くシューティングゲームを作成するには、HTMLとCSSだけでは不十分です。JavaScriptも使用する必要があります。HTMLはページの構造を定義し、CSSはスタイルやレイアウトを決定し、JavaScriptは動的な要素やインタラクションを制御します。
ゲームを作成するにあたって、以下の情報が必要です:
- ゲームのコンセプト: ゲームのアイデアや目標を明確に説明してください。例えば、宇宙を舞台にしたシューティングゲームであるなど。
- グラフィックスとアートワーク: ゲーム内で使用するキャラクター、背景、オブジェクトなどのグラフィックデザインを提供してください。これらはCSSやHTML Canvasでスタイリングできます。
- ゲームプレイ: ゲームの操作方法、勝利条件、敵の行動パターン、難易度の設定、得点システムなど、ゲームプレイに関する詳細を決定してください。
- 音楽と効果音: ゲームのバックグラウンドミュージックや効果音を提供してください。これらはJavaScriptとHTMLの<audio>タグを使用して制御できます。
- ゲームの開発期間: ゲームの完成を望む日付を指定してください。開発期間によって、実装できる機能や品質が変わることがあります。
これらの情報が揃ったら、HTML、CSS、JavaScriptを使ってシューティングゲームを作成することができます。また、ゲーム開発にはPhaserやThree.jsなどのJavaScriptライブラリを利用することで、開発効率や品質を向上させることができます。
ゲームの開発期間まで聞かれるとは思いませんでしたが、時間の指定を変えるとゲームの複雑さが変わるのかもしれません。さっさと作りたいので1時間としました。
ありがとうございます。
1と3と4の情報について、案を出してください。2についてはCSSで作成できるような単純なデザインで構いません。5については1時間です。ではお願いします。
了解しました。1時間の制限時間内で作成できるシンプルなシューティングゲームの案を以下に示します。
- ゲームのコンセプト:「シンプル・インベーダー」 – クラシックなスペースインベーダーゲームをモチーフにした、シンプルなシューティングゲーム。プレイヤーは地球を侵略しようとするエイリアンの群れから地球を守ることが目的です。
- ゲームプレイ:
- 操作方法: 左右矢印キーでプレイヤーのスペースシップを動かし、スペースキーで弾を発射します。
- 勝利条件: 画面上の全てのエイリアンを倒すと、プレイヤーの勝利です。
- 敵の行動パターン: エイリアンは左右に移動し、一定の間隔で下に降ります。時間が経つごとに移動速度が速くなります。
- 難易度の設定: ゲームに難易度の設定はありませんが、エイリアンの速度が徐々に上がることで自然と難易度が上がります。
- 得点システム: エイリアンを倒すごとに10ポイント獲得します。全てのエイリアンを倒すと、ボーナスとして残り時間に応じたポイントが加算されます。
- 音楽と効果音:
- バックグラウンドミュージック: ループするシンプルな8ビット音楽を使用します。ただし、制限時間内で作成する場合は、音楽を省略することも検討してください。
- 効果音: エイリアンが倒されたとき、弾を発射したとき、プレイヤーがエイリアンに当たったときにそれぞれ短い効果音を再生します。同様に、制限時間内で作成する場合は、効果音を省略することも検討してください。
この案に基づいて、CSSで単純なデザインを作成し、JavaScriptを使用してゲームロジックを実装できます。1時間の制限時間内でできるだけ多くの要素を実装し、シンプルながら楽しめるシューティングゲーム
上のChatGPTの回答は途中で途切れています。GPT4の場合も回答が長文だとこのように途切れてしまいますが、「続けてください」と返すとその続きから書いてくれます。しかしネットワークエラーで途切れた場合は、続けて質問することができず、やり取りが完全に無かったことになるようです。それに気づかず作成を進めたので、ここでのやり取りを忘れられてしまい、このあと作成したゲームのルールがおかしくなりました。
ゲームのプログラムの作成を依頼
実際にゲームのプログラムを作成してください。
すると、以下のようにプログラムを出力します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Shooting Game</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #222;
}
canvas {
border: 1px solid #ffffff;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="480" height="320"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const player = {
x: canvas.width / 2,
y: canvas.height - 30,
size: 10,
speed: 4,
bullets: []
};
const enemies = [];
let enemySpawnCounter = 0;
function drawPlayer() {
ctx.fillStyle = 'white';
ctx.fillRect(player.x, player.y, player.size, player.size);
}
function drawEnemies() {
ctx.fillStyle = 'red';
for (const enemy of enemies) {
ctx.fillRect(enemy.x, enemy.y, enemy.size, enemy.size);
}
}
function drawBullets() {
ctx.fillStyle = 'green';
for (const bullet of player.bullets) {
ctx.fillRect(bullet.x, bullet.y, bullet.size, bullet.size);
}
}
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPlayer();
drawEnemies();
drawBullets();
player.x += player.speed;
if (player.x < 0 || player.x > canvas.width - player.size) {
player.speed = -player.speed;
}
for (const bullet of player.bullets) {
bullet.y -= bullet.speed;
}
player.bullets = player.bullets.filter(bullet => bullet.y > 0);
enemySpawnCounter++;
if (enemySpawnCounter >= 30) {
enemies.push({
x: Math.random() * (canvas.width - 20),
y: 10,
size: 20,
speed: 2
});
enemySpawnCounter = 0;
}
for (const enemy of enemies) {
enemy.y += enemy.speed;
}
enemies = enemies.filter(enemy => enemy.y < canvas.height);
requestAnimationFrame(update);
}
canvas.addEventListener('click', (e) => {
player.bullets.push({
x: player.x + player.size / 2 - 2,
y: player.y - 10,
size: 4,
speed: 6
});
});
update();
</script>
</body>
</html>
このコードをテキストファイルに保存し、「.html」の拡張子で保存します。ファイルを選択して右クリック⇒「プログラムで開く」⇒Webブラウザを選択、という手順を実施すればゲームを開始できます。メモし忘れましたが、こういった説明もChatGPTがしてくれました。
実際にブラウザで開いてみると、以下のような画面が表示されました。枠の中にプレイヤーのような白い四角がありますが、それだけです。敵もいなければ自分を動かすこともできず、このままではゲームとして成立していません。

コードの修正とバグの解決
残念ながら一発でゲーム完成となりませんでした。ここからはChatGPTに修正を依頼してバグを解決していきます。
画面上にプレイヤーしか表示されないバグの修正
画面上にプレイヤーしか表示されない件について、ChatGPTに質問しました。すると、一部の配列がconstで宣言されているため再代入できないというバグがあることが判明しました。質問する際に毎回プログラムのコードを入力しているわけではありませんが、今回はネットワークエラーで途切れたため打ち直しています。
下記のプログラムを作成してもらいましたが、ブラウザ上で動きません。原因は何でしょうか。
(以下、実際のコードを入力する)
プログラムがブラウザ上で動かない原因は、enemies配列を再代入しようとしているためです。以下の箇所でenemies配列をフィルタリングしていますが、constで宣言されているため再代入ができません。
enemies = enemies.filter(enemy => enemy.y < canvas.height);
この問題を解決するには、enemies配列をletで宣言することで再代入が可能になります。
let enemies = [];
また、同じ問題がplayer.bullets配列でも発生しています。以下の箇所でplayer.bulletsをフィルタリングしていますが、こちらもconstで宣言されているため再代入ができません。
player.bullets = player.bullets.filter(bullet => bullet.y > 0);
この問題を解決するには、player.bullets配列もletで宣言することで再代入が可能になります。
let player = {
x: canvas.width / 2,
y: canvas.height - 30,
size: 10,
speed: 4,
bullets: []
};
白い四角は左右に動き、新たに表示された敵を表す赤い四角は、下に自動的に動くようになりました。しかし白い四角を操作できず、スペースキーを押しても弾を発射できません。

キーボードイベントの追加
敵は動くようになりましたが、プレイヤーは何もできない状態です。追加で修正を依頼しました。ちなみに
白い四角は左右に、赤い四角は下に自動的に動くようになりました。しかし白い四角を操作できず、弾も発射できません。コードの修正箇所のみ出力してください。
白い四角(プレイヤー)を操作できるようにし、弾を発射できるようにするために、以下の修正を行ってください。
- プレイヤーの左右移動のため、キーボードイベントを追加し、矢印キーで操作できるようにします。
// 追加
const keys = {};
document.addEventListener('keydown', (e) => {
keys[e.code] = true;
});
document.addEventListener('keyup', (e) => {
keys[e.code] = false;
});
// 修正
function update() {
// ...
// 左矢印キーが押された場合
if (keys['ArrowLeft']) {
player.x -= player.speed;
}
// 右矢印キーが押された場合
if (keys['ArrowRight']) {
player.x += player.speed;
}
// ...
}
2.マウスクリックで弾を発射できるようにします。
// 修正
canvas.addEventListener('click', (e) => {
player.bullets.push({
x: player.x + player.size / 2 - 2,
y: player.y - 10,
size: 4,
speed: 6
});
});
言われたとおりに修正を加えてみましたが、何も変化はありませんでした。しかも、ChatGPTが最初提案したゲームのルールではスペースキーで弾を発射することになっていましたが、今の回答ではマウスクリックで発射することになっています。
ネットワークエラーのせいで無かったことにされたようです。もしくは単に忘れ去られたのかもしれませんが。改めてゲームの条件を伝えます。
ゲームの条件を伝える
当初ChatGPTが示してくれたゲームプレイの条件を伝えました。
以下が実現できるよう修正してください。
2. ゲームプレイ:
– 操作方法: 左右矢印キーでプレイヤーのスペースシップを動かし、スペースキーで弾を発射します。
– 勝利条件: 画面上の全てのエイリアンを倒すと、プレイヤーの勝利です。
– 敵の行動パターン: エイリアンは左右に移動し、一定の間隔で下に降ります。時間が経つごとに移動速度が速くなります。
– 難易度の設定: ゲームに難易度の設定はありませんが、エイリアンの速度が徐々に上がることで自然と難易度が上がります。
– 得点システム: エイリアンを倒すごとに10ポイント獲得します。全てのエイリアンを倒すと、ボーナスとして残り時間に応じたポイントが加算されます。
以下の修正を行い、指定されたゲームプレイ条件を実現します。
- キーボードイベントを修正し、スペースキーで弾を発射できるようにします。
- 敵の行動パターンを修正し、左右に移動し、一定の間隔で下に降りるようにします。また、時間経過で速度が上がるようにします。
- 得点システムを追加し、エイリアンを倒すごとに10ポイント獲得できるようにします。
以下にコードの修正箇所を示します。
// 1. キーボードイベントを修正
document.addEventListener('keydown', (e) => {
keys[e.code] = true;
// スペースキーが押されたときに弾を発射
if (e.code === 'Space') {
player.bullets.push({
x: player.x + player.size / 2 - 2,
y: player.y - 10,
size: 4,
speed: 6
});
}
});
// 2. 敵の行動パターンと速度の修正
let moveDirection = 1;
let moveDownCounter = 0;
function update() {
// ...
// 敵の行動パターンと速度の修正
moveDownCounter++;
if (moveDownCounter >= 60) {
moveDirection = -moveDirection;
for (const enemy of enemies) {
enemy.y += 20;
}
moveDownCounter = 0;
}
for (const enemy of enemies) {
enemy.x += enemy.speed * moveDirection;
}
// ...
// 得点システムの追加(エイリアンを倒すごとに10ポイント獲得)
for (const bullet of player.bullets) {
for (const enemy of enemies) {
if (
bullet.x < enemy.x + enemy.size &&
bullet.x + bullet.size > enemy.x &&
bullet.y < enemy.y + enemy.size &&
bullet.y + bullet.size > enemy.y
) {
bullet.toRemove = true;
enemy.toRemove = true;
score += 10;
}
}
}
player.bullets = player.bullets.filter(bullet => !bullet.toRemove);
enemies = enemies.filter(enemy => !enemy.toRemove);
// ...
}
この修正により、指定されたゲームプレイ条件が実現されます。ただし、ボーナスポイントに関する機能は実装されていません。この機能を追加するには、ゲームの状態管理やタイマー機能などを実装する必要があります。
修正内容を示してくれましたが、実際どのように直してよいか、初心者の私にはわかりません。そこで再度コードの情報を渡して修正を依頼しました。しかしコード全部を出力させようとすると長文になりすぎるため、ネットワークエラーで止まってしまいます。そこでコードの一部分のみを出力してもらうよう指示しました。
修正内容を以下のコードに反映してください。<script>以下の部分のみ出力してください。
(以下、ここまで作成したコードを入力)
ChatGPTが出力したコードで実行すると、プレイヤーは矢印キーで左右に動けるようになりました。しかし弾は出ません。また、プレイヤーが敵と接触しても何も起こらないことに気づきました。当初ChatGPTが出力したルールを見返すと、プレイヤーと敵の接触に関しては何も記載が無かったため、検討漏れです。そのため特に気にしないことにしました。

指示を出すとき注意すること
長くなるので割愛しますが、この後も3往復ほどやり取りして修正を続けるとゲームが完成しました。完成したゲームは後で公開しますが、ChatGPTに指示を出す際の注意点を記載します。
ネットワークエラー時は事前に出した指示を忘れる
今回苦労した一番の原因はネットワークエラーです。ゲームを製作した次の日の午前中に試したときはエラーが全く発生しなかったので、単に混む時間帯だったのかもしれません。
エラーになると前回の質問の答えを途中まで出していても、その内容をChatGPTは記憶していないようです。ほぼ最後まで出力されているときは、まあいいかとなって次の質問に進めがちですが、そのとき出力された内容は記憶していないと考えておくべきでしょう。
コードの修正時は修正前後の部分のみ出力させる
現時点だけの問題かもしれませんが、GPT4はGPT3.5に比べて文字を出力する速度が遅いです。修正のたびに全コードを出力していては時間がかかり過ぎますし、ネットワークエラーが頻発するタイミングだとまともに会話を進められないでしょう。
修正するときは、「前後のコードも合わせて出力してください」と指示するとその通りに対応してくれます。該当部分を見つけやすく、修正のコピペも楽です。ちなみに修正箇所が何行目か出力させようとしても、上手くいきませんでした。文字数の指定にもあまり従わないので、このあたりは苦手な操作かもしれません。
またプログラムが全然動かない場合は、途中のコードを全て貼り付けて「以下のコードからバグを見つけてください」と指示するのも楽です。指示の入力であれば、ある程度の長文であってもそれほど問題なさそうです。
実際に作成したゲームを公開
HTMLやJavaScriptに詳しくなくても、GPT4バージョンのChatGPTの助けを借りることで、ゲーム制作ができました。実際のゲーム画面でええは左上にスコアが表示され、敵を倒すごとに10点加算されます。非常にシンプルなゲームとなっており、もっと時間をかければさまざまな要素を取り入れられそうです。元々ChatGPTに指示した納期は1時間なので納期遅延ですが、ネットワークエラーが主要因なので大目に見ましょう。

記事の最後には実際のソースコード全てを載せています。コードをテキストファイルにコピーし、「.html」の拡張子を付けてWebブラウザで開けば実際にゲームで遊ぶことができます。GPT4へのバージョンアップで面白いことにいろいろ取り組めそうです。ゲームに限らず今後も検討を続けていきたいと思います。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Shooting Game</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #222;
}
canvas {
border: 1px solid #ffffff;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="480" height="320"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const keys = {};
document.addEventListener('keydown', (e) => {
keys[e.code] = true;
// 追加: スペースキーが押されたときに弾を発射
if (e.code === 'Space') {
player.bullets.push({
x: player.x + player.size / 2 - 2,
y: player.y - 10,
size: 4,
speed: 6
});
}
});
document.addEventListener('keyup', (e) => {
keys[e.code] = false;
});
let player = {
x: canvas.width / 2,
y: canvas.height - 30,
size: 10,
speed: 4,
bullets: []
};
let enemies = [];
let enemySpawnCounter = 0;
let moveDirection = 1;
let moveDownCounter = 0;
let score = 0;
function drawPlayer() {
ctx.fillStyle = 'white';
ctx.fillRect(player.x, player.y, player.size, player.size);
}
function drawEnemies() {
ctx.fillStyle = 'red';
for (const enemy of enemies) {
ctx.fillRect(enemy.x, enemy.y, enemy.size, enemy.size);
}
}
function drawBullets() {
ctx.fillStyle = 'green';
for (const bullet of player.bullets) {
ctx.fillRect(bullet.x, bullet.y, bullet.size, bullet.size);
}
}
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPlayer();
drawEnemies();
drawBullets();
if (keys['ArrowLeft']) {
player.x -= player.speed;
}
if (keys['ArrowRight']) {
player.x += player.speed;
}
for (const bullet of player.bullets) {
bullet.y -= bullet.speed;
}
player.bullets = player.bullets.filter(bullet => bullet.y > 0);
// 弾と敵の衝突判定を追加
player.bullets.forEach((bullet) => {
enemies.forEach((enemy) => {
if (
bullet.x < enemy.x + enemy.size &&
bullet.x + bullet.size > enemy.x &&
bullet.y < enemy.y + enemy.size &&
bullet.y + bullet.size > enemy.y
) {
bullet.toRemove = true;
enemy.toRemove = true;
score += 10;
}
});
});
// 以下のコードを追加
player.bullets = player.bullets.filter((bullet) => !bullet.toRemove);
enemies = enemies.filter((enemy) => !enemy.toRemove);
enemySpawnCounter++;
if (enemySpawnCounter >= 30) {
enemies.push({
x: Math.random() * (canvas.width - 20),
y: 10,
size: 20,
speed: 2
});
enemySpawnCounter = 0;
}
moveDownCounter++;
if (moveDownCounter >= 60) {
moveDirection = -moveDirection;
for (const enemy of enemies) {
enemy.y += 20;
}
moveDownCounter = 0;
}
for (const enemy of enemies) {
enemy.x += enemy.speed * moveDirection;
}
enemies = enemies.filter(enemy => enemy.y < canvas.height);
// スコア表示を追加(画面左上に表示)
ctx.fillStyle = "white";
ctx.font = "16px Arial";
ctx.fillText("Score: " + score, 10, 20);
requestAnimationFrame(update);
}
canvas.addEventListener('click', (e) => {
player.bullets.push({
x: player.x + player.size / 2 - 2,
y: player.y - 10,
size: 4,
speed: 6
});
});
update();
</script>
</body>
</html>
ディスカッション
コメント一覧
まだ、コメントがありません