3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Three.jsで太陽系っぽい3Dアプリを開発してみた

Last updated at Posted at 2024-11-01

はじめに

Three.jsで太陽系っぽい3Dアプリを開発しました。

デモ

画面:
スクリーンショット 2024-11-01 11.36.06.png

有名な衛星

有名な衛星も追加しました。

  • 地球の月
  • 木星のイオ
  • 木星のエウロパ
  • 木星のガニメデ
  • 木星のカリスト
  • 土星のエンケラドゥス

コード

solarSystem.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Solar System in 3D</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
        #controls { position: absolute; top: 10px; left: 10px; color: white; }
        #slider { width: 200px; }
        label { color: white; }
    </style>
</head>
<body>
    <div id="controls">
        <label for="speed">速度:</label>
        <input type="range" id="speed" name="speed" min="0.1" max="10" value="1" step="0.1">
        <br>
        <button id="camera-up"></button>
        <button id="camera-down"></button>
        <button id="camera-in">拡大</button>
        <button id="camera-out">縮小</button>
    </div>
    <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 pointLight = new THREE.PointLight(0xffffff, 1, 1000);
        pointLight.position.set(0, 0, 0);
        scene.add(pointLight);

        const ambientLight = new THREE.AmbientLight(0x404040, 1.5);
        scene.add(ambientLight);

        // 惑星作成関数
        function createPlanet(size, color, distance) {
            const geometry = new THREE.SphereGeometry(size, 32, 32);
            const material = new THREE.MeshPhongMaterial({ color: color });
            const planet = new THREE.Mesh(geometry, material);
            planet.userData.distance = distance;
            scene.add(planet);
            return planet;
        }

        // 軌道作成関数
        function createOrbit(distance) {
            const curve = new THREE.EllipseCurve(0, 0, distance, distance, 0, 2 * Math.PI, false, 0);
            const points = curve.getPoints(50);
            const geometry = new THREE.BufferGeometry().setFromPoints(points);
            const material = new THREE.LineBasicMaterial({ color: 0xffffff });
            const ellipse = new THREE.Line(geometry, material);
            ellipse.rotation.x = Math.PI / 2;  // XZ平面に回転
            scene.add(ellipse);
        }

        // 衛星作成関数
        function createMoon(size, color, distance, planet) {
            const geometry = new THREE.SphereGeometry(size, 32, 32);
            const material = new THREE.MeshPhongMaterial({ color: color });
            const moon = new THREE.Mesh(geometry, material);
            moon.userData = { distance: distance, parent: planet };
            scene.add(moon);
            return moon;
        }

        // 太陽と惑星の生成
        const sun = createPlanet(5, 0xffff00, 0);
        const mercury = createPlanet(0.5, 0xaaaaaa, 8);
        createOrbit(8);

        const venus = createPlanet(0.9, 0xffa500, 12);
        createOrbit(12);

        const earth = createPlanet(1, 0x0000ff, 16);
        createOrbit(16);

        const mars = createPlanet(0.7, 0xff4500, 20);
        createOrbit(20);

        const jupiter = createPlanet(2.2, 0xffa07a, 28);
        createOrbit(28);

        const saturn = createPlanet(2, 0xf4a460, 35);
        createOrbit(35);

        // 各惑星の有名な衛星の追加
        const earthMoon = createMoon(0.3, 0x888888, 2, earth);  // 地球の月
        const io = createMoon(0.4, 0xffddaa, 3, jupiter);        // 木星のイオ
        const europa = createMoon(0.35, 0xaaccff, 4, jupiter);   // 木星のエウロパ
        const ganymede = createMoon(0.5, 0xcccccc, 5, jupiter);  // 木星のガニメデ
        const callisto = createMoon(0.45, 0x999999, 6, jupiter); // 木星のカリスト
        const titan = createMoon(0.45, 0xffdd88, 4, saturn);     // 土星のタイタン
        const enceladus = createMoon(0.2, 0xffffff, 2.5, saturn); // 土星のエンケラドゥス

        // カメラの位置設定
        camera.position.set(30, 30, 50);
        camera.lookAt(0, 0, 0);

        // 公転周期の比率 (地球 = 1)
        const orbitSpeeds = {
            mercury: 4.15,  // 約88日
            venus: 1.62,    // 約225日
            earth: 1.0,     // 約365日
            mars: 0.53,     // 約687日
            jupiter: 0.084, // 約12年
            saturn: 0.034,  // 約29年
        };

        // アニメーションループ
        let speedMultiplier = 1;

        function animate() {
            requestAnimationFrame(animate);
            speedMultiplier = document.getElementById("speed").value;

            // 各惑星の公転を周期比率で調整
            mercury.position.x = mercury.userData.distance * Math.cos(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.mercury);
            mercury.position.z = mercury.userData.distance * Math.sin(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.mercury);

            venus.position.x = venus.userData.distance * Math.cos(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.venus);
            venus.position.z = venus.userData.distance * Math.sin(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.venus);

            earth.position.x = earth.userData.distance * Math.cos(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.earth);
            earth.position.z = earth.userData.distance * Math.sin(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.earth);

            mars.position.x = mars.userData.distance * Math.cos(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.mars);
            mars.position.z = mars.userData.distance * Math.sin(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.mars);

            jupiter.position.x = jupiter.userData.distance * Math.cos(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.jupiter);
            jupiter.position.z = jupiter.userData.distance * Math.sin(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.jupiter);

            saturn.position.x = saturn.userData.distance * Math.cos(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.saturn);
            saturn.position.z = saturn.userData.distance * Math.sin(Date.now() * 0.002 * speedMultiplier * orbitSpeeds.saturn);

            // 地球の月の回転
            earthMoon.position.x = earth.position.x + earthMoon.userData.distance * Math.cos(Date.now() * 0.005 * speedMultiplier);
            earthMoon.position.z = earth.position.z + earthMoon.userData.distance * Math.sin(Date.now() * 0.005 * speedMultiplier);

            // 木星の衛星
            io.position.x = jupiter.position.x + io.userData.distance * Math.cos(Date.now() * 0.004 * speedMultiplier);
            io.position.z = jupiter.position.z + io.userData.distance * Math.sin(Date.now() * 0.004 * speedMultiplier);

            europa.position.x = jupiter.position.x + europa.userData.distance * Math.cos(Date.now() * 0.003 * speedMultiplier);
            europa.position.z = jupiter.position.z + europa.userData.distance * Math.sin(Date.now() * 0.003 * speedMultiplier);

            ganymede.position.x = jupiter.position.x + ganymede.userData.distance * Math.cos(Date.now() * 0.0025 * speedMultiplier);
            ganymede.position.z = jupiter.position.z + ganymede.userData.distance * Math.sin(Date.now() * 0.0025 * speedMultiplier);

            callisto.position.x = jupiter.position.x + callisto.userData.distance * Math.cos(Date.now() * 0.002 * speedMultiplier);
            callisto.position.z = jupiter.position.z + callisto.userData.distance * Math.sin(Date.now() * 0.002 * speedMultiplier);

            // 土星の衛星
            titan.position.x = saturn.position.x + titan.userData.distance * Math.cos(Date.now() * 0.0015 * speedMultiplier);
            titan.position.z = saturn.position.z + titan.userData.distance * Math.sin(Date.now() * 0.0015 * speedMultiplier);

            enceladus.position.x = saturn.position.x + enceladus.userData.distance * Math.cos(Date.now() * 0.003 * speedMultiplier);
            enceladus.position.z = saturn.position.z + enceladus.userData.distance * Math.sin(Date.now() * 0.003 * speedMultiplier);

            renderer.render(scene, camera);
        }
        animate();

        // カメラ操作
        document.getElementById("camera-up").addEventListener("click", function() {
            camera.position.y += 5;
            camera.lookAt(0, 0, 0);
        });
        document.getElementById("camera-down").addEventListener("click", function() {
            camera.position.y -= 5;
            camera.lookAt(0, 0, 0);
        });
        document.getElementById("camera-in").addEventListener("click", function() {
            camera.position.z -= 5;
            camera.lookAt(0, 0, 0);
        });
        document.getElementById("camera-out").addEventListener("click", function() {
            camera.position.z += 5;
            camera.lookAt(0, 0, 0);
        });
    </script>
</body>
</html>
3
2
2

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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?