28
30

僕だけのオープンワールド というゲーム。

Last updated at Posted at 2024-08-11

スクリーンショット 2024-08-12 054033.png

スクリーンショット 2024-08-12 053753.png

スクリーンショット 2024-08-12 082900.png

視点の先に画像を配置するだけのシンプルすぎるゲームです。

スペースキーを押すと画像を空間内に置けます。
マウスで視点移動です。

import http.server  # HTTPサーバーの機能を提供
import socketserver  # 簡単にサーバーを構築するためのモジュール
import tempfile  # 一時ファイルを作成するためのモジュール
import webbrowser  # デフォルトのウェブブラウザを操作するためのモジュール
import random  # ランダムな値を生成するためのモジュール

# HTMLとJavaScriptのコードを文字列として定義
html_content = """
<!DOCTYPE html>
<html>
<head>
    <title>僕だけのオープンワールド</title>
    <style>
        body { margin: 0; overflow: hidden; }
        #fileInput { display: none; }
    </style>
</head>
<body>
    <input type="file" id="fileInput" accept="image/*">
    <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);

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

        // クリックでマウスをロック
        document.body.addEventListener('click', function() {
            controls.lock();
        });

        // 地面の作成
        var geometry = new THREE.PlaneGeometry(1000, 1000);
        var material = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide });
        var plane = new THREE.Mesh(geometry, material);
        plane.rotation.x = Math.PI / 2;
        plane.position.y = -0.5;
        scene.add(plane);

        // 画像を視点の先に配置し、地面に接するようにする関数
        function addImage(image) {
            var texture = new THREE.TextureLoader().load(URL.createObjectURL(image));
            var geometry = new THREE.PlaneGeometry(10, 10);
            var material = new THREE.MeshBasicMaterial({ map: texture });
            var imageMesh = new THREE.Mesh(geometry, material);

            // カメラの方向に向けて、地面に配置
            var distanceFromCamera = 15;
            var cameraDirection = new THREE.Vector3();
            camera.getWorldDirection(cameraDirection);
            cameraDirection.multiplyScalar(distanceFromCamera);

            imageMesh.position.copy(camera.position).add(cameraDirection);
            imageMesh.position.y = 5; // 地面に接する高さ
            imageMesh.lookAt(camera.position);

            scene.add(imageMesh);
        }

        // テキストを視点の先に配置し、地面に接するようにする関数
        function addText(text) {
            var loader = new THREE.FontLoader();
            loader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', function(font) {
                var geometry = new THREE.TextGeometry(text, {
                    font: font,
                    size: 2,
                    height: 0.2
                });
                var material = new THREE.MeshBasicMaterial({ color: 0x000000 });
                var textMesh = new THREE.Mesh(geometry, material);

                // カメラの方向に向けて、地面に配置
                var distanceFromCamera = 20;
                var cameraDirection = new THREE.Vector3();
                camera.getWorldDirection(cameraDirection);
                cameraDirection.multiplyScalar(distanceFromCamera);

                textMesh.position.copy(camera.position).add(cameraDirection);
                textMesh.position.y = 0; // 地面に接する高さ
                textMesh.lookAt(camera.position);

                scene.add(textMesh);
            });
        }

        // ファイル選択の処理
        document.getElementById('fileInput').addEventListener('change', function(event) {
            var file = event.target.files[0];
            if (file) {
                addImage(file);
            }
        });

        // キーボードイベントの設定
        document.addEventListener('keydown', function(event) {
            if (event.code === 'Space') {
                document.getElementById('fileInput').click();
            } else if (event.code === 'Enter') {
                var text = prompt('Enter text to display:');
                if (text) {
                    addText(text);
                }
            }
        });

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);
            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"))  # HTMLコンテンツを一時ファイルに書き込み
    temp_html.flush()  # 書き込みを確実にディスクに反映
    webbrowser.open(f"file://{temp_html.name}")  # デフォルトブラウザで一時ファイルを開く

# サーバーを作成してHTMLを提供する
PORT = 8000  # 使用するポート番号
Handler = http.server.SimpleHTTPRequestHandler  # HTTPリクエストを処理するハンドラー

# サーバーを作成して開始
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print(f"Serving at port {PORT}")  # サーバーが起動したことをコンソールに表示
    httpd.serve_forever()  # サーバーを無限ループで実行し続ける


28
30
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
28
30