視点の先に画像を配置するだけのシンプルすぎるゲームです。
スペースキーを押すと画像を空間内に置けます。
マウスで視点移動です。
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() # サーバーを無限ループで実行し続ける