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);
    }
}