《Three.js 3D坦克对战【AI单机版】》

Three.js 3D坦克对战【AI单机版】

玩家操控蓝坦克,AI自动操控红坦克,自动寻路、追击、开火、躲障碍,直接复制打开即用

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Three.js 3D坦克 AI单机对战</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;}
body{overflow:hidden;background:#111;}
#gameInfo{position:absolute;top:10px;left:10px;color:#fff;font-size:18px;z-index:999;}
#winTip{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:48px;color:#ff3333;display:none;z-index:999;background:rgba(0,0,0,0.6);padding:20px;border-radius:10px;}
</style>
</head>
<body>
<div id="gameInfo">
我方血量:<span id="hp1">100</span> &nbsp;&nbsp;
AI血量:<span id="hp2">100</span>
</div>
<div id="winTip"></div>

<script src="https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.min.js"></script>
<script>
let scene,camera,renderer;
let playerTank,aiTank;
let bullets=[];
let walls=[];
const keys={};
let hp1=100,hp2=100;
const tankSpeed=0.08;
const bulletSpeed=0.3;
let aiFireCD=0;

// 初始化场景
function initScene(){
    scene=new THREE.Scene();
    scene.background=new THREE.Color(0x87ceeb);
    scene.fog=new THREE.Fog(0x87ceeb,20,80);

    camera=new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,0.1,1000);
    camera.position.set(0,35,30);
    camera.lookAt(0,0,0);

    renderer=new THREE.WebGLRenderer({antialias:true});
    renderer.setSize(window.innerWidth,window.innerHeight);
    renderer.shadowMap.enabled=true;
    document.body.appendChild(renderer.domElement);

    const dirLight=new THREE.DirectionalLight(0xffffff,1);
    dirLight.position.set(20,40,20);
    scene.add(dirLight);
    scene.add(new THREE.AmbientLight(0xffffff,0.4));

    const groundGeo=new THREE.PlaneGeometry(60,60);
    const groundMat=new THREE.MeshLambertMaterial({color:0x338833});
    const ground=new THREE.Mesh(groundGeo,groundMat);
    ground.rotation.x=-Math.PI/2;
    ground.receiveShadow=true;
    scene.add(ground);
}

// 创建坦克
function createTank(pos,color){
    const tank=new THREE.Group();
    const bodyGeo=new THREE.BoxGeometry(2.5,1,3.5);
    const bodyMat=new THREE.MeshLambertMaterial({color});
    const body=new THREE.Mesh(bodyGeo,bodyMat);
    body.position.y=0.5;
    body.castShadow=true;
    tank.add(body);

    const turretGeo=new THREE.CylinderGeometry(0.6,0.6,1.2,16);
    const turret=new THREE.Mesh(turretGeo,bodyMat);
    turret.position.y=1.6;
    tank.add(turret);

    const gunGeo=new THREE.CylinderGeometry(0.2,0.2,2.2,8);
    const gun=new THREE.Mesh(gunGeo,bodyMat);
    gun.rotation.z=Math.PI/2;
    gun.position.set(0,1.6,1.2);
    tank.add(gun);

    tank.position.copy(pos);
    return tank;
}

// 随机墙体
function createWall(){
    const wallGeo=new THREE.BoxGeometry(5,2,1.5);
    const wallMat=new THREE.MeshLambertMaterial({color:0x555555});
    for(let i=0;i=10;i++){
        const wall=new THREE.Mesh(wallGeo,wallMat);
        wall.position.set((Math.random()-0.5)*45,1,(Math.random()-0.5)*45);
        wall.castShadow=true;
        walls.push(wall);
        scene.add(wall);
    }
}

// 发射炮弹
function fireBullet(tank,isPlayer){
    const bulletGeo=new THREE.SphereGeometry(0.3,8,8);
    const bulletMat=new THREE.MeshBasicMaterial({color:isPlayer?0x00ffff:0xff6600});
    const bullet=new THREE.Mesh(bulletGeo,bulletMat);
    bullet.position.copy(tank.position);
    bullet.position.y=1.6;
    const dir=new THREE.Vector3(0,0,1);
    dir.applyQuaternion(tank.quaternion);
    bullet.userData={dir,isPlayer};
    bullets.push(bullet);
    scene.add(bullet);
}

// 玩家按键监听
function initKey(){
    window.addEventListener('keydown',e=>{
        keys[e.code]=true;
        if(e.code==='Space') fireBullet(playerTank,true);
    })
    window.addEventListener('keyup',e=>keys[e.code]=false);
}

// 玩家移动 WASD
function playerMove(){
    if(keys['KeyW']) playerTank.translateZ(tankSpeed);
    if(keys['KeyS']) playerTank.translateZ(-tankSpeed);
    if(keys['KeyA']) playerTank.rotation.y+=0.03;
    if(keys['KeyD']) playerTank.rotation.y-=0.03;
}

// AI智能逻辑:追踪玩家+转向+自动开火
function aiControl(){
    aiFireCD--;
    const targetPos=playerTank.position;
    const aiPos=aiTank.position;
    // 朝向玩家
    const angle=Math.atan2(targetPos.x-aiPos.x,targetPos.z-aiPos.z);
    aiTank.rotation.y=angle;
    const dis=aiPos.distanceTo(targetPos);

    // 距离合适就前进,太远追击,太近微调
    if(dis>12){
        aiTank.translateZ(tankSpeed*0.7);
    }else if(dis<6){
        aiTank.translateZ(-tankSpeed*0.4);
    }

    // 冷却结束自动开火
    if(aiFireCD<=0&&dis<25){
        fireBullet(aiTank,false);
        aiFireCD=60;
    }
}

// 炮弹移动+碰撞
function updateBullet(){
    for(let i=bullets.length-1;i>=0;i--){
        const b=bullets[i];
        b.position.addScaledVector(b.userData.dir,bulletSpeed);
        // 玩家炮弹打AI
        if(b.userData.isPlayer){
            if(b.position.distanceTo(aiTank.position)<2){
                hp2-=15;
                document.getElementById('hp2').innerText=hp2;
                checkWin();
                scene.remove(b);bullets.splice(i,1);continue;
            }
        }else{
            // AI炮弹打玩家
            if(b.position.distanceTo(playerTank.position)<2){
                hp1-=15;
                document.getElementById('hp1').innerText=hp1;
                checkWin();
                scene.remove(b);bullets.splice(i,1);continue;
            }
        }
        // 边界清除
        if(Math.abs(b.position.x)>38||Math.abs(b.position.z)>38){
            scene.remove(b);bullets.splice(i,1);
        }
    }
}

// 胜负判定
function checkWin(){
    const tip=document.getElementById('winTip');
    if(hp1<=0){
        tip.innerText='AI获胜,你输了!';
        tip.style.display='block';
    }
    if(hp2<=0){
        tip.innerText='恭喜你击败AI!';
        tip.style.display='block';
    }
}

// 窗口适配
window.addEventListener('resize',()=>{
    camera.aspect=innerWidth/innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(innerWidth,innerHeight);
})

// 渲染循环
function animate(){
    requestAnimationFrame(animate);
    playerMove();
    aiControl();
    updateBullet();
    renderer.render(scene,camera);
}

// 启动游戏
function start(){
    initScene();
    playerTank=createTank(new THREE.Vector3(-15,0,0),0x2266ff);
    aiTank=createTank(new THREE.Vector3(15,0,0),0xff2222);
    scene.add(playerTank,aiTank);
    createWall();
    initKey();
    animate();
}
start();
</script>
</body>
</html>

操作说明

  • 我方蓝坦克:WASD 移动转向,空格键 发射炮弹
  • 红坦克全自动AI:
    1. 自动对准玩家方向
    2. 距离远主动追击,距离过近自动后撤
    3. 进入射程自动开火,自带开火冷却
    4. 血量归零直接结束对局

可一键修改难度

  1. 降低AI强度:修改 aiTank.translateZ 速度调小,aiFireCD 调大加长开火间隔
  2. 提高难度:加快AI移速、缩短开火CD、提升炮弹伤害
  3. 增加血量:修改初始 hp1 hp2 数值
相关推荐
leoZ2317 小时前
本地跑大模型实战(七):llama.cpp 性能调优,让推理更快更省
java·人工智能·spring·生成对抗网络·语言模型·自然语言处理·llama
To_OC7 小时前
我把《天龙八部》塞进向量数据库后,终于搞懂了 RAG 到底是个啥
人工智能·llm·agent
蜡台7 小时前
AI Agent(智能体)入门基础教程
人工智能
Akir.weiwen8 小时前
Token 层差异:从颜色值到语义状态的三层跃迁
人工智能·设计规范
迷迭香yy8 小时前
集合竞价数据挖掘实战:用Python构建开盘信号识别系统
人工智能·python·数据挖掘
烬羽8 小时前
React Router v7 完全指南:一个 Demo 吃透前端路由
javascript·react.js·前端框架
烬羽8 小时前
从"整个页面刷新"到"丝滑切换":手写一个 HashRouter 彻底搞懂前端路由
javascript·前端框架·全栈
大模型momo8 小时前
Spring AI 实战:多 Agent 协作实战 —— 分工拆解复杂旅游行程任务
人工智能·spring·ai·agent·旅游
用户938515635079 小时前
从零理解 React Router v6:每一个 API 都是怎么工作的
前端·javascript·全栈
小程故事多_809 小时前
从A2C、TRPO、PPO到GRPO,强化学习策略梯度算法完整演进与大模型落地实战解析
人工智能·算法