Automatic Enemy Spawn
Steps
1. Manage enemy objects using a class and a list.
2. If optimization is needed due to excessive creation and deletion, implement a recycling list for enemies to avoid excessive creation and deletion after they die.
Add the Enemy Object to the Scene
- Add the enemy object to the scene.
- Select the enemy object in the left explorer panel.
- Rename the object to βEnemyβ.
- Set the enemyβs detailed parameters (position, angle, size, etc.) in the right properties panel.
- Continuously adjust the values until you find the desired settings.
Specify the Spawn Location for Enemies and Write the Code
const enemys = [];
const object = WORLD.getObject("Enemy");
const minSpawnRadius = 20;
const maxSpawnRadius = 100;
function SpawnRandomPos() {
// Select a random angle
const angle = Math.random() * 2 * Math.PI; // Random angle from 0 to 2Ο
const radius = Math.random() * (maxSpawnRadius - minSpawnRadius) + minSpawnRadius; // Random radius from 20 to 100
// Calculate x, z coordinates based on the angle and radius
const x = PLAYER.position.x + radius * Math.cos(angle);
const z = PLAYER.position.z + radius * Math.sin(angle);
const y = 2; // The y coordinate is set to a fixed value.
const clone = THREEADDON.SkeletonUtils.clone(object);
object.animations.forEach((item) => {
clone.animations.push(item);
});
clone.position.set(x, y, z);
WORLD.add(clone);
const enemy = new Enemy(clone);
enemys.push(enemy);
}
Set the Time for Enemy Creation to Spawn at Regular Intervals
let spawnInterval = 300;
let startSpawn;
function CreateEnemy() {
clearInterval(startSpawn);
startSpawn = setInterval(() => {
SpawnRandomPos();
}, spawnInterval);
}
Write Code for Enemies to Move Towards the Player
function Move(dt, object) {
const radius = 1.5;
const direction = new THREE.Vector3(
PLAYER.position.x - object.position.x,
0,
PLAYER.position.z - object.position.z
);
direction.normalize();
// move
object.position.add(direction.multiplyScalar(speed * dt));
const lookAtPosition = new THREE.Vector3(
PLAYER.position.x,
object.position.y,
PLAYER.position.z
);
object.lookAt(lookAtPosition);
}
Add Collision Logic to Prevent Overlapping
function Move(dt, object) {
const radius = 1.5;
const direction = new THREE.Vector3(
PLAYER.position.x - object.position.x,
0,
PLAYER.position.z - object.position.z
);
direction.normalize();
// maintain distance from other enemies
const separationForce = new THREE.Vector3();
for (let enemy of enemys) {
if (enemy !== object) {
const distance = object.position.distanceTo(enemy._object
.position);
if (distance < radius + enemy._radius) { // Collision avoidance distance
const diff = new THREE.Vector3().subVectors(object.position,
enemy._object.position);
diff.normalize();
separationForce.add(diff); // Add force to push them away from each other
}
}
}
direction.add(separationForce);
direction.normalize();
// move
object.position.add(direction.multiplyScalar(speed * dt));
const lookAtPosition = new THREE.Vector3(
PLAYER.position.x,
object.position.y,
PLAYER.position.z
);
object.lookAt(lookAtPosition);
}
Complete the Code Using the Below Example
class Enemy {
constructor(object) {
this._object = object;
this._speed = 10;
this._hp = 100;
//Collision avoidance distance
this._radius = 1;
// animation
this._mixer = new THREE.AnimationMixer(this._object);
this._moveAction = this._mixer.clipAction(this._object.animations[1]);
// animation speed
this._moveAction.setEffectiveTimeScale(1.7); // movement animation speed
this.Init();
}
Init() {
// move action play
this._moveAction.play();
}
Move(dt) {
if (this._hp < 0) return;
// animation update
if (this._mixer) { this._mixer.update(dt); }
console.log("move")
const direction = new THREE.Vector3(
PLAYER.position.x - this._object.position.x,
0,
PLAYER.position.z - this._object.position.z
);
direction.normalize();
// maintain distance from other enemies
const separationForce = new THREE.Vector3();
for (let enemy of enemys) {
if (enemy !== this) {
const distance = this._object.position.distanceTo(enemy._object
.position);
if (distance < this._radius + enemy._radius) { // Collision avoidance distance
const diff = new THREE.Vector3().subVectors(this._object.position,
enemy._object.position);
diff.normalize();
separationForce.add(diff); // Add force to push them away from each other
}
}
}
direction.add(separationForce);
direction.normalize();
// move
this._object.position.add(direction.multiplyScalar(this._speed * dt));
const lookAtPosition = new THREE.Vector3(
PLAYER.position.x,
this._object.position.y,
PLAYER.position.z
);
this._object.lookAt(lookAtPosition);
}
}
const object = WORLD.getObject("Enemy");
const enemys = [];
let spawnInterval = 300;
const startButton = GUI.getObject("StartButton");
const minSpawnRadius = 20;
const maxSpawnRadius = 100;
function SpawnRandomPos() {
// Select a random angle
const angle = Math.random() * 2 * Math.PI; // Random angle from 0 to 2Ο
const radius = Math.random() * (maxSpawnRadius - minSpawnRadius) + minSpawnRadius; // Random radius from 20 to 100
// Calculate x, z coordinates based on the angle and radius
const x = PLAYER.position.x + radius * Math.cos(angle);
const z = PLAYER.position.z + radius * Math.sin(angle);
const y = 2; // The y coordinate is set to a fixed value.
const clone = THREEADDON.SkeletonUtils.clone(object);
object.animations.forEach((item) => {
clone.animations.push(item);
});
clone.position.set(x, y, z);
WORLD.add(clone);
const enemy = new Enemy(clone);
enemys.push(enemy);
}
function CreateEnemy() {
clearInterval(spawnInterval);
spawnInterval = setInterval(() => {
SpawnRandomPos();
}, spawnInterval);
}
function Start() {
startButton.onClick(function() {
CreateEnemy();
startButton.hide();
})
}
function Update(dt) {
for (let i = 0; i < enemys.length; i++) {
enemys[i].Move(dt);
}
}