Help with queuing and dequeuing properly for NPCs to follow the player

Hi,
I want some NPCs to follow the player with a delay of x seconds. I’m using a queue of positions and times. I can’t get the delay to work: now followers move at the exact same position as the player, instead of moving x seconds behind.

Thanks!

update(time,delta) {

let isPlayerMoving = this.player.body.velocity.x != 0 || this.player.body.velocity.y != 0;

    if(isPlayerMoving) {

        //add the player's current position at time now
	    this.queue.add(time, this.player.body.position);
			
			//if the player met the follower
			if(this.follower.canFollow) {
				
				//remove all positions older than the time of the position we want to follow
				while(this.queue.getFirst().time < (time - this.followerDelay)){
					this.queue.remove();
				}
				
				//pass to the follower the first position that hasn't been removed
				let pos = this.queue.getFirst().position;
				this.follower.move(pos.x, pos.y);
			}
}

This is the Queue class

class TimePositionData {
	constructor(time, position) {
		this.time = time;
		this.position = position;
	}
}

export default class Queue {
	constructor() {
		this.elements = [];
	}
	
	//add an element at the end of the queue
	add(time, position) {
		let data = new TimePositionData(time, position);
		this.elements.push(data);
	}
	
	// remove an element from the beginning of the queue
	remove() {
		return this.elements.shift();
	}
	
	//get the element at the beginning of the queue
	getFirst() {
		return !this.isEmpty() ? this.elements[0] : undefined;
	}

So every follower deletes all the player data up to its own delay? That seems wrong… As soon as there are multiple followers, some won’t be getting data.
Try to visualize the queue (on screen).

I started working on one follower first (and that’s the code I posted) but your are right, other followers would not get any data.

So I modified my script to include more followers, and they all follow now but irregularly and not taking in account the delay at all times. So any suggestion would be appreciated, thanks!

let isPlayerMoving = this.player.body.velocity.x != 0 || this.player.body.velocity.y != 0;
				
		if(isPlayerMoving) {
			//add the player's current position at time now
			this.queue.add(time, this.player.body.position);
						
			for (var i = 0; i < this.followers.getChildren().length; i++) {
				
				let follower = this.followers.getChildren()[i];
				
					//if a follower exists and has met the player				
					if(follower && follower.canFollow) {
						let positionData = this.queue.getAtTime(time - this.followersDelay * (i+1));	
						if(positionData) follower.move(positionData.x,positionData.y);
					}
   		 		}			
		    }
			

Here’s a new function in the Queue class

getAtTime(time) {
		if (!this.isEmpty()) {
			for (var i = 0; i < this.getLength(); i++) {
				
				if (Phaser.Math.CeilTo(this.elements[i].time,0) === Phaser.Math.CeilTo(time,0)) {
					return this.elements[i].position;
				}
			}
		}
	}

This seems incredibly expensive. I wouldn’t be surprised if your CPU is boiling :slight_smile:
Obviously you need a better lookup system than a for loop…
I seem to recall we did this before, that didn’t work?

Yes, we did! I was trying to expand on that and find a solution that was independent of the frame rate, that’s why I’m storing time with the position.

Personally, I would just adapt the numbers. Get the fps, divide it by 60 and use that as multiplier.
With your timestamp method you’ll need to use a timer (an increasing value) not ‘time’ itself. Then you can store it with timer as key, and have instant lookup. Obviously this number will grow fast, so it will have to reset after a while…

Thanks! I’ll try that.