🎮ArcadeLab

草原AI逃生

by RocketTiger53
674 lines26.0 KB🛠️ Three.js (3D graphics)
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>草原AI逃生</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            overflow: hidden;
            background: #72a8e8;
            touch-action: none;
        }
        canvas { display: block; }
        #joystick {
            position: fixed;
            bottom: 30px;
            left: 30px;
            width: 120px;
            height: 120px;
            background: rgba(255, 255, 255, 0.15);
            border-radius: 50%;
            border: 2px solid rgba(255,255,255,0.3);
            z-index: 10;
        }
        #fireBtn {
            position: fixed;
            bottom: 30px;
            right: 30px;
            width: 100px;
            height: 100px;
            background: rgba(255, 80, 80, 0.35);
            border-radius: 50%;
            color: #fff;
            font-size: 22px;
            border: 2px solid rgba(255,255,255,0.4);
            z-index: 10;
        }
        #pickBtn {
            position: fixed;
            bottom: 140px;
            right: 30px;
            width: 80px;
            height: 80px;
            background: rgba(255, 220, 0, 0.45);
            border-radius: 50%;
            color: #000;
            font-weight: bold;
            font-size: 18px;
            border: 3px solid #ffdd00;
            z-index: 10;
        }
        #nightVisionBtn {
            position: fixed;
            bottom: 30px;
            right: 150px;
            width: 60px;
            height: 60px;
            background: rgba(80, 255, 80, 0.35);
            border-radius: 50%;
            color: #fff;
            font-size: 16px;
            border: 2px solid rgba(80, 255, 80, 0.6);
            z-index: 10;
        }
        #hud {
            position: fixed;
            top: 20px;
            left: 20px;
            color: #fff;
            font-size: 18px;
            text-shadow: 0 0 4px #000;
            z-index: 10;
        }
        #taskBar {
            position: fixed;
            top: 60px;
            left: 20px;
            color: #ffe644;
            font-size: 19px;
            font-weight: bold;
            text-shadow: 0 0 5px #000;
            z-index: 10;
        }
        #crosshair {
            position: fixed;
            top: 50%;
            left: 50%;
            width: 10px;
            height: 10px;
            background: #ffffff;
            border-radius: 50%;
            transform: translate(-50%, -50%);
            z-index: 5;
            box-shadow: 0 0 5px #ff3333;
        }
        #nightVisionOverlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 6;
            background: rgba(0, 255, 0, 0.18);
            opacity: 0;
            transition: opacity 0.15s ease;
        }
        #nightVisionOverlay.active { opacity: 1; }
        #nvGoggleFrame {
            position: fixed;
            top: 50%;
            left: 50%;
            width: 0;
            height: 0;
            border-radius: 50%;
            background: radial-gradient(circle, transparent 25%, rgba(0, 0, 0, 0.65) 48%, rgba(0, 0, 0.88) 100%);
            transform: translate(-50%, -50%);
            pointer-events: none;
            z-index: 7;
            opacity: 0;
            transition: all 0.18s ease;
        }
        #nvGoggleFrame.active {
            width: 280px;
            height: 280px;
            opacity: 1;
        }
        .scanLine {
            position: absolute;
            left: 0;
            width: 100%;
            height: 2px;
            background: rgba(255, 255, 255, 0.85);
            opacity: 0;
            z-index: 8;
            pointer-events: none;
        }
    </style>
</head>
<body>
    <div id="hud">血量:100 | 弹药:30 | AI血量:1000</div>
    <div id="taskBar">任务:收集电池 0/10 | 集齐后爬绳索通关</div>
    <div id="crosshair"></div>
    <div id="joystick"></div>
    <button id="fireBtn">开火</button>
    <button id="pickBtn">拾取</button>
    <button id="nightVisionBtn">夜视仪</button>
    <div id="nightVisionOverlay"></div>
    <div id="nvGoggleFrame"></div>

    <script>
        let scene, camera, renderer, raycaster;
        let gun, nightVisionGoggle, rightHand;
        let aiList = [];
        let rope = null;
        let health = 100;
        let ammo = 30;
        let singleAiHP = 1000;
        let batteryCount = 0;
        const targetBatteryTotal = 10;
        let batteryList = [];
        let almondWaterList = [];
        const hud = document.getElementById('hud');
        const taskBar = document.getElementById('taskBar');

        let moveForward = false;
        const moveSpeed = 0.08;
        let touchStartX = 0;
        let touchStartY = 0;
        let yaw = 0;
        let pitch = 0.12;
        const maxPitch = 0.6;
        const minPitch = -0.6;
        let nightVisionEnabled = false;
        let isGoggleRaising = false;
        let aiSpawned = false;

        function init() {
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x72a8e8);
            scene.fog = new THREE.Fog(0x72a8e8, 40, 180);
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(0, 1.6, 0);
            camera.rotation.order = "YXZ";
            camera.rotation.y = yaw;
            camera.rotation.x = pitch;
            renderer = new THREE.WebGLRenderer({ antialias: false });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = false;
            renderer.toneMapping = THREE.NoToneMapping;
            document.body.appendChild(renderer.domElement);
            raycaster = new THREE.Raycaster();
            raycaster.far = 100;

            const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
            scene.add(ambientLight);
            const sunLight = new THREE.DirectionalLight(0xfff4d6, 0.8);
            sunLight.position.set(25, 35, 15);
            scene.add(sunLight);

            createLightGrassScene();
            spawnAllBatteries();
            spawnAlmondWater();
            createGunSimple();
            createHandWithGoggle();
            delayedSpawnAI();
            initTouchControls();
            initNightScanLines();
            window.addEventListener('resize', onWindowResize);
            animateLoop();
        }

        function createLightGrassScene() {
            const grassMat = new THREE.MeshLambertMaterial({ color: 0x8fa860 });
            const roadMat = new THREE.MeshLambertMaterial({ color: 0x5a6058 });
            const roadLineMat = new THREE.MeshLambertMaterial({ color: 0xd8d8c8 });
            const houseMat = new THREE.MeshLambertMaterial({ color: 0xd9c9a3 });
            const roofMat = new THREE.MeshLambertMaterial({ color: 0x8b6914 });
            const towerMat = new THREE.MeshLambertMaterial({ color: 0x919191 });

            const ground = new THREE.Mesh(new THREE.PlaneGeometry(200, 200), grassMat);
            ground.rotation.x = -Math.PI / 2;
            ground.position.y = 0;
            scene.add(ground);

            const road = new THREE.Mesh(new THREE.PlaneGeometry(6, 120), roadMat);
            road.rotation.x = -Math.PI / 2;
            road.position.set(-50, 0.01, 0);
            scene.add(road);
            for(let i = -50; i < 50; i += 8) {
                const line = new THREE.Mesh(new THREE.PlaneGeometry(0.4, 3), roadLineMat);
                line.rotation.x = -Math.PI / 2;
                line.position.set(-50, 0.02, i);
                scene.add(line);
            }

            const house1 = new THREE.Group();
            const h1Body = new THREE.Mesh(new THREE.BoxGeometry(4, 2.5, 5), houseMat);
            h1Body.position.y = 1.25;
            house1.add(h1Body);
            const h1Roof = new THREE.Mesh(new THREE.ConeGeometry(3.2, 1.5, 4), roofMat);
            h1Roof.position.y = 3.2;
            h1Roof.rotation.y = Math.PI / 4;
            house1.add(h1Roof);
            house1.position.set(-75, 0, -40);
            scene.add(house1);
            const house2 = house1.clone();
            house2.position.set(-10, 0, -70);
            scene.add(house2);
            const house3 = house1.clone();
            house3.position.set(60, 0, -85);
            scene.add(house3);

            const tower = new THREE.Group();
            const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0.25, 6, 6), towerMat);
            pole.position.y = 3;
            tower.add(pole);
            const tank = new THREE.Mesh(new THREE.CylinderGeometry(1.2, 1.2, 2.5, 6), towerMat);
            tank.position.y = 7.5;
            tower.add(tank);
            tower.position.set(30, 0, -90);
            scene.add(tower);
        }

        function spawnRope() {
            const ropeMat = new THREE.MeshStandardMaterial({color:0x554433,roughness:0.8});
            rope = new THREE.Mesh(new THREE.CylinderGeometry(0.15,0.15,12,6), ropeMat);
            rope.position.set(0,6,-95);
            scene.add(rope);
        }

        function spawnAllBatteries() {
            const batteryMaterial = new THREE.MeshStandardMaterial({
                color: 0xffdd00,
                emissive: 0xffdd00,
                emissiveIntensity: 0.5
            });
            for(let i = 0; i < targetBatteryTotal; i++){
                const batteryMesh = new THREE.Mesh(
                    new THREE.BoxGeometry(0.22, 0.42, 0.16),
                    batteryMaterial
                );
                batteryMesh.userData.type = "battery";
                batteryMesh.position.set(
                    (Math.random() - 0.5) * 160,
                    0.3,
                    (Math.random() - 0.5) * 160
                );
                scene.add(batteryMesh);
                batteryList.push(batteryMesh);
            }
        }

        function spawnAlmondWater() {
            const waterMat = new THREE.MeshStandardMaterial({
                color:0x88ddff,
                emissive:0x66bbff,
                emissiveIntensity:0.4
            });
            for(let i=0;i<12;i++){
                const bottle = new THREE.Mesh(new THREE.CylinderGeometry(0.12,0.12,0.35,6), waterMat);
                bottle.userData.type = "almondWater";
                bottle.position.set((Math.random()-0.5)*160,0.25,(Math.random()-0.5)*160);
                scene.add(bottle);
                almondWaterList.push(bottle);
            }
        }

        function delayedSpawnAI() {
            const randomDelay = 3000 + Math.random() * 8000;
            setTimeout(()=>{
                createSingleAI(18,0,-12);
                createSingleAI(-22,0,-35);
                aiSpawned = true;
                refreshHud();
            }, randomDelay);
        }

        function createSingleAI(posX, posY, posZ) {
            const aiEnemy = new THREE.Group();
            aiEnemy.userData.hp = singleAiHP;
            const bodyMat = new THREE.MeshLambertMaterial({color:0x111111});
            const metalMat = new THREE.MeshStandardMaterial({color:0x333333,metalness:0.5,roughness:0.4});
            const torso = new THREE.Mesh(new THREE.BoxGeometry(0.9,1.1,0.5), bodyMat);
            torso.position.y = 1.1;
            aiEnemy.add(torso);
            const head = new THREE.Mesh(new THREE.SphereGeometry(0.3,8,6), new THREE.MeshLambertMaterial({color:0x1a0000}));
            head.position.y = 2.2;
            aiEnemy.add(head);
            const eyeLight = new THREE.PointLight(0xff0000, 2, 8);
            eyeLight.position.set(0,0.1,0.3);
            head.add(eyeLight);
            const armL = new THREE.Mesh(new THREE.BoxGeometry(0.25,0.9,0.25), metalMat);
            armL.position.set(-0.7,1.1,0);
            aiEnemy.add(armL);
            const armR = armL.clone();
            armR.position.set(0.7,1.1,0);
            aiEnemy.add(armR);
            const legL = new THREE.Mesh(new THREE.BoxGeometry(0.25,1.1,0.25), metalMat);
            legL.position.set(-0.35,0.45,0);
            aiEnemy.add(legL);
            const legR = legL.clone();
            legR.position.set(0.35,0.45,0);
            aiEnemy.add(legR);
            aiEnemy.position.set(posX, posY, posZ);
            scene.add(aiEnemy);
            aiList.push(aiEnemy);
        }

        function createGunSimple() {
            gun = new THREE.Group();
            const metalMat = new THREE.MeshStandardMaterial({color:0x222222,metalness:0.7,roughness:0.4});
            const gripMat = new THREE.MeshStandardMaterial({color:0x111111,metalness:0.1,roughness:0.9});
            const barrel = new THREE.Mesh(new THREE.CylinderGeometry(0.03,0.03,0.3,8), metalMat);
            barrel.rotation.z = Math.PI/2;
            barrel.position.set(0.3,-0.18,-0.6);
            gun.add(barrel);
            const slide = new THREE.Mesh(new THREE.BoxGeometry(0.16,0.07,0.35), metalMat);
            slide.position.set(0.3,-0.18,-0.4);
            gun.add(slide);
            const grip = new THREE.Mesh(new THREE.BoxGeometry(0.05,0.16,0.06), gripMat);
            grip.position.set(0.35,-0.28,-0.18);
            grip.rotation.x = -0.3;
            gun.add(grip);
            camera.add(gun);
        }

        function createHandWithGoggle() {
            rightHand = new THREE.Group();
            const handMat = new THREE.MeshStandardMaterial({color:0xd8b98a,roughness:0.7});
            const forearm = new THREE.Mesh(new THREE.BoxGeometry(0.12,0.35,0.08), handMat);
            forearm.position.set(0.55,-0.25,-0.45);
            rightHand.add(forearm);
            const hand = new THREE.Mesh(new THREE.BoxGeometry(0.15,0.18,0.1), handMat);
            hand.position.set(0.55,-0.12,-0.55);
            rightHand.add(hand);

            nightVisionGoggle = new THREE.Group();
            const goggleBody = new THREE.Mesh(new THREE.BoxGeometry(0.22,0.12,0.08), new THREE.MeshStandardMaterial({color:0x1a1a1a,metalness:0.9,roughness:0.2}));
            nightVisionGoggle.add(goggleBody);
            const lens = new THREE.Mesh(new THREE.CylinderGeometry(0.04,0.04,0.02,8), new THREE.MeshStandardMaterial({color:0x00ff00,emissive:0x00ff00,emissiveIntensity:0.5,transparent:true,opacity:0.35}));
            lens.rotation.z = Math.PI/2;
            lens.position.set(-0.08,0,0.05);
            nightVisionGoggle.add(lens);
            const lens2 = lens.clone();
            lens2.position.set(0.08,0,0.05);
            nightVisionGoggle.add(lens2);
            nightVisionGoggle.position.set(0.65,-0.15,-0.65);
            nightVisionGoggle.rotation.x = -0.25;
            rightHand.add(nightVisionGoggle);
            camera.add(rightHand);
        }

        function initTouchControls() {
            const joy = document.getElementById('joystick');
            const fireBtn = document.getElementById('fireBtn');
            const pickBtn = document.getElementById('pickBtn');
            const nvBtn = document.getElementById('nightVisionBtn');

            fireBtn.addEventListener('touchstart', e=>{
                e.preventDefault();
                if(ammo<=0) return;
                ammo--;
                shootRay();
                recoilAnim();
                refreshHud();
            })
            pickBtn.addEventListener('touchstart', e=>{
                e.preventDefault();
                tryPickItem();
            })
            joy.addEventListener('touchstart', ()=>moveForward=true);
            joy.addEventListener('touchend', ()=>moveForward=false);
            nvBtn.addEventListener('touchstart', e=>{
                e.preventDefault();
                switchNightVision();
            })

            document.addEventListener('touchstart', e=>{
                const x = e.touches[0].clientX;
                const y = e.touches[0].clientY;
                const jRect = joy.getBoundingClientRect();
                const fRect = fireBtn.getBoundingClientRect();
                const pRect = pickBtn.getBoundingClientRect();
                const nRect = nvBtn.getBoundingClientRect();
                const inJoy = (x>=jRect.left&&x<=jRect.right&&y>=jRect.top&&y<=jRect.bottom);
                const inFire = (x>=fRect.left&&x<=fRect.right&&y>=fRect.top&&y<=fRect.bottom);
                const inPick = (x>=pRect.left&&x<=pRect.right&&y>=pRect.top&&y<=pRect.bottom);
                const inNV = (x>=nRect.left&&x<=nRect.right&&y>=nRect.top&&y<=nRect.bottom);
                if(!inJoy&&!inFire&&!inPick&&!inNV){
                    touchStartX = x;
                    touchStartY = y;
                }
            })
            document.addEventListener('touchmove', e=>{
                const x = e.touches[0].clientX;
                const y = e.touches[0].clientY;
                const jRect = joy.getBoundingClientRect();
                const fRect = fireBtn.getBoundingClientRect();
                const pRect = pickBtn.getBoundingClientRect();
                const nRect = nvBtn.getBoundingClientRect();
                const inJoy = (x>=jRect.left&&x<=jRect.right&&y>=jRect.top&&y<=jRect.bottom);
                const inFire = (x>=fRect.left&&x<=fRect.right&&y>=fRect.top&&y<=fRect.bottom);
                const inPick = (x>=pRect.left&&x<=pRect.right&&y>=pRect.top&&y<=pRect.bottom);
                const inNV = (x>=nRect.left&&x<=nRect.right&&y>=nRect.top&&y<=nRect.bottom);
                if(!inJoy&&!inFire&&!inPick&&!inNV) {
                    const dx = x - touchStartX;
                    const dy = y - touchStartY;
                    yaw += dx * 0.005;
                    pitch += dy * 0.005;
                    pitch = Math.max(minPitch, Math.min(maxPitch, pitch));
                    touchStartX = x;
                    touchStartY = y;
                }
            })
        }

        function switchNightVision() {
            if(isGoggleRaising) return;
            nightVisionEnabled = !nightVisionEnabled;
            isGoggleRaising = true;
            const overlay = document.getElementById('nightVisionOverlay');
            const frame = document.getElementById('nvGoggleFrame');
            if(nightVisionEnabled) {
                raiseGoggle(()=>{
                    overlay.classList.add('active');
                    frame.classList.add('active');
                    aiList.forEach(ai=>ai.traverse(c=>{if(c.material.emissive) c.material.emissiveIntensity=1.2;}))
                    isGoggleRaising = false;
                })
            } else {
                overlay.classList.remove('active');
                frame.classList.remove('active');
                aiList.forEach(ai=>ai.traverse(c=>{if(c.material.emissive) c.material.emissiveIntensity=0.6;}))
                lowerGoggle(()=>isGoggleRaising=false);
            }
        }
        function raiseGoggle(cb) {
            let t = 0;
            const start = {x:0.65,y:-0.15,z:-0.65,rx:-0.25};
            const end = {x:0.35,y:0.15,z:-0.9,rx:0.1};
            const anim = ()=>{
                t += 0.08;
                if(t>=1) {
                    nightVisionGoggle.position.set(end.x,end.y,end.z);
                    nightVisionGoggle.rotation.x = end.rx;
                    cb();
                    return;
                }
                nightVisionGoggle.position.x = start.x + (end.x-start.x)*t;
                nightVisionGoggle.position.y = start.y + (end.y-start.y)*t;
                nightVisionGoggle.position.z = start.z + (end.z-start.z)*t;
                nightVisionGoggle.rotation.x = start.rx + (end.rx-start.rx)*t;
                requestAnimationFrame(anim);
            }
            anim();
        }
        function lowerGoggle(cb) {
            let t = 0;
            const start = {x:0.35,y:0.15,z:-0.9,rx:0.1};
            const end = {x:0.65,y:-0.15,z:-0.65,rx:-0.25};
            const anim = ()=>{
                t += 0.08;
                if(t>=1) {
                    nightVisionGoggle.position.set(end.x,end.y,end.z);
                    nightVisionGoggle.rotation.x = end.rx;
                    cb();
                    return;
                }
                nightVisionGoggle.position.x = start.x + (end.x-start.x)*t;
                nightVisionGoggle.position.y = start.y + (end.y-start.y)*t;
                nightVisionGoggle.position.z = start.z + (end.z-start.z)*t;
                nightVisionGoggle.rotation.x = start.rx + (end.rx-start.rx)*t;
                requestAnimationFrame(anim);
            }
            anim();
        }

        function initNightScanLines() {
            setInterval(()=>{
                if(!nightVisionEnabled) return;
                const overlay = document.getElementById('nightVisionOverlay');
                const line = document.createElement('div');
                line.className = 'scanLine';
                line.style.top = Math.random()*100 + '%';
                overlay.appendChild(line);
                setTimeout(()=>{
                    line.style.opacity = '0';
                    setTimeout(()=>line.remove(),300);
                },150);
            },1500)
        }

        function shootRay() {
            raycaster.setFromCamera(new THREE.Vector2(0,0), camera);
            const hits = raycaster.intersectObjects(scene.children, true);
            if(hits.length>0) {
                const hitObj = hits[0].object;
                for(let ai of aiList){
                    if(hitObj.parent === ai || hitObj.parent?.parent === ai){
                        hitAI(ai);
                        break;
                    }
                }
            }
            createBulletVfx();
        }
        function createBulletVfx() {
            const bullet = new THREE.Mesh(new THREE.SphereGeometry(0.06,4,4), new THREE.MeshBasicMaterial({color:0xffdd00}));
            bullet.position.copy(camera.position);
            bullet.position.add(camera.getWorldDirection().clone().multiplyScalar(0.8));
            scene.add(bullet);
            const dir = camera.getWorldDirection().clone().multiplyScalar(0.3);
            const timer = setInterval(()=>bullet.position.add(dir),30);
            setTimeout(()=>{
                clearInterval(timer);
                if(scene.children.includes(bullet)) scene.remove(bullet);
            },700);
        }
        function hitAI(targetAi) {
            targetAi.userData.hp -= 20;
            refreshHud();
            if(targetAi.userData.hp <= 0) killAI(targetAi);
        }
        function killAI(targetAi) {
            targetAi.rotation.z = Math.PI/2;
            targetAi.position.y = 0.5;
            setTimeout(()=>{
                scene.remove(targetAi);
                const idx = aiList.indexOf(targetAi);
                if(idx>-1) aiList.splice(idx,1);
                refreshHud();
            },800);
        }
        function recoilAnim() {
            gun.position.z += 0.12;
            gun.rotation.x -= 0.06;
            setTimeout(()=>{
                gun.position.z = -0.5;
                gun.rotation.x = 0;
            },100);
        }
        function refreshHud() {
            if(aiList.length === 0){
                hud.innerText = `血量:${Math.floor(health)} | 弹药:${ammo} | 全部AI已清除`;
            }else{
                hud.innerText = `血量:${Math.floor(health)} | 弹药:${ammo} | 剩余AI血量:${aiList[0].userData.hp}`;
            }
        }
        function refreshTaskUI() {
            if(batteryCount >= targetBatteryTotal){
                taskBar.innerText = "任务完成!对准绳索按拾取通关!";
                if(!rope) spawnRope();
            }else{
                taskBar.innerText = `任务:收集电池 ${batteryCount}/${targetBatteryTotal} | 集齐后爬绳索通关`;
            }
        }

        function tryPickItem() {
            raycaster.setFromCamera(new THREE.Vector2(0,0), camera);
            const hits = raycaster.intersectObjects(scene.children, true);
            if(hits.length === 0) return;
            const hitObj = hits[0].object;
            const hitParent = hitObj.parent;

            if(batteryList.includes(hitParent)){
                scene.remove(hitParent);
                const idx = batteryList.indexOf(hitParent);
                batteryList.splice(idx,1);
                batteryCount++;
                refreshTaskUI();
                return;
            }
            if(almondWaterList.includes(hitParent)){
                scene.remove(hitParent);
                const idx = almondWaterList.indexOf(hitParent);
                almondWaterList.splice(idx,1);
                health = Math.min(100, health + 30);
                refreshHud();
                return;
            }
            if(rope && hitParent === rope){
                taskBar.innerText = "恭喜成功逃离!游戏通关!";
            }
        }

        function aiTick() {
            const pPos = camera.position;
            for(let ai of aiList){
                if(!ai) continue;
                const aPos = ai.position;
                ai.lookAt(pPos.x, aPos.y, pPos.z);
                const dir = new THREE.Vector3().subVectors(pPos, aPos).normalize();
                ai.position.add(dir.multiplyScalar(0.018));
                ai.position.y = 0;
                const dist = aPos.distanceTo(pPos);
                if(dist < 12) {
                    health -= 0.04;
                    refreshHud();
                    if(health <= 0) hud.innerText = "你被AI击杀!游戏结束";
                }
            }
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        function animateLoop() {
            requestAnimationFrame(animateLoop);
            camera.position.y = 1.6;
            camera.rotation.y = yaw;
            camera.rotation.x = pitch;

            if(moveForward) camera.translateZ(-moveSpeed);
            aiTick();
            renderer.render(scene, camera);
        }
        // 平台自动注入Three,无需外部script引入
        window.addEventListener('load', ()=>{
            window.THREE = window.three;
            init();
        })
    </script>
</body>
</html>

Game Source: 草原AI逃生

Creator: RocketTiger53

Libraries: three

Complexity: complex (674 lines, 26.0 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: ai-rockettiger53" to link back to the original. Then publish at arcadelab.ai/publish.