スペースキー押下でランダム再描画です。
<!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>