こんにちは、AM0500です。
作成するゲームのネタが思いつかなかったのでお任せで作ってみました。物理演算の機能が使えると知ったので、物理演算の機能を使って何か作ってもらい、そこからアイデアを広げようという企画になります。
🔗 実際にプレイしてみる
▼ ブラウザでそのまま遊べます:
👉 物理演算を使ったボール跳ね返しゲーム
👉 使用したAI Claude(claude 4 sonnet)はこちら
🎮 ゲーム画面(スクリーンショット)

📝 一言コメント
具体的なアイデアが無い状態で試してみた企画です。
そして完成したゲームから適切なプロンプトを吐き出してもらい、どんな指定が行われるのかを確認した企画になります。
🧠 使用したClaudeへのプロンプト
今回、使用したプロンプトです。
物理演算を使用して、PCとスマホのどちらでも遊べるようなシンプルなゲームを作ってもらえますか?
ジャンルはアクションでボタン一つで遊べる内容を希望します。
最初に作成に使ったプロンプトは上記の通りです。
このままでは再現性が無いので、このゲームを作ったあとにClaudeから再現させるためのプロンプトを作ってもらいました。
プロンプト
PCとスマホの両方で遊べる物理演算アクションゲームを作ってください。以下の詳細仕様に従って実装してください:
基本仕様
ゲーム名: ボール跳ね返しゲーム
プラットフォーム: PC(クリック操作)とスマホ(タップ操作)両対応
ゲームサイズ: 400px × 600px(レスポンシブ対応)
言語: 日本語UI
操作方法
メイン操作: 画面上の任意の位置をクリック/タップすると、その位置に向かってボールが飛ぶ
キーボード: スペースキーで上方向にジャンプ(PC用補助操作)
タッチ操作: タップした位置に向かってボールが飛ぶ
ゲームルール
プレイヤーは赤いボールを操作する
上から赤い長方形の障害物が落ちてくる
障害物を避けてスコアを稼ぐ(障害物が画面外に出ると10点獲得)
障害物に当たるとライフが1減る(初期ライフ3)
ライフが0になるとゲームオーバー
時間経過とともにゲームスピードが上昇
物理演算の要件
重力: 常時下向きに作用(0.5程度)
衝突判定:
地面:弾性衝突(反発係数0.6)
左右の壁:弾性衝突(反発係数0.7)
天井:弾性衝突(反発係数0.3)
摩擦: 水平方向の速度に摩擦を適用(0.98倍)
ジャンプ力: クリック位置への方向に一定の力(15程度)を適用
ビジュアル要件
背景: 空色から緑色へのグラデーション
ボール:
赤色のグラデーション(#ff8a8a → #ff4444)
半径20px
トレイル効果(8フレーム分の軌跡)
障害物:
赤色のグラデーション(#ff6666 → #cc3333)
サイズ:60px × 30px
影付き
パーティクル効果:
地面着地時:緑色パーティクル
ジャンプ時:水色パーティクル
障害物衝突時:赤色パーティクル
背景装飾: 流れる白い雲
UI要件
スタート画面: タイトル、操作説明、開始ボタン
ゲーム画面:
左上にスコア表示
右上にライフ表示
下部に操作説明
ゲームオーバー画面: 最終スコア表示、リトライボタン
デザイン: モダンなグラデーション、ボタンホバー効果
技術要件
実装: HTML5 Canvas + JavaScript
レスポンシブ: さまざまなデバイスサイズに対応
パフォーマンス: 60FPS で滑らかな動作
ゲームループ: requestAnimationFrame を使用
状態管理: start/playing/gameOver の3状態
追加要素
難易度調整: 時間経過で障害物の速度と生成頻度が増加
視覚効果: 美しいグラデーション、影、アニメーション
音響効果: 不要(視覚効果のみ)
このゲームは直感的な操作で楽しめ、物理演算によるリアルな動きが特徴です。完全に動作するHTMLファイルとして作成してください。🕹️ Claudeが返してくれた説明文
ゲームの特徴:
🏀 リアルな物理演算: 重力、摩擦、衝突などの物理法則を実装
📱 クロスプラットフォーム: PC(クリック/スペースキー)とスマホ(タップ)の両方で操作可能
🎯 シンプル操作: ボタン一つ(タップ/クリック)でボールを跳ね上げる
🌈 視覚効果: トレイル、パーティクル、グラデーションなどの美しいエフェクト
📊 スコアシステム: 障害物を避けるたびにスコアが増加し、ゲームスピードも徐々に上昇
ゲームルール:
画面をタップ/クリックしてボールを跳ね上げる
上から落ちてくる赤い障害物を避ける
障害物に当たるとライフが減る(3回でゲームオーバー)
障害物を避けるたびにスコアが増加
時間が経つにつれてゲームスピードが上がる
物理演算の実装:
重力によるボールの自然な落下
地面や壁との弾性衝突
摩擦による速度の減衰
障害物との衝突による反発力
レスポンシブデザインで様々なデバイスサイズに対応しており、直感的で楽しいゲームプレイを提供します!
💡 ソースコード(HTML)
下記がゲーム本体のhtmlの中身になります。ご自由にご利用ください。もちろん改変などもOKです。リンクや紹介していただけたら嬉しいです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ボール跳ね返しゲーム</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: Arial, sans-serif;
touch-action: none;
}
.game-container {
position: relative;
width: 400px;
height: 600px;
background: linear-gradient(180deg, #87CEEB 0%, #98FB98 100%);
border: 3px solid #333;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.game-info {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
z-index: 100;
display: flex;
justify-content: space-between;
color: #333;
font-weight: bold;
font-size: 18px;
}
.start-screen, .game-over-screen {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.8);
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 200;
text-align: center;
}
.start-screen h1, .game-over-screen h1 {
font-size: 28px;
margin-bottom: 20px;
}
.start-screen p, .game-over-screen p {
font-size: 16px;
margin-bottom: 30px;
line-height: 1.5;
}
.btn {
padding: 15px 30px;
font-size: 18px;
background: #ff6b6b;
color: white;
border: none;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 5px 15px rgba(255,107,107,0.4);
}
.btn:hover {
background: #ff5252;
transform: translateY(-2px);
box-shadow: 0 7px 20px rgba(255,107,107,0.6);
}
.instructions {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
color: #666;
font-size: 14px;
text-align: center;
}
canvas {
display: block;
background: transparent;
}
</style>
</head>
<body>
<div class="game-container">
<div class="game-info">
<div>スコア: <span id="score">0</span></div>
<div>ライフ: <span id="lives">3</span></div>
</div>
<canvas id="gameCanvas" width="400" height="600"></canvas>
<div class="start-screen" id="startScreen">
<h1>🏀 ボール跳ね返しゲーム</h1>
<p>タップ/クリックした位置に<br>ボールが飛んでいきます!<br><br>赤い障害物を避けて<br>スコアを稼ごう!</p>
<button class="btn" onclick="startGame()">ゲーム開始</button>
</div>
<div class="game-over-screen" id="gameOverScreen" style="display: none;">
<h1>ゲームオーバー</h1>
<p>最終スコア: <span id="finalScore">0</span></p>
<button class="btn" onclick="restartGame()">もう一度プレイ</button>
</div>
<div class="instructions">
タップ/クリックした位置にボールが飛ぶ
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const livesElement = document.getElementById('lives');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const finalScoreElement = document.getElementById('finalScore');
let gameState = 'start'; // 'start', 'playing', 'gameOver'
let score = 0;
let lives = 3;
let gameSpeed = 1;
// 物理演算の定数
const GRAVITY = 0.5;
const JUMP_FORCE = -12;
const FRICTION = 0.98;
// ボール
const ball = {
x: canvas.width / 2,
y: canvas.height - 100,
radius: 20,
vx: 0,
vy: 0,
color: '#ff6b6b',
trail: []
};
// 障害物
let obstacles = [];
// パーティクル
let particles = [];
function createObstacle() {
obstacles.push({
x: Math.random() * (canvas.width - 60),
y: -30,
width: 60,
height: 30,
speed: 2 + gameSpeed * 0.5,
color: '#ff4444'
});
}
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 updateBall() {
// 重力適用
ball.vy += GRAVITY;
// 速度適用
ball.x += ball.vx;
ball.y += ball.vy;
// 摩擦(水平方向)
ball.vx *= FRICTION;
// 壁との衝突(左右)
if (ball.x - ball.radius < 0 || ball.x + ball.radius > canvas.width) {
ball.vx *= -0.7;
ball.x = Math.max(ball.radius, Math.min(canvas.width - ball.radius, ball.x));
}
// 地面との衝突
if (ball.y + ball.radius > canvas.height) {
ball.y = canvas.height - ball.radius;
ball.vy *= -0.6;
ball.vx *= 0.8;
// 地面に着地時のパーティクル
if (Math.abs(ball.vy) > 3) {
createParticle(ball.x, ball.y + ball.radius, '#98FB98');
}
}
// 天井との衝突
if (ball.y - ball.radius < 0) {
ball.y = ball.radius;
ball.vy *= -0.3;
}
// トレイル効果
ball.trail.push({x: ball.x, y: ball.y});
if (ball.trail.length > 8) {
ball.trail.shift();
}
}
function updateObstacles() {
for (let i = obstacles.length - 1; i >= 0; i--) {
const obstacle = obstacles[i];
obstacle.y += obstacle.speed;
// 画面外に出たら削除してスコア増加
if (obstacle.y > canvas.height) {
obstacles.splice(i, 1);
score += 10;
gameSpeed += 0.02;
}
// ボールとの衝突判定
if (ball.x + ball.radius > obstacle.x &&
ball.x - ball.radius < obstacle.x + obstacle.width &&
ball.y + ball.radius > obstacle.y &&
ball.y - ball.radius < obstacle.y + obstacle.height) {
// 衝突時の処理
createParticle(ball.x, ball.y, '#ff6b6b');
obstacles.splice(i, 1);
lives--;
// ボールを押し返す
const centerX = obstacle.x + obstacle.width / 2;
const centerY = obstacle.y + obstacle.height / 2;
const dx = ball.x - centerX;
const dy = ball.y - centerY;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
ball.vx += (dx / distance) * 8;
ball.vy += (dy / distance) * 8;
}
if (lives <= 0) {
gameState = 'gameOver';
}
}
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const particle = particles[i];
particle.x += particle.vx;
particle.y += particle.vy;
particle.vx *= 0.98;
particle.vy *= 0.98;
particle.life--;
if (particle.life <= 0) {
particles.splice(i, 1);
}
}
}
function drawBall() {
// トレイル描画
ctx.globalAlpha = 0.3;
for (let i = 0; i < ball.trail.length; i++) {
const trail = ball.trail[i];
const alpha = i / ball.trail.length;
ctx.globalAlpha = alpha * 0.5;
ctx.beginPath();
ctx.arc(trail.x, trail.y, ball.radius * alpha, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
}
// ボール本体
ctx.globalAlpha = 1;
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
// グラデーション
const gradient = ctx.createRadialGradient(
ball.x - ball.radius/3, ball.y - ball.radius/3, 0,
ball.x, ball.y, ball.radius
);
gradient.addColorStop(0, '#ff8a8a');
gradient.addColorStop(1, '#ff4444');
ctx.fillStyle = gradient;
ctx.fill();
// 影
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius - 2, 0, Math.PI * 2);
ctx.strokeStyle = '#cc3333';
ctx.lineWidth = 2;
ctx.stroke();
}
function drawObstacles() {
obstacles.forEach(obstacle => {
// 影
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.fillRect(obstacle.x + 2, obstacle.y + 2, obstacle.width, obstacle.height);
// 本体
const gradient = ctx.createLinearGradient(
obstacle.x, obstacle.y,
obstacle.x, obstacle.y + obstacle.height
);
gradient.addColorStop(0, '#ff6666');
gradient.addColorStop(1, '#cc3333');
ctx.fillStyle = gradient;
ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
// ハイライト
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, 5);
});
}
function drawParticles() {
particles.forEach(particle => {
const alpha = particle.life / particle.maxLife;
ctx.globalAlpha = alpha;
ctx.beginPath();
ctx.arc(particle.x, particle.y, 3, 0, Math.PI * 2);
ctx.fillStyle = particle.color;
ctx.fill();
});
ctx.globalAlpha = 1;
}
function drawBackground() {
// 雲
ctx.fillStyle = 'rgba(255,255,255,0.8)';
for (let i = 0; i < 3; i++) {
const x = (Date.now() * 0.02 + i * 150) % (canvas.width + 100) - 50;
const y = 50 + i * 30;
drawCloud(x, y);
}
}
function drawCloud(x, y) {
ctx.beginPath();
ctx.arc(x, y, 20, 0, Math.PI * 2);
ctx.arc(x + 20, y, 30, 0, Math.PI * 2);
ctx.arc(x + 40, y, 20, 0, Math.PI * 2);
ctx.arc(x + 10, y - 20, 20, 0, Math.PI * 2);
ctx.arc(x + 30, y - 20, 20, 0, Math.PI * 2);
ctx.fill();
}
function jump(targetX, targetY) {
if (gameState === 'playing') {
// クリック/タップ位置への方向を計算
const dx = targetX - ball.x;
const dy = targetY - ball.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// 正規化して適切な力を適用
const force = 15;
if (distance > 0) {
ball.vx = (dx / distance) * force;
ball.vy = (dy / distance) * force;
}
// ジャンプ時のパーティクル
createParticle(ball.x, ball.y + ball.radius, '#87CEEB');
}
}
function gameLoop() {
if (gameState === 'playing') {
// 更新
updateBall();
updateObstacles();
updateParticles();
// 新しい障害物を生成
if (Math.random() < 0.02 + gameSpeed * 0.01) {
createObstacle();
}
// UI更新
scoreElement.textContent = score;
livesElement.textContent = lives;
}
// ゲームオーバー判定(ゲームループの外で処理)
if (gameState === 'gameOver') {
finalScoreElement.textContent = score;
gameOverScreen.style.display = 'flex';
}
// 描画(常に実行)
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBackground();
drawParticles();
drawObstacles();
drawBall();
requestAnimationFrame(gameLoop);
}
function startGame() {
gameState = 'playing';
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
// 初期化
score = 0;
lives = 3;
gameSpeed = 1;
obstacles = [];
particles = [];
ball.x = canvas.width / 2;
ball.y = canvas.height - 100;
ball.vx = 0;
ball.vy = 0;
ball.trail = [];
// UI更新
scoreElement.textContent = score;
livesElement.textContent = lives;
}
function restartGame() {
startGame();
}
// イベントリスナー
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
jump(x, y);
});
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
jump(x, y);
});
// キーボード操作(上方向にジャンプ)
document.addEventListener('keydown', (e) => {
if (e.code === 'Space' || e.code === 'ArrowUp') {
e.preventDefault();
jump(ball.x, ball.y - 100);
}
});
// ゲームループ開始
gameLoop();
</script>
</body>
</html>🔔 補足・注意事項
このコードや説明は、Claudeのバージョンや環境によって出力内容が異なる可能性があります。
同じ命令をしても、まったく違うコードが返ってくることもあるので、再現性は参考程度にご覧ください。

0 件のコメント:
コメントを投稿