SnippetTipsEnemy spawn, move

Automatic Enemy Spawn


enemy-spawn-result

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

  1. Add the enemy object to the scene.
  2. Select the enemy object in the left explorer panel.
  3. Rename the object to β€œEnemy”.
  4. Set the enemy’s detailed parameters (position, angle, size, etc.) in the right properties panel.
  5. Continuously adjust the values until you find the desired settings.
Create_enemy

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

enemy-spawn
let spawnInterval = 300;
let startSpawn;
function CreateEnemy() {
    clearInterval(startSpawn);
    startSpawn = setInterval(() => {
        SpawnRandomPos();
    }, spawnInterval);
}

Write Code for Enemies to Move Towards the Player

enemy-move
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

enemy-spawn-result
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);
    }
}