SFML - C# 垃圾收集器删除正在使用的对象
SFML - C# Garbage Collector deletes object which is in use
我正在为 C# 使用 SFML。我想创建一个 BackgroundImage Sprite,然后开始用一个 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,return "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() 函数有什么方法可以 return 一个一旦函数的局部 "render" 变量被销毁就不会变白的 Sprite?
您的假设并没有完全偏离。简而言之,SFML 知道两种类型的资源:
光资源是可以快速创建和销毁的小对象。丢弃它们并稍后重新创建它们并没有那么糟糕。典型的例子是 Sprite
、Sound
、Text
,基本上大多数 SFML 类.
大量资源 通常是大对象或需要文件访问才能创建或使用的对象。典型示例为 Image
、Texture
、SoundBuffer
和 Font
。您不应该重新创建它们,而应该在需要时让它们保持活力。如果过早处理它们,使用它们的光源将以某种方式失效。
正如您所发现的,精灵的纹理变白是指定纹理 freed/disposed 的典型标志。
对此有许多不同的方法,但我建议您创建某种简单的资源管理器,它会及时加载资源或 return 它们,如果它们已经加载。
我没有将 SFML 与 C# 结合使用,而且我已经有一段时间没有真正接触过 C#,但是对于一个简单的实现,您只需要一个 Dictionary<string, Texture>
。当你想加载像 texture.png
这样的纹理文件时,你会查看是否有具有该键名的字典条目。如果有,就 return 吧。如果没有,创建新条目并加载纹理,然后 return 它。
我没有练习,所以请考虑这个伪代码!
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
,您只需确保它在使用期间保持活动状态(例如,作为单独的成员)。
事实证明,答案比我预期的要简单。我所要做的就是创建新的 Texture 对象,然后用它制作一个 Sprite。所以而不是
Sprite result = new Sprite(render.Texture);
我写了
Sprite result = new Sprite(new Texture(render.Texture));
现在垃圾收集器不会处理 Sprite 的纹理
我正在为 C# 使用 SFML。我想创建一个 BackgroundImage Sprite,然后开始用一个 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,return "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() 函数有什么方法可以 return 一个一旦函数的局部 "render" 变量被销毁就不会变白的 Sprite?
您的假设并没有完全偏离。简而言之,SFML 知道两种类型的资源:
光资源是可以快速创建和销毁的小对象。丢弃它们并稍后重新创建它们并没有那么糟糕。典型的例子是
Sprite
、Sound
、Text
,基本上大多数 SFML 类.大量资源 通常是大对象或需要文件访问才能创建或使用的对象。典型示例为
Image
、Texture
、SoundBuffer
和Font
。您不应该重新创建它们,而应该在需要时让它们保持活力。如果过早处理它们,使用它们的光源将以某种方式失效。
正如您所发现的,精灵的纹理变白是指定纹理 freed/disposed 的典型标志。
对此有许多不同的方法,但我建议您创建某种简单的资源管理器,它会及时加载资源或 return 它们,如果它们已经加载。
我没有将 SFML 与 C# 结合使用,而且我已经有一段时间没有真正接触过 C#,但是对于一个简单的实现,您只需要一个 Dictionary<string, Texture>
。当你想加载像 texture.png
这样的纹理文件时,你会查看是否有具有该键名的字典条目。如果有,就 return 吧。如果没有,创建新条目并加载纹理,然后 return 它。
我没有练习,所以请考虑这个伪代码!
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
,您只需确保它在使用期间保持活动状态(例如,作为单独的成员)。
事实证明,答案比我预期的要简单。我所要做的就是创建新的 Texture 对象,然后用它制作一个 Sprite。所以而不是
Sprite result = new Sprite(render.Texture);
我写了
Sprite result = new Sprite(new Texture(render.Texture));
现在垃圾收集器不会处理 Sprite 的纹理