2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お空を守る。自動迎撃レーザービームでカラフルなターゲットドローンを打ち落とすゲーム。

Last updated at Posted at 2024-10-18

スクリーンショット 2024-10-18 224248.png

スクリーンショット 2024-10-18 224309.png

レーザービームでターゲットドローンを打ち落とすゲーム。

上空に侵入した敵ドローン1000機に対して、可及的速やかにこれを排除してください。

スペースキーを押すと自動迎撃開始です。

(敵にレーザが命中すると10個の破片となり爆散します。)

レーザーで迎撃する設定なのでミサイルのような弾道計算が不要です。

コードがシンプルになります。

コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「index.html」として保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。

実行結果。

image.png

迎撃しないほうが良かった。!

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Particle Explosion Simulation</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

    <script>
        // シーン、カメラ、レンダラーをセットアップ
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 地面と空を設定
        const groundGeometry = new THREE.PlaneGeometry(1000, 1000);
        const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = -Math.PI / 2; // 横に倒して地面として使う
        ground.position.y = -100; // 地面の高さを設定
        scene.add(ground);

        scene.background = new THREE.Color(0x87ceeb); // 空の色を青に設定

        // パーティクルの配列を保持
        const particles = [];
        const explosions = []; // 爆発用の小さなパーティクル
        const particleCount = 1000; // パーティクルの数
        let shooting = false; // レーザー発射中かどうか
        let targetIndex = 0; // 次に打ち落とすパーティクルのインデックス

        // カメラの位置を設定
        camera.position.z = 200;

        // パーティクル用のジオメトリとマテリアルを定義
        const particleGeometry = new THREE.SphereGeometry(1, 8, 8);

        // パーティクルクラス
        class Particle {
            constructor(x, y, z, color) {
                this.mesh = new THREE.Mesh(particleGeometry, new THREE.MeshBasicMaterial({ color: color }));
                this.mesh.position.set(x, y, z); // 初期位置
                this.vx = (Math.random() - 0.5) * 2; // X方向の速度
                this.vy = (Math.random() - 0.5) * 2; // Y方向の速度
                this.vz = (Math.random() - 0.5) * 2; // Z方向の速度
                this.alive = true; // パーティクルが生存しているかどうか
                scene.add(this.mesh); // シーンに追加
            }

            // パーティクルの位置を更新
            update() {
                if (this.alive) {
                    this.mesh.position.x += this.vx;
                    this.mesh.position.y += this.vy;
                    this.mesh.position.z += this.vz;

                    // 画面の範囲内で動き続けるように反射させる
                    if (this.mesh.position.x > 100 || this.mesh.position.x < -100) this.vx *= -1;
                    if (this.mesh.position.y > 100 || this.mesh.position.y < -100) this.vy *= -1;
                    if (this.mesh.position.z > 100 || this.mesh.position.z < -100) this.vz *= -1;
                }
            }

            // パーティクルを打ち落とす
            shoot() {
                this.alive = false;
                scene.remove(this.mesh); // シーンから削除

                // 爆発(10個の小さなパーティクルを生成)
                for (let i = 0; i < 10; i++) {
                    const explosionParticle = new Particle(
                        this.mesh.position.x,
                        this.mesh.position.y,
                        this.mesh.position.z,
                        new THREE.Color(Math.random(), Math.random(), Math.random()) // ランダムな色
                    );
                    explosionParticle.vx = (Math.random() - 0.5) * 5; // 爆発の速度を大きめに
                    explosionParticle.vy = (Math.random() - 0.5) * 5;
                    explosionParticle.vz = (Math.random() - 0.5) * 5;
                    explosions.push(explosionParticle);
                }
            }
        }

        // パーティクルを作成
        function createParticles() {
            for (let i = 0; i < particleCount; i++) {
                const x = (Math.random() - 0.5) * 200;
                const y = (Math.random() - 0.5) * 200;
                const z = (Math.random() - 0.5) * 200;
                const color = new THREE.Color(Math.random(), Math.random(), Math.random()); // カラフルな色をランダム生成
                particles.push(new Particle(x, y, z, color));
            }
        }

        // レーザーを発射してパーティクルを打ち落とす
        function shootParticle() {
            if (targetIndex < particles.length) {
                const target = particles[targetIndex];
                if (target.alive) {
                    // レーザービームを描画(カメラの中心からターゲットへ)
                    const laserGeometry = new THREE.BufferGeometry();
                    const vertices = new Float32Array([
                        0, 0, 0, // カメラの位置(レーザー発射点)
                        target.mesh.position.x, target.mesh.position.y, target.mesh.position.z // パーティクルの位置(ターゲット)
                    ]);
                    laserGeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));

                    const laserMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
                    const laser = new THREE.Line(laserGeometry, laserMaterial);
                    scene.add(laser);

                    // パーティクルを打ち落とす
                    setTimeout(() => {
                        scene.remove(laser); // レーザーを消す
                        target.shoot(); // パーティクルを打ち落とす(爆発を含む)
                        targetIndex++; // 次のターゲットへ
                    }, 100); // 0.1秒後にパーティクルを消去
                } else {
                    targetIndex++;
                }
            } else {
                shooting = false; // 全てのパーティクルを打ち落としたら終了
                targetIndex = 0; // インデックスをリセット
            }
        }

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);

            // 全パーティクルを更新
            particles.forEach(particle => {
                particle.update();
            });

            // 爆発パーティクルを更新
            explosions.forEach(particle => {
                particle.update();
            });

            // レーザー発射中であれば、パーティクルを順に打ち落とす
            if (shooting) {
                shootParticle();
            }

            // シーンをレンダリング
            renderer.render(scene, camera);
        }

        // ユーザーがスペースキーを押したときの処理
        document.addEventListener('keydown', (e) => {
            if (e.code === 'Space') {
                shooting = true; // レーザー発射を開始
            }
        });

        // パーティクルを作成してアニメーション開始
        createParticles();
        animate();

        // ウィンドウサイズ変更時の対応
        window.addEventListener('resize', () => {
            renderer.setSize(window.innerWidth, window.innerHeight);
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
        });

    </script>
</body>
</html>

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?