RMMZ Disposing Sprites and Managing Memory: Am I doing it right?

Wavelength

MSD Strong
Global Mod
Joined
Jul 22, 2014
Messages
5,527
Reaction score
4,984
First Language
English
Primarily Uses
RMVXA
I'm starting to get very involved with MZ plugin making now and while I have been able to create some nifty functionality, I think I'm lacking on the fundamentals, especially when it comes to sprites and how to handle them. In particular, I am worried that I am missing steps along the way and that, in the long run, I will be tanking the technical performance of people who use my plugins.

And I would really appreciate any guidance on whether my approach will lead to any memory leaks or other significant technical issues.

Here's a simple scene I made last month, mostly just as a learning experience. The scene simply shows a background, allows the player to create a new faerie sprite onscreen with each left-click, and allows the player to remove all faerie sprites with a right-click. It was partially based off of the Game Over scene code, and it uses Scene_Base as its "parent class".

Code:
function Scene_Zero() {
    this.initialize(...arguments);
}

Scene_Zero.prototype = Object.create(Scene_Base.prototype);
Scene_Zero.prototype.constructor = Scene_Zero;

Scene_Zero.prototype.initialize = function() {
    Scene_Base.prototype.initialize.call(this);
};

Scene_Zero.prototype.create = function() {
    Scene_Base.prototype.create.call(this);
    this.playZeroMusic();
    this.createBackground();
    this.sprites = [];
};

Scene_Zero.prototype.update = function() {
    if (this.isActive() && !this.isBusy()) {
        if (Input.isTriggered("ok") || TouchInput.isTriggered()) {
           SoundManager.playUseSkill();
           var faesprite = new Sprite();
           faesprite.bitmap = ImageManager.loadEnemy("Sylph");
           this.addChild(faesprite);
           this.sprites.push(faesprite);
           faesprite._hue = Number(Math.random() * 360);
           faesprite.x = Number(100 + Math.random() * 600);
           faesprite.y = Number(100 + Math.random() * 400);
           faesprite._updateColorFilter();
        } else if (Input.isTriggered("cancel") || TouchInput.isCancelled()) {
            for (fs of this.sprites) {
                // REMOVE CHILD
                this.removeChild(fs);
                // DESTROY SPRITE
                fs.destroy();
            }
            this.sprites = [];
        }
    }
    Scene_Base.prototype.update.call(this);
};

Scene_Zero.prototype.terminate = function() {
    Scene_Base.prototype.terminate.call(this);
    AudioManager.stopAll();
};

Scene_Zero.prototype.playZeroMusic = function() {
    AudioManager.stopBgm();
    AudioManager.stopBgs();
};

Scene_Zero.prototype.createBackground = function() {
    this._backSprite = new Sprite();
    this._backSprite.bitmap = ImageManager.loadTitle1("Mountain");
    this.addChild(this._backSprite);
};
In particular, a few of the things that I'm doing:
  • To create both the background and the faeries, I create a new sprite, then assign them a bitmap property using the ImageManager.loadwhatever method, then add the new sprite to the scene as its Child - is this a wise way to do it?
  • When it's time to delete all the faerie sprites (due to a right-click), I iterate through the array of sprites, remove each as a Child from the scene, and then run the sprite's destroy() method - will this process free up all of the memory that the sprite was consuming, or do I need to do something else?
  • I haven't added an actual way to exit the Scene yet, but assume that the terminate() function will run when the player leaves the scene - will the terminate() function automatically dispose of the this._backSprite sprite and free up its memory, or do I need to explicitly destroy it or do something else? How about existing faerie sprites that weren't cleared via right-click - will those be automatically disposed when the scene is terminated?
Any other advice about how to better create, handle, and destroy sprites would be most welcome, as well.
 

caethyril

^_^
Veteran
Joined
Feb 21, 2018
Messages
1,915
Reaction score
1,362
First Language
EN
Primarily Uses
RMMZ
  1. Create sprite, set image, add to container...seems OK to me! Note that you can assign a bitmap to a Sprite via its constructor, e.g.
    JavaScript:
    var faesprite = new Sprite(ImageManager.loadEnemy('Sylph'))

  2. I'm not certain of exactly what happens internally, but destroying a sprite should tell the app that all the sprite's values are now disposable and will not be accessed again. It should suffice to prevent associated memory leaks. :)

  3. Scene_Base.prototype.terminate does nothing. :kaoblush: That said, the SceneManager destroys the previous scene before starting the next one, so I don't think you need to explicitly destroy each sprite~
    JavaScript:
    SceneManager.onBeforeSceneStart = function() {
        if (this._previousScene) {
            this._previousScene.destroy();
            this._previousScene = null;
        }
        if (Graphics.effekseer) {
            Graphics.effekseer.stopAll();
        }
    };
    ...and scenes inherit from Stage, which destroys its children when it gets destroyed:
    JavaScript:
    Stage.prototype.destroy = function() {
        const options = { children: true, texture: true };
        PIXI.Container.prototype.destroy.call(this, options);
    };
    JavaScript:
    /**
     * Removes all internal references and listeners as well as removes children from the display list.
     * Do not use a Container after calling `destroy`.
     *
     * @param {object|boolean} [options] - Options parameter. A boolean will act as if all options
     *  have been set to that value
     * @param {boolean} [options.children=false] - if set to true, all the children will have their destroy
     *  method called as well. 'options' will be passed on to those calls.
     * @param {boolean} [options.texture=false] - Only used for child Sprites if options.children is set to true
     *  Should it destroy the texture of the child sprite
     * @param {boolean} [options.baseTexture=false] - Only used for child Sprites if options.children is set to true
     *  Should it destroy the base texture of the child sprite
     */
    Container.prototype.destroy = function destroy (options)
    {
    	DisplayObject.prototype.destroy.call(this);
    
    	this.sortDirty = false;
    
    	var destroyChildren = typeof options === 'boolean' ? options : options && options.children;
    
    	var oldChildren = this.removeChildren(0, this.children.length);
    
    	if (destroyChildren)
    	{
    		for (var i = 0; i < oldChildren.length; ++i)
    		{
    			oldChildren[i].destroy(options);
    		}
    	}
    };
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Those kittens I met the other day all found loving homes. I might miss playing with them, but I'm glad they found owners to care for them. In the meantime, my Orlando trip is likely to happen on the 29th. While I wait for that, I'll be working on Angel of Justice...
I really need to work on my time management skills... I have one plot-based roleplay here, another plot-based roleplay there, I have the fangame to work on, I'm trying to get into Runescape again....

Forum statistics

Threads
104,230
Messages
1,004,868
Members
135,750
Latest member
nonsense
Top