Many GameObjects (PathFollower) causing high CPU usage even if destroyed

I have a game that spawns many custom objects extended from the class Phaser.GameObjects.PathFollower. Sometimes there can be a few thousand active at once, but many more are created and destroyed.

I’ve noticed that the CPU usage on my computer gets very high after letting the game run for 10+ minutes. Even after destroying all of the objects, the CPU usage is still high and the game performance is bad. I’ve tried running the same game, except commenting out the part where the PathFollowers are spawned, and the performance is fine. So it seems that the issue is that something is leftover after destroying them.

Example code:

class Person extends Phaser.GameObjects.PathFollower{

    constructor(scene, data){
    	super(scene);
        //needed to manually add scene, because sometimes it was undefined
    	this.scene = scene;
        this.setTexture('sprites');
    	this.setData('myData', data);
    	this.setScale(0.45);
        this.setSize(64,64);
	    this.setDepth(5);
    }
}

this.people = this.physics.add.group();

//this will run ~4 times per second
createPerson(data){
    let person = this.add.existing(new Person(this, data));
    this.people.add(person);

    //testing destroying right away, and still getting high CPU usage and bad performance
    //trying multiple things to fix the issue here
    this.people.remove(person);
    person.path.destroy(true);
    person.destroy(true);
}

Any idea what is causing the issue? Is there a way to properly destroy the game objects?

Have you profiled your memory usage? Chrome > Dev Tools > Performance > Memory + record, and comparing heap snapshots.

And where are you defining person.destroy()? You might want to add your own destroy/shutdown method to your Person class (and call super.destroy() from it).

I did not define person.destroy(), it was using the method inherited from Phaser.GameObjects.PathFollower. I will try defining it and profiling memory.

But is there any difference between super.destroy() and this.destroy() ?

Creating and destroying complex objects in an update loop will cause a high CPU usage. It is best if you pool your people so you aren’t constantly creating new ones and destroying old ones. Otherwise, I tested if there was a performance issue with the following code and everything seems to work:

export default class Test extends Phaser.Scene
{
    create()
    {
        this.people = this.physics.add.group();
        this.cameras.main.backgroundColor = new Phaser.Display.Color(100, 0, 0);

        for(let i = 0; i < 4000; i++){ //Create 4000 people
            let person = this.add.existing(new Person(this));
            this.people.add(person);
        }

        this.time.delayedCall(3000, ()=>{ //Destroy 4000 people after 3 seconds.
            let entries = this.people.children.entries;
            while(entries.length > 0) {
                let person = entries.pop();
                this.people.remove(person);
                person.body.destroy();
                person.destroy();
            }
        });
    }
}

class Person extends Phaser.GameObjects.PathFollower
{
    constructor(scene)
    {
    	super(scene);
    	this.scene = scene;
        this.setTexture("btf");
    }
}

Here is the performance graph:
image

I did some more debugging and found that it’s not the creation and destruction of the game objects that’s causing the high CPU usage, but the creation of the paths like this.

let path = new Phaser.Curves.Path(currentX, currentY);
path.lineTo(newX, newY);
person.setPath(path);
person.startFollow( {
    duration: 1000,
    rotateToPath: true,
    yoyo: false,
    repeat: 0,
    onComplete: () => {
        //won't get called because I am destroying person right away
        this.curveComplete();
    }
});

Each person is getting their own unique path. Maybe this is hanging around even after the person is destroyed?

Paths have a destroy method. Can you try specifically calling destroy on the path right before removing the person and see if that has any impact? Probably not a permanent solution but if it does help, it would pretty much prove the Paths aren’t being destroyed for some reason.

Thanks @Jackolantern I had tried that previously but it did not help.

Going back to my original post, I think that the creation of many sprites might actually still be the issue. Paths might just amplify it.

The CPU usage increases very gradually, will take minutes to notice, and an hour to get bad. It would be fine if the CPU usage was high at a sustained level, but it is gradually increasing from 20% until it is over 100%, even when the number of active sprites in memory is the same at all times. I am running a 2017 MacBook pro with 2.9 GHz Intel Core i7, so if you have a higher spec cpu, it might take longer.

Here is minimal code that will show the CPU usage getting higher over time, when it shouldn’t. Let it run for a while and monitor CPU usage.

class myScene extends Phaser.Scene{
    constructor(){
		super({key:"myScene"});
	}
	create(){
        //will create and destroy 100 sprites every 1/10 second.
        //or 1000 every second. you can increase this if you have a powerful cpu
		setInterval(() => {
			for (let i = 0; i < 100; i++) {
                //this.add.follower() has same effect
				let person = this.add.sprite();
				person.destroy();
			}
		}, 100);
	}
}

var config = {
	type: Phaser.WEBGL,
	width: 1920,
	height: 1080,
	scale: {
        mode: Phaser.Scale.WIDTH_CONTROLS_HEIGHT
    },
	scene: [ myScene ]
};

var game = new Phaser.Game(config);

I know this is a lot of sprites, but my game requires many many sprites to be created and destroyed. There has to be a way to get person completely gone from the game after it is destroyed. It seems like a part of it is still hanging around afterwards, otherwise the CPU usage would be at a constant level.