What is going on with my overlays?!

Started by MurrayL, Mon 12/11/2012 12:00:19

Previous topic - Next topic

MurrayL

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:

Code: AGS

// 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:
Code: AGS

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:
Code: AGS

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?

geork

Doesn't this:
Code: AGS
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! :)

Crimson Wizard

A note.
Overlay creation functions are static. That's why you do not have to use object's name:
Code: ags

invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");

You may write just:
Code: ags

Overlay.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");


MurrayL

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:
Code: ags

invOverlay1.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");

You may write just:
Code: ags

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:
Code: AGS
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.

Crimson Wizard

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:
Code: ags

invOverlay1 = Overlay.CreateTextual(20, 20, 400, Game.SpeechFont, 10, "");

Crimson Wizard

#5
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:
Code: ags

if(invOverlay1 != null && !invOverlay1.Valid) { // Check if the first overlay is free or not

or simply
Code: ags

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).

Khris

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
Code: ags
if(invOverlay1)

instead of
Code: ags
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.

Crimson Wizard

#7
Quote from: Khris on Tue 13/11/2012 09:29:13
The other thing is regarding
Code: ags
if(invOverlay1)

instead of
Code: ags
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:
Code: ags

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
Code: ags

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
Code: ags
if (ptr)

you can't write
Code: ags
if (ptr && something else)

because
Quote from: AGS script compilerType mismatch: cannot convert 'Overlay*' to 'bool'

So, well, yes, you have to use this:
Code: ags
if(invOverlay1 != null && !invOverlay1.Valid)


Khris

Right, that's it! Thanks for clearing it up.

MurrayL

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!

SMF spam blocked by CleanTalk