簡単な3Dオープンワールドのドライブゲームです。カーソルキーで移動です。上でアクセル、下でブレーキ、左右でハンドルです。物理計算してます。
実行結果。
ジュピター ノートブックにコードを貼り付けて実行すると、サーバーが起動してブラウザ上でゲームが始まります。
ドライブ ゲームです。
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()