2
2

楽しいな。簡単な 3Dの オープンワールドのフライトシュミレータ ゲームです。

Last updated at Posted at 2024-08-09

簡単な3Dオープンワールドのフライトシュミレータゲームです。カーソルキーで移動です。上で機首上げ、下で機首下げ、左右で旋回です。(操作に慣れてくると面白いです。)

実行結果。

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

スクリーンショット 2024-08-09 192356.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>
    <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();
        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        var renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

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

        // 飛行機(3つのボックスで構成)
        var fuselageGeometry = new THREE.BoxGeometry(1, 0.2, 2);
        var wingGeometry = new THREE.BoxGeometry(3, 0.1, 0.5);
        var tailGeometry = new THREE.BoxGeometry(0.5, 0.1, 0.5);

        var planeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });

        var fuselage = new THREE.Mesh(fuselageGeometry, planeMaterial);
        var wing = new THREE.Mesh(wingGeometry, planeMaterial);
        var tail = new THREE.Mesh(tailGeometry, planeMaterial);

        wing.position.set(0, 0.15, 0);
        tail.position.set(0, 0.2, -0.75);

        var plane = new THREE.Group();
        plane.add(fuselage);
        plane.add(wing);
        plane.add(tail);

        plane.position.y = 2; // 飛行機を初期位置で空中に設定
        scene.add(plane);

        // 茶色の地面を作成
        var groundGeometry = new THREE.PlaneGeometry(1000, 1000);
        var groundMaterial = new THREE.MeshBasicMaterial({ color: 0x8B4513, side: THREE.DoubleSide });
        var ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = Math.PI / 2;
        scene.add(ground);

        // カラフルなボックスをランダムに地面と空中に配置
        var blockCount = 500;
        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,
                Math.random() * 10 + 0.5,
                (Math.random() - 0.5) * 1000
            );
            scene.add(block);
        }

        // カメラコントロールの設定
        var controls = new THREE.PointerLockControls(camera, document.body);
        camera.position.set(0, 2.5, 5);

        document.body.addEventListener('click', function() {
            controls.lock();
        });

        // 操作フラグ
        var yawLeft = false;
        var yawRight = false;
        var pitchUp = false;
        var pitchDown = false;
        var planeSpeed = 0.5;

        // キーが押された時の処理
        function onKeyDown(event) {
            switch(event.code) {
                case 'ArrowLeft': 
                    yawLeft = true;
                    break;
                case 'ArrowRight': 
                    yawRight = true;
                    break;
                case 'ArrowUp':
                    pitchUp = true;
                    break;
                case 'ArrowDown':
                    pitchDown = true;
                    break;
            }
        }

        // キーが離された時の処理
        function onKeyUp(event) {
            switch(event.code) {
                case 'ArrowLeft': 
                    yawLeft = false;
                    break;
                case 'ArrowRight': 
                    yawRight = false;
                    break;
                case 'ArrowUp':
                    pitchUp = false;
                    break;
                case 'ArrowDown':
                    pitchDown = false;
                    break;
            }
        }

        // キーボードイベントリスナー
        document.addEventListener('keydown', onKeyDown);
        document.addEventListener('keyup', onKeyUp);

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);

            // 機体の旋回操作(ヨー)
            if (yawLeft) plane.rotation.y += 0.05;  // 左旋回
            if (yawRight) plane.rotation.y -= 0.05; // 右旋回

            // 機首の上下操作(ピッチ)
            if (pitchUp) plane.rotation.x -= 0.05;  // 機首上げ
            if (pitchDown) plane.rotation.x += 0.05; // 機首下げ

            // 機体の進行方向を計算
            var forwardDirection = new THREE.Vector3(0, 0, -1);
            forwardDirection.applyQuaternion(plane.quaternion);

            plane.position.addScaledVector(forwardDirection, planeSpeed);

            // カメラの追従
            camera.position.set(
                plane.position.x - 3 * Math.sin(plane.rotation.y),
                plane.position.y + 2,
                plane.position.z - 3 * Math.cos(plane.rotation.y)
            );
            camera.lookAt(plane.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()

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