もう「作れません」とは言わせない。AIに"丸投げ"で、Webサイトにゲームを実装した全記録
- 本間 充/マーケティングサイエンスラボ所長
- 6月21日
- 読了時間: 7分
「自社のWebサイト、もう少し訪問者に楽しんでもらえないかな…」
ある週末、ふとそんなことを思いました。例えば、ちょっとした息抜きになるような、懐かしのブロック崩しゲームがあったら面白いかもしれない。
しかし、多くの人はプログラミングの専門家ではありません。以前なら「エンジニアに相談して、見積もりを取って、要件を伝えて…」と、考えただけで気が遠くなるような道のりでした。時間もコストもかかります。
でも、今は違います。私たちには、超優秀なAIアシスタント「Google Gemini」がいます。
「もしかして、AIに頼めば一瞬で作ってくれるのでは?」
そんな淡い期待を胸に、週末のAIプログラミングに挑戦してみることにしました。
AIアシスタントへの最初の"お願い"
早速Geminiに、こんな風に話しかけてみました。まるで作ってほしいものを部下に説明するような感覚です。
「Webページに埋め込める、シンプルなブロック崩しゲームを作ってくれないかな?スコアは、今のプレイ中のハイスコアが分かればOK。あと、見た目はカラフルで楽しい感じにしてほしい!」
専門的な言葉は使っていません。ポイントは「何がしたいか」を具体的に伝えることだけ。 すると、どうでしょう。
数秒後、Geminiは「はい、承知いたしました」とでも言うかのように、ずらっとプログラムのコードを書き出したのです。まるで魔法を見ているようでした。
「あ、ここ修正して」と追加で"お願い"
出来上がったコードを早速自分のテストページに設置してみると、ゲームはきちんと動きます。しかし、少しだけ気になる点が。
キーボードでの操作が、なんだかスムーズじゃない。
ゲームオーバーになったら、そこで終わり。もう一度プレイできない。
これでは、せっかく訪れてくれたユーザーをがっかりさせてしまいます。
昔なら、ここからの修正依頼がまた大変な作業でした。しかし、AIアシスタントなら話は別です。私はGeminiに、こうフィードバックを送りました。
「ありがとう!動いたよ。ただ、キーボードの操作がうまくいかないみたい。それと、ゲームオーバーになったら、もう一度遊べるように修正してほしいな。」
この、まるで同僚に「ちょっと修正お願い!」と頼むような気軽さ。するとGeminiは、再び完璧な修正コードを提案してくれました。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ブロック崩しゲーム</title>
<style>
body {
font-family: 'Arial', sans-serif;
text-align: center;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
/* ユーザーがキー入力でページをスクロールさせないように */
overflow: hidden;
}
h1 {
font-size: 24px;
color: #333;
}
#gameContainer {
display: inline-block;
border: 2px solid #333;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
canvas {
display: block;
background: #fff;
cursor: pointer; /* クリック可能であることを示すカーソル */
}
#infoPanel {
display: flex;
justify-content: space-around;
padding: 10px;
background-color: #333;
color: #fff;
font-size: 16px;
}
#instructions {
margin-top: 15px;
color: #555;
font-size: 14px;
}
</style>
</head>
<body>
<div id="gameContainer">
<h1>ブロック崩し</h1>
<canvas id="gameCanvas" width="480" height="360"></canvas>
<div id="infoPanel">
<div>SCORE: <span id="score">0</span></div>
<div>HIGH SCORE: <span id="highScore">0</span></div>
</div>
</div>
<div id="instructions">
操作: マウス移動 または ← → キー<br>
リスタート: ゲームオーバー後に画面をクリック
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// ゲームの状態管理
let gameState = "start"; // "start", "playing", "gameOver", "gameClear"
// ゲームの変数
let ballRadius = 10;
let x, y, dx, dy;
// パドルの変数
let paddleHeight = 10;
let paddleWidth = 75;
let paddleX;
// キーボード操作用の変数
let rightPressed = false;
let leftPressed = false;
// ブロックの変数
const brickRowCount = 5;
const brickColumnCount = 7;
const brickWidth = 55;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 30;
const brickOffsetLeft = 30;
const brickColors = ["#e53935", "#d81b60", "#8e24aa", "#5e35b1", "#3949ab", "#1e88e5", "#039be5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e"];
let bricks = [];
// スコア
let score = 0;
let highScore = 0;
// DOM要素
const scoreDisplay = document.getElementById('score');
const highScoreDisplay = document.getElementById('highScore');
// イベントリスナー
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);
canvas.addEventListener("click", clickHandler, false);
function keyDownHandler(e) {
if(e.key == "Right" || e.key == "ArrowRight") rightPressed = true;
else if(e.key == "Left" || e.key == "ArrowLeft") leftPressed = true;
}
function keyUpHandler(e) {
if(e.key == "Right" || e.key == "ArrowRight") rightPressed = false;
else if(e.key == "Left" || e.key == "ArrowLeft") leftPressed = false;
}
function mouseMoveHandler(e) {
let relativeX = e.clientX - canvas.getBoundingClientRect().left;
if(relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth / 2;
if (paddleX < 0) paddleX = 0;
if (paddleX + paddleWidth > canvas.width) paddleX = canvas.width - paddleWidth;
}
}
function clickHandler() {
if (gameState === "gameOver" || gameState === "gameClear" || gameState === "start") {
resetGame();
gameState = "playing";
}
}
function resetGame() {
// ボールとパドルの位置をリセット
x = canvas.width / 2;
y = canvas.height - 30;
dx = 3;
dy = -3;
paddleX = (canvas.width - paddleWidth) / 2;
// スコアをリセット
score = 0;
scoreDisplay.textContent = score;
// ブロックをリセット
for(let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for(let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1, color: brickColors[(c + r * brickColumnCount) % brickColors.length] };
}
}
}
function collisionDetection() {
for(let c = 0; c < brickColumnCount; c++) {
for(let r = 0; r < brickRowCount; r++) {
let b = bricks[c][r];
if(b.status == 1) {
if(x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
dy = -dy;
b.status = 0;
score += 10;
scoreDisplay.textContent = score;
if (isGameClear()) {
gameState = "gameClear";
updateHighScore();
}
}
}
}
}
}
function isGameClear() {
for(let c=0; c<brickColumnCount; c++) {
for(let r=0; r<brickRowCount; r++) {
if(bricks[c][r].status === 1) return false;
}
}
return true;
}
function updateHighScore() {
if (score > highScore) {
highScore = score;
highScoreDisplay.textContent = highScore;
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#333";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for(let c = 0; c < brickColumnCount; c++) {
for(let r = 0; r < brickRowCount; r++) {
if(bricks[c][r].status == 1) {
let brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft;
let brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = bricks[c][r].color;
ctx.fill();
ctx.closePath();
}
}
}
}
function drawMessage(message, subMessage) {
ctx.font = "32px Arial";
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.textAlign = "center";
ctx.fillText(message, canvas.width/2, canvas.height/2 - 20);
ctx.font = "16px Arial";
ctx.fillText(subMessage, canvas.width/2, canvas.height/2 + 20);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawPaddle();
drawBall();
if (gameState === "playing") {
collisionDetection();
// パドル移動
if(rightPressed && paddleX < canvas.width - paddleWidth) paddleX += 7;
else if(leftPressed && paddleX > 0) paddleX -= 7;
// ボールの移動と衝突判定
if(x + dx > canvas.width - ballRadius || x + dx < ballRadius) dx = -dx;
if(y + dy < ballRadius) dy = -dy;
else if(y + dy > canvas.height - ballRadius) {
if(x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
gameState = "gameOver";
updateHighScore();
}
}
x += dx;
y += dy;
}
if (gameState === "start") {
drawMessage("ブロック崩し", "画面をクリックしてスタート");
} else if (gameState === "gameOver") {
drawMessage("GAME OVER", "クリックしてリトライ");
} else if (gameState === "gameClear") {
drawMessage("GAME CLEAR!", "クリックしてもう一度プレイ");
}
requestAnimationFrame(draw);
}
// 初期化してゲームループを開始
resetGame();
draw();
</script>
</body>
</html>
このコードを貼り付けるだけで、私のWebサイトには、思い描いていた通りのブロック崩しゲームが完成してしまいました。
アイデアさえあれば、誰でも「開発者」になれる時代
今回の体験で、確信したことがあります。
これからの時代に求められるのは、必ずしもプログラミングを書く技術だけではない。「何を、どのように作れば面白いか、役に立つか」というアイデアを考え、AIに的確に伝える能力こそが、新しい武器になるのだと。
もちろん、世界を変えるような全く新しいサービスには、今もこれからもプロのエンジニアの力が必要です。しかし、「社内アンケート用の簡単なツールが欲しい」「商品説明に、動きのあるシミュレーターを入れたい」といった、過去に前例のあるプログラムであれば、AIは非常に強力なパートナーになります。
「エンジニアがいないから」「予算がないから」と諦めていた、あなたの頭の中にある「あったらいいな」。それを形にするのに、もう特別なスキルは必要ありません。
ぜひ、あなたもAIアシスタントに、ビジネスのアイデアを打ち明けてみませんか? 思ったよりずっと簡単に、未来が動き出すかもしれませんよ。
Komentarze