見出し画像

ClaudeのArtifactsでシューティングゲームを作ってみよう!

Claude 3.5の新機能であるArtifactsを使えば、簡単にブラウザ上でプレーできるゲームを作ることができます。


1.HTMLアーティファクトでのゲーム作成

今回使用したプロンプトは以下の通りです。

HTMLアーティファクトでシューティングゲームを作成して。
・得点を表示し、敵の機体や弾に当たったら「GAME OVER」と表示してゲームを終了。
・敵の機体も下に向けて弾を撃ってくる。
・背景は星空。
・敵を50機撃つと、図形が組み合わさった複雑な形をしたボスキャラが登場する。
・ボスキャラは8方向に向けて沢山の弾を撃ってくる。
・ボスキャラはユーザーの弾が30発当たると爆発し、「Congratulations!!」と画面に表示され、最終得点を表示してゲームを終了する。

以下は実際のプレー画面です。
矢印キーで移動し、スペースキーを押すと弾を撃ちます。
敵機を50機倒すと、弾を8方向に撃ちまくるボスキャラが登場します。
非常に難易度の高いシューティングゲームです。


以下はClaudeの回答です。

はい、シューティングゲームのHTMLアーティファクトを作成しましょう。このゲームには、ご要望の機能を全て含めています。

<!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; overflow: hidden; background-color: black; }
        canvas { display: block; }
        #score, #gameOver, #congratulations { 
            position: absolute; 
            top: 10px; 
            left: 10px; 
            color: white; 
            font-family: Arial, sans-serif; 
            font-size: 20px; 
        }
        #gameOver, #congratulations { 
            display: none; 
            top: 50%; 
            left: 50%; 
            transform: translate(-50%, -50%); 
            font-size: 40px; 
            text-align: center; 
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    <div id="score">得点: 0</div>
    <div id="gameOver">GAME OVER</div>
    <div id="congratulations">Congratulations!!<br>最終得点: <span id="finalScore"></span></div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        const player = {
            x: canvas.width / 2,
            y: canvas.height - 50,
            width: 50,
            height: 50,
            speed: 5
        };

        let enemies = [];
        let playerBullets = [];
        let enemyBullets = [];
        let score = 0;
        let enemiesDestroyed = 0;
        let boss = null;
        let gameOver = false;

        const keys = {};

        document.addEventListener('keydown', (e) => keys[e.code] = true);
        document.addEventListener('keyup', (e) => keys[e.code] = false);

        function createEnemy() {
            return {
                x: Math.random() * (canvas.width - 30),
                y: 0,
                width: 30,
                height: 30,
                speed: 2
            };
        }

        function createBoss() {
            return {
                x: canvas.width / 2,
                y: 100,
                width: 100,
                height: 100,
                hp: 30,
                speed: 2,
                direction: 1
            };
        }

        function drawStarryBackground() {
            ctx.fillStyle = 'black';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            for (let i = 0; i < 100; i++) {
                ctx.fillStyle = 'white';
                ctx.fillRect(
                    Math.random() * canvas.width,
                    Math.random() * canvas.height,
                    1,
                    1
                );
            }
        }

        function drawPlayer() {
            ctx.fillStyle = 'blue';
            ctx.fillRect(player.x, player.y, player.width, player.height);
        }

        function drawEnemies() {
            ctx.fillStyle = 'red';
            enemies.forEach(enemy => {
                ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
            });
        }

        function drawBoss() {
            if (boss) {
                ctx.fillStyle = 'purple';
                ctx.beginPath();
                ctx.moveTo(boss.x, boss.y);
                ctx.lineTo(boss.x + 50, boss.y + 50);
                ctx.lineTo(boss.x + 100, boss.y);
                ctx.lineTo(boss.x + 75, boss.y + 75);
                ctx.lineTo(boss.x + 100, boss.y + 100);
                ctx.lineTo(boss.x + 50, boss.y + 75);
                ctx.lineTo(boss.x, boss.y + 100);
                ctx.lineTo(boss.x + 25, boss.y + 75);
                ctx.closePath();
                ctx.fill();
            }
        }

        function drawBullets() {
            ctx.fillStyle = 'yellow';
            playerBullets.forEach(bullet => {
                ctx.fillRect(bullet.x, bullet.y, 5, 10);
            });

            ctx.fillStyle = 'orange';
            enemyBullets.forEach(bullet => {
                ctx.fillRect(bullet.x, bullet.y, 5, 10);
            });
        }

        function movePlayer() {
            if (keys['ArrowLeft'] && player.x > 0) player.x -= player.speed;
            if (keys['ArrowRight'] && player.x < canvas.width - player.width) player.x += player.speed;
            if (keys['ArrowUp'] && player.y > 0) player.y -= player.speed;
            if (keys['ArrowDown'] && player.y < canvas.height - player.height) player.y += player.speed;
        }

        function moveEnemies() {
            enemies.forEach(enemy => {
                enemy.y += enemy.speed;
                if (Math.random() < 0.01) {
                    enemyBullets.push({ x: enemy.x + enemy.width / 2, y: enemy.y + enemy.height, speed: 5 });
                }
            });
            // 画面外に出た敵を削除
            enemies = enemies.filter(enemy => enemy.y < canvas.height);
        }

        function moveBoss() {
            if (boss) {
                boss.x += boss.speed * boss.direction;
                if (boss.x <= 0 || boss.x + boss.width >= canvas.width) {
                    boss.direction *= -1;
                }
                if (Math.random() < 0.1) {
                    for (let i = 0; i < 8; i++) {
                        const angle = (Math.PI * 2 / 8) * i;
                        enemyBullets.push({
                            x: boss.x + boss.width / 2,
                            y: boss.y + boss.height / 2,
                            speed: 3,
                            dx: Math.cos(angle) * 3,
                            dy: Math.sin(angle) * 3
                        });
                    }
                }
            }
        }

        function moveBullets() {
            playerBullets.forEach(bullet => bullet.y -= 10);
            playerBullets = playerBullets.filter(bullet => bullet.y > 0);

            enemyBullets.forEach(bullet => {
                if (bullet.dx !== undefined && bullet.dy !== undefined) {
                    bullet.x += bullet.dx;
                    bullet.y += bullet.dy;
                } else {
                    bullet.y += bullet.speed;
                }
            });
            enemyBullets = enemyBullets.filter(bullet => bullet.y < canvas.height);
        }

        function checkCollisions() {
            enemies = enemies.filter(enemy => {
                if (
                    player.x < enemy.x + enemy.width &&
                    player.x + player.width > enemy.x &&
                    player.y < enemy.y + enemy.height &&
                    player.y + player.height > enemy.y
                ) {
                    gameOver = true;
                    return false;
                }
                return true;
            });

            playerBullets = playerBullets.filter(bullet => {
                let hit = false;
                enemies = enemies.filter(enemy => {
                    if (
                        bullet.x < enemy.x + enemy.width &&
                        bullet.x + 5 > enemy.x &&
                        bullet.y < enemy.y + enemy.height &&
                        bullet.y + 10 > enemy.y
                    ) {
                        score += 10;
                        enemiesDestroyed++;
                        hit = true;
                        return false;
                    }
                    return true;
                });
                return !hit;
            });

            if (boss) {
                playerBullets = playerBullets.filter(bullet => {
                    if (
                        bullet.x < boss.x + boss.width &&
                        bullet.x + 5 > boss.x &&
                        bullet.y < boss.y + boss.height &&
                        bullet.y + 10 > boss.y
                    ) {
                        boss.hp--;
                        score += 20;
                        if (boss.hp <= 0) {
                            boss = null;
                            gameOver = true;
                            document.getElementById('congratulations').style.display = 'block';
                            document.getElementById('finalScore').textContent = score;
                        }
                        return false;
                    }
                    return true;
                });
            }

            enemyBullets.forEach(bullet => {
                if (
                    bullet.x < player.x + player.width &&
                    bullet.x + 5 > player.x &&
                    bullet.y < player.y + player.height &&
                    bullet.y + 10 > player.y
                ) {
                    gameOver = true;
                }
            });
        }

        function updateScore() {
            document.getElementById('score').textContent = `得点: ${score}`;
        }

        function gameLoop() {
            if (gameOver) {
                if (boss === null && document.getElementById('congratulations').style.display !== 'block') {
                    document.getElementById('gameOver').style.display = 'block';
                }
                return;
            }

            ctx.clearRect(0, 0, canvas.width, canvas.height);

            drawStarryBackground();
            drawPlayer();
            drawEnemies();
            drawBoss();
            drawBullets();

            movePlayer();
            moveEnemies();
            moveBoss();
            moveBullets();
            checkCollisions();
            updateScore();

            if (Math.random() < 0.02 && enemies.length < 10) {
                enemies.push(createEnemy());
            }

            if (enemiesDestroyed >= 50 && !boss) {
                boss = createBoss();
            }

            if (keys['Space'] && playerBullets.length < 5) {
                playerBullets.push({ x: player.x + player.width / 2 - 2.5, y: player.y });
            }

            requestAnimationFrame(gameLoop);
        }

        gameLoop();
    </script>
</body>
</html>

このHTMLファイルには、ご要望のすべての機能を含むシューティングゲームが実装されています。主な特徴は以下の通りです:

  1. 星空の背景

  2. プレイヤーの操作(矢印キーで移動、スペースキーで射撃)

  3. 敵機の出現と攻撃

  4. 得点表示

  5. 敵機や弾に当たるとゲームオーバー

  6. 50機の敵を倒すとボスキャラ出現

  7. ボスキャラの8方向攻撃

  8. ボスキャラを倒すとゲームクリア

このゲームをプレイするには、HTMLファイルをブラウザで開くだけです。ゲームの難易度や見た目をさらに調整したい場合は、JavaScriptやCSSを編集することで簡単にカスタマイズできます。

何か追加の機能や変更点があればお知らせください。
(ここまでがClaudeの回答)

HTMLアーティファクトの場合は、ダウンロードしたファイルをブラウザで開くと、そのまま大きな画面でゲームをプレーすることができます。


2.Reactでのゲーム作成

Reactも使用しても、ゲームを作成することができます。
今度は、マウスで操作して、クリックすると弾を撃つことができます。

Reactは、ユーザーインターフェースをコンポーネントに分割して効率的に構築するJavaScriptライブラリで、高速なレンダリングを実現し、JSXというHTMLのような構文を用いて直感的にコードを書くことができます。

Claudeに指示するためのプロンプトはHTMLアーティファクトの場合と基本的に同じです。

Reactでシューティングゲームを作成して。
・得点を表示し、敵の機体や弾に当たったら「GAME OVER」と表示してゲームを終了。
・敵の機体も下に向けて弾を撃ってくる。
・背景は星空。
・敵を20機撃つと、図形が組み合わさった複雑な形をしたボスキャラが登場する。・ボスキャラは8方向に向けて沢山の弾を撃ってくる。
・ボスキャラはユーザーの弾が30発当たると爆発し、「Congratulations!!」と画面に表示され、最終得点を表示してゲームを終了する。

以下はClaudeの回答です。

シューティングゲームを React で作成するというのは素晴らしいプロジェクトですね。ご要望の機能を含む基本的なゲームを作成してみましょう。
(コードは省略)

このReactコンポーネントは、要求された機能を持つ基本的なシューティングゲームを実装しています。主な特徴は以下の通りです:

  1. プレイヤーの宇宙船をマウスで操作し、クリックで弾を発射します。

  2. 敵の宇宙船が上から現れ、下に向かって弾を発射します。

  3. 背景は星空になっています。

  4. スコアが表示され、敵の弾に当たるとゲームオーバーになります。

  5. 50機の敵を倒すと、ボスキャラが登場します。

  6. ボスキャラは8方向に弾を発射します。

  7. ボスキャラに30発の弾を当てると、ゲームクリアとなり、「Congratulations!!」と最終スコアが表示されます。

このゲームをさらに改善したり、機能を追加したりする方法はたくさんあります。例えば:

  • グラフィックスの改善(SVGやカスタムイメージの使用)

  • サウンドエフェクトとBGMの追加

  • 難易度レベルの実装

  • パワーアップアイテムの追加

  • ハイスコアの保存と表示

これらの機能を追加したい場合や、ゲームの特定の部分について質問がある場合は、お知らせください。
(ここまでがClaudeの回答)

エラーが発生して、ゲームが動かなかったので、エラーの修正を依頼しました。エラーメッセージをそのまま貼り付けて、修正を指示します。

エラーを修正して
「Unsupported Libraries or Icons Detected The generated code includes unsupported libraries: * { Motion } from "lucide-react" The code itself may still be valid and functional. However, due to the missing components, we are unable to render or execute it in this environment.」

以下はClaudeの回答です。

申し訳ありません。そのエラーを修正しましょう。`Motion`アイコンは`lucide-react`ライブラリの一部ですが、このライブラリが利用できない場合があります。代わりに、簡単な形状を使用してプレイヤーと敵を表現することができます。

以下に、`Motion`アイコンを使用せずに同様の機能を持つ修正版のコードを提供します。

import React, { useState, useEffect, useCallback } from 'react';
import { Star } from 'lucide-react';

const GAME_WIDTH = 800;
const GAME_HEIGHT = 600;
const PLAYER_SIZE = 40;
const ENEMY_SIZE = 30;
const BULLET_SIZE = 5;
const BOSS_SIZE = 100;
const BOSS_APPEARANCE_SCORE = 20;  // Changed from 50 to 20

const Game = () => {
  const [player, setPlayer] = useState({ x: GAME_WIDTH / 2, y: GAME_HEIGHT - PLAYER_SIZE });
  const [playerBullets, setPlayerBullets] = useState([]);
  const [enemies, setEnemies] = useState([]);
  const [enemyBullets, setEnemyBullets] = useState([]);
  const [score, setScore] = useState(0);
  const [gameOver, setGameOver] = useState(false);
  const [boss, setBoss] = useState(null);
  const [bossBullets, setBossBullets] = useState([]);
  const [bossHealth, setBossHealth] = useState(30);
  const [congratulations, setCongratulations] = useState(false);

  const movePlayer = useCallback((e) => {
    if (gameOver) return;
    const { clientX } = e;
    const newX = clientX - PLAYER_SIZE / 2;
    setPlayer(prev => ({ ...prev, x: Math.max(0, Math.min(newX, GAME_WIDTH - PLAYER_SIZE)) }));
  }, [gameOver]);

  const shootPlayerBullet = useCallback(() => {
    if (gameOver) return;
    setPlayerBullets(prev => [...prev, { x: player.x + PLAYER_SIZE / 2, y: player.y }]);
  }, [player, gameOver]);

  useEffect(() => {
    window.addEventListener('mousemove', movePlayer);
    window.addEventListener('click', shootPlayerBullet);
    return () => {
      window.removeEventListener('mousemove', movePlayer);
      window.removeEventListener('click', shootPlayerBullet);
    };
  }, [movePlayer, shootPlayerBullet]);

  useEffect(() => {
    if (gameOver || congratulations) return;

    const gameLoop = setInterval(() => {
      // Move player bullets
      setPlayerBullets(prev => prev.map(bullet => ({ ...bullet, y: bullet.y - 5 })).filter(bullet => bullet.y > 0));

      // Move enemy bullets
      setEnemyBullets(prev => prev.map(bullet => ({ ...bullet, y: bullet.y + 3 })).filter(bullet => bullet.y < GAME_HEIGHT));

      // Move boss bullets
      setBossBullets(prev => prev.map(bullet => ({
        ...bullet,
        x: bullet.x + bullet.dx,
        y: bullet.y + bullet.dy
      })).filter(bullet => bullet.x > 0 && bullet.x < GAME_WIDTH && bullet.y > 0 && bullet.y < GAME_HEIGHT));

      // Move enemies
      setEnemies(prev => prev.map(enemy => ({ ...enemy, y: enemy.y + 1 })).filter(enemy => enemy.y < GAME_HEIGHT));

      // Spawn new enemies
      if (Math.random() < 0.02 && enemies.length < 5 && !boss) {
        setEnemies(prev => [...prev, { x: Math.random() * (GAME_WIDTH - ENEMY_SIZE), y: 0 }]);
      }

      // Enemy shooting
      enemies.forEach(enemy => {
        if (Math.random() < 0.01) {
          setEnemyBullets(prev => [...prev, { x: enemy.x + ENEMY_SIZE / 2, y: enemy.y + ENEMY_SIZE }]);
        }
      });

      // Boss shooting
      if (boss && Math.random() < 0.1) {
        const angles = [0, 45, 90, 135, 180, 225, 270, 315];
        angles.forEach(angle => {
          const radians = angle * Math.PI / 180;
          setBossBullets(prev => [...prev, {
            x: boss.x + BOSS_SIZE / 2,
            y: boss.y + BOSS_SIZE / 2,
            dx: Math.cos(radians) * 3,
            dy: Math.sin(radians) * 3
          }]);
        });
      }

      // Collision detection
      setPlayerBullets(prevBullets => {
        let newBullets = [...prevBullets];
        setEnemies(prevEnemies => {
          let newEnemies = [...prevEnemies];
          newBullets = newBullets.filter(bullet => {
            let bulletHit = false;
            newEnemies = newEnemies.filter(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
              ) {
                setScore(prev => prev + 1);
                bulletHit = true;
                return false;  // remove this enemy
              }
              return true;  // keep this enemy
            });
            return !bulletHit;  // keep the bullet if it didn't hit anything
          });
          return newEnemies;
        });
        return newBullets;
      });

      if (boss) {
        setPlayerBullets(prevBullets => 
          prevBullets.filter(bullet => {
            if (
              bullet.x < boss.x + BOSS_SIZE &&
              bullet.x + BULLET_SIZE > boss.x &&
              bullet.y < boss.y + BOSS_SIZE &&
              bullet.y + BULLET_SIZE > boss.y
            ) {
              setBossHealth(prev => prev - 1);
              if (bossHealth <= 1) {
                setBoss(null);
                setCongratulations(true);
              }
              return false;  // remove this bullet
            }
            return true;  // keep this bullet
          })
        );
      }

      // Player collision with enemy bullets
      enemyBullets.forEach(bullet => {
        if (
          bullet.x < player.x + PLAYER_SIZE &&
          bullet.x + BULLET_SIZE > player.x &&
          bullet.y < player.y + PLAYER_SIZE &&
          bullet.y + BULLET_SIZE > player.y
        ) {
          setGameOver(true);
        }
      });

      // Player collision with boss bullets
      bossBullets.forEach(bullet => {
        if (
          bullet.x < player.x + PLAYER_SIZE &&
          bullet.x + BULLET_SIZE > player.x &&
          bullet.y < player.y + PLAYER_SIZE &&
          bullet.y + BULLET_SIZE > player.y
        ) {
          setGameOver(true);
        }
      });

      // Spawn boss
      if (score >= BOSS_APPEARANCE_SCORE && !boss) {
        setBoss({ x: GAME_WIDTH / 2 - BOSS_SIZE / 2, y: 50 });
      }
    }, 1000 / 60);

    return () => clearInterval(gameLoop);
  }, [player, playerBullets, enemies, enemyBullets, boss, bossBullets, score, gameOver, congratulations, bossHealth]);

  return (
    <div style={{ width: GAME_WIDTH, height: GAME_HEIGHT, background: '#000', position: 'relative', overflow: 'hidden' }}>
      {/* Stars */}
      {[...Array(100)].map((_, i) => (
        <Star
          key={i}
          size={Math.random() * 2 + 1}
          style={{
            position: 'absolute',
            left: Math.random() * GAME_WIDTH,
            top: Math.random() * GAME_HEIGHT,
            color: '#fff'
          }}
        />
      ))}

      {/* Player */}
      <div
        style={{
          position: 'absolute',
          left: player.x,
          top: player.y,
          width: PLAYER_SIZE,
          height: PLAYER_SIZE,
          background: '#00f',
          clipPath: 'polygon(50% 0%, 0% 100%, 100% 100%)'
        }}
      />

      {/* Player Bullets */}
      {playerBullets.map((bullet, index) => (
        <div
          key={index}
          style={{
            position: 'absolute',
            left: bullet.x,
            top: bullet.y,
            width: BULLET_SIZE,
            height: BULLET_SIZE,
            background: '#0f0',
            borderRadius: '50%'
          }}
        />
      ))}

      {/* Enemies */}
      {enemies.map((enemy, index) => (
        <div
          key={index}
          style={{
            position: 'absolute',
            left: enemy.x,
            top: enemy.y,
            width: ENEMY_SIZE,
            height: ENEMY_SIZE,
            background: '#f00',
            clipPath: 'polygon(50% 0%, 0% 100%, 100% 100%)',
            transform: 'rotate(180deg)'
          }}
        />
      ))}

      {/* Enemy Bullets */}
      {enemyBullets.map((bullet, index) => (
        <div
          key={index}
          style={{
            position: 'absolute',
            left: bullet.x,
            top: bullet.y,
            width: BULLET_SIZE,
            height: BULLET_SIZE,
            background: '#f00',
            borderRadius: '50%'
          }}
        />
      ))}

      {/* Boss */}
      {boss && (
        <div
          style={{
            position: 'absolute',
            left: boss.x,
            top: boss.y,
            width: BOSS_SIZE,
            height: BOSS_SIZE,
            background: '#f0f',
            clipPath: 'polygon(50% 0%, 0% 100%, 100% 100%)'
          }}
        />
      )}

      {/* Boss Bullets */}
      {bossBullets.map((bullet, index) => (
        <div
          key={index}
          style={{
            position: 'absolute',
            left: bullet.x,
            top: bullet.y,
            width: BULLET_SIZE,
            height: BULLET_SIZE,
            background: '#ff0',
            borderRadius: '50%'
          }}
        />
      ))}

      {/* Score */}
      <div style={{ position: 'absolute', top: 10, left: 10, color: '#fff', fontSize: 20 }}>
        Score: {score}
      </div>

      {/* Game Over */}
      {gameOver && (
        <div style={{
          position: 'absolute',
          top: '50%',
          left: '50%',
          transform: 'translate(-50%, -50%)',
          color: '#fff',
          fontSize: 40,
          textAlign: 'center'
        }}>
          GAME OVER
          <div style={{ fontSize: 24 }}>Final Score: {score}</div>
        </div>
      )}

      {/* Congratulations */}
      {congratulations && (
        <div style={{
          position: 'absolute',
          top: '50%',
          left: '50%',
          transform: 'translate(-50%, -50%)',
          color: '#fff',
          fontSize: 40,
          textAlign: 'center'
        }}>
          Congratulations!!
          <div style={{ fontSize: 24 }}>Final Score: {score}</div>
        </div>
      )}
    </div>
  );
};

export default Game;

この更新版では、以下の変更を行いました:

  1. `Motion`コンポーネントの代わりに、`div`要素とCSS `clipPath`プロパティを使用してプレイヤーと敵の形状を作成しました。

  2. プレイヤーの宇宙船は上向きの三角形、敵の宇宙船は下向きの三角形として表現しています。

  3. ボスキャラクターも同様に`div`要素で表現しています。

これらの変更により、`lucide-react`ライブラリの`Motion`コンポーネントへの依存がなくなり、エラーが解消されるはずです。ゲームの基本的な機能と外観は以前のバージョンと同様に維持されています。

このバージョンでゲームが正しく動作するはずですが、さらに調整や機能追加が必要な場合は、お知らせください。
(ここまでがClaudeの回答)


3.Three.jsを使用した3Dゲーム作成

Three.jsライブラリを使用して、3Dシューティングゲームを作成することもできます。

Three.jsは、JavaScriptで3Dグラフィックスを簡単に描画できるライブラリです。3Dゲームなどの3Dコンテンツの作成をClaudeに指示すると、ほぼ自動的にThree.jsが使用されます。

HTMLで絵文字を使った3Dシューティングゲームを作成して

以下はClaudeの回答です。

HTMLと関連技術を使って3Dシューティングゲームを作成するのは面白いアイデアですね。絵文字を使うことで、ビジュアル面でユニークな要素を加えることができます。このゲームを実装するために、HTML、CSS、JavaScriptを組み合わせて使用します。
(コードは省略)

このHTMLファイルには、Three.jsライブラリを使用した基本的な3Dシューティングゲームが含まれています。ゲームの主な特徴は以下の通りです:

  1. プレイヤーは画面下部の🚀絵文字をマウスで操作します。

  2. クリックすると💥絵文字の弾を発射します。

  3. 🎯、🛸、👾などのターゲット絵文字がランダムに現れます。

  4. 弾がターゲットに当たるとスコアが増加します。

ゲームを改善するためのいくつかのアイデアがあります:

  1. 難易度の調整:時間とともにターゲットの速度や出現頻度を増やす。

  2. パワーアップ:特殊な絵文字を追加し、一時的な能力強化を提供する。

  3. サウンドエフェクト:射撃や衝突時に音を追加する。

  4. モバイル対応:タッチスクリーンデバイス用の制御を追加する。

これらの機能を追加したり、ゲームプレイを調整したりする方法について、さらに詳しく知りたい場合はお知らせください。
(ここまでがClaudeの回答)

さらにプロンプトで指示をして修正を加えます。

背景を宇宙にして大きな土星の絵文字を配置して

以下はClaudeの回答です。

もちろんです。背景を宇宙にし、大きな土星の絵文字を配置するアイデアは素晴らしいですね。これによってゲームの雰囲気がより宇宙らしくなります。早速、このアイデアを実装しましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>宇宙絵文字3Dシューター</title>
    <style>
        body { margin: 0; overflow: hidden; }
        #gameCanvas { width: 100%; height: 100%; }
        #score { position: absolute; top: 10px; left: 10px; color: white; font-size: 24px; }
    </style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    <div id="score">スコア: 0</div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        let scene, camera, renderer, player, targets = [], score = 0;
        const emojis = ['🎯', '🚀', '💥', '🛸', '👾'];

        function init() {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            renderer = new THREE.WebGLRenderer({canvas: document.getElementById('gameCanvas')});
            renderer.setSize(window.innerWidth, window.innerHeight);

            // 宇宙の背景を追加
            const spaceTexture = new THREE.TextureLoader().load('/api/placeholder/2048/1024');
            scene.background = spaceTexture;

            player = createEmoji('🚀', 0, 0, 5);
            scene.add(player);

            // 大きな土星を追加
            const saturn = createEmoji('🪐', 0, 0, -20);
            saturn.scale.set(10, 10, 1); // 土星を大きくする
            scene.add(saturn);

            camera.position.z = 10;

            // 星を追加
            addStars();

            window.addEventListener('mousemove', onMouseMove);
            window.addEventListener('click', shoot);

            animate();
        }

        function createEmoji(emoji, x, y, z) {
            const canvas = document.createElement('canvas');
            canvas.width = 256;
            canvas.height = 256;
            const context = canvas.getContext('2d');
            context.font = '200px Arial';
            context.fillText(emoji, 28, 200);

            const texture = new THREE.CanvasTexture(canvas);
            const material = new THREE.SpriteMaterial({map: texture});
            const sprite = new THREE.Sprite(material);
            sprite.position.set(x, y, z);
            sprite.scale.set(1, 1, 1);
            return sprite;
        }

        function addStars() {
            for (let i = 0; i < 200; i++) {
                const geometry = new THREE.SphereGeometry(0.1, 24, 24);
                const material = new THREE.MeshBasicMaterial({color: 0xffffff});
                const star = new THREE.Mesh(geometry, material);

                const [x, y, z] = Array(3).fill().map(() => THREE.MathUtils.randFloatSpread(100));
                star.position.set(x, y, z);
                scene.add(star);
            }
        }

        function onMouseMove(event) {
            const x = (event.clientX / window.innerWidth) * 2 - 1;
            const y = -(event.clientY / window.innerHeight) * 2 + 1;
            player.position.x = x * 5;
            player.position.y = y * 3;
        }

        function shoot() {
            const bullet = createEmoji('💥', player.position.x, player.position.y, player.position.z);
            scene.add(bullet);
            bullet.velocity = new THREE.Vector3(0, 0, -0.5);
        }

        function spawnTarget() {
            const x = (Math.random() - 0.5) * 10;
            const y = (Math.random() - 0.5) * 6;
            const target = createEmoji(emojis[Math.floor(Math.random() * emojis.length)], x, y, -15);
            scene.add(target);
            targets.push(target);
        }

        function animate() {
            requestAnimationFrame(animate);

            if (Math.random() < 0.02) spawnTarget();

            scene.children.forEach(child => {
                if (child.velocity) child.position.add(child.velocity);
            });

            targets.forEach((target, index) => {
                target.position.z += 0.05;
                if (target.position.z > 10) {
                    scene.remove(target);
                    targets.splice(index, 1);
                }
            });

            scene.children.forEach(child => {
                if (child.velocity) {
                    targets.forEach((target, index) => {
                        if (child.position.distanceTo(target.position) < 0.5) {
                            scene.remove(child);
                            scene.remove(target);
                            targets.splice(index, 1);
                            score += 10;
                            document.getElementById('score').textContent = `スコア: ${score}`;
                        }
                    });
                }
            });

            renderer.render(scene, camera);
        }

        init();
    </script>
</body>
</html>

このバージョンのゲームでは、以下の変更を加えました:

  1. 宇宙の背景:`scene.background`に宇宙のテクスチャを設定しました。実際のゲームでは、適切な宇宙の画像を使用する必要があります。

  2. 大きな土星:`🪐`絵文字を使用して大きな土星を作成し、背景に配置しました。`saturn.scale.set(10, 10, 1)`で土星を大きくしています。

  3. 星の追加:`addStars()`関数を作成し、小さな白い球体を多数配置することで、星空の効果を出しています。

  4. ターゲットの出現位置:ターゲットがより遠くから出現するように、z座標を-15に設定しました。

これらの変更により、ゲームはより宇宙らしい雰囲気になりました。プレイヤーは宇宙空間を飛行しながら、様々な絵文字のターゲットを撃つことになります。

さらに改善するためのアイデア:

  1. 土星を少しずつ回転させる。

  2. 星をきらきらと点滅させる。

  3. 背景に小惑星や他の惑星の絵文字を追加する。

  4. プレイヤーの宇宙船に簡単なパーティクルエフェクトを追加して、エンジンの炎のように見せる。

これらの機能を追加したり、他の要素を改善したりしたい場合は、お知らせください。
(ここまでがClaudeの回答)

シンプルですが、絵文字を使った3Dシューティングゲームができました。ここから修正して、さらに複雑で面白いゲームにしたかったのですが、修正はあまり上手くいきませんでした。


4.ChatGPTで作ったゲームをアーティファクトで実行

Claudeのアーティファクトはコードの実行環境として便利なので、ChatGPT(GPT-4o)で作成したゲームのコードをアーティファクトで実行してみました。

(1) ChatGPTでシューティングゲームのコード作成

javascriptでシューティングゲームを作って

以下はChatGPTの回答です。

JavaScriptで簡単なシューティングゲームを作る方法を説明します。今回はHTML5のCanvasを使って、プレイヤーキャラクターが弾を撃つシンプルなゲームを作ります。

以下はその基本的なコード例です:

1.HTMLファイルを作成
(コードを省略)

2.JavaScriptファイル (game.js) を作成
(コードを省略)

このコードでは、以下の機能を実装しています:

  1. HTMLとCanvasのセットアップ。

  2. プレイヤーキャラクターの描画と移動。

  3. 弾を発射し、画面上で弾が移動する。

  4. 簡単なキーイベント処理(矢印キーで移動、スペースキーで弾を発射)。

これをさらに発展させるためには、敵キャラクターの追加、衝突判定、スコアシステムなどを実装すると良いでしょう。
(ここまでがChatGPTの回答)

これだけでは、敵も現れなくて、つまらないので、機能を追加します。

敵キャラクターの追加、衝突判定、スコアシステムのコードも書いて

以下はChatGPTの回答です。

では、敵キャラクターの追加、弾との衝突判定、スコアシステムを含む完全なコードを提供します。

完全なコード

  1. HTMLファイル

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>シューティングゲーム</title>
    <style>
        canvas {
            background: #000;
            display: block;
            margin: 0 auto;
        }
        #score {
            color: white;
            text-align: center;
            font-size: 24px;
        }
    </style>
</head>
<body>
    <div id="score">Score: 0</div>
    <canvas id="gameCanvas" width="800" height="600"></canvas>
    <script src="game.js"></script>
</body>
</html>
  1. JavaScriptファイル (game.js)

// canvasの設定
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// プレイヤーの設定
const player = {
    x: canvas.width / 2,
    y: canvas.height - 50,
    width: 50,
    height: 50,
    speed: 5,
    dx: 0,
    dy: 0
};

// 弾の設定
const bullets = [];
const bulletSpeed = 7;
const bulletWidth = 5;
const bulletHeight = 10;

// 敵の設定
const enemies = [];
const enemyWidth = 50;
const enemyHeight = 50;
const enemySpeed = 2;
const enemyInterval = 2000;
let lastEnemySpawnTime = 0;

// スコアの設定
let score = 0;
const scoreElement = document.getElementById('score');

// キーイベントの設定
document.addEventListener('keydown', keyDown);
document.addEventListener('keyup', keyUp);

function keyDown(e) {
    if (e.key === 'ArrowRight' || e.key === 'Right') {
        player.dx = player.speed;
    } else if (e.key === 'ArrowLeft' || e.key === 'Left') {
        player.dx = -player.speed;
    } else if (e.key === ' ') {
        shoot();
    }
}

function keyUp(e) {
    if (e.key === 'ArrowRight' || e.key === 'Right' || e.key === 'ArrowLeft' || e.key === 'Left') {
        player.dx = 0;
    }
}

// 弾を発射
function shoot() {
    bullets.push({
        x: player.x + player.width / 2 - bulletWidth / 2,
        y: player.y,
        width: bulletWidth,
        height: bulletHeight,
        dy: -bulletSpeed
    });
}

// プレイヤーを描画
function drawPlayer() {
    ctx.fillStyle = 'blue';
    ctx.fillRect(player.x, player.y, player.width, player.height);
}

// 弾を描画
function drawBullets() {
    ctx.fillStyle = 'red';
    bullets.forEach((bullet, index) => {
        bullet.y += bullet.dy;
        ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);

        // 画面外の弾を削除
        if (bullet.y < 0) {
            bullets.splice(index, 1);
        }
    });
}

// 敵を生成
function createEnemy() {
    const x = Math.random() * (canvas.width - enemyWidth);
    enemies.push({
        x: x,
        y: 0,
        width: enemyWidth,
        height: enemyHeight,
        dy: enemySpeed
    });
}

// 敵を描画
function drawEnemies() {
    ctx.fillStyle = 'green';
    enemies.forEach((enemy, index) => {
        enemy.y += enemy.dy;
        ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);

        // 画面外の敵を削除
        if (enemy.y > canvas.height) {
            enemies.splice(index, 1);
        }
    });
}

// 衝突判定
function checkCollisions() {
    bullets.forEach((bullet, bulletIndex) => {
        enemies.forEach((enemy, enemyIndex) => {
            if (bullet.x < enemy.x + enemy.width &&
                bullet.x + bullet.width > enemy.x &&
                bullet.y < enemy.y + enemy.height &&
                bullet.y + bullet.height > enemy.y) {
                // 衝突した弾と敵を削除
                bullets.splice(bulletIndex, 1);
                enemies.splice(enemyIndex, 1);

                // スコアを更新
                score += 10;
                scoreElement.textContent = `Score: ${score}`;
            }
        });
    });
}

// プレイヤーを更新
function updatePlayer() {
    player.x += player.dx;

    // 壁の衝突判定
    if (player.x < 0) {
        player.x = 0;
    } else if (player.x + player.width > canvas.width) {
        player.x = canvas.width - player.width;
    }
}

// ゲームループ
function update(timestamp) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawPlayer();
    drawBullets();
    drawEnemies();
    updatePlayer();
    checkCollisions();

    // 敵を生成
    if (timestamp - lastEnemySpawnTime > enemyInterval) {
        createEnemy();
        lastEnemySpawnTime = timestamp;
    }

    requestAnimationFrame(update);
}

// ゲームを開始
update();

説明

  1. 敵キャラクターの生成:

    • `createEnemy`関数でランダムな位置に敵を生成。

    • `drawEnemies`関数で敵を描画し、画面外に出た敵を削除。

  2. 衝突判定:

    • `checkCollisions`関数で弾と敵の衝突をチェックし、衝突が発生した場合、弾と敵を削除し、スコアを更新。

  3. スコアシステム:

    • スコアを`score`変数で管理し、HTML内の`scoreElement`を更新。

このコードを使うことで、プレイヤーが弾を撃ち、敵キャラクターを倒してスコアを稼ぐ基本的なシューティングゲームが完成します。これを元に、さらに多くの機能を追加してカスタマイズすることも可能です。
(ここまでがChatGPTの回答)

(2) コードをアーティファクトで実行

ChatGPTが作成したコードをClaudeのアーティファクトで実行します。

以下のコードを1つのファイルでhtmlで実行できるようにして。
コード:(ChatGPTが作成した2種類のコードをコピペ)

以下はClaudeの回答です。

はい、HTMLファイルに統合して1つのファイルで実行できるようにします。以下が統合されたHTMLファイルです:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>シューティングゲーム</title>
    <style>
        canvas {
            background: #000;
            display: block;
            margin: 0 auto;
        }
        #score {
            color: white;
            text-align: center;
            font-size: 24px;
        }
    </style>
</head>
<body>
    <div id="score">Score: 0</div>
    <canvas id="gameCanvas" width="800" height="600"></canvas>
    <script>
        // canvasの設定
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');

        // プレイヤーの設定
        const player = {
            x: canvas.width / 2,
            y: canvas.height - 50,
            width: 50,
            height: 50,
            speed: 5,
            dx: 0,
            dy: 0
        };

        // 弾の設定
        const bullets = [];
        const bulletSpeed = 7;
        const bulletWidth = 5;
        const bulletHeight = 10;

        // 敵の設定
        const enemies = [];
        const enemyWidth = 50;
        const enemyHeight = 50;
        const enemySpeed = 2;
        const enemyInterval = 2000;
        let lastEnemySpawnTime = 0;

        // スコアの設定
        let score = 0;
        const scoreElement = document.getElementById('score');

        // キーイベントの設定
        document.addEventListener('keydown', keyDown);
        document.addEventListener('keyup', keyUp);

        function keyDown(e) {
            if (e.key === 'ArrowRight' || e.key === 'Right') {
                player.dx = player.speed;
            } else if (e.key === 'ArrowLeft' || e.key === 'Left') {
                player.dx = -player.speed;
            } else if (e.key === ' ') {
                shoot();
            }
        }

        function keyUp(e) {
            if (e.key === 'ArrowRight' || e.key === 'Right' || e.key === 'ArrowLeft' || e.key === 'Left') {
                player.dx = 0;
            }
        }

        // 弾を発射
        function shoot() {
            bullets.push({
                x: player.x + player.width / 2 - bulletWidth / 2,
                y: player.y,
                width: bulletWidth,
                height: bulletHeight,
                dy: -bulletSpeed
            });
        }

        // プレイヤーを描画
        function drawPlayer() {
            ctx.fillStyle = 'blue';
            ctx.fillRect(player.x, player.y, player.width, player.height);
        }

        // 弾を描画
        function drawBullets() {
            ctx.fillStyle = 'red';
            bullets.forEach((bullet, index) => {
                bullet.y += bullet.dy;
                ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);

                // 画面外の弾を削除
                if (bullet.y < 0) {
                    bullets.splice(index, 1);
                }
            });
        }

        // 敵を生成
        function createEnemy() {
            const x = Math.random() * (canvas.width - enemyWidth);
            enemies.push({
                x: x,
                y: 0,
                width: enemyWidth,
                height: enemyHeight,
                dy: enemySpeed
            });
        }

        // 敵を描画
        function drawEnemies() {
            ctx.fillStyle = 'green';
            enemies.forEach((enemy, index) => {
                enemy.y += enemy.dy;
                ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);

                // 画面外の敵を削除
                if (enemy.y > canvas.height) {
                    enemies.splice(index, 1);
                }
            });
        }

        // 衝突判定
        function checkCollisions() {
            bullets.forEach((bullet, bulletIndex) => {
                enemies.forEach((enemy, enemyIndex) => {
                    if (bullet.x < enemy.x + enemy.width &&
                        bullet.x + bullet.width > enemy.x &&
                        bullet.y < enemy.y + enemy.height &&
                        bullet.y + bullet.height > enemy.y) {
                        // 衝突した弾と敵を削除
                        bullets.splice(bulletIndex, 1);
                        enemies.splice(enemyIndex, 1);

                        // スコアを更新
                        score += 10;
                        scoreElement.textContent = `Score: ${score}`;
                    }
                });
            });
        }

        // プレイヤーを更新
        function updatePlayer() {
            player.x += player.dx;

            // 壁の衝突判定
            if (player.x < 0) {
                player.x = 0;
            } else if (player.x + player.width > canvas.width) {
                player.x = canvas.width - player.width;
            }
        }

        // ゲームループ
        function update(timestamp) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawPlayer();
            drawBullets();
            drawEnemies();
            updatePlayer();
            checkCollisions();

            // 敵を生成
            if (timestamp - lastEnemySpawnTime > enemyInterval) {
                createEnemy();
                lastEnemySpawnTime = timestamp;
            }

            requestAnimationFrame(update);
        }

        // ゲームを開始
        update();
    </script>
</body>
</html>

このHTMLファイルには、元のHTMLとJavaScriptのコードが統合されています。これで、このファイルを直接ブラウザで開くことで、シューティングゲームを実行できます。

ゲームの遊び方:

  1. 左右の矢印キーでプレイヤーを移動させます。

  2. スペースキーで弾を発射します。

  3. 上から降ってくる緑の敵を撃ち落とし、スコアを稼ぎます。

このファイルをローカルに保存し、ウェブブラウザで開くだけでゲームを楽しむことができます。ゲームの動作や見た目を変更したい場合は、HTMLファイル内のJavaScriptコードを直接編集することができます。
(ここまでがClaudeの回答)

以下がアーティファクトのコード実行画面です。

敵が弾を撃ってこず、スコア表示もないシンプルなゲームですが、一応、動かすことができました。


5.Geminiで作ったゲームをアーティファクトで実行

同じようにGoogle AI StudioGemini 1.5 Proで作成したゲームのコードをアーティファクトで実行してみました。

(1) Geminiでシューティングゲームのコード作成

javascriptでシューティングゲームを作成して

以下はGeminiの回答です。

      const canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");

// キーボード入力
const keys = {};
document.addEventListener("keydown", (e) => keys[e.code] = true);
document.addEventListener("keyup", (e) => keys[e.code] = false);

// プレイヤー
const player = {
  x: canvas.width / 2 - 25,
  y: canvas.height - 75,
  width: 50,
  height: 50,
  speed: 5,
};

// 弾丸
const bullets = [];
const bulletSpeed = 10;

// 敵
const enemies = [];
const enemySpawnRate = 50; // 敵の出現頻度 (低いほど頻繁に出現)

// ゲームループ
function gameLoop() {
  requestAnimationFrame(gameLoop);
  update();
  render();
}

// 更新処理
function update() {
  // プレイヤーの移動
  if (keys["ArrowLeft"] && player.x > 0) {
    player.x -= player.speed;
  }
  if (keys["ArrowRight"] && player.x < canvas.width - player.width) {
    player.x += player.speed;
  }
  // 弾丸の発射
  if (keys["Space"] && bullets.length < 5) { // 最大5発まで
    bullets.push({
      x: player.x + player.width / 2 - 5,
      y: player.y,
      width: 10,
      height: 20,
    });
  }

  // 弾丸の移動
  for (let i = 0; i < bullets.length; i++) {
    bullets[i].y -= bulletSpeed;
    // 画面外に出たら弾丸を削除
    if (bullets[i].y < 0) {
      bullets.splice(i, 1);
      i--;
    }
  }

  // 敵の出現
  if (Math.random() < 1 / enemySpawnRate) {
    enemies.push({
      x: Math.random() * (canvas.width - 50),
      y: -50,
      width: 50,
      height: 50,
      speed: 3,
    });
  }

  // 敵の移動
  for (let i = 0; i < enemies.length; i++) {
    enemies[i].y += enemies[i].speed;
    // 画面外に出たら敵を削除
    if (enemies[i].y > canvas.height) {
      enemies.splice(i, 1);
      i--;
    }
  }

  // 衝突判定 (弾丸と敵)
  for (let i = 0; i < bullets.length; i++) {
    for (let j = 0; j < enemies.length; j++) {
      if (
        bullets[i].x < enemies[j].x + enemies[j].width &&
        bullets[i].x + bullets[i].width > enemies[j].x &&
        bullets[i].y < enemies[j].y + enemies[j].height &&
        bullets[i].y + bullets[i].height > enemies[j].y
      ) {
        // 衝突した場合、弾丸と敵を削除
        bullets.splice(i, 1);
        i--;
        enemies.splice(j, 1);
        j--;
      }
    }
  }
}

// 描画処理
function render() {
  // 画面クリア
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // プレイヤーの描画
  ctx.fillStyle = "blue";
  ctx.fillRect(player.x, player.y, player.width, player.height);

  // 弾丸の描画
  ctx.fillStyle = "red";
  bullets.forEach((bullet) => {
    ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
  });

  // 敵の描画
  ctx.fillStyle = "green";
  enemies.forEach((enemy) => {
    ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
  });
}

// ゲーム開始
gameLoop();
    

content_copyUse code with caution.JavaScript

遊び方:

  • 左右の矢印キーでプレイヤーを動かす

  • スペースキーで弾丸を発射する

  • 敵を撃ち落とす

ポイント:

  • requestAnimationFrame を使用してスムーズなアニメーションを実現

  • キーボード入力は keydown と keyup イベントで処理

  • 弾丸と敵は配列で管理し、ループ処理で移動や衝突判定を行う

  • collisionDetection 関数で衝突判定を実装

拡張性:

  • 敵の種類を増やす

  • アイテムを追加する

  • スコアを実装する

  • 背景画像を追加する

参考資料:

このコードをHTMLファイルに貼り付けて、ブラウザで開くとシューティングゲームを楽しむことができます。
(ここまでがGeminiの回答)

(2) コードをアーティファクトで実行

Geminiが作成したコードをClaudeのアーティファクトで実行します。

以下のコードをhtmlで実行できるようにして。
コード:(Geminiが作成したコードをコピペ)

以下はClaudeの回答です。

はい、このJavaScriptコードをHTML内で実行できるようにします。以下にHTMLファイルの形式でコードを組み込みました。

<!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;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f0f0f0;
        }
        canvas {
            border: 1px solid #000;
        }
    </style>
</head>
<body>
    <script>
        const canvas = document.createElement("canvas");
        canvas.width = 800;
        canvas.height = 600;
        document.body.appendChild(canvas);
        const ctx = canvas.getContext("2d");

        // キーボード入力
        const keys = {};
        document.addEventListener("keydown", (e) => keys[e.code] = true);
        document.addEventListener("keyup", (e) => keys[e.code] = false);

        // プレイヤー
        const player = {
            x: canvas.width / 2 - 25,
            y: canvas.height - 75,
            width: 50,
            height: 50,
            speed: 5,
        };

        // 弾丸
        const bullets = [];
        const bulletSpeed = 10;

        // 敵
        const enemies = [];
        const enemySpawnRate = 50; // 敵の出現頻度 (低いほど頻繁に出現)

        // ゲームループ
        function gameLoop() {
            requestAnimationFrame(gameLoop);
            update();
            render();
        }

        // 更新処理
        function update() {
            // プレイヤーの移動
            if (keys["ArrowLeft"] && player.x > 0) {
                player.x -= player.speed;
            }
            if (keys["ArrowRight"] && player.x < canvas.width - player.width) {
                player.x += player.speed;
            }

            // 弾丸の発射
            if (keys["Space"] && bullets.length < 5) { // 最大5発まで
                bullets.push({
                    x: player.x + player.width / 2 - 5,
                    y: player.y,
                    width: 10,
                    height: 20,
                });
            }

            // 弾丸の移動
            for (let i = 0; i < bullets.length; i++) {
                bullets[i].y -= bulletSpeed;
                // 画面外に出たら弾丸を削除
                if (bullets[i].y < 0) {
                    bullets.splice(i, 1);
                    i--;
                }
            }

            // 敵の出現
            if (Math.random() < 1 / enemySpawnRate) {
                enemies.push({
                    x: Math.random() * (canvas.width - 50),
                    y: -50,
                    width: 50,
                    height: 50,
                    speed: 3,
                });
            }

            // 敵の移動
            for (let i = 0; i < enemies.length; i++) {
                enemies[i].y += enemies[i].speed;
                // 画面外に出たら敵を削除
                if (enemies[i].y > canvas.height) {
                    enemies.splice(i, 1);
                    i--;
                }
            }

            // 衝突判定 (弾丸と敵)
            for (let i = 0; i < bullets.length; i++) {
                for (let j = 0; j < enemies.length; j++) {
                    if (
                        bullets[i].x < enemies[j].x + enemies[j].width &&
                        bullets[i].x + bullets[i].width > enemies[j].x &&
                        bullets[i].y < enemies[j].y + enemies[j].height &&
                        bullets[i].y + bullets[i].height > enemies[j].y
                    ) {
                        // 衝突した場合、弾丸と敵を削除
                        bullets.splice(i, 1);
                        i--;
                        enemies.splice(j, 1);
                        j--;
                    }
                }
            }
        }

        // 描画処理
        function render() {
            // 画面クリア
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // プレイヤーの描画
            ctx.fillStyle = "blue";
            ctx.fillRect(player.x, player.y, player.width, player.height);

            // 弾丸の描画
            ctx.fillStyle = "red";
            bullets.forEach((bullet) => {
                ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
            });

            // 敵の描画
            ctx.fillStyle = "green";
            enemies.forEach((enemy) => {
                ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
            });
        }

        // ゲーム開始
        gameLoop();
    </script>
</body>
</html>

このHTMLファイルには、元のJavaScriptコードがすべて含まれています。以下の変更点があります:

  1. HTML構造を追加し、必要なメタタグとタイトルを設定しました。

  2. ゲームのキャンバスを中央に配置し、背景色を設定するためのCSSを追加しました。

  3. JavaScriptコードを`<script>`タグ内に配置しました。

このファイルをブラウザで開くと、すぐにゲームをプレイすることができます。プレイヤーは左右の矢印キーで移動し、スペースキーで弾を発射します。緑色の敵を倒すのが目的です。

ゲームの動作や見た目をさらに改善したい場合は、お知らせください。例えば、スコア表示の追加、ゲームオーバー条件の設定、グラフィックの改善などが考えられます。
(ここまでがGeminiの回答)

以下がアーティファクトのコード実行画面です。

敵が弾を撃ってこず、スコア表示もないのはChatGPTと同じですが、機体の動きや弾の連射はこちらの方がスムーズでした。シンプルなゲームですが、一応、動かすことができました。

さらに複雑なゲームを作成することもできますが、その場合は、エラー修正などが必要になります。


いいなと思ったら応援しよう!