2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

楽しいな 未来のロボタクシー。 車の内部からの視点で、タイヤが描画された車で街を走るゲーム。

Last updated at Posted at 2024-10-20

image.png

スクリーンショット 2024-10-21 065412.png

スクリーンショット 2024-10-21 065359.png

ロボタクシーに乗ってみるゲーム。

車の内部からの視点でシミュレーションが動作し、タイヤが描画された車が道路を走ります。

車の数とスピードを調整できるスライダーを装備してます。

コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「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>

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?