2
5

java script のパワーに惚れた。揺れる水槽の中のパーテクルの動きゲーム。

Last updated at Posted at 2024-08-16

スクリーンショット 2024-08-16 195202.png

スクリーンショット 2024-08-16 195218.png

image.png

java script のパワーに惚れた。GPUを使用しているみたいだ。どういう仕組みなんだ。

水槽の中のパーテクルの動きゲーム。
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>Water Tank Particle 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 tankWidth = 40, tankHeight = 20, tankDepth = 20;

        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) * tankWidth; // X座標
                positions[i * 3 + 1] = (Math.random() - 0.5) * tankHeight; // Y座標
                positions[i * 3 + 2] = (Math.random() - 0.5) * tankDepth; // Z座標

                velocities[i * 3] = (Math.random() - 0.5) * 0.1; // X方向速度
                velocities[i * 3 + 1] = (Math.random() - 0.5) * 0.1; // Y方向速度
                velocities[i * 3 + 2] = (Math.random() - 0.5) * 0.1; // 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, 20, 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] += particleVelocities[i * 3]; // X座標の更新
                positions[i * 3 + 1] += particleVelocities[i * 3 + 1]; // Y座標の更新
                positions[i * 3 + 2] += particleVelocities[i * 3 + 2]; // Z座標の更新

                // パーティクルが水槽の端に達した場合の反射処理
                if (positions[i * 3] < -tankWidth / 2 || positions[i * 3] > tankWidth / 2) {
                    particleVelocities[i * 3] = -particleVelocities[i * 3]; // X方向の速度を反転
                }
                if (positions[i * 3 + 1] < -tankHeight / 2 || positions[i * 3 + 1] > tankHeight / 2) {
                    particleVelocities[i * 3 + 1] = -particleVelocities[i * 3 + 1]; // Y方向の速度を反転
                }
                if (positions[i * 3 + 2] < -tankDepth / 2 || positions[i * 3 + 2] > tankDepth / 2) {
                    particleVelocities[i * 3 + 2] = -particleVelocities[i * 3 + 2]; // Z方向の速度を反転
                }

                // 水槽を揺らすシミュレーション(重力のような効果を追加)
                particleVelocities[i * 3 + 1] -= 0.002; // Y軸方向に重力を適用
            }

            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 = 'water_tank_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.")

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