Add game source, converter pipeline, and web port
This commit is contained in:
+585
@@ -0,0 +1,585 @@
|
||||
import * as THREE from 'three';
|
||||
import { Player } from './Player';
|
||||
import { Enemy, initSpiderModel } from './Enemy';
|
||||
import { Sword } from './Sword';
|
||||
import { Staff } from './Staff';
|
||||
import { Fireball } from './Fireball';
|
||||
import { Cross, initCrossModel } from './Cross';
|
||||
import { Arena } from './Arena';
|
||||
import { initSounds, playSound, resumeContext } from './SoundManager';
|
||||
|
||||
const SPAWN_TIME = 10;
|
||||
|
||||
export class Game {
|
||||
scene: THREE.Scene;
|
||||
camera: THREE.PerspectiveCamera;
|
||||
renderer: THREE.WebGLRenderer;
|
||||
clock: THREE.Clock;
|
||||
|
||||
player!: Player;
|
||||
enemies: Enemy[] = [];
|
||||
fireballs: Fireball[] = [];
|
||||
crosses: Cross[] = [];
|
||||
|
||||
keys: Set<string> = new Set();
|
||||
mouseButtons: Set<number> = new Set();
|
||||
private prevMouseButtons: Set<number> = new Set();
|
||||
mouseX = 0;
|
||||
mouseY = 0;
|
||||
groundPoint = new THREE.Vector3();
|
||||
|
||||
spawnTimer = 0;
|
||||
spawns = 0;
|
||||
killed = 0;
|
||||
gameOver = false;
|
||||
|
||||
// Debug hitzone visualization
|
||||
private showHitzones = false;
|
||||
private playerHitRing!: THREE.Mesh;
|
||||
private enemyHitRings: Map<Enemy, THREE.Mesh> = new Map();
|
||||
private fireballHitRings: Map<Fireball, THREE.Mesh> = new Map();
|
||||
private crossHitRings: Map<Cross, THREE.Mesh> = new Map();
|
||||
|
||||
// UI elements
|
||||
private uiHealthBar!: HTMLElement;
|
||||
private uiHealthText!: HTMLElement;
|
||||
private uiWaveLabel!: HTMLElement;
|
||||
private uiKillsLabel!: HTMLElement;
|
||||
private uiFireballCD!: HTMLElement;
|
||||
private uiSwordCD!: HTMLElement;
|
||||
private uiTeleportCD!: HTMLElement;
|
||||
private uiWaveBarFill!: HTMLElement;
|
||||
private uiWaveBarLabel!: HTMLElement;
|
||||
private uiGameOver!: HTMLElement;
|
||||
private mouseOnCanvas = false;
|
||||
|
||||
constructor() {
|
||||
this.scene = new THREE.Scene();
|
||||
this.scene.background = new THREE.Color(0x0d1117);
|
||||
this.scene.fog = new THREE.Fog(0x0d1117, 25, 70);
|
||||
|
||||
this.camera = new THREE.PerspectiveCamera(
|
||||
60, window.innerWidth / window.innerHeight, 1, 150
|
||||
);
|
||||
this.camera.position.set(0, 25, -15);
|
||||
this.camera.lookAt(0, 0, 0);
|
||||
|
||||
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
this.renderer.shadowMap.enabled = true;
|
||||
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
document.body.prepend(this.renderer.domElement);
|
||||
|
||||
this.clock = new THREE.Clock();
|
||||
|
||||
this.setupUI();
|
||||
this.setupInput();
|
||||
this.setupLighting();
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.setupArena();
|
||||
this.setupPlayer();
|
||||
this.player.setGame(this);
|
||||
|
||||
initSounds();
|
||||
|
||||
await Promise.all([
|
||||
this.player.init(),
|
||||
this.player.rightHandWeapon.init(),
|
||||
this.player.leftHandWeapon.init(),
|
||||
initSpiderModel(),
|
||||
initCrossModel(),
|
||||
]);
|
||||
}
|
||||
|
||||
start() {
|
||||
window.addEventListener('resize', () => {
|
||||
this.camera.aspect = window.innerWidth / window.innerHeight;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
|
||||
this.animate();
|
||||
}
|
||||
|
||||
private setupUI() {
|
||||
this.uiHealthBar = document.getElementById('health-bar')!;
|
||||
this.uiHealthText = document.getElementById('health-text')!;
|
||||
this.uiWaveLabel = document.getElementById('wave-label')!;
|
||||
this.uiKillsLabel = document.getElementById('kills-label')!;
|
||||
this.uiFireballCD = document.querySelector('#fireball-cooldown .cooldown-fill')!;
|
||||
this.uiSwordCD = document.querySelector('#sword-cooldown .cooldown-fill')!;
|
||||
this.uiTeleportCD = document.querySelector('#teleport-cooldown .cooldown-fill')!;
|
||||
this.uiWaveBarFill = document.getElementById('wave-bar-fill')!;
|
||||
this.uiWaveBarLabel = document.getElementById('wave-bar-label')!;
|
||||
this.uiGameOver = document.getElementById('game-over')!;
|
||||
|
||||
const canvas = this.renderer.domElement;
|
||||
canvas.addEventListener('mouseenter', () => this.mouseOnCanvas = true);
|
||||
canvas.addEventListener('mouseleave', () => this.mouseOnCanvas = false);
|
||||
canvas.addEventListener('contextmenu', e => e.preventDefault());
|
||||
}
|
||||
|
||||
private setupInput() {
|
||||
window.addEventListener('keydown', (e) => {
|
||||
this.keys.add(e.code);
|
||||
if (e.code === 'Space') {
|
||||
this.player.jump();
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.code === 'KeyH') {
|
||||
this.showHitzones = !this.showHitzones;
|
||||
}
|
||||
});
|
||||
window.addEventListener('keyup', (e) => {
|
||||
this.keys.delete(e.code);
|
||||
if (e.code === 'KeyE' && !this.gameOver) {
|
||||
this.player.leftHandWeapon.skill2(this.groundPoint, this.player);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('mousedown', (e) => {
|
||||
resumeContext();
|
||||
this.mouseButtons.add(e.button);
|
||||
this.prevMouseButtons.add(e.button);
|
||||
});
|
||||
window.addEventListener('mouseup', (e) => {
|
||||
resumeContext();
|
||||
this.mouseButtons.delete(e.button);
|
||||
this.handleMouseRelease(e.button);
|
||||
});
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
this.mouseX = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
this.mouseY = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||
});
|
||||
}
|
||||
|
||||
private handleMouseRelease(button: number) {
|
||||
if (this.gameOver) return;
|
||||
if (button === 0) {
|
||||
this.player.leftHandWeapon.skill1(this.groundPoint, this.player);
|
||||
} else if (button === 1) {
|
||||
this.player.switchHands();
|
||||
} else if (button === 2) {
|
||||
this.player.rightHandWeapon.skill1(this.groundPoint, this.player);
|
||||
}
|
||||
}
|
||||
|
||||
private setupLighting() {
|
||||
const ambient = new THREE.AmbientLight(0x556688, 0.35);
|
||||
this.scene.add(ambient);
|
||||
|
||||
const dirLight = new THREE.DirectionalLight(0xccddff, 0.55);
|
||||
dirLight.position.set(30, 40, 20);
|
||||
dirLight.castShadow = true;
|
||||
dirLight.shadow.mapSize.width = 1024;
|
||||
dirLight.shadow.mapSize.height = 1024;
|
||||
dirLight.shadow.camera.near = 1;
|
||||
dirLight.shadow.camera.far = 150;
|
||||
dirLight.shadow.camera.left = -60;
|
||||
dirLight.shadow.camera.right = 60;
|
||||
dirLight.shadow.camera.top = 60;
|
||||
dirLight.shadow.camera.bottom = -60;
|
||||
this.scene.add(dirLight);
|
||||
}
|
||||
|
||||
private setupArena() {
|
||||
const arena = new Arena();
|
||||
this.scene.add(arena);
|
||||
}
|
||||
|
||||
private setupPlayer() {
|
||||
this.player = new Player();
|
||||
this.player.body.position.set(0, 0.5, 0);
|
||||
this.scene.add(this.player.body);
|
||||
|
||||
// Player hitzone ring (contact damage radius = 2)
|
||||
this.playerHitRing = this.createHitRing(2, 0xff0000);
|
||||
this.scene.add(this.playerHitRing);
|
||||
}
|
||||
|
||||
private createHitRing(radius: number, color: number): THREE.Mesh {
|
||||
const geom = new THREE.RingGeometry(radius - 0.05, radius, 48);
|
||||
const mat = new THREE.MeshBasicMaterial({
|
||||
color,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
side: THREE.DoubleSide,
|
||||
depthWrite: false,
|
||||
});
|
||||
const ring = new THREE.Mesh(geom, mat);
|
||||
ring.rotation.x = -Math.PI / 2;
|
||||
ring.position.y = 0.05;
|
||||
ring.renderOrder = 999;
|
||||
return ring;
|
||||
}
|
||||
|
||||
private updateHitzones() {
|
||||
this.playerHitRing.position.x = this.player.body.position.x;
|
||||
this.playerHitRing.position.z = this.player.body.position.z;
|
||||
this.playerHitRing.visible = this.showHitzones;
|
||||
|
||||
// Enemy rings
|
||||
for (const enemy of this.enemies) {
|
||||
let ring = this.enemyHitRings.get(enemy);
|
||||
if (!ring) {
|
||||
ring = this.createHitRing(2, 0xff8800);
|
||||
this.scene.add(ring);
|
||||
this.enemyHitRings.set(enemy, ring);
|
||||
}
|
||||
ring.position.x = enemy.body.position.x;
|
||||
ring.position.z = enemy.body.position.z;
|
||||
ring.visible = this.showHitzones && !enemy.dead;
|
||||
}
|
||||
// Clean up removed enemies
|
||||
for (const [enemy, ring] of this.enemyHitRings) {
|
||||
if (!this.enemies.includes(enemy)) {
|
||||
this.scene.remove(ring);
|
||||
ring.geometry.dispose();
|
||||
(ring.material as THREE.Material).dispose();
|
||||
this.enemyHitRings.delete(enemy);
|
||||
}
|
||||
}
|
||||
|
||||
// Fireball rings (impact radius 1.5 / AoE 5)
|
||||
for (const fb of this.fireballs) {
|
||||
let ring = this.fireballHitRings.get(fb);
|
||||
if (!ring) {
|
||||
ring = this.createHitRing(fb.exploding ? 5 : 1.5, fb.exploding ? 0xffff00 : 0xff6600);
|
||||
this.scene.add(ring);
|
||||
this.fireballHitRings.set(fb, ring);
|
||||
}
|
||||
ring.position.x = fb.body.position.x;
|
||||
ring.position.z = fb.body.position.z;
|
||||
ring.visible = this.showHitzones;
|
||||
}
|
||||
for (const [fb, ring] of this.fireballHitRings) {
|
||||
if (!this.fireballs.includes(fb)) {
|
||||
this.scene.remove(ring);
|
||||
ring.geometry.dispose();
|
||||
(ring.material as THREE.Material).dispose();
|
||||
this.fireballHitRings.delete(fb);
|
||||
}
|
||||
}
|
||||
|
||||
// Cross rings (pickup radius 2)
|
||||
for (const cross of this.crosses) {
|
||||
let ring = this.crossHitRings.get(cross);
|
||||
if (!ring) {
|
||||
ring = this.createHitRing(2, 0x00ff00);
|
||||
this.scene.add(ring);
|
||||
this.crossHitRings.set(cross, ring);
|
||||
}
|
||||
ring.position.x = cross.body.position.x;
|
||||
ring.position.z = cross.body.position.z;
|
||||
ring.visible = this.showHitzones;
|
||||
}
|
||||
for (const [cross, ring] of this.crossHitRings) {
|
||||
if (!this.crosses.includes(cross)) {
|
||||
this.scene.remove(ring);
|
||||
ring.geometry.dispose();
|
||||
(ring.material as THREE.Material).dispose();
|
||||
this.crossHitRings.delete(cross);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private spawnEnemy() {
|
||||
const x = (Math.random() - 0.5) * 80;
|
||||
const z = (Math.random() - 0.5) * 80;
|
||||
const enemy = new Enemy(this.player.body.position);
|
||||
enemy.body.position.set(x, 0.1, z);
|
||||
this.scene.add(enemy.body);
|
||||
this.enemies.push(enemy);
|
||||
}
|
||||
|
||||
private spawnCross(position: THREE.Vector3) {
|
||||
if (Math.random() > 0.9) {
|
||||
const cross = new Cross(position.clone());
|
||||
this.scene.add(cross.body);
|
||||
this.crosses.push(cross);
|
||||
}
|
||||
}
|
||||
|
||||
private updateGroundPoint() {
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(new THREE.Vector2(this.mouseX, this.mouseY), this.camera);
|
||||
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
const intersection = new THREE.Vector3();
|
||||
if (raycaster.ray.intersectPlane(plane, intersection)) {
|
||||
intersection.y = 0;
|
||||
intersection.clamp(
|
||||
new THREE.Vector3(-48, 0, -48),
|
||||
new THREE.Vector3(48, 0, 48)
|
||||
);
|
||||
this.groundPoint.copy(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
private updatePlayerMovement(dt: number) {
|
||||
if (this.gameOver) {
|
||||
this.player.body.position.y = 0.5;
|
||||
return;
|
||||
}
|
||||
|
||||
const camFwd = new THREE.Vector3();
|
||||
this.camera.getWorldDirection(camFwd);
|
||||
camFwd.y = 0;
|
||||
camFwd.normalize();
|
||||
|
||||
const camLeft = new THREE.Vector3();
|
||||
camLeft.crossVectors(new THREE.Vector3(0, 1, 0), camFwd).normalize();
|
||||
|
||||
const moveDir = new THREE.Vector3();
|
||||
if (this.keys.has('KeyW') || this.keys.has('ArrowUp')) moveDir.add(camFwd);
|
||||
if (this.keys.has('KeyS') || this.keys.has('ArrowDown')) moveDir.sub(camFwd);
|
||||
if (this.keys.has('KeyA') || this.keys.has('ArrowLeft')) moveDir.add(camLeft);
|
||||
if (this.keys.has('KeyD') || this.keys.has('ArrowRight')) moveDir.sub(camLeft);
|
||||
|
||||
let speed = 7.5;
|
||||
let isMoving = false;
|
||||
if (moveDir.length() > 0) {
|
||||
moveDir.normalize();
|
||||
this.player.velocity.set(moveDir.x * speed, 0, moveDir.z * speed);
|
||||
isMoving = true;
|
||||
} else {
|
||||
this.player.velocity.set(0, 0, 0);
|
||||
}
|
||||
|
||||
// Play walk/stand animations
|
||||
if (isMoving && this.player.getCurrentAnimation() !== 'walk') {
|
||||
this.player.playAnimation('walk');
|
||||
} else if (!isMoving && this.player.getCurrentAnimation() !== 'stand') {
|
||||
this.player.playAnimation('stand');
|
||||
}
|
||||
|
||||
// Jump / gravity
|
||||
if (!this.player.onGround) {
|
||||
this.player.jumpVelocity -= 20 * dt;
|
||||
this.player.body.position.y += this.player.jumpVelocity * dt;
|
||||
if (this.player.body.position.y <= 0.5) {
|
||||
this.player.body.position.y = 0.5;
|
||||
this.player.jumpVelocity = 0;
|
||||
this.player.onGround = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply horizontal movement
|
||||
this.player.body.position.x += this.player.velocity.x * dt;
|
||||
this.player.body.position.z += this.player.velocity.z * dt;
|
||||
this.clampToArena(this.player.body.position);
|
||||
|
||||
// Look at ground point
|
||||
this.player.body.lookAt(
|
||||
this.groundPoint.x,
|
||||
this.player.body.position.y,
|
||||
this.groundPoint.z
|
||||
);
|
||||
}
|
||||
|
||||
private clampToArena(pos: THREE.Vector3) {
|
||||
pos.x = Math.max(-47, Math.min(47, pos.x));
|
||||
pos.z = Math.max(-47, Math.min(47, pos.z));
|
||||
}
|
||||
|
||||
private updateEnemies(dt: number) {
|
||||
for (const enemy of this.enemies) {
|
||||
if (enemy.dead) {
|
||||
enemy.updateAnimation(dt);
|
||||
continue;
|
||||
}
|
||||
|
||||
const toPlayer = new THREE.Vector3()
|
||||
.subVectors(this.player.body.position, enemy.body.position);
|
||||
toPlayer.y = 0;
|
||||
|
||||
const dist = toPlayer.length();
|
||||
if (dist > 0.1) {
|
||||
const dir = toPlayer.normalize();
|
||||
enemy.body.lookAt(
|
||||
enemy.body.position.x + dir.x,
|
||||
enemy.body.position.y,
|
||||
enemy.body.position.z + dir.z
|
||||
);
|
||||
|
||||
enemy.playAnim('walk');
|
||||
|
||||
let moveDir = dir.multiplyScalar(6);
|
||||
if (enemy.knockback > 0) {
|
||||
moveDir = dir.multiplyScalar(-6);
|
||||
enemy.knockback -= dt;
|
||||
}
|
||||
enemy.body.position.x += moveDir.x * dt;
|
||||
enemy.body.position.z += moveDir.z * dt;
|
||||
this.clampToArena(enemy.body.position);
|
||||
} else {
|
||||
enemy.playAnim('stand');
|
||||
}
|
||||
|
||||
enemy.updateAnimation(dt);
|
||||
}
|
||||
}
|
||||
|
||||
private updateFireballs(dt: number) {
|
||||
for (let i = this.fireballs.length - 1; i >= 0; i--) {
|
||||
const fb = this.fireballs[i];
|
||||
fb.lifetime -= dt;
|
||||
|
||||
if (fb.exploding) {
|
||||
fb.timer += dt;
|
||||
if (fb.timer > 0.2 && !fb.damageDone) {
|
||||
fb.damageDone = true;
|
||||
for (const enemy of this.enemies) {
|
||||
if (enemy.dead) continue;
|
||||
const dist = enemy.body.position.distanceTo(fb.body.position);
|
||||
if (dist < 8) {
|
||||
enemy.takeDamage(100, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fb.timer > 2) {
|
||||
this.scene.remove(fb.body);
|
||||
this.fireballs.splice(i, 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
fb.body.position.x += fb.direction.x * dt;
|
||||
fb.body.position.z += fb.direction.z * dt;
|
||||
fb.updateTrail(dt, fb.body.position);
|
||||
|
||||
for (const enemy of this.enemies) {
|
||||
if (enemy.dead) continue;
|
||||
const dist = enemy.body.position.distanceTo(fb.body.position);
|
||||
if (dist < 1.8) {
|
||||
fb.explode(this.scene);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fb.lifetime <= 0) {
|
||||
fb.explode(this.scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateUI() {
|
||||
const healthPct = this.player.health / this.player.MAX_HEALTH;
|
||||
this.uiHealthBar.style.width = `${Math.max(0, healthPct * 250)}px`;
|
||||
this.uiHealthText.textContent = `${Math.ceil(this.player.health)}%`;
|
||||
|
||||
this.uiWaveLabel.textContent = `Wave ${this.spawns}`;
|
||||
this.uiKillsLabel.textContent = `Kills: ${this.killed}`;
|
||||
|
||||
const sword = this.player.rightHandWeapon as Sword;
|
||||
const staff = this.player.leftHandWeapon as Staff;
|
||||
|
||||
const fbPct = Math.max(0, (staff.COOLDOWN1_TIME - staff.cooldown1) / staff.COOLDOWN1_TIME);
|
||||
this.uiFireballCD.style.width = `${fbPct * 100}%`;
|
||||
|
||||
const swordPct = Math.max(0, (sword.COOLDOWN1_TIME - sword.cooldown1) / sword.COOLDOWN1_TIME);
|
||||
this.uiSwordCD.style.width = `${swordPct * 100}%`;
|
||||
|
||||
const tpPct = Math.max(0, (staff.COOLDOWN2_TIME - staff.cooldown2) / staff.COOLDOWN2_TIME);
|
||||
this.uiTeleportCD.style.width = `${tpPct * 100}%`;
|
||||
|
||||
const wavePct = this.spawnTimer / SPAWN_TIME;
|
||||
this.uiWaveBarFill.style.width = `${wavePct * 100}%`;
|
||||
this.uiWaveBarLabel.textContent = `Next Wave: ${Math.ceil(SPAWN_TIME - this.spawnTimer)}s`;
|
||||
|
||||
if (this.player.health <= 0 && !this.gameOver) {
|
||||
this.gameOver = true;
|
||||
this.player.die();
|
||||
this.uiGameOver.style.opacity = '1';
|
||||
playSound('gameover', 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
private animate() {
|
||||
requestAnimationFrame(() => this.animate());
|
||||
|
||||
const dt = Math.min(this.clock.getDelta(), 0.1);
|
||||
|
||||
this.updateGroundPoint();
|
||||
this.updatePlayerMovement(dt);
|
||||
this.updateEnemies(dt);
|
||||
this.updateFireballs(dt);
|
||||
this.updateHitzones();
|
||||
|
||||
// Update animations
|
||||
this.player.updateAnimations(dt);
|
||||
this.player.rightHandWeapon.updateAnimation(dt);
|
||||
|
||||
// Spawn enemies in waves
|
||||
if (!this.gameOver) {
|
||||
this.spawnTimer += dt;
|
||||
if (this.spawnTimer >= SPAWN_TIME) {
|
||||
this.spawns++;
|
||||
for (let i = 0; i < this.spawns; i++) {
|
||||
this.spawnEnemy();
|
||||
}
|
||||
this.spawnTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check close encounters (spider touching player)
|
||||
for (const enemy of this.enemies) {
|
||||
if (enemy.dead) continue;
|
||||
const dist = enemy.body.position.distanceTo(this.player.body.position);
|
||||
if (dist < 2) {
|
||||
this.player.health -= 25 * dt;
|
||||
}
|
||||
}
|
||||
|
||||
// Cross collisions
|
||||
for (let i = this.crosses.length - 1; i >= 0; i--) {
|
||||
const cross = this.crosses[i];
|
||||
cross.update(dt);
|
||||
const dist = cross.body.position.distanceTo(this.player.body.position);
|
||||
if (dist < 2) {
|
||||
this.player.heal(25);
|
||||
this.scene.remove(cross.body);
|
||||
this.crosses.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Update cooldowns
|
||||
this.player.rightHandWeapon.reduceCooldowns(dt);
|
||||
this.player.leftHandWeapon.reduceCooldowns(dt);
|
||||
|
||||
// Camera follows player
|
||||
this.camera.position.set(
|
||||
this.player.body.position.x,
|
||||
25,
|
||||
this.player.body.position.z - 15
|
||||
);
|
||||
this.camera.lookAt(
|
||||
this.player.body.position.x,
|
||||
0,
|
||||
this.player.body.position.z
|
||||
);
|
||||
|
||||
// Clean dead enemies
|
||||
for (let i = this.enemies.length - 1; i >= 0; i--) {
|
||||
const enemy = this.enemies[i];
|
||||
if (enemy.dead) {
|
||||
enemy.deathTimer += dt;
|
||||
if (enemy.deathTimer > 10) {
|
||||
this.scene.remove(enemy.body);
|
||||
this.enemies.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateUI();
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
}
|
||||
|
||||
removeEnemy(enemy: Enemy) {
|
||||
enemy.dead = true;
|
||||
this.killed++;
|
||||
this.spawnCross(enemy.body.position);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user