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>
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:
- 自动对准玩家方向
- 距离远主动追击,距离过近自动后撤
- 进入射程自动开火,自带开火冷却
- 血量归零直接结束对局
可一键修改难度
- 降低AI强度:修改
aiTank.translateZ速度调小,aiFireCD调大加长开火间隔 - 提高难度:加快AI移速、缩短开火CD、提升炮弹伤害
- 增加血量:修改初始
hp1 hp2数值