7
8

雲が生成され雨が降るゲーム。

Last updated at Posted at 2024-08-20

スクリーンショット 2024-08-21 060338.png

スクリーンショット 2024-08-21 060416.png

パーティクルが徐々に上昇し、一定の高度に達すると重力で落下します。つまり、雲が生成されて雨が降るシミュレーションです。

import os
import webbrowser
from http.server import SimpleHTTPRequestHandler, HTTPServer
import threading

# HTMLコードを生成
html_content = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cloud and Rain Simulation</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        let scene, camera, renderer, particles;
        let particleCount = 20000;
        let particlePositions = [];
        let particleVelocities = [];
        let maxHeight = 20;  // 雲の最大高度
        let gravity = -9.8; // 重力加速度
        let riseSpeed = 0.02; // 上昇速度

        function init() {
            scene = new THREE.Scene(); // シーンの作成
            camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); // カメラの作成
            renderer = new THREE.WebGLRenderer(); // レンダラーの作成
            renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーのサイズ設定
            document.body.appendChild(renderer.domElement); // レンダラーをHTMLに追加

            let geometry = new THREE.BufferGeometry(); // パーティクルのジオメトリ作成
            let positions = new Float32Array(particleCount * 3); // パーティクルの位置配列
            let velocities = new Float32Array(particleCount * 3); // パーティクルの速度配列

            // パーティクルの初期位置と速度を設定
            for (let i = 0; i < particleCount; i++) {
                positions[i * 3] = (Math.random() - 0.5) * 20; // X座標
                positions[i * 3 + 1] = Math.random() * maxHeight / 2; // Y座標 (地面から始まる)
                positions[i * 3 + 2] = (Math.random() - 0.5) * 20; // Z座標

                velocities[i * 3] = 0; // X方向速度
                velocities[i * 3 + 1] = riseSpeed; // Y方向速度(初めは上昇)
                velocities[i * 3 + 2] = 0; // Z方向速度
            }

            geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); // 位置属性をジオメトリに追加
            let material = new THREE.PointsMaterial({ color: 0x00aaff, size: 0.1 }); // パーティクルのマテリアル設定
            particles = new THREE.Points(geometry, material); // パーティクルの作成
            scene.add(particles); // シーンにパーティクルを追加

            particlePositions = positions; // パーティクルの位置配列を保存
            particleVelocities = velocities; // パーティクルの速度配列を保存

            // カメラ位置を斜め上からに調整
            camera.position.set(30, 30, 30); // カメラを斜め上から見た位置に設定
            camera.lookAt(scene.position); // カメラがシーンの中心を向くように設定

            animate(); // アニメーションを開始
        }

        function animate() {
            requestAnimationFrame(animate); // 次のフレームのアニメーションをリクエスト

            let positions = particles.geometry.attributes.position.array; // パーティクルの位置配列を取得
            for (let i = 0; i < particleCount; i++) {
                // パーティクルの位置を更新
                positions[i * 3 + 1] += particleVelocities[i * 3 + 1]; // Y座標の更新(上下動)

                // 高度が一定以上なら、パーティクルを落下させる
                if (positions[i * 3 + 1] > maxHeight) {
                    particleVelocities[i * 3 + 1] = gravity; // 重力で落下させる
                }

                // 地面に達したら、また上昇を開始する
                if (positions[i * 3 + 1] < 0) {
                    positions[i * 3 + 1] = 0;
                    particleVelocities[i * 3 + 1] = riseSpeed; // 上昇を再開
                }
            }

            particles.geometry.attributes.position.needsUpdate = true; // パーティクルの位置の更新を反映

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

        // ウィンドウリサイズ時の処理
        window.addEventListener('resize', function() {
            let width = window.innerWidth;
            let height = window.innerHeight;
            renderer.setSize(width, height); // レンダラーのサイズをウィンドウに合わせる
            camera.aspect = width / height; // カメラのアスペクト比を更新
            camera.updateProjectionMatrix(); // カメラのプロジェクションマトリックスを更新
        });

        init(); // 初期化関数を呼び出す
    </script>
</body>
</html>
"""

# HTMLファイルとして保存
html_file = 'cloud_rain_simulation.html'
with open(html_file, 'w') as file:
    file.write(html_content)

# サーバーをバックグラウンドで起動
def run_server():
    server_address = ('', 8000)
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    httpd.serve_forever()

# サーバーを別スレッドで実行
thread = threading.Thread(target=run_server)
thread.daemon = True
thread.start()

# デフォルトのブラウザでHTMLファイルを開く
webbrowser.open(f'http://localhost:8000/{html_file}')

# サーバー停止まで待機
try:
    thread.join()
except KeyboardInterrupt:
    print("Server stopped.")

7
8
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
7
8