I have three GUIs on screen (in 2.72). Each GUI has a background image created using DynamicSprite.CreateFromBackground
E.g.
DynamicSprite* selected_sprite; export selected_sprite;
function changeSelectedLabel(String text, int font, int color)
{ if(text == "") { gSelectedperson.BackgroundGraphic = 82; return; }
SetBackgroundFrame(0);
RawSaveScreen();
RawClearScreen(63519);
RawPrintOutlined(0,0, font, text, color);// just prints text on the blank background
selected_sprite = DynamicSprite.CreateFromBackground(0, 0, 0, GetTextWidth(text,font)+2, 12);
gSelectedperson.Width = selected_sprite.Width;
gSelectedperson.Height = selected_sprite.Height;
gSelectedperson.BackgroundGraphic = selected_sprite.Graphic;
gSelectedperson.X = 160 -(width/2);
RawRestoreScreen();
SetBackgroundFrame(-1);
}
I get a recurring crash, and I can make it happen when character.say is called. I think it's something to do with creating dynamic overlays and not releasing them. I don't want to call sprite.Delete because I need those sprites. As you can see, pointers are not by strong suit, so I'd be grateful for some guidance!
You need to call selected_sprite.Delete().
Quote from: wonkyth on Tue 08/12/2009 22:53:32
You need to call selected_sprite.Delete().
But won't that make the GUI background disappear? And when I tried adding Delete I just got a null error next time I called that function. Sorry if I sound like an idiot!
Oh, dang, I somehow feel I did not understand your intentions quite well... silly post, sorry :P
(deleted contents)
Quote
And when I tried adding Delete I just got a null error next time I called that function. Sorry if I sound like an idiot!
Try to write this:
if (selected_sprite)
{
selected_sprite.Delete();
}
This will only delete your sprite if it actually exists.
if (selected_sprite)
{
selected_sprite.Delete();
}
Thanks for the quick reply. I'm a little vague over what's actually happening there. selected_sprite is a pointer defined outside the function, so presumably "deleting" it only removes what it pointed to? So after deleting it I could call selected_sprite.PointToSomethingNew and the pointer would still be there ready to grab some more memory?
Quote from: tolworthy on Tue 08/12/2009 23:21:16
Thanks for the quick reply. I'm a little vague over what's actually happening there. selected_sprite is a pointer defined outside the function, so presumably "deleting" it only removes what it pointed to?
Yes. And actually it doesn't matter where you define pointer, inside or outside function, deleting always delete data to which the pointer points, not pointer variable itself.
Quote from: tolworthy on Tue 08/12/2009 23:21:16
So after deleting it I could call selected_sprite.PointToSomethingNew and the pointer would still be there ready to grab some more memory?
Rather "selected_sprite =" to assign new memory block adress to this pointer. But yes, you have right idea in general.
Ah... the fog begins to clear... :)
One last question. The pointer was used for grabbing a part of the background that is now used as a sprite. Does the sprite disappear when the pointer is deleted? (I know I should just test this, but I'm trying to understand WHY it works.)
Quote from: tolworthy on Tue 08/12/2009 23:34:58
One last question. The pointer was used for grabbing a part of the background that is now used as a sprite. Does the sprite disappear when the pointer is deleted?
Perhaps here is some misunderstanding.
When you call DynamicSprite.CreateFromBackground, pointer does not receive "part of background". Instead, a
new sprite is created and a background image is
copied to that new sprite.
So, when you delete that sprite, it deletes only the newly created sprite, not the original background.
Well, and answering your question directly, - yes, ofcourse, sprite dissapear when you tell to delete it.
Again, you do not "delete pointer", you delete data which is being pointed by pointer.
Thanks. I get that. It's the GUI background that I'm vague on:
gSelectedperson.BackgroundGraphic = selected_sprite.Graphic;
Is it true to say that the GUI (gSelectedperson) never had its own background image: (i.e. when selected_sprite is deleted, the GUI loses its image)
Again, it just makes a copy.
Thanks. I appreciate your help.
EDIT:
After tidying everything up, I discovered that my crash had nothing to do with DynamicSprites. Somehow my sprite bank had become corrupted, and it only showed up when certain characters tried to speak or idle. Oh well. It was good to learn more about pointers anyway.
Quote from: tolworthy on Tue 08/12/2009 23:49:32
It's the GUI background that I'm vague on:
gSelectedperson.BackgroundGraphic = selected_sprite.Graphic;
Is it true to say that the GUI (gSelectedperson) never had its own background image: (i.e. when selected_sprite is deleted, the GUI loses its image)
Well, first of all, I don't know if gSelectedperson ever had "its own" image. It may had, if you set it up in GUI editor, or previously in script somewhere, but in any case as soon as you set different image for it, previous is "forgotten". So, as soon as you delete the dynamic sprite, GUI will loose background as well.
However I am afraid that this could cause crashes, because GUI still have sprite number set for its background. You better make this after you deleted your sprite:
gSelectedperson.BackgroundGraphic = 0;
Sorry to come back with this, I must be a very slow learner. A very similar but different crash now occurs:
â€"â€"â€"â€"â€"â€"â€"â€"â€"
Adventure Game Studio
â€"â€"â€"â€"â€"â€"â€"â€"â€"
An error has occured. Please contact the game author for support, as this
is likely to be a scripting error and not a bug in AGS.
(ACI version 2.72.920)
in imported modules (line 341)
from imported modules (line 1016)
Error: DeleteSprite: Attempted to free a static sprite that was not loaded by the script
â€"â€"â€"â€"â€"â€"â€"â€"â€"
OK
â€"â€"â€"â€"â€"â€"â€"â€"â€"
this is the line of code it refers to (line numbers added):
line 340: RawPrintOutlined(0,0, font, text, color);
line 341: mouseover_sprite = DynamicSprite.CreateFromBackground(0, 0, 0, GetTextWidth(text,font)+12, lineheight+4); // this is the evil line
line 342: mouseover_prevtext = text;
How is line 341 deleting any sprite? How is it "not loaded by the script "â€" mouseover_sprite is declared outside any function, it’s used many times, and is never deleted. In particular, why does it suddenly crash after working perfectly five or ten times? I’m stumped.
I did try this:
if(mouseover_sprite){mouseover_sprite.Delete();}
mouseover_sprite = DynamicSprite.CreateFromBackground(0, 0, 0, GetTextWidth(text,font)+12, lineheight+4);
But that crashed the first time the Delete line was reached (despite the sprite being declared at the start of the function), so I removed that line. Without that line the game at least plays for a few seconds.
Have you checked line 1016?
How many script modules do you have by the way?
Quote from: Crimson Wizard on Wed 09/12/2009 21:15:19Have you checked line 1016?
line 1016 is part of rep_ex:
if(GetGlobalInt(22) ==1) // hotspotsa re shown?
{ old_overhot = overhot_text; // assume there is no change in hotspot
if(!override_overhot) // can be overridden elsewhere in the game
{ overhot_text = Game.GetLocationName(mouse.x, mouse.y);
if(checkLocationAlias(overhot_text)) overhot_text = locationAlias; // some hotspots are renamed during the game
gTextgui.Visible =true; // the hotspot GUI
}
if(old_overhot !=overhot_text) // don't bother if nothing has changed
changeMouseOver(overhot_text, OverHot_Font, myOverHotColor);// *** line 1016 ***
// position of overHot ------------------------------------
int overhotmp = mouse.x + OverHot_XOff;
if ((overhotmp + GetTextWidth(overhot_text, OverHot_Font)) > OverHot_BoundX2)
overhotmp = OverHot_BoundX2 - GetTextWidth(overhot_text, OverHot_Font);
if (overhotmp < OverHot_BoundX1) overhotmp = OverHot_BoundX1;
gTextgui.X = overhotmp; // so the position of the overlay will be adjusted every game loop
overhotmp = mouse.y + OverHot_YOff;
if ((overhotmp + GetTextHeight(overhot_text, OverHot_Font, OverHot_Width)) > OverHot_BoundY2) overhotmp = OverHot_BoundY2 - GetTextHeight(overhot_text, OverHot_Font, OverHot_Width);
if (overhotmp < OverHot_BoundY1) overhotmp = OverHot_BoundY1;
gTextgui.Y = overhotmp;
}
Here is the original function in more detail:
function changeMouseOver(String text, int font, int color)
// me: DELETED from end of function:, int outlinecolor, int outlinefont, Alignment alignment)
{ if(text == "") { gTextgui.BackgroundGraphic = 82; return; }// no text, don't bother to do anything
if(text ==mouseover_prevtext){gTextgui.BackgroundGraphic = mouseover_sprite.Graphic;return;}// same text as before?
SetBackgroundFrame(0); // for animating screens
RawSaveScreen();
RawClearScreen(63519);
int lineheight = GetTextHeight(text, font, 200); // 200 is any number bigger than the width (i.e. this does not wrap)
RawPrintOutlined(0,0, font, text, color);
mouseover_sprite = DynamicSprite.CreateFromBackground(0, 0, 0, GetTextWidth(text,font)+12, lineheight+4);// line 341
mouseover_prevtext = text;
// set width and height (this once CRASHed with "SetGUISize: invalid dimensions" - hence checks)
int t =mouseover_sprite.Width; if(t <30) t =30; if(t >150) t =150;
if(gTextgui.X >(320 -t)) gTextgui.X = 320 -t;
gTextgui.Width = t;
t =mouseover_sprite.Height; if(t <8) t =8; if(t >50) t =50;
if(gTextgui.Y >(240 -t)) gTextgui.Y = 240 -t;
gTextgui.Height = t;
gTextgui.BackgroundGraphic = mouseover_sprite.Graphic;
RawRestoreScreen();
SetBackgroundFrame(-1); //for animating screens
}
Quote from: Crimson Wizard on Wed 09/12/2009 21:15:19How many script modules do you have by the way?
I Keep it to ten, so it's easy to Ctrl-N between them:
Ctrl-1: imported modules plus code that has to come first
Ctrl-2: most functions
Ctrl-3: cut-scenes
Ctrl-4: clue dialog
Ctrl-5: hotspot comments
Ctrl-6: conversations
Ctrl-7: story outline
Ctrl-8: the function that decides how to forward the story
Ctrl-9: commented-out notes (list of what's in every global variable, etc.)
Ctrl-G: anything that naturally goes with mouse click, rep_ex, GUIs, etc.
All that I can think of is that the DynamicSprite being assigned as a GUI background while it is updated maybe prevents the old version of the sprite from being deleted from memory. Try inserting a line earlier in the script that sets the GUI background sprite to 0 to check whether that's the case.
The trouble with DynamicSprite.Delete is that it frees up the memory but leaves the pointer variable still pointing to the invalid memory area. So I ALWAYS do this when deleting sprites:
if (selected_sprite != null)
selected_sprite.Delete();
selected_sprite=null;
}
Of course, this would all be much easier in the latest version where you can manipulate dynamic sprites directly without having to do all that dodgy background stuff :)
Line 341 is deleting a sprite because whenever you put a new value in a pointer, the old value can lose its last reference and so is automatically delted by AGS's garbage collection. Are you reassigning mouseover_sprite anywhere else in the script?
Thanks for the suggestions. What you say makes a lot of sense. However, I've decided however to cut the Gordian knot and remove antialiasing from my games. Non-essential features make the code more complex without improving the story, so I've decided to go back to basics and spend my time on story telling instead.
Some problems were found with dynamic sprite management in 2.72, these are resolved in the latest versions of AGS.