【问题标题】:SFML - C# Garbage Collector deletes object which is in useSFML - C# 垃圾收集器删除正在使用的对象
【发布时间】:2019-02-26 12:58:07
【问题描述】:

我将 SFML 用于 C#。我想创建一个 BackgroundImage Sprite,然后开始在它上面用一个表示为 Circle 的 Agent 绘制它:

    static void Main(string[] args)
    {
        Window = new RenderWindow(new VideoMode((uint)map.Size.X * 30, (uint)map.Size.Y * 30), map.Name + " - MAZE", Styles.Default);

        while (Window.IsOpen)
        {
            Update();
        }
    }
    static public RenderWindow Window { get; private set; }
    static Map map = new Map(string.Format(@"C:\Users\{0}\Desktop\Maze.png", Environment.UserName));

    static public void Update()
    {
        Window.Clear(Color.Blue);

        DrawBackground();
        DrawAgent();

        Window.Display();
    }

    static void DrawAgent()
    {
        using (CircleShape tempCircle = new CircleShape
        {
            FillColor = Color.Cyan,
            Radius = 15,
            Position = new Vector2f(30, 30),
            Origin = new Vector2f(30, 30),
            Scale = new Vector2f(.5f, .5f)
        })
        {
            Window.Draw(tempCircle);
        }

    }

    static private Sprite BackgroundImage { get; set; }
    static void DrawBackground()
    {
        if (BackgroundImage == null)
            BackgroundImage = GetBackground();

        Window.Draw(BackgroundImage);

    }

    static Sprite GetBackground()
    {
        RenderTexture render = new RenderTexture((uint)map.Size.X * 30, (uint)map.Size.Y * 30);
        foreach (var point in map.Grid.Points)
        {
            RectangleShape pointShape = new RectangleShape(new Vector2f(30, 30));
            switch (point.PointType)
            {
                case PointType.Walkable:
                    pointShape.FillColor = Color.White;
                    break;
                case PointType.NotWalkable:
                    pointShape.FillColor = Color.Black;
                    break;
                case PointType.Start:
                    pointShape.FillColor = Color.Red;
                    break;
                case PointType.Exit:
                    pointShape.FillColor = Color.Blue;
                    break;
            }
            pointShape.Position = new Vector2f(point.Position.X * 30, point.Position.Y * 30);
            render.Draw(pointShape);

        }
        Sprite result = new Sprite(render.Texture);
        result.Origin = new Vector2f(0, result.GetLocalBounds().Height);
        result.Scale = new Vector2f(1, -1);
        return result;
    }

当我启动它时,一切都按预期工作,但几秒钟后,大约在进程内存达到 70MB 的时候,BackgroundImage 变成了完全白色的精灵。如果我将 BackgroundImage 和 GetBackground() 的类型更改为 RenderTexture,则返回“render”对象,然后像这样更改 DrawBackground() 函数

 void RenderBackground()
        {
            if (BackgroundImage == null)
                BackgroundImage = GetBackground();

            using (Sprite result = new Sprite(BackgroundImage.Texture))
            {
                result.Origin = new Vector2f(0, result.GetLocalBounds().Height);
                result.Scale = new Vector2f(1, -1);
                Window.Draw(result);
            }
        }

那么背景精灵不会变白,而是存储整个 RenderTexture,而不是 Sprite,然后每次调用 RenderBackground() 函数时不断创建新的 Sprite 对象似乎是个坏主意。 GetBackground() 函数有没有办法返回一个 Sprite,一旦函数的本地“render”变量被破坏,它就不会变白?

【问题讨论】:

    标签: c# garbage-collection sfml


    【解决方案1】:

    你并没有完全放弃你的假设。简化后,SFML 知道两种类型的资源:

    • 轻资源是可以快速创建和销毁的小对象。丢弃它们并稍后重新创建它们并不是那么糟糕。典型的例子是SpriteSoundText,基本上是大多数 SFML 类。

    • 大量资源通常是大对象或需要文件访问权限才能创建或使用的对象。典型示例为ImageTextureSoundBufferFont。您不应该重新创建它们,而是在需要它们时让它们保持活力。如果处理得太早,使用它们的轻资源会以某种方式失效。

    如您所见,精灵的纹理变白是分配的纹理被释放/处置的典型标志。

    对此有很多不同的方法,但我建议您创建某种简单的资源管理器,它可以及时加载资源,或者如果它们已经加载,则直接返回它们。

    我没有在 C# 中使用过 SFML,也有一段时间没有真正接触过 C#,但对于一个简单的实现,你只需要一个 Dictionary<string, Texture>。当您想加载像texture.png 这样的纹理文件时,您会查看是否存在具有该键名的字典条目。如果有,就退货吧。如果没有,则创建新条目并加载纹理,然后返回它。

    我没有练习,所以请考虑这个伪代码!

    private Dictionary<string, Texture> mTextureCache; // initialized in constructor
    
    public Texture getTexture(file) {
        Texture tex;
        if (mTextureCache.TryGetValue(file, out tex))
            return tex;
        tex = new Texture(file);
        mTextureCache.add(file, tex);
        return tex;
    }
    
    // Somewhere else in your code:
    Sprite character = new Sprite(getTexture("myCharacter.png"));
    

    如果您的重资源是 RenderTexture,您只需确保它在使用期间一直保持活动状态(例如,作为单独的成员)。

    【讨论】:

      【解决方案2】:

      事实证明,答案比我预期的要简单。我所要做的就是创建一个新的 Texture 对象,然后用它制作一个 Sprite。所以不是

      Sprite result = new Sprite(render.Texture);
      

      我写的

      Sprite result = new Sprite(new Texture(render.Texture));
      

      现在垃圾收集器不会处理 Sprite 的纹理

      【讨论】:

        猜你喜欢
        • 2012-03-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-21
        • 1970-01-01
        • 2013-04-01
        • 1970-01-01
        相关资源
        最近更新 更多