I've got some random problems with on-screen overlays that are really confusing me!
Whenever a player character gets a new inventory item, I display the name of the item as an overlay (which is then shown for a few seconds and then shuffled off-screen). This was working fine, but in a newer build I'm testing, the overlay doesn't display. I should stress that I haven't touched any script to do with the overlays - it just stopped working today.
The overlays are created initially in game_start() to avoid null pointer errors:
// invOverlay1 and 2 have already been declared at the top of globalscript.asc
invOverlay1 = invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
invOverlay2 = invOverlay2.CreateTextual(20, 40, 400, Game.SpeechFont, 12, "");
Here's my event handler function from the global script:
function on_event(EventType event, int data){
if(event==eEventAddInventory){
if(!invOverlay1.Valid){ // Check if the first overlay is free or not
Display("Setting overlay 1");
invOverlay1 = invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "+ %s", inventory[data].Name);
SetTimer(19, 120);
}else if(!invOverlay2.Valid){
Display("Setting overlay 2");
invOverlay2 = invOverlay2.CreateTextual(20, 40, 400, Game.SpeechFont, 10, "+ %s", inventory[data].Name);
SetTimer(19, 120);
}/*else{ // Force overlay 1 to be used, even if it's already in use (overwrites existing text)
invOverlay1 = invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "+ %s", inventory[data].Name);
SetTimer(19, 120);
}*/
}
}
The overlays are then modified by repeatedly_execute_always thusly:
if(IsTimerExpired(19)){ // Initial timer has expired - start moving the overlay off-screen
if(invOverlay1.Valid && invOverlay1.X == 20){
invOverlay1.X --;
}
if(invOverlay2.Valid && invOverlay2.X == 20){
invOverlay2.X --;
}
}
if(invOverlay1.Valid && invOverlay1.X < 20){ // Overlay is valid and has started moving off-screen
if(invOverlay1.X > -100){
invOverlay1.X -=2; // Not off-screen yet - keep moving
}else{
invOverlay1.Remove(); // Overlay.X is <= -100, so remove it
}
}
if(invOverlay2.Valid && invOverlay2.X < 20){
if(invOverlay2.X > -100){
invOverlay2.X -=2;
}else{
invOverlay2.Remove();
}
}
With the above code, the overlay does not appear at all when picking up an item.
If, however, I un-comment the 'else' condition in on_event ('// Force overlay 1 to be used'), it shows up.
It works, then, but I have no idea why it suddenly didn't until I added the else condition. As I said, it was working absolutely fine when I started the game in an earlier build, and I hadn't touched the overlay code until it stopped working today.
Any ideas to set my mind at ease?
Doesn't this:
invOverlay1 = invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
invOverlay2 = invOverlay2.CreateTextual(20, 40, 400, Game.SpeechFont, 12, "");
validate both overlays? This would explain why your first two conditions when adding an inventory item don't execute.
Hope this solves it! :)
A note.
Overlay creation functions are static. That's why you do not have to use object's name:
invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
You may write just:
Overlay.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
Quote from: Crimson Wizard on Mon 12/11/2012 13:13:00
A note.
Overlay creation functions are static. That's why you do not have to use object's name:
invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
You may write just:
Overlay.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
But unless I assign it to an Overlay* variable, I can't move it around in rep_exec_always - unless I'm missing something?
Quote from: geork on Mon 12/11/2012 12:48:06
Doesn't this:
invOverlay1 = invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
invOverlay2 = invOverlay2.CreateTextual(20, 40, 400, Game.SpeechFont, 12, "");
validate both overlays? This would explain why your first two conditions when adding an inventory item don't execute.
Hope this solves it! :)
I dropped a couple of lines in afterwards (invOverlay1.Remove();) and that seems to have cleared the problem up. Not sure how it worked before, though?!
To anyone thinking that I'm insane to create two overlays in game_start and then immediately remove them, I was getting null pointer errors if I didn't do it.
Quote from: MurrayL on Mon 12/11/2012 14:26:02
But unless I assign it to an Overlay* variable, I can't move it around in rep_exec_always - unless I'm missing something?
Probably I wasn't clear enough and made a mistake by dropping part of the line :)
I mean his of course:
invOverlay1 = Overlay.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");
Sorry for double post, I did not have time yesterday to read this thread more carefully.
Quote from: MurrayL
To anyone thinking that I'm insane to create two overlays in game_start and then immediately remove them, I was getting null pointer errors if I didn't do it.
Simply checking for null pointer when using overlays should be better solution here. There are many cases when the pointer to the object could be null in AGS, and precreating unnecessary objects at start to evade null pointer error is an ugly workaround.
For example:
if(invOverlay1 != null && !invOverlay1.Valid) { // Check if the first overlay is free or not
or simply
if(invOverlay1 && !invOverlay1.Valid) { // Check if the first overlay is free or not
(Checking the value of pointer in condition is like implicitly comparing it to null)
As geork pointed out, with the two overlays created at game start certain time must pass before new overlays will be created (since a valid overlay must wait for timer, then move offscreen before being removed).
Two small points: Overlays are destroyed when the room is changed, so at that point, global Overlay* pointers are null again. I'd use a GUI and two buttons anyway; I rarely had any use for an Overlay.
The other thing is regarding
if(invOverlay1)
instead of
if(invOverlay1 != null)
I remember trying this myself after coding lots of PHP, then getting back to AGS. IIRC, it didn't work as expected or not at all. Maybe I'm confusing it with some other shortcut though, I didn't test it, but something was fishy there.
Quote from: Khris on Tue 13/11/2012 09:29:13
The other thing is regarding
if(invOverlay1)
instead of
if(invOverlay1 != null)
I remember trying this myself after coding lots of PHP, then getting back to AGS. IIRC, it didn't work as expected or not at all. Maybe I'm confusing it with some other shortcut though, I didn't test it, but something was fishy there.
Hmm, I thought it should work. It doesn't work in C# - that's for sure - but since AGS script looks a bit like C# I confuse their pointers rule sometimes.
//---------------------------------------------------------
EDIT:
I could not resist this and tested engine under debugger. :)
The lines:
Overlay *o;
if (o)
{
}
First the stack variable is initialized with null ptr (basically, 0).
Then, it makes conditional jump (JZ), comparing pointer on stack with 0.
Similarly
Overlay *o = Overlay.CreateTextual...
if (o)
{
}
First it initializes pointer on stack with return value from CreateTextual, then does JZ.
//---------------------------------------------------------
EDIT2:
Alright, it seems AGS script is stuck somewhere in the midst between C++ and C# on this.
While you can write
if (ptr)
you can't write
if (ptr && something else)
because
Quote from: AGS script compilerType mismatch: cannot convert 'Overlay*' to 'bool'
So, well, yes, you have to use this:
if(invOverlay1 != null && !invOverlay1.Valid)
Right, that's it! Thanks for clearing it up.
Thanks - I removed the overlay fiddling in game_start and added "invOverlayX!= null" conditions to all the functions which modify them instead, and it seems to be working A-OK. No more null pointer errors!