2
3

加速器 陽子の衝突ゲーム。ボールの衝突後にパーティクルを生成。

Last updated at Posted at 2024-09-09

スクリーンショット 2024-09-10 044507.png

スクリーンショット 2024-09-10 044450.png

スペースキー押下でランダム再描画です。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>3D Ball Collision Simulation</title>
  <style>
    body { margin: 0; overflow: hidden; }
  </style>
</head>
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
  <script>
    let scene, camera, renderer, balls, particleSystems, collisionHappened;

    const PARTICLE_COUNT = 100;
    const PARTICLE_SPEED_MULTIPLIER = 2.8;  // パーティクルの速度倍率を上げる
    const BALL_RADIUS = 1;  // ボールの半径
    const BALL_MASS = [2, 5];  // ボールの質量 (例として2つのボールに異なる質量を設定)

    init();
    animate();

    // 初期化関数
    function init() {
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.z = 20;

      renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      resetSimulation(); // 初期のシミュレーション状態を設定

      window.addEventListener('keydown', (event) => {
        if (event.code === 'Space') {
          resetSimulation(); // スペースキー押下でランダム再描画
        }
      });
    }

    // シミュレーションをリセットする関数
    function resetSimulation() {
      if (balls) {
        balls.forEach(ball => scene.remove(ball.mesh));
      }
      if (particleSystems) {
        particleSystems.forEach(particles => {
          particles.forEach(p => scene.remove(p.mesh));
        });
      }

      // ボールを確実に衝突させるために位置と速度を調整
      balls = [
        createBall({ x: -10, y: 0, z: 0 }, { x: 0.5, y: 0, z: 0 }, BALL_MASS[0]),
        createBall({ x: 10, y: 0, z: 0 }, { x: -0.5, y: 0, z: 0 }, BALL_MASS[1])
      ];
      balls.forEach(ball => scene.add(ball.mesh));

      particleSystems = [];
      collisionHappened = false;
    }

    // ボールを作成する関数
    function createBall(position, velocity, mass) {
      const geometry = new THREE.SphereGeometry(BALL_RADIUS, 32, 32);
      const material = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff });  // マットな質感
      const mesh = new THREE.Mesh(geometry, material);
      mesh.position.set(position.x, position.y, position.z);
      return { mesh, velocity: new THREE.Vector3(velocity.x, velocity.y, velocity.z), mass: mass };
    }

    // 衝突後にパーティクルを生成する関数(衝突の力を考慮)
    function createParticles(ball) {
      const particles = [];
      for (let i = 0; i < PARTICLE_COUNT; i++) {
        const particleGeometry = new THREE.SphereGeometry(0.1, 16, 16);
        const particleMaterial = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff });  // マットな質感
        const particle = new THREE.Mesh(particleGeometry, particleMaterial);
        particle.position.copy(ball.mesh.position);

        // 衝突の力を考慮したパーティクルの初期速度
        const speed = Math.random() * PARTICLE_SPEED_MULTIPLIER + ball.velocity.length();  // ボールの速度を加味
        const angleXY = Math.random() * Math.PI * 2;
        const angleZ = (Math.random() - 0.5) * Math.PI;
        const velocity = new THREE.Vector3(
          Math.cos(angleXY) * Math.cos(angleZ) * speed,
          Math.sin(angleXY) * Math.cos(angleZ) * speed,
          Math.sin(angleZ) * speed
        );

        particles.push({ mesh: particle, velocity });
        scene.add(particle);
      }
      particleSystems.push(particles);
    }

    // パーティクルの更新処理
    function updateParticles() {
      particleSystems.forEach(particles => {
        particles.forEach(p => {
          p.mesh.position.add(p.velocity.clone().multiplyScalar(0.01));  // パーティクルの移動
        });
      });
    }

    // ボールの衝突判定
    function checkCollision() {
      const dist = balls[0].mesh.position.distanceTo(balls[1].mesh.position);
      return dist <= BALL_RADIUS * 2;
    }

    // アニメーション処理
    function animate() {
      requestAnimationFrame(animate);

      if (!collisionHappened) {
        balls.forEach(ball => {
          ball.mesh.position.add(ball.velocity);
        });

        if (checkCollision()) {
          collisionHappened = true;
          balls.forEach(ball => createParticles(ball));  // 衝突後パーティクル生成
          balls.forEach(ball => scene.remove(ball.mesh));  // ボールを削除

          // 衝突後に一定時間経ったらシミュレーションをリセット
          setTimeout(resetSimulation, 25000);  // 25秒後にリセット
        }
      }

      if (collisionHappened) {
        updateParticles();  // パーティクルの移動更新
      }

      renderer.render(scene, camera);
    }
  </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