I'm working on a 2D fighting game with XNA (C#). Everything was going well in my game until I added a story mode. There are more than 60 frames. I have a variable whose value changes each time the person presses the spacebar.
I wanted to experiment game state transitions and dispose() (or .Unload()) content when a screen was no longer needed. I have searched about Dispose() and Unload () to save memory that take my textures, but I can not give a value to my variable Texture2D after having called Dispose() for it. (texture.IsDisposed = true).
I have created a ContentManager xContent in a Manager Class.
Texture2D texture;
int image = 0;
____________________
if (Keyboard.GetState().IsKeyDown(Keys.Space))
{
if (image == 0)
{
texture = Manager.xContent.Load<Texture2D>("texture1");
}
else if (image == 1)
{
texture.Dispose();
texture = Manager.xContent.Load<Texture2D>("texture2");
}
}
Draw :
spriteBatch.Begin();
...
spriteBatch.End();
I received an error on spriteBatch.End() of the above paste because I have reloaded the texture. What should I do?
You should not be calling 'Dispose()' on any of the content which was loaded by the XNA content manager. The content manager will manage the lifespan of the content (that's it's job, after all). It ought to make reasonable decisions about when to load/unload specific textures based on usage.
When the content manager itself is disposed then it will dispose all of the content that it's currently managing, so you don't need to dispose individual pieces of content.
Try to to organize it inside your manager. Loading the appropriate texture to the collection as needed and use them and remove if they become do not really need. If the texture will not link it sooner or later pick up the garbage collector.
Related
My 2D platformer game level has treasure chests placed all over the map and when a chest is collected I need to display a message. The messages are contained in a List<string> and they are displayed one by one as the treasures are collected.
These messages are to be displayed in a UI>Text gameObject which is anchored to the top-center of the canvas. I want to display these texts as floating up(fading in/out) when the treasures are collected, by updating the text component of this gameObject. However, the problem arises when two or more treasures are collected before the animation for the previous one could be complete. I can easily concatenate the new messages to the existing ones, but I want the old ones to fade out and new ones to fade in. This can be done by creating multiple UI>Texts, but there are a lot of messages and I do not want to create so many redundant gameobjects. Is there any good workaround for this problem?
The way I handled this in a project of mine was to create a queue of messages to display (as immediacy was not a concern, but being able to only display one at a time was). This sounds very similar to your own problem.
// NotificationItem is just a wrapper around some text and accompanying image
private static List<NotificationItem> notificationQueue = new List<NotificationItem>();
// reference to the on-screen object
public GameObject notification;
// "Hey! I want to display a notification!"
public static void ShowNotification(NotificationItem item) {
notificationQueue.Add(item);
}
// I was using the DO Tween asset here, but the logic can be converted to
// coroutines or straight update cycles
private void Update() {
// If there is no currently displayed notification (ie the notification object is
// not being animated) and there is at least one item to display
if(!DOTween.IsTweening(notification.transform) && notificationQueue.Count > 0) {
// ...get the first one
NotificationItem item = notificationQueue[0];
// ...pop it from the list
notificationQueue.RemoveAt(0);
// ...set the notification object to the details
notification.transform.Find("Title").GetComponent<Text>().text = item.title;
notification.transform.Find("Text").GetComponent<Text>().text = item.text;
notification.transform.Find("Img").GetComponent<Image>().sprite = item.image;
// ...start the animation
// (in my case, the notification animates down from the top of the screen
// waits 2.5 seconds, then animates back up)
notification.transform.DOMoveY(Screen.height - 85, 0.5f, false).SetEase(Ease.InOutQuad).OnComplete(PauseCallback);
}
}
// An admittedly hacky way of getting the notification to do nothing for 2.5 seconds:
// Animate it to where it already is.
private void PauseCallback() {
notification.transform.DOMoveY(Screen.height - 85, 2.5f, false).SetEase(Ease.InOutQuad).OnComplete(ReturnCallback);
}
private void ReturnCallback() {
notification.transform.DOMoveY(Screen.height + 2, 0.5f, false).SetEase(Ease.InOutQuad);
}
The difference between my implementation and yours will be largely in the animation (as well as your Queue lists's type; e.g. you might be able to just use a List<string>). You already have your animation coded, all you need is the queue and a way to determine that your animation is complete.
You will not be generating more objects than the system can handle if you utilize the Flyweight pattern (object pooling). Unity has an Object Pooling tutorial on their site.
I currently create AnimationClips dynamically while my game is running to move some pieces around. You can see what I mean here: https://youtu.be/u1My9JX4K-c?t=8s , when the pieces get grouped together and then open again. I cannot reuse the clip because the pieces will have a different position next time, so all the movements need to be changed.
Right now, I create a new clip whenever I need them to be grouped and separated, and attempt to replace any existing clip with this:
private void ReplaceAndPlay(AnimationClip clip, string name) {
var old = Anim.GetClip(name);
if (old != null) {
Anim.RemoveClip(name);
Destroy(old);
}
Anim.AddClip(clip, name);
Anim.Play(name);
}
(Anim just points to the gameObject's Animation component)
But using the profiler, I see that the animation clips just keep accumulating, they don't seem to be garbage collected. Am I missing something or doing something wrong? Can they be garbage collected?
I am going to have lots of (same looking) zombies in my game.
Is it a good idea to make the texture static, so that SpriteBatch wont need to load a new texture?
I go through the whole zombie list and draw every zombie with the same call, just changing the position. Will SpriteBatch get it? That its exactly the same texture every time? Where could be the disadvantage?
I don't think that the use of static will give you some kind of benefit.
What's sure is that, if you load your Texture2D only once, you save memory and you can draw it how many times you need using the same variable.
Anyway, if you're using only one texture you don't have any problem, because:
Each instance of ContentManager will only load any given resource
once. The second time you ask for a resource, it will return the same
instance that it returned last time.
ContentManager maintains a list of all the content it has loaded
internally. This list prevents the garbage collector from cleaning up
those resources.
Reference here.
I am currently writing a small app which shows the preview from the phone camera using a SharpDX sprite batch. For those who have an nokia developer account, the code is mainly from this article.
Problem
Occasionally, it seems like previous frames are drawn to the screeb (the "video" jumps back and forth), for the fracture of a second, which looks like oscillation/flicker.
I thought of a threading problem (since the PreviewFrameAvailable event handler is called by a different thread than the method which is responsible for rendering), but inserting a lock statement into both methods makes the code too slow (the frame rate drops below 15 frames/sec).
Does anyone have an idea how to resolve this issue or how to accoplish thread synchronization in this case without loosing too much performance?
Code
First, all resources are created, whereas device is a valid instance of GraphicsDevice:
spriteBatch = new SpriteBatch(device);
photoDevice = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back, captureSize);
photoDevice.FocusRegion = null;
width = (int)photoDevice.PreviewResolution.Width;
height = (int)photoDevice.PreviewResolution.Height;
previewData = new int[width * height];
cameraTexture = Texture2D.New(device, width, height, PixelFormat.B8G8R8A8.UNorm);
photoDevice.PreviewFrameAvailable += photoDevice_PreviewFrameAvailable;
Then, whenever the preview frame changes, I set the data to the texture:
void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
sender.GetPreviewBufferArgb(previewData);
cameraTexture.SetData(previewData);
}
Finally, the Texture is drawn using a SpriteBatch whereas the parameters backBufferCenter, textureCenter, textureScaling and Math.Pi / 2 are used to center and adjust the texture in landscape orientation:
spriteBatch.Begin();
spriteBatch.Draw(cameraTexture, backBufferCenter, null, Color.White, (float)Math.PI / 2, textureCenter, textureScaling, SpriteEffects.None, 1.0f);
spriteBatch.End();
The render method is called by the SharpDX game class, which basically uses the IDrawingSurfaceBackgroundContentProvider interface, which is called by the DrawingSurfaceBackgroundGrid component of the Windows Phone 8 runtime.
Solution
Additional to Olydis solution (see below), I also had to set Game.IsFixedTimeStep to false, due to a SharpDX bug (see this issue on GitHub for details).
Furthermore, it is not safe to call sender.GetPreviewBufferArgb(previewData) inside the handler for PreviewFrameAvailable, due to cross thread access. See the corresponding thread in the windows phone developer community.
My Guess
As you guessed, I'm also pretty sure this may be due to threading. I suspect that, for example, the relatively lengthy SetData call may be intercepted by the Draw call, leading to unexpected output.
Solution
The following solution does not use synchronization, but instead moves "critical" parts (access to textures) to the same context.
Also, let's allocate two int[] instead of one, which we will use in an alternating fashion.
Code Fragments
void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
sender.GetPreviewBufferArgb(previewData2);
// swap buffers
var previewDataTemp = previewData1;
previewData1 = previewData2;
previewData2 = previewDataTemp;
}
Then add this to your Draw call (or equal context):
cameraTexture.SetData(previewData1);
Conclusion
This should practically prevent your problem since only "fully updated" textures are drawn and there is no concurrenct access to them. The use of two int[] reduces the risk of having SetData and GetPreviewBufferArgb access the same array concurrently - however, it does not eliminate the risk (but no idea if concurrent access to the int[] can result in weird behaviour in the first place).
I have the following code as part of a game:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
terrainSprite.Draw(spriteBatch);
if (resourceMap.pixels.IsDisposed == false)
{
resources.Draw(spriteBatch, spriteFont);
}
spriteBatch.End();
base.Draw(gameTime);
//Disposes the texture here:
resources.pixels.Dispose();
}
//In the resources class
public void Update()
{
//gD = graphics device
pixels = new Texture2D(gD, 800, 353);
//big update method
//manipulate the pixels texture
}
When I open task manager and look at the resource monitor, the memory usage for 'myGame.exe' is constantly going up by about 8 KB (I realize this is small, but my game holds a LOT of data, so saving every bit I can is important, and it builds up fairly quickly). This is after all other code is commented out except for what is shown here. Then, when I comment out the code: "pixels = new Texture2D(gD, 800, 353);", the memory usage stays constant. I also tried GC.Collect(), but no dice.
Is there anything else I can do to try and stop it? (Sorry, getting rid of the code is not an option :p, renewing the texture is much faster than any other method I've come across to make the texture go blank)
Depending on your Game configuration and really, many other factors, such as how slow everything is running, etc., Update and Draw are not perfectly synchronous with each other and are not guaranteed to be run in the following fashion:
Update
Draw
Update
Draw
Update
Draw
Update
Draw
....
Therefore, since you're Disposeing in one and creating a brand new one in the other, something like this can definitely happen:
Update: create new
Update: create new //PREVIOUS ONE LEAKED!
Draw: disposes only current
Update: create new
Update: create new //AGAIN LEAK
Draw: disposes only current
...
Thus, do not Dispose separately in this fashion; Dispose one time for each new one created, no matter what.
I should also add on that textures, along with some other XNA classes (sound and music, and Effects, to name a few) are unmanaged resources, meaning the GC does not see them at all. You must call Dispose on these.
As Andrew points out in his comment, the best way to avoid these pitfalls is not to recreate textures so often - simply reuse the same one and modify it as you see fit.
It appears that Texture2D are not fully handled by the garbage collector.
So when you stop using it (when reusing a variable referencing it, like here, or during the OnDestroy callback), you have to manually destroy the texture. Here :
if(pixels != null) {
Destroy(pixels);
}
pixels = new Texture2D(gD, 800, 353);