fixup
This commit is contained in:
@@ -34,6 +34,8 @@ export class GameEngine {
|
||||
this.zombieBullets = new Map()
|
||||
// 掉落物列表
|
||||
this.loots = new Map()
|
||||
// 机枪塔列表
|
||||
this.turrets = new Map()
|
||||
|
||||
// 游戏运行状态
|
||||
this.running = false
|
||||
@@ -55,9 +57,9 @@ export class GameEngine {
|
||||
[WEAPONS.MACHINE_GUN]: 100,
|
||||
[WEAPONS.SHOTGUN]: 20,
|
||||
[WEAPONS.GRENADE]: 10,
|
||||
[WEAPONS.MOLOTOV]: 5,
|
||||
[WEAPONS.NUT_WALL]: 3,
|
||||
[WEAPONS.AUTO_TURRET]: 2
|
||||
[WEAPONS.MOLOTOV]: 10,
|
||||
[WEAPONS.NUT_WALL]: 10,
|
||||
[WEAPONS.AUTO_TURRET]: 5
|
||||
}
|
||||
|
||||
// 手雷蓄力相关
|
||||
@@ -396,7 +398,7 @@ export class GameEngine {
|
||||
zombie.y = zs.y
|
||||
zombie.health = zs.health
|
||||
const angle = zs.angle || 0
|
||||
this.scene.updateZombie(zs.id, zs.x, zs.y, angle, zs.health)
|
||||
this.scene.updateZombie(zs.id, zs.x, zs.y, angle, zs.health, zs.isAttacking || false)
|
||||
// 受伤特效
|
||||
if (prevHealth > zs.health && zs.health > 0) {
|
||||
this.scene.addHitEffect(zs.x, zs.y)
|
||||
@@ -465,13 +467,46 @@ export class GameEngine {
|
||||
|
||||
// 爆炸效果
|
||||
if (state.explosions) {
|
||||
console.log('Explosions received:', state.explosions)
|
||||
for (const exp of state.explosions) {
|
||||
console.log('Creating explosion at:', exp.x, exp.y, 'radius:', exp.radius)
|
||||
this.scene.addExplosion(exp.x, exp.y, exp.radius || 3)
|
||||
}
|
||||
}
|
||||
|
||||
// 燃烧区域
|
||||
if (state.fireZones) {
|
||||
const serverIds = new Set(state.fireZones.map(f => f.id))
|
||||
// 新增或更新
|
||||
for (const fz of state.fireZones) {
|
||||
this.scene.addFireZone(fz.id, fz.x, fz.y, fz.radius || 2, fz.duration || 5, fz.elapsed || 0)
|
||||
}
|
||||
// 移除已消失的
|
||||
for (const [id] of this.scene.fireZones) {
|
||||
if (!serverIds.has(id)) {
|
||||
this.scene.removeFireZone(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 机枪塔
|
||||
if (state.turrets) {
|
||||
const serverIds = new Set(state.turrets.map(t => t.id))
|
||||
for (const ts of state.turrets) {
|
||||
if (!this.turrets.has(ts.id)) {
|
||||
this.turrets.set(ts.id, ts)
|
||||
this.scene.addTurret(ts.id, ts.x, ts.y, ts.health, ts.maxHealth, ts.isAutoTurret)
|
||||
} else {
|
||||
this.turrets.set(ts.id, ts)
|
||||
this.scene.updateTurret(ts.id, ts.health)
|
||||
}
|
||||
}
|
||||
for (const [id] of this.turrets) {
|
||||
if (!serverIds.has(id)) {
|
||||
this.turrets.delete(id)
|
||||
this.scene.removeTurret(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 命中效果
|
||||
if (state.hits) {
|
||||
for (const hit of state.hits) {
|
||||
|
||||
@@ -31,6 +31,8 @@ export class GameScene {
|
||||
this.bullets = [] // 玩家子弹数组
|
||||
this.zombieBullets = [] // 僵尸子弹数组
|
||||
this.loots = new Map() // Map<lootId, {mesh, type}>
|
||||
this.fireZones = new Map() // Map<fireZoneId, {mesh, light, startTime, duration}
|
||||
this.turrets = new Map() // Map<turretId, {group, healthBar, healthBarBg, isAutoTurret}
|
||||
this.effects = [] // 特效数组
|
||||
this.wallMeshes = [] // 墙壁网格
|
||||
this.nutWalls = new Map() // Map<key, {mesh, healthBar}>
|
||||
@@ -413,18 +415,24 @@ export class GameScene {
|
||||
head.castShadow = true
|
||||
group.add(head)
|
||||
|
||||
// 手臂
|
||||
const armGeo = new THREE.BoxGeometry(0.12, 0.6, 0.12)
|
||||
// 手臂(比身体宽,确保从各个角度都可见)
|
||||
const armGeo = new THREE.BoxGeometry(0.18, 0.7, 0.18)
|
||||
const armMat = new THREE.MeshLambertMaterial({ color: armColor })
|
||||
const leftArm = new THREE.Mesh(armGeo, armMat)
|
||||
leftArm.position.set(-0.35, 0.5, 0.2)
|
||||
leftArm.position.set(-0.5, 0.5, 0.0)
|
||||
leftArm.rotation.x = -0.5
|
||||
leftArm.castShadow = true
|
||||
group.add(leftArm)
|
||||
const rightArm = new THREE.Mesh(armGeo, armMat)
|
||||
rightArm.position.set(0.35, 0.5, 0.2)
|
||||
rightArm.position.set(0.5, 0.5, 0.0)
|
||||
rightArm.rotation.x = -0.5
|
||||
rightArm.castShadow = true
|
||||
group.add(rightArm)
|
||||
|
||||
// 存储手臂引用供攻击动画使用
|
||||
group.userData.leftArm = leftArm
|
||||
group.userData.rightArm = rightArm
|
||||
|
||||
// 精英僵尸发光效果
|
||||
if (isElite) {
|
||||
const glowGeo = new THREE.SphereGeometry(0.6, 8, 8)
|
||||
@@ -514,7 +522,12 @@ export class GameScene {
|
||||
const model = this.createZombieModel(isElite, isSplitter)
|
||||
model.position.set(x, 0, y)
|
||||
this.scene.add(model)
|
||||
this.zombies.set(id, { model, isElite, isSplitter })
|
||||
this.zombies.set(id, {
|
||||
model, isElite, isSplitter,
|
||||
leftArm: model.userData.leftArm,
|
||||
rightArm: model.userData.rightArm,
|
||||
isAttacking: false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,19 +547,34 @@ export class GameScene {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新僵尸位置和角度
|
||||
* 更新僵尸位置、角度和攻击动画
|
||||
* @param {string} id 僵尸ID
|
||||
* @param {number} x X坐标
|
||||
* @param {number} y Y坐标
|
||||
* @param {number} angle 旋转角度
|
||||
* @param {number} health 生命值
|
||||
* @param {boolean} isAttacking 是否正在攻击
|
||||
*/
|
||||
updateZombie(id, x, y, angle, health) {
|
||||
updateZombie(id, x, y, angle, health, isAttacking = false) {
|
||||
const zombie = this.zombies.get(id)
|
||||
if (zombie) {
|
||||
zombie.model.position.x = x
|
||||
zombie.model.position.z = y
|
||||
zombie.model.rotation.y = angle
|
||||
|
||||
// 攻击动画:手臂前后挥动
|
||||
if (zombie.leftArm && zombie.rightArm) {
|
||||
if (isAttacking) {
|
||||
const t = Date.now() * 0.012
|
||||
const swing = Math.sin(t) * 1.0
|
||||
zombie.leftArm.rotation.x = -1.5 + swing
|
||||
zombie.rightArm.rotation.x = -1.5 - swing
|
||||
} else {
|
||||
zombie.leftArm.rotation.x = -0.5
|
||||
zombie.rightArm.rotation.x = -0.5
|
||||
}
|
||||
}
|
||||
zombie.isAttacking = isAttacking
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,6 +892,164 @@ export class GameScene {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加火焰区域效果
|
||||
* @param {number} id 火焰区域ID
|
||||
* @param {number} x X坐标
|
||||
* @param {number} y Y坐标
|
||||
* @param {number} radius 火焰半径
|
||||
* @param {number} duration 持续时间(秒)
|
||||
* @param {number} elapsed 已经过时间(秒)
|
||||
*/
|
||||
addFireZone(id, x, y, radius, duration, elapsed) {
|
||||
if (this.fireZones.has(id)) return
|
||||
|
||||
// 火焰地面圆
|
||||
const geo = new THREE.CircleGeometry(radius, 24)
|
||||
const mat = new THREE.MeshBasicMaterial({
|
||||
color: 0xff4400,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
side: THREE.DoubleSide
|
||||
})
|
||||
const mesh = new THREE.Mesh(geo, mat)
|
||||
mesh.rotation.x = -Math.PI / 2
|
||||
mesh.position.set(x, 0.05, y)
|
||||
this.scene.add(mesh)
|
||||
|
||||
// 火焰光源
|
||||
const light = new THREE.PointLight(0xff6600, 2, radius * 3)
|
||||
light.position.set(x, 1, y)
|
||||
this.scene.add(light)
|
||||
|
||||
this.fireZones.set(id, { mesh, light, startTime: Date.now() - elapsed * 1000, duration: duration * 1000 })
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除火焰区域效果
|
||||
* @param {number} id 火焰区域ID
|
||||
*/
|
||||
removeFireZone(id) {
|
||||
const fz = this.fireZones.get(id)
|
||||
if (!fz) return
|
||||
this.scene.remove(fz.mesh)
|
||||
this.scene.remove(fz.light)
|
||||
fz.mesh.geometry.dispose()
|
||||
fz.mesh.material.dispose()
|
||||
this.fireZones.delete(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加机枪塔渲染
|
||||
* @param {number} id 实体ID
|
||||
* @param {number} x X坐标
|
||||
* @param {number} y Y坐标
|
||||
* @param {number} health 当前生命值
|
||||
* @param {number} maxHealth 最大生命值
|
||||
* @param {boolean} isAutoTurret 是否为自动机枪塔(false则为坚果墙塔基)
|
||||
*/
|
||||
addTurret(id, x, y, health, maxHealth, isAutoTurret) {
|
||||
if (this.turrets.has(id)) return
|
||||
|
||||
const group = new THREE.Group()
|
||||
|
||||
// 塔基(底座)
|
||||
const baseGeo = new THREE.CylinderGeometry(0.35, 0.4, 0.3, 8)
|
||||
const baseMat = new THREE.MeshLambertMaterial({ color: 0x555555 })
|
||||
const base = new THREE.Mesh(baseGeo, baseMat)
|
||||
base.position.y = 0.15
|
||||
base.castShadow = true
|
||||
group.add(base)
|
||||
|
||||
if (isAutoTurret) {
|
||||
// 机身
|
||||
const bodyGeo = new THREE.CylinderGeometry(0.2, 0.25, 0.4, 8)
|
||||
const bodyMat = new THREE.MeshLambertMaterial({ color: 0x336633 })
|
||||
const body = new THREE.Mesh(bodyGeo, bodyMat)
|
||||
body.position.y = 0.5
|
||||
body.castShadow = true
|
||||
group.add(body)
|
||||
|
||||
// 枪管
|
||||
const barrelGeo = new THREE.CylinderGeometry(0.05, 0.05, 0.5, 6)
|
||||
const barrelMat = new THREE.MeshLambertMaterial({ color: 0x222222 })
|
||||
const barrel = new THREE.Mesh(barrelGeo, barrelMat)
|
||||
barrel.rotation.x = Math.PI / 2
|
||||
barrel.position.set(0, 0.55, 0.25)
|
||||
barrel.castShadow = true
|
||||
group.add(barrel)
|
||||
|
||||
// 炮塔顶部
|
||||
const topGeo = new THREE.SphereGeometry(0.15, 8, 6)
|
||||
const topMat = new THREE.MeshLambertMaterial({ color: 0x446644 })
|
||||
const top = new THREE.Mesh(topGeo, topMat)
|
||||
top.position.y = 0.75
|
||||
group.add(top)
|
||||
} else {
|
||||
// 坚果墙塔基 - 木箱外观
|
||||
const boxGeo = new THREE.BoxGeometry(0.7, 0.8, 0.7)
|
||||
const boxMat = new THREE.MeshLambertMaterial({ color: 0x8B4513 })
|
||||
const box = new THREE.Mesh(boxGeo, boxMat)
|
||||
box.position.y = 0.7
|
||||
box.castShadow = true
|
||||
group.add(box)
|
||||
}
|
||||
|
||||
// 血条背景
|
||||
const barBgGeo = new THREE.PlaneGeometry(0.8, 0.08)
|
||||
const barBgMat = new THREE.MeshBasicMaterial({ color: 0x333333, side: THREE.DoubleSide })
|
||||
const barBg = new THREE.Mesh(barBgGeo, barBgMat)
|
||||
barBg.position.set(0, 1.3, 0)
|
||||
barBg.rotation.x = -0.3
|
||||
group.add(barBg)
|
||||
|
||||
// 血条前景
|
||||
const barGeo = new THREE.PlaneGeometry(0.78, 0.06)
|
||||
const barMat = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide })
|
||||
const healthBar = new THREE.Mesh(barGeo, barMat)
|
||||
healthBar.position.set(0, 1.3, 0.01)
|
||||
healthBar.rotation.x = -0.3
|
||||
group.add(healthBar)
|
||||
|
||||
group.position.set(x, 0, y)
|
||||
this.scene.add(group)
|
||||
this.turrets.set(id, { group, healthBar, healthBarBg: barBg, isAutoTurret, maxHealth })
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新机枪塔血量显示
|
||||
*/
|
||||
updateTurret(id, health) {
|
||||
const turret = this.turrets.get(id)
|
||||
if (!turret) return
|
||||
|
||||
const pct = Math.max(0, health / turret.maxHealth)
|
||||
turret.healthBar.scale.x = pct
|
||||
turret.healthBar.position.x = -(1 - pct) * 0.39
|
||||
|
||||
if (pct > 0.5) {
|
||||
turret.healthBar.material.color.setHex(0x00ff00)
|
||||
} else if (pct > 0.25) {
|
||||
turret.healthBar.material.color.setHex(0xffff00)
|
||||
} else {
|
||||
turret.healthBar.material.color.setHex(0xff0000)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除机枪塔
|
||||
*/
|
||||
removeTurret(id) {
|
||||
const turret = this.turrets.get(id)
|
||||
if (!turret) return
|
||||
this.scene.remove(turret.group)
|
||||
turret.group.traverse(child => {
|
||||
if (child.geometry) child.geometry.dispose()
|
||||
if (child.material) child.material.dispose()
|
||||
})
|
||||
this.turrets.delete(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加枪口火焰效果
|
||||
* @param {number} x X坐标
|
||||
@@ -1084,6 +1270,19 @@ export class GameScene {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新火焰区域闪烁效果
|
||||
for (const [id, fz] of this.fireZones) {
|
||||
const elapsed = (now - fz.startTime) / 1000
|
||||
if (elapsed * 1000 >= fz.duration) {
|
||||
this.removeFireZone(id)
|
||||
} else {
|
||||
// 火焰闪烁
|
||||
const flicker = 0.3 + Math.sin(now * 0.01 + id) * 0.15 + Math.sin(now * 0.023) * 0.1
|
||||
fz.mesh.material.opacity = flicker
|
||||
fz.light.intensity = 1.5 + Math.sin(now * 0.015) * 0.5
|
||||
}
|
||||
}
|
||||
|
||||
// 更新子弹拖尾
|
||||
for (const bullet of this.bullets) {
|
||||
if (!bullet.isGrenade && !bullet.isMolotov && bullet.trail) {
|
||||
|
||||
@@ -94,8 +94,8 @@ export const WEAPON_CONFIG = {
|
||||
name: 'Molotov',
|
||||
damage: 80,
|
||||
fireRate: 2000,
|
||||
ammo: 5,
|
||||
maxAmmo: 5,
|
||||
ammo: 10,
|
||||
maxAmmo: 10,
|
||||
speed: 12,
|
||||
spread: 0,
|
||||
pellets: 1,
|
||||
@@ -110,8 +110,8 @@ export const WEAPON_CONFIG = {
|
||||
name: 'Wall',
|
||||
damage: 0,
|
||||
fireRate: 1000,
|
||||
ammo: 3,
|
||||
maxAmmo: 3,
|
||||
ammo: 10,
|
||||
maxAmmo: 10,
|
||||
speed: 0,
|
||||
spread: 0,
|
||||
pellets: 0,
|
||||
@@ -124,8 +124,8 @@ export const WEAPON_CONFIG = {
|
||||
name: 'Turret',
|
||||
damage: 0,
|
||||
fireRate: 2000,
|
||||
ammo: 2,
|
||||
maxAmmo: 2,
|
||||
ammo: 5,
|
||||
maxAmmo: 5,
|
||||
speed: 0,
|
||||
spread: 0,
|
||||
pellets: 0,
|
||||
|
||||
Reference in New Issue
Block a user