平板三角洲战术枪战
by SparkOwl95207 lines7.0 KB
<!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>平板三角洲战术枪战</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%;overflow:hidden;background:#000;font-family:system-ui}
canvas{display:block;width:100%;height:100%}
/* 平板触屏UI */
.joystick{position:absolute;left:20px;bottom:20px;width:140px;height:140px;background:rgba(25,25,25,0.4);border-radius:50%;touch-action:none}
.joystick-knob{width:60px;height:60px;background:rgba(255,255,255,0.5);border-radius:50%;position:absolute;left:40px;top:40px}
.fire-btn{position:absolute;right:24px;bottom:30px;width:100px;height:100px;border-radius:50%;background:rgba(200,30,30,0.4);color:#fff;font-size:24px;border:none;touch-action:none}
.hud{position:absolute;top:16px;left:16px;color:#fff;font-size:20px;background:rgba(0,0,0,0.5);padding:8px;border-radius:8px}
.crosshair{position:absolute;top:50%;left:50%;width:20px;height:20px;margin:-10px 0 0 -10px;color:#fff;font-size:24px;text-align:center;pointer-events:none}
</style>
</head>
<body>
<div class="hud">分数:<span id="score">0</span><br>血量:<span id="hp">100</span></div>
<div class="crosshair">+</div>
<div class="joystick" id="joystick">
<div class="joystick-knob" id="joystickKnob"></div>
</div>
<button class="fire-btn" id="fireBtn">开火</button>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script>
// ========== 基础初始化 ==========
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x334433);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const light = new THREE.DirectionalLight(0xffffff,0.8);
light.position.set(20,40,20);
scene.add(light);
scene.add(new THREE.AmbientLight(0x404040));
// 玩家
let playerPos = new THREE.Vector3(0,1.6,0);
let yaw=0,pitch=0;
const playerSpeed=4;
let hp=100;
let score=0;
// 摇杆变量
let joy={active:false,dx:0,dy:0};
// 触屏滑动转向
let touchLookId=null;
let lastTouch={x:0,y:0};
// 地图墙体(坚固的墙,阻挡子弹和移动,对应你的作文)
const walls=[];
function createWall(x,z,w,h,d){
const geo=new THREE.BoxGeometry(w,h,d);
const mat=new THREE.MeshLambertMaterial({color:0x777777});
const wall=new THREE.Mesh(geo,mat);
wall.position.set(x,h/2,z);
scene.add(wall);
walls.push({mesh:wall,x,z,w,d})
}
createWall(8,0,1,4,12);
createWall(-8,0,1,4,12);
createWall(0,8,1,16,1);
createWall(0,-8,1,16,1);
createWall(-4,-4,1,5,5);
createWall(4,4,1,5,5);
// AI敌人
let enemies=[];
function spawnEnemy(){
const geo=new THREE.CylinderGeometry(0.4,0.4,1.6,8);
const mat=new THREE.MeshLambertMaterial({color:0xcc3333});
const enemyMesh=new THREE.Mesh(geo,mat);
enemyMesh.position.set( (Math.random()>0.5?1:-1)* (6+Math.random()*4), 0.8, (Math.random()>0.5?1:-1)*(6+Math.random()*4) );
scene.add(enemyMesh);
enemies.push({mesh:enemyMesh,hp:3,lastShot:0})
}
for(let i=0;i<3;i++) spawnEnemy();
// ========== 触屏摇杆 ==========
const joystick = document.getElementById("joystick");
const knob = document.getElementById("joystickKnob");
joystick.addEventListener("touchstart",e=>{
e.preventDefault();
joy.active=true;
})
joystick.addEventListener("touchmove",e=>{
e.preventDefault();
const t=e.touches[0];
const rect=joystick.getBoundingClientRect();
const cx=rect.left+rect.width/2;
const cy=rect.top+rect.height/2;
joy.dx = (t.clientX-cx)/60;
joy.dy = (t.clientY-cy)/60;
const len=Math.sqrt(joy.dx*joy.dx+joy.dy*joy.dy);
if(len>1){joy.dx/=len;joy.dy/=len}
knob.style.transform=`translate(${joy.dx*40}px,${joy.dy*40}px)`;
})
joystick.addEventListener("touchend",()=>{
joy.active=false;
joy.dx=0;joy.dy=0;
knob.style.transform="translate(0,0)";
})
// 触屏滑动旋转视角
renderer.domElement.addEventListener("touchstart",e=>{
for(let t of e.touches){
if(t.target!==joystick && t.target!==fireBtn){
touchLookId=t.identifier;
lastTouch.x=t.clientX; lastTouch.y=t.clientY;
}
}
})
renderer.domElement.addEventListener("touchmove",e=>{
for(let t of e.touches){
if(t.identifier === touchLookId){
const dx = t.clientX - lastTouch.x;
const dy = t.clientY - lastTouch.y;
yaw -= dx *0.004;
pitch = Math.max(-1.2, Math.min(1.2, pitch - dy*0.004));
lastTouch.x = t.clientX; lastTouch.y = t.clientY;
}
}
})
// 开火按钮
const fireBtn = document.getElementById("fireBtn");
fireBtn.addEventListener("touchstart",shoot);
function shoot(){
const raycaster=new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(0,0),camera);
const allHits=raycaster.intersectObjects(enemies.map(e=>e.mesh).concat(walls.map(w=>w.mesh)));
if(allHits.length>0){
const hit=allHits[0];
for(let e of enemies){
if(hit.object === e.mesh){
e.hp--;
if(e.hp<=0){
scene.remove(e.mesh);
enemies = enemies.filter(x=>x!==e);
score += 50;
document.getElementById("score").innerText=score;
}
}
}
}
}
// 敌人AI逻辑
function updateEnemies(delta){
const now=Date.now();
enemies.forEach(e=>{
const ePos = e.mesh.position;
const dir = new THREE.Vector3(playerPos.x-ePos.x,0,playerPos.z-ePos.z).normalize();
const dist = playerPos.distanceTo(ePos);
if(dist < 12 && dist>2){
e.mesh.position.add(dir.multiplyScalar(0.025));
}
//敌人射击
if(dist<10 && now-e.lastShot>1200){
e.lastShot=now;
hp -=8;
document.getElementById("hp").innerText=Math.max(0,hp);
if(hp<=0) alert("游戏结束!分数:"+score);
}
e.mesh.lookAt(playerPos.x, e.mesh.position.y, playerPos.z);
})
}
// 游戏循环
let lastTime=0;
function loop(time){
requestAnimationFrame(loop);
const delta = (time-lastTime)/1000;
lastTime=time;
//摇杆移动
if(joy.active){
const forward=new THREE.Vector3(-Math.sin(yaw),0,-Math.cos(yaw));
const right=new THREE.Vector3(Math.cos(yaw),0,-Math.sin(yaw));
playerPos.add(forward.multiplyScalar(-joy.dy * playerSpeed*delta));
playerPos.add(right.multiplyScalar(joy.dx * playerSpeed*delta));
}
camera.position.copy(playerPos);
camera.rotation.order="YXZ";
camera.rotation.y=yaw;
camera.rotation.x=pitch;
updateEnemies(delta);
renderer.render(scene,camera);
}
requestAnimationFrame(loop);
// 窗口适配
window.addEventListener("resize",()=>{
camera.aspect=window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth,window.innerHeight);
})
</script>
</body>
</html>
Game Source: 平板三角洲战术枪战
Creator: SparkOwl95
Libraries: none
Complexity: complex (207 lines, 7.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: game-sparkowl95-mw0ltt7n" to link back to the original. Then publish at arcadelab.ai/publish.