ロボタクシーに乗ってみるゲーム。
車の内部からの視点でシミュレーションが動作し、タイヤが描画された車が道路を走ります。
車の数とスピードを調整できるスライダーを装備してます。
コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「index.html」として保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>車の内部からの視点シミュレーション</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
body { margin: 0; }
canvas { display: block; }
#slider { position: absolute; top: 10px; left: 10px; z-index: 1; }
#speedSlider { position: absolute; top: 50px; left: 10px; z-index: 1; }
</style>
</head>
<body>
<input type="range" id="slider" min="1" max="50" value="20" />
<label for="slider" style="color: white;">車の数: <span id="carCountLabel">20</span></label>
<input type="range" id="speedSlider" min="1" max="10" value="5" />
<label for="speedSlider" style="color: white;">スピード: <span id="speedLabel">5</span></label>
<script>
// シーン、カメラ、レンダラーの設定
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 背景を青色の空に設定
scene.background = new THREE.Color(0x87CEEB); // 空の青色
// 緑色の地面を作成(草地のように見せる)
const groundGeometry = new THREE.PlaneGeometry(100, 100);
const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 緑色
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2; // 地面を水平にするために回転
scene.add(ground);
// 建物を作成する関数
function createBuilding(x, z, width, height) {
const buildingGeometry = new THREE.BoxGeometry(width, height, width);
const buildingMaterial = new THREE.MeshBasicMaterial({ color: 0x808080 }); // 建物を灰色に設定
const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
building.position.set(x, height / 2, z);
scene.add(building);
}
// 複数の建物を作成
createBuilding(-20, 10, 2, 5); // 建物1
createBuilding(10, 10, 3, 8); // 建物2
createBuilding(-10, 10, 1, 6); // 建物3
createBuilding(20, 10, 4, 10); // 建物4
createBuilding(-30, 15, 5, 12); // 建物5
createBuilding(30, 15, 3, 7); // 建物6
createBuilding(0, -30, 6, 9); // 建物7
// 車の配列を作成
const cars = [];
// 車を作成する関数
function createCar() {
const carGroup = new THREE.Group(); // 車をグループ化
// 車の本体
const carGeometry = new THREE.BoxGeometry(1.5, 1, 3); // 車のサイズを調整
const carMaterial = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff });
const carBody = new THREE.Mesh(carGeometry, carMaterial);
carGroup.add(carBody); // 車本体をグループに追加
// タイヤを作成
const tireGeometry = new THREE.CylinderGeometry(0.3, 0.3, 0.5, 32); // タイヤのサイズ
const tireMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 }); // タイヤは黒色
// タイヤを配置
const positions = [
[-0.75, -0.5, 1.5], // 左前
[0.75, -0.5, 1.5], // 右前
[-0.75, -0.5, -1.5], // 左後
[0.75, -0.5, -1.5] // 右後
];
positions.forEach(pos => {
const tire = new THREE.Mesh(tireGeometry, tireMaterial);
tire.position.set(pos[0], pos[1], pos[2]);
tire.rotation.z = Math.PI / 2; // タイヤを横向きにする
carGroup.add(tire); // タイヤをグループに追加
});
// ランダムな位置と方向を設定
carGroup.position.set((Math.random() - 0.5) * 80, 0.5, (Math.random() - 0.5) * 80);
carGroup.direction = new THREE.Vector3((Math.random() - 0.5), 0, (Math.random() - 0.5)).normalize();
cars.push(carGroup);
scene.add(carGroup);
}
// 初期の車の数
let carCount = document.getElementById("slider").value;
for (let i = 0; i < carCount; i++) {
createCar(); // 車を作成
}
// カメラを車の中に配置
let cameraCar = cars[0];
camera.position.set(0, 0.5, -3); // 車の運転席近くにカメラを配置
cameraCar.add(camera); // カメラを車にアタッチ
// スピードの初期値
let speed = 0.1;
// 移動速度をスライダーで調整する
const speedSlider = document.getElementById("speedSlider");
const speedLabel = document.getElementById("speedLabel");
speedSlider.addEventListener("input", function() {
speed = speedSlider.value / 10; // スライダーの値を0.1〜1.0にスケール
speedLabel.textContent = speedSlider.value; // ラベルを更新
});
// 衝突検出関数
function detectCollisions() {
cars.forEach((car1, index1) => {
cars.forEach((car2, index2) => {
if (index1 !== index2) {
const distance = car1.position.distanceTo(car2.position);
if (distance < 3) { // 車が近すぎる場合
// 衝突回避のために方向を調整
car1.direction.add(new THREE.Vector3((Math.random() - 0.5) * 0.1, 0, (Math.random() - 0.5) * 0.1)).normalize();
}
}
});
});
}
// レンダリングループ
function animate() {
requestAnimationFrame(animate);
// 車を移動させる
cars.forEach(car => {
car.position.add(car.direction.clone().multiplyScalar(speed));
// 道路の端を超えないようにする
if (car.position.x > 50) car.position.x = -50;
if (car.position.x < -50) car.position.x = 50;
if (car.position.z > 50) car.position.z = -50;
if (car.position.z < -50) car.position.z = 50;
});
// 衝突を検出
detectCollisions();
// シーンをレンダリング
renderer.render(scene, camera);
}
// スライダーのイベントリスナー
const slider = document.getElementById("slider");
const carCountLabel = document.getElementById("carCountLabel");
slider.addEventListener("input", function() {
carCount = slider.value;
carCountLabel.textContent = carCount; // ラベルを更新
// 新しい車を作成
while (cars.length) {
scene.remove(cars.pop()); // 既存の車を削除
}
for (let i = 0; i < carCount; i++) {
createCar(); // 車を作成
}
cameraCar = cars[0]; // 新しい最初の車にカメラを追従させる
cameraCar.add(camera); // 新しい車にカメラをアタッチ
});
animate(); // アニメーションを開始
// ウィンドウサイズ変更時にカメラとレンダラーを更新
window.addEventListener('resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>