4
4

楽しいな。簡単な 3Dの オープンワールドのドライブ ゲームです。

Last updated at Posted at 2024-08-08

簡単な3Dオープンワールドのドライブゲームです。カーソルキーで移動です。上でアクセル、下でブレーキ、左右でハンドルです。物理計算してます。

実行結果。

image.png

スクリーンショット 2024-08-09 052403.png

ジュピター ノートブックにコードを貼り付けて実行すると、サーバーが起動してブラウザ上でゲームが始まります。

ドライブ ゲームです。


import http.server
import socketserver
import tempfile
import webbrowser
import random

# HTMLとJavaScriptのコード
html_content = """
<!DOCTYPE html>
<html>
<head>
    <title>簡単なドライブゲーム</title>
    <style>
        /* キャンバス全体のスタイル設定 */
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <!-- Three.jsライブラリとPointerLockControlsのインポート -->
    <script src="https://cdn.jsdelivr.net/npm/three@0.139.2/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.139.2/examples/js/controls/PointerLockControls.js"></script>
    <script>
        // シーン、カメラ、レンダラーの作成
        var scene = new THREE.Scene(); // 3D空間を管理するシーンを作成
        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // カメラを作成
        var renderer = new THREE.WebGLRenderer(); // WebGLを使ったレンダラーを作成
        renderer.setSize(window.innerWidth, window.innerHeight); // ウィンドウ全体のサイズにレンダラーを設定
        document.body.appendChild(renderer.domElement); // レンダラーのDOM要素をHTMLに追加

        // 背景色を青色に設定
        scene.background = new THREE.Color(0x87CEEB); // 背景色を空の青色に設定

        // 車を赤いキューブで表現(簡易的な車モデル)
        var carGeometry = new THREE.BoxGeometry(1, 0.5, 2); // 車の形状を定義(幅1、高さ0.5、長さ2の箱)
        var carMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // 車の素材を定義(赤色)
        var car = new THREE.Mesh(carGeometry, carMaterial); // 車を作成(メッシュとして)
        car.position.y = 0.25; // 車の位置を設定(地面から少し上に浮かせる)
        scene.add(car); // 車をシーンに追加

        // 地面を作成
        var groundGeometry = new THREE.PlaneGeometry(1000, 1000); // 大きな平面を地面として定義
        var groundMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide }); // 緑色の地面素材を作成
        var ground = new THREE.Mesh(groundGeometry, groundMaterial); // 地面をメッシュとして作成
        ground.rotation.x = Math.PI / 2; // 地面を水平に回転させる
        ground.position.y = 0; // 地面の位置を設定
        scene.add(ground); // 地面をシーンに追加

        // ランダムなブロックを地面に多数配置
        var blockCount = 100; // ブロックの数を設定
        for (var i = 0; i < blockCount; i++) {
            var blockGeometry = new THREE.BoxGeometry(
                Math.random() * 2 + 0.5, // 幅をランダムに設定
                Math.random() * 2 + 0.5, // 高さをランダムに設定
                Math.random() * 2 + 0.5  // 奥行きをランダムに設定
            );
            var blockMaterial = new THREE.MeshBasicMaterial({ 
                color: Math.random() * 0xffffff // 色をランダムに設定
            });
            var block = new THREE.Mesh(blockGeometry, blockMaterial); // ブロックを作成
            block.position.set(
                (Math.random() - 0.5) * 1000, // X軸位置をランダムに設定
                blockGeometry.parameters.height / 2, // 地面からの高さを設定
                (Math.random() - 0.5) * 1000  // Z軸位置をランダムに設定
            );
            scene.add(block); // ブロックをシーンに追加
        }

        // カメラコントロールを設定
        var controls = new THREE.PointerLockControls(camera, document.body); // マウスでカメラを操作するためのコントロールを設定
        camera.position.set(0, 1.5, 3); // カメラを車の上方後ろに配置

        // 画面をクリックするとカメラコントロールが有効化される
        document.body.addEventListener('click', function() {
            controls.lock(); // カメラコントロールをロック(マウスでカメラ操作)
        });

        // カメラコントロールがロックされた時にメッセージを表示
        controls.addEventListener('lock', function() {
            console.log('Pointer locked');
        });

        // カメラコントロールが解除された時にメッセージを表示
        controls.addEventListener('unlock', function() {
            console.log('Pointer unlocked');
        });

        // 移動と方向転換に関する変数の設定
        var moveForward = false;  // 前進フラグ
        var moveBackward = false; // 後退フラグ
        var rotateLeft = false;   // 左旋回フラグ
        var rotateRight = false;  // 右旋回フラグ
        var carSpeed = 0;         // 車の速度
        var carRotationSpeed = 0.05; // 車の旋回速度

        // キーが押された時の処理
        function onKeyDown(event) {
            switch(event.code) {
                case 'ArrowUp':   // 上矢印キー
                case 'KeyW':      // Wキー
                    moveForward = true; // 前進を開始
                    break;
                case 'ArrowLeft': // 左矢印キー
                case 'KeyA':      // Aキー
                    rotateLeft = true; // 左旋回を開始
                    break;
                case 'ArrowDown': // 下矢印キー
                case 'KeyS':      // Sキー
                    moveBackward = true; // 後退を開始
                    break;
                case 'ArrowRight': // 右矢印キー
                case 'KeyD':       // Dキー
                    rotateRight = true; // 右旋回を開始
                    break;
            }
        }

        // キーが離された時の処理
        function onKeyUp(event) {
            switch(event.code) {
                case 'ArrowUp':   // 上矢印キー
                case 'KeyW':      // Wキー
                    moveForward = false; // 前進を停止
                    break;
                case 'ArrowLeft': // 左矢印キー
                case 'KeyA':      // Aキー
                    rotateLeft = false; // 左旋回を停止
                    break;
                case 'ArrowDown': // 下矢印キー
                case 'KeyS':      // Sキー
                    moveBackward = false; // 後退を停止
                    break;
                case 'ArrowRight': // 右矢印キー
                case 'KeyD':       // Dキー
                    rotateRight = false; // 右旋回を停止
                    break;
            }
        }

        // キーボードのイベントリスナーを設定
        document.addEventListener('keydown', onKeyDown); // キーが押された時にonKeyDownを呼び出す
        document.addEventListener('keyup', onKeyUp);     // キーが離された時にonKeyUpを呼び出す

        // アニメーションループ(毎フレーム呼び出される)
        function animate() {
            requestAnimationFrame(animate); // 次のフレームを要求

            var speed = 0.5; // 基本の移動速度

            // 速度を加速または減速
            if (moveForward) carSpeed = Math.min(carSpeed + 2.5, speed);  // 前進
            if (moveBackward) carSpeed = Math.max(carSpeed - 0.1, -speed); // 後退
            if (!moveForward && !moveBackward) carSpeed *= 0.98; // ブレーキ(徐々に減速)

            // 旋回の処理
            if (rotateLeft) car.rotation.y += carRotationSpeed; // 左に旋回
            if (rotateRight) car.rotation.y -= carRotationSpeed; // 右に旋回

            // 車の移動を計算
            var direction = new THREE.Vector3(Math.sin(car.rotation.y), 0, Math.cos(car.rotation.y));
            car.position.addScaledVector(direction, carSpeed);

            // カメラを車の後ろに追従させる
            camera.position.set(
                car.position.x - 3 * Math.sin(car.rotation.y),
                car.position.y + 2,
                car.position.z - 3 * Math.cos(car.rotation.y)
            );
            camera.lookAt(car.position); // カメラが常に車を見つめるように設定

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

        animate(); // アニメーションを開始
    </script>
</body>
</html>
"""

# 一時的なファイルにHTMLコンテンツを書き込む
with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as temp_html:
    temp_html.write(html_content.encode("utf-8"))
    temp_html.flush()
    webbrowser.open(f"file://{temp_html.name}")

# サーバーを作成してHTMLを提供する
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print(f"Serving at port {PORT}")
    httpd.serve_forever()


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