prime31 / Nez

Nez is a free 2D focused framework that works with MonoGame and FNA
MIT License
1.78k stars 362 forks source link

Memory Leak in simple project - based on ninja sample #813

Closed rufreakde closed 3 months ago

rufreakde commented 3 months ago

Hi all, I am trying Nez for the first time and just took the ninja example threw away a lot of stuff I dont need like triggers/collisions/projectiles.

And have now an entity without the trigger interface (so no collider I guess). Flying around the scene but I noticed the memory allocation to rise.

After some dotnet memory analysis I saw the following:

// value rises on moving around of my sprite
7ffaab3b2628 212.472 10.198.656 System.Collections.Generic.HashSet<Nez.Pair<Nez.Collider>>+Enumerator

So the culprit is found but I really don't know why this happens. It always happens when I move my sprite around the scene. If it just stays in position and plays its idle animation no memory leak.

I enabled the debug view but only see yellow lines (sprite borders) no red lines (colliders)

Any idea how this happens?

using Nez.Sprites;
using Microsoft.Xna.Framework.Graphics;
using Nez.Textures;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Nez;
using System.Collections.Generic;
using System.Security.AccessControl;
using Nez.Tweens;
using System;

namespace engine;
[Serializable]
public class Unit : Component, IUpdatable, IPathProvider
{
    #region UnitData.cs implementation
    public string Type { get; set; }
    public string Path { get; set; }
    public string Name { get; set; }
    public string Hash { get; set; }
    public string ProductionClass { get; set; }
    public bool Invincibility { get; set; }
    public int LuckModifier { get; set; }
    public Dictionary<string, Weapon> Weapons { get; set; }
    public Armor Armor { get; set; }
    public Movement Movement { get; set; }
    #endregion

    #region Game Entity Management
    SubpixelVector2 _subpixelV2 = new SubpixelVector2();
    Mover _mover;
    SpriteChild _spriteChild;
    readonly float _moveSpeed = 512f;

    VirtualIntegerAxis _xAxisInput;
    VirtualIntegerAxis _yAxisInput;
    #endregion

    public override void OnAddedToEntity()
    {
        _mover = Entity.AddComponent(new Mover());
        // TODO index 0 fix
        _spriteChild = Entity.Transform.GetChild(0).Entity.GetComponent<SpriteChild>();
        SetupInput();
    }

    public override void OnRemovedFromEntity()
    {
        // deregister virtual input
        _xAxisInput.Deregister();
        _yAxisInput.Deregister();
    }

    void SetupInput()
    {
        // horizontal input from dpad, left stick or keyboard left/right
        _xAxisInput = new VirtualIntegerAxis();
        _xAxisInput.Nodes.Add(new VirtualAxis.GamePadDpadLeftRight());
        _xAxisInput.Nodes.Add(new VirtualAxis.GamePadLeftStickX());
        _xAxisInput.Nodes.Add(new VirtualAxis.KeyboardKeys(VirtualInput.OverlapBehavior.TakeNewer, Keys.Left, Keys.Right));

        // vertical input from dpad, left stick or keyboard up/down
        _yAxisInput = new VirtualIntegerAxis();
        _yAxisInput.Nodes.Add(new VirtualAxis.GamePadDpadUpDown());
        _yAxisInput.Nodes.Add(new VirtualAxis.GamePadLeftStickY());
        _yAxisInput.Nodes.Add(new VirtualAxis.KeyboardKeys(VirtualInput.OverlapBehavior.TakeNewer, Keys.Up, Keys.Down));
    }

    void IUpdatable.Update()
    {
        // handle movement and animations
        var moveDir = Vector2.Zero;
        var animation = AnimationStates.WalkLeft;

        if (_xAxisInput.Value < 0)
        {
            animation = AnimationStates.WalkLeft;
            moveDir.X = _xAxisInput.Value;
        }
        else if (_xAxisInput.Value > 0)
        {
            animation = AnimationStates.WalkRight;
            moveDir.X = _xAxisInput.Value;
        }
        else if (_yAxisInput.Value < 0)
        {
            animation = AnimationStates.WalkUp;
            moveDir.Y = _yAxisInput.Value;
        }
        else if (_yAxisInput.Value > 0)
        {
            animation = AnimationStates.WalkDown;
            moveDir.Y = _yAxisInput.Value;
        }

        if (moveDir != Vector2.Zero)
        {
            TriggerAndPlayAnimation(animation);

            var movement = moveDir * _moveSpeed * Time.DeltaTime;

            _mover.CalculateMovement(ref movement, out var res);
            _subpixelV2.Update(ref movement);
            _mover.ApplyMovement(movement);
        }
        else if (!_spriteChild.IsPausedAnimation())
        {
            TriggerAndPlayAnimation(AnimationStates.Idle);
        }
    }

    public void TriggerAndPlayAnimation(string animation)
    {
        if (!_spriteChild.IsThisAnimationPlaying(animation))
        {
            _spriteChild.PlayAnimation(animation);
        }
    }

    public int GetLayer()
    {
        throw new System.NotImplementedException();
    }

    public int SetLayer()
    {
        throw new System.NotImplementedException();
    }

}
rufreakde commented 3 months ago

Checked on my entity:

Collider test = Entity.GetComponent<Collider>();
Collider test1 = Entity.GetComponent<BoxCollider>();
Collider test2 = Entity.GetComponent<CircleCollider>();
Collider test3 = Entity.GetComponent<PolygonCollider>();
var test4 = Entity.GetComponent<ColliderTriggerHelper>();

all are null

rufreakde commented 3 months ago

Okay after some look through the example ninja code I realized the example code has memory leaks. So I removed the Mover component which I dont need as I do not have physics or collision.

No Memory leak since then :)

rufreakde commented 3 months ago

PS: for anyone who has memory leaks with NEY or MONGAME here is a good easy tutorial to check stuff out: https://www.youtube.com/watch?v=9QPgfJPaGvY