《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 数值
相关推荐
想你依然心痛1 小时前
HarmonyOS 6(API 23)实战:基于HMAF的「智链中枢」——PC端AI智能体多Agent协同编排与任务调度平台
人工智能·华为·harmonyos·智能体
默_笙1 小时前
🚲 手写一个"Claude Code":我用 LangChain + ReAct 循环,让 AI 自己读文件、解释代码
前端·javascript
阿虎儿1 小时前
Dify: The length of output variable result must be less than 30 elements.
人工智能
火山引擎开发者社区2 小时前
企业级 Agent 开发实战:从 Identity 开始进入生产环境
人工智能
IvorySQL2 小时前
深度拆解 IvorySQL 去 O 核心解决方案
数据库·人工智能·postgresql
等咸鱼的狸猫2 小时前
# JS 核心六大主题:闭包内存模型、原型链查找、事件流机制与 Promise / Event Loop 实现视角复盘
前端·javascript
般若-波罗蜜2 小时前
MinerU高级用法,避坑指南(持续更新)
人工智能·python·语言模型·自然语言处理
安卓修改大师2 小时前
安卓修改大师 vs MT管理器:反编译工具终极对决与全景解析
android·人工智能·机器翻译
A15362552 小时前
国内好用的WMS仓储管理系统有哪些?万里牛WMS深度评测
大数据·数据库·人工智能