
AI100日チャレンジ 38日目 たった数分で本格アクションゲームが完成!AIに「避けゲー作って」と頼んだら想像以上にクオリティ高かった

今日も続けているAI100日チャレンジ、本日は「シンプルなアクションゲームを作ってみよう」というテーマで挑戦しました。結果的に予想を遥かに超える出来栄えになって、一人で興奮してしまいました😅
■ 今回作ったもの クラシックな「避けゲー」です。上から落ちてくる障害物を左右の矢印キーで避けながら、どこまでスコアを伸ばせるかを競うシンプルなゲーム。
ただ、このシンプルさが侮れない...!一度始めると「もうちょっと、もうちょっと」と止まらなくなる中毒性があります🎮
■ Claudeのグラフィック能力に驚愕 これが一番驚いたポイントなんですが、AIが生成したビジュアルが想像以上に美しい✨
・ネオン風に光るプレイヤーキャラクター ・グラデーションが美しい宇宙空間風の背景 ・衝突時に散るパーティクルエフェクト ・回転しながら落下する障害物の演出 ・きらめく星空のアニメーション
正直、「HTMLで作る簡単なゲーム」というイメージを完全に覆されました。これ、本当にコード生成だけで作ったの?と疑いたくなるレベル。現代のAIの表現力、恐るべしです🤖
■ 開発プロセスの面白さ 今回特に感じたのは、AIとの「共創」という感覚でした。
私:「避けゲーを作りたい」 Claude:「分かりました!どんな要素を入れましょうか?」 私:「シンプルだけど中毒性のあるものを」 Claude:「了解です!」
そして数分後に完成したのが、光るエフェクトやパーティクル、段階的な難易度調整まで含んだ本格的なゲーム。まるで優秀なゲームデザイナーと組んで開発してるような感覚でした。
■ 技術的な発見 ・Canvas APIを使った滑らかなアニメーション ・リアルタイムの当たり判定処理 ・プロシージャルな障害物生成システム ・パフォーマンスを考慮した描画最適化
これらが全て一つのHTMLファイルに収まっているという事実。Web技術の進歩を改めて実感します💻
■ 遊んでみた感想 開発者として客観的に見ようと思ったのですが、普通に夢中になってしまいました😂 スコア300を超えたあたりから急激に難しくなって、「あと1回だけ...」が止まらない。
特に3つのライフシステムが絶妙で、1回のミスで即ゲームオーバーじゃないから「まだ挽回できる!」という気持ちになるんですよね。この辺りのゲームバランス調整も、AIが自然に組み込んでくれたのが驚きです。
■ AI開発の可能性を感じた瞬間 従来なら、こういうゲームを作るのに必要だった工程:
企画・仕様書作成
グラフィック素材の準備
プログラミング
デバッグ・調整
UI/UXの改善
これらが「避けゲーを作って」という一言でほぼ同時に解決されてしまう。もちろん細かい調整は必要ですが、この開発速度は革命的です🚀
■ 30代の学び 正直、最新技術についていくのが年々大変になってきているのを感じる今日この頃😅 でも、こういう体験を通じて「AIは敵じゃなくて最強のパートナーなんだ」ということを実感できました。
大切なのは、AIに何をさせるかではなく、AIと一緒に何を作るかという発想の転換。これからの時代は、この「共創」のスキルが重要になってくるのかもしれません。
■ 明日への展望 100日チャレンジもまだまだ続きます。明日は何を作ろうかな...🤔
今回のゲーム、思った以上に楽しめる出来になったので、もし興味があれば遊んでみてください!(コメントで最高スコア教えてもらえると嬉しいです👍)
#AI100日チャレンジ #Claude #ゲーム開発 #HTML5 #JavaScript #AI開発 #プログラミング #WebGame #創作活動 #30代プログラマー
皆さんのAI活用事例もぜひ聞かせてください!コメントお待ちしています✨
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>避けゲー - Dodge Game</title>
<style>
body {
margin: 0;
padding: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: 'Arial', sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
.game-container {
position: relative;
width: 400px;
height: 600px;
background: #1a1a2e ;
border: 3px solid #16213e ;
border-radius: 15px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.game-canvas {
width: 100%;
height: 100%;
display: block;
background: linear-gradient(180deg, #0f0f23 0%, #16213e 100%);
}
.ui-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.score {
position: absolute;
top: 20px;
left: 20px;
color: #00ff88 ;
font-size: 24px;
font-weight: bold;
text-shadow: 0 0 10px #00ff88 ;
}
.lives {
position: absolute;
top: 20px;
right: 20px;
color: #ff4757 ;
font-size: 20px;
font-weight: bold;
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: white;
font-size: 24px;
background: rgba(0,0,0,0.8);
padding: 30px;
border-radius: 15px;
border: 2px solid #ff4757 ;
display: none;
}
.start-screen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: white;
background: rgba(0,0,0,0.8);
padding: 30px;
border-radius: 15px;
border: 2px solid #00ff88 ;
}
.btn {
background: linear-gradient(45deg, #00ff88 , #007f44 );
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
margin-top: 15px;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0,255,136,0.3);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0,255,136,0.4);
}
.controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
color: #888 ;
font-size: 14px;
text-align: center;
}
.particle {
position: absolute;
width: 2px;
height: 2px;
background: #00ff88 ;
border-radius: 50%;
pointer-events: none;
}
</style>
</head>
<body>
<div class="game-container">
<canvas class="game-canvas" id="gameCanvas" width="400" height="600"></canvas>
<div class="ui-overlay">
<div class="score" id="score">スコア: 0</div>
<div class="lives" id="lives">♥ ♥ ♥</div>
<div class="start-screen" id="startScreen">
<h2>🚀 避けゲー 🚀</h2>
<p>矢印キーで移動して<br>落下物を避けよう!</p>
<button class="btn" onclick="startGame()">ゲーム開始</button>
</div>
<div class="game-over" id="gameOverScreen">
<h2>ゲームオーバー</h2>
<p id="finalScore"></p>
<button class="btn" onclick="restartGame()">もう一度</button>
</div>
</div>
<div class="controls">
← → 矢印キー: 移動 | スペース: 開始/ポーズ/リスタート
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let gameState = 'start'; // 'start', 'playing', 'paused', 'gameOver'
let player = {
x: canvas.width / 2 - 15,
y: canvas.height - 60,
width: 30,
height: 30,
speed: 5,
color: '#00ff88'
};
let obstacles = [];
let particles = [];
let score = 0;
let lives = 3;
let gameSpeed = 2;
let spawnRate = 0.02;
let lastTime = 0;
const keys = {};
// キーボードイベント
document.addEventListener('keydown', (e) => {
keys[e.key] = true;
if (e.key === ' ') {
e.preventDefault();
if (gameState === 'start') {
startGame();
} else if (gameState === 'playing') {
gameState = 'paused';
} else if (gameState === 'paused') {
gameState = 'playing';
} else if (gameState === 'gameOver') {
restartGame();
}
}
});
document.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
function startGame() {
gameState = 'playing';
document.getElementById('startScreen').style.display = 'none';
resetGame();
gameLoop();
}
function restartGame() {
document.getElementById('gameOverScreen').style.display = 'none';
startGame();
}
function resetGame() {
player.x = canvas.width / 2 - 15;
player.y = canvas.height - 60;
obstacles = [];
particles = [];
score = 0;
lives = 3;
gameSpeed = 2;
spawnRate = 0.02;
updateUI();
}
function updateUI() {
document.getElementById('score').textContent = `スコア: ${score}`;
const heartsDisplay = '♥ '.repeat(lives) + '♡ '.repeat(3 - lives);
document.getElementById('lives').innerHTML = heartsDisplay;
}
function createObstacle() {
const types = [
{ width: 30, height: 30, color: '#ff4757', speed: gameSpeed },
{ width: 20, height: 40, color: '#ff6b7a', speed: gameSpeed * 1.2 },
{ width: 40, height: 20, color: '#ff3742', speed: gameSpeed * 0.8 }
];
const type = types[Math.floor(Math.random() * types.length)];
obstacles.push({
x: Math.random() * (canvas.width - type.width),
y: -type.height,
width: type.width,
height: type.height,
color: type.color,
speed: type.speed,
rotation: 0
});
}
function createParticle(x, y, color) {
for (let i = 0; i < 8; i++) {
particles.push({
x: x,
y: y,
vx: (Math.random() - 0.5) * 10,
vy: (Math.random() - 0.5) * 10,
life: 30,
maxLife: 30,
color: color
});
}
}
function updatePlayer() {
if (keys['ArrowLeft'] && player.x > 0) {
player.x -= player.speed;
}
if (keys['ArrowRight'] && player.x < canvas.width - player.width) {
player.x += player.speed;
}
}
function updateObstacles() {
// 新しい障害物の生成
if (Math.random() < spawnRate) {
createObstacle();
}
// 障害物の更新
for (let i = obstacles.length - 1; i >= 0; i--) {
const obs = obstacles[i];
obs.y += obs.speed;
obs.rotation += 0.1;
// 画面外に出た障害物を削除してスコア追加
if (obs.y > canvas.height) {
obstacles.splice(i, 1);
score += 10;
// 難易度調整
if (score % 100 === 0) {
gameSpeed += 0.5;
spawnRate += 0.005;
}
}
// 衝突判定
else if (obs.x < player.x + player.width &&
obs.x + obs.width > player.x &&
obs.y < player.y + player.height &&
obs.y + obs.height > player.y) {
// パーティクル効果
createParticle(obs.x + obs.width/2, obs.y + obs.height/2, obs.color);
obstacles.splice(i, 1);
lives--;
if (lives <= 0) {
gameState = 'gameOver';
document.getElementById('finalScore').textContent = `最終スコア: ${score}`;
document.getElementById('gameOverScreen').style.display = 'block';
}
}
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.vx *= 0.98;
p.vy *= 0.98;
p.life--;
if (p.life <= 0) {
particles.splice(i, 1);
}
}
}
function drawPlayer() {
ctx.save();
ctx.translate(player.x + player.width/2, player.y + player.height/2);
// プレイヤーの光る効果
ctx.shadowColor = player.color;
ctx.shadowBlur = 15;
ctx.fillStyle = player.color;
ctx.fillRect(-player.width/2, -player.height/2, player.width, player.height);
// 中央の明るい部分
ctx.fillStyle = '#88ffaa';
ctx.fillRect(-player.width/4, -player.height/4, player.width/2, player.height/2);
ctx.restore();
}
function drawObstacles() {
obstacles.forEach(obs => {
ctx.save();
ctx.translate(obs.x + obs.width/2, obs.y + obs.height/2);
ctx.rotate(obs.rotation);
ctx.shadowColor = obs.color;
ctx.shadowBlur = 10;
ctx.fillStyle = obs.color;
ctx.fillRect(-obs.width/2, -obs.height/2, obs.width, obs.height);
ctx.restore();
});
}
function drawParticles() {
particles.forEach(p => {
const alpha = p.life / p.maxLife;
ctx.save();
ctx.globalAlpha = alpha;
ctx.fillStyle = p.color;
ctx.fillRect(p.x, p.y, 3, 3);
ctx.restore();
});
}
function drawBackground() {
// 動く星背景
ctx.fillStyle = '#ffffff';
for (let i = 0; i < 50; i++) {
const x = (Date.now() * 0.01 + i * 137.5) % canvas.width;
const y = (Date.now() * 0.005 + i * 67.3) % canvas.height;
const opacity = Math.sin(Date.now() * 0.003 + i) * 0.5 + 0.5;
ctx.globalAlpha = opacity * 0.3;
ctx.fillRect(x, y, 1, 1);
}
ctx.globalAlpha = 1;
}
function render() {
// 背景をクリア
ctx.fillStyle = 'rgba(15, 15, 35, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawBackground();
drawParticles();
drawObstacles();
drawPlayer();
// ポーズ表示
if (gameState === 'paused') {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.fillText('ポーズ中', canvas.width/2, canvas.height/2);
ctx.fillText('スペースキーで再開', canvas.width/2, canvas.height/2 + 40);
}
}
function gameLoop(currentTime) {
if (gameState === 'playing') {
updatePlayer();
updateObstacles();
updateParticles();
updateUI();
}
render();
if (gameState !== 'gameOver') {
requestAnimationFrame(gameLoop);
}
}
// 初期表示
render();
</script>
</body>
</html>