PDA

View Full Version : Floaty text



Queex
29-10-09, 02:51 PM
What I want is to be able to spawn free-floating text somewhere on the map, which then rises slowly to a second or so and disappears.

In other words, so when you collect a coin, instead of the dialogue box appearing and needing to be cleared away, you can keep on playing while the notification scrolls up.

Now, I think this can be done by:
* Making the ActionPickup coin trigger the spawn of the message.
* Adding an 'UpdateNotification' function to the global room update to move it.

But I also want to generate the text somewhat dynamically, using the normal font rather than having to create a brush for each possible collectible (I plan on having a few). Is that feasible?

macon
29-10-09, 03:50 PM
I have actually made free floating text by accident. What happened is that I used a non exsistant tile for the dialog box border and he result was it just drew the text only.

However, dialog boxes freeze the action. You would need to create your own by modifying a copy of the original code and altering its draw position in the room update.

It could be pretty complicated. It might be easier to have a graphic for each message and use the inbuilt functions for moving spiders, platforms etc.

Queex
29-10-09, 04:44 PM
I did a little digging, and it turns out that any code showing how to write text to screen isn't exposed- there a high-level functions in the engine that handle messages, but not at a basic enough level that they can be co-opted for this. I might have to fall back on pre-built brushes, in that case, which would be a pity.

delta
29-10-09, 05:36 PM
I wouldn't give up just yet. It sounds interesting, and I think it can be done. Ill give some thought as to how it could be done, but off the top of my head it sounds like you need something like the text in TLoD, which is drawn by the HUD, rather than as a message.

I'll try and come up with some solid code for you over the weekend.

why does everyone always want complicated stuff in their first game these days? :p

Queex
29-10-09, 07:10 PM
why does everyone always want complicated stuff in their first game these days? :p

I've been made bold by my coding successes at work. :v2_dizzy_tongue2:

macon
29-10-09, 07:49 PM
I'm kicking myself for not thinking of this earlier.

Look up HudDrawText() in the manual. This will print text anywhere. The manual states it should only be called from within HandlerDrawHud(). Look at the line that displays the room name to see how it works. In fact print the room name in the middle of the screen to see it in action.

delta
29-10-09, 08:35 PM
The issue is not displaying it on the HUD, it's being able to easily display different messages on the HUD, and show them where-ever you want to, for however long you want to. It's definitely possible, and i'll write some code that will do exactly that before the end of the weekend. :)

xelanoimis
29-10-09, 08:39 PM
There are 2 ways to do it.
It's not quite the beginner's thing but it's not too complicated either.

One is to do it in the draw handler (as mentioned). It will work like other menu messages. By the way the messages are not blocking - it's just the implementation in the template what pause the game while the message is displayed. You can have a message displayed and play the game in the background.

However take care not to do much calculations in the draw handler - that is called at true frame rate (not at 37 fps or so). It can affect the game's fps. The best solution is to do all your work on the update handler, store the result (the text, position, etc) in script global variables and on draw, just display them. Also avoid doing much string operations.

The second way to implement such text tricks, is to have a number of dynamic objects used as letters (8x8 pixels) and to set their mapping to the characters in the font tile and to align and move them depending on what you want to do. If you organize the code well, you can simply have to call something like ShowFloatingText( x, y, text ). From the game update handler you call an UpdateFloatingText which test if any text is visble and updates the letter objects to move up or fade or whatever. You only need as many objects as the longest line of text you want to display once.

I would recommend the second way since is more flexible for complex moving texts (spiral or other tricky movements). If it's just a line of text moving up, do it in the draw handler as a message.

Alex

DizzyFanUK
30-10-09, 05:27 PM
Would someone be able to pop a screenshot of just what floaty text looks like in a game?

Im trying to imagine it in practice?
is it like the intro screen where big DIZZY letters dance about?
or is it like when you click on a power up and for a few seconds some flashy points numbers float up into the air, then fade away?
Cool effects:)

btw, I like to follow these dizzyage with qs - not because of any technical knowledge - but its fun to appreciate the weird and wonderful complexities that go into making games.
A bit like a child watching someone fix the car - not knowing whats going on - but it looks impressive:)

Meph
30-10-09, 05:33 PM
Wonder if anyone will make a dizzy first person shooter with dizzyage!!!

delta
30-10-09, 05:37 PM
Would someone be able to pop a screenshot of just what floaty text looks like in a game?

Im trying to imagine it in practice?
is it like the intro screen where big DIZZY letters dance about?
or is it like when you click on a power up and for a few seconds some flashy points numbers float up into the air, then fade away?
Cool effects:)

I think the closest thing to what he's trying to do is the '10' points text that appears in the arcade games in IID when you kill the baddies. That, however, was just a 3 or 4 frame graphical animation.

delta
30-10-09, 07:07 PM
Right, here is how to display 'floating' text as a message:

firstly, put the following in gamedef.gs:



#def G_FLOAT 80 // set floating text display. 0=off, 1+ =on
#def G_FLOATXPOS 81 // set floating text X position
#def G_FLOATYPOS 82 // set floating text Y position
#def G_FLOATNUM 83 // set floating text Maximum movement


#def FLOATTEXT // set floating text message

use different numbers to 80-83 if you're already using those for other game variables.

At the bottom of util.gs, put the following code:



/////////////////////////////////////////////////////////////////////////////////
// IN: int; x; horizontal coordinate in characters, multiple of 8 pixels
// IN: int; y; vertical coordinate in characters, multiple of 8 pixels
// IN: str; text; message text
// IN: int; speed; amount of pixels to float up by
// Sets a transparent message (floating up) in a similar way to Message()
/////////////////////////////////////////////////////////////////////////////////
func MesFloatUp( x, y, text , speed )
{
GameSet(G_FLOATNUM, speed*2);
GameSet(G_FLOATXPOS,x);
GameSet(G_FLOATYPOS,y);

FLOATTEXT = text;

GameSet(G_FLOAT, 1);
}

/////////////////////////////////////////////////////////////////////////////////
// IN: int; x; horizontal coordinate in characters, multiple of 8 pixels
// IN: int; y; vertical coordinate in characters, multiple of 8 pixels
// IN: str; text; message text
// IN: int; speed; time to display message (in seconds, roughly)
// Sets a transparent message (static) in a similar way to Message()
/////////////////////////////////////////////////////////////////////////////////
func MesFloatStatic( x, y, text , speed )
{
GameSet(G_FLOATNUM, 0);
GameSet(G_FLOATXPOS,x);
GameSet(G_FLOATYPOS,y);

FLOATTEXT = text;

GameSet(G_FLOAT, speed*37);
}

/////////////////////////////////////////////////////////////////////////////////
// Updates the floating text to move up / disappear after a set time
/////////////////////////////////////////////////////////////////////////////////
func UpdateFloatText()
{
floattext = GameGet(G_FLOAT);
floatnum = GameGet(G_FLOATNUM);
if(floattext>=1&&floatnum!=0) // if text is to float up
{
GameSet(G_FLOAT,floattext+1);
if (GameGet(G_FLOAT)>=floatnum) { GameSet(G_FLOAT,0); } // stop text when planned

if ((GameGet(G_FLOATYPOS)*8)-(floattext/2)<=2) { GameSet(G_FLOAT,0); } // stop text if it tries to move higher than the game screen
}
if(floattext>=1&&floatnum==0) // if text is to stay static
{
GameSet(G_FLOAT,floattext-1);
}
}


in function HandlerGameUpdate() in handlers.gs, put the following line of code:



UpdateFloatText();


and at the bottom of function HandlerDrawHud() in handlers.gs, put the following code:



// float text
floattext = GameGet(G_FLOAT);
floatnum = GameGet(G_FLOATNUM);
fx = GameGet(G_FLOATXPOS);
fy = GameGet(G_FLOATYPOS);

if(floattext>=1&&floatnum!=0)
{
text = FLOATTEXT;
w = HudGetTextWidth( text );
h = HudGetTextHeight( text );
HudColor(0xff000000); // set colour as black
HudDrawText( fontid, (fx*8)+8,(fy*8)+47-(floattext/2),w,h, text, 0 ); // background text
HudDrawText( fontid, (fx*8)+8,(fy*8)+49-(floattext/2),w,h, text, 0 ); // background text
HudDrawText( fontid, (fx*8)+7,(fy*8)+48-(floattext/2),w,h, text, 0 ); // background text
HudDrawText( fontid, (fx*8)+9,(fy*8)+48-(floattext/2),w,h, text, 0 ); // background text
HudColor(0xffffff00); // set colour as yellow
HudDrawText( fontid, (fx*8)+8,(fy*8)+48-(floattext/2),w,h, text, 0 ); // foreground text
}
if(floattext>=1&&floatnum==0)
{
text = FLOATTEXT;
w = HudGetTextWidth( text );
h = HudGetTextHeight( text );
HudColor(0xff000000); // set colour as black
HudDrawText( fontid, (fx*8)+8,(fy*8)+47,w,h, text, 0 ); // background text
HudDrawText( fontid, (fx*8)+8,(fy*8)+49,w,h, text, 0 ); // background text
HudDrawText( fontid, (fx*8)+7,(fy*8)+48,w,h, text, 0 ); // background text
HudDrawText( fontid, (fx*8)+9,(fy*8)+48,w,h, text, 0 ); // background text
HudColor(0xffffff00); // set colour as yellow
HudDrawText( fontid, (fx*8)+8,(fy*8)+48,w,h, text, 0 ); // foreground text
}


you may have noticed that there are two functions you can call. They are MesFloatUp and MesFloatStatic. These do pretty much as they say, MesFloatUp makes the floating message float up, and MesFloatStatic displays the floating message as static in one place.

They are used in almost exactly the same way as you'd use Message(), with the first two numbers being the X and Y positions, and the text between the "" being, well, the text that is displayed. The additional number at the end differs depending on which function is used. In MesFloatUp it is the number of pixels the text will rise up by before disappearing, and in MesFloatStatic it is the (rough) number of seconds the text will display for.

They are called in the normal places you'd call a message from, as so:



MesFloatUp(4,9,"THIS TEXT WILL FLOAT UP BY 40 PIXELS",40);

MesFloatStatic(4,9,"THIS TEXT WILL STAY STATIC FOR 3 SECONDS",3);


MessagePop() is not used with either of the two functions.

Note that when using MesFloatUp, if the text gets higher than the top of the 'room' window (which is 40 pixels from the top of the HUD), then the text will automatically disappear, so that it doesn't overlap the HUD.

any questions, just ask! :)

delta
30-10-09, 07:18 PM
Regarding doing the message when you find a coin, you'd need to replace the following highlighted code in function DoPickupObject(idx) in action.gs:



if(class==CLASS_COIN) // coins are to be collected
{
coins = PlayerGet(P_COINS)+1;
ObjSet(idx, O_DISABLE, 1); // make disabled (picked up)
PlayerSet(P_COINS,coins); // store coins counter
SamplePlay(FX_COIN);

// check if found them all - edit these messages
if( coins==MAXCOINS )
OpenDialogMessage("YOU HAVE FOUND\nALL THE COINS");
else
OpenDialogMessage("YOU HAVE FOUND\nA COIN");
}


with this code:



if(class==CLASS_COIN) // coins are to be collected
{
coins = PlayerGet(P_COINS)+1;
ObjSet(idx, O_DISABLE, 1); // make disabled (picked up)
PlayerSet(P_COINS,coins); // store coins counter
SamplePlay(FX_COIN);

// check if found them all - edit these messages
if( coins==MAXCOINS )
MesFloatUp(8,7,"YOU HAVE FOUND\nALL THE COINS",40);
else
MesFloatUp(8,7,"YOU HAVE FOUND\nA COIN",40);
}

Queex
31-10-09, 10:11 PM
That's excellent! Pretty much exactly what I needed.

I'm going to embellish it a little- making the text change based on which graphic is used (possible by accessing the tilemap properties and more foolproof than setting it for each coin individually).

I'll also fiddle with it to use pixel co-ordinates (so it's easy to snap and centre to Dizzy's position when he collects the coin).

But many thanks for showing me the way!

Queex
31-10-09, 10:33 PM
For reference, my adjustments:

In HandlerDrawHud():

// float text
floattext = GameGet(G_FLOAT);
floatnum = GameGet(G_FLOATNUM);
fx = GameGet(G_FLOATXPOS);
fy = GameGet(G_FLOATYPOS);
absx = fx - GameGet(G_ROOMX)*GameGet(G_ROOMW);
absy = fy - GameGet(G_ROOMY)*GameGet(G_ROOMH);

if(floattext>=1&&floatnum!=0)
{
text = FLOATTEXT;
w = HudGetTextWidth( text );
h = HudGetTextHeight( text );

HudColor(0xff000000); // set colour as black
HudDrawText( fontid, absx+8,absy+47-(floattext/2),w,h, text, 0 ); // background text
HudDrawText( fontid, absx+8,absy+49-(floattext/2),w,h, text, 0 ); // background text
HudDrawText( fontid, absx+7,absy+48-(floattext/2),w,h, text, 0 ); // background text
HudDrawText( fontid, absx+9,absy+48-(floattext/2),w,h, text, 0 ); // background text
HudColor(0xffffff00); // set colour as yellow
HudDrawText( fontid, absx+8,absy+48-(floattext/2),w,h, text, 0 ); // foreground text
}
if(floattext>=1&&floatnum==0)
{
text = FLOATTEXT;
w = HudGetTextWidth( text );
h = HudGetTextHeight( text );
HudColor(0xff000000); // set colour as black
HudDrawText( fontid, absx+8,absy+47,w,h, text, 0 ); // background text
HudDrawText( fontid, absx+8,absy+49,w,h, text, 0 ); // background text
HudDrawText( fontid, absx+7,absy+48,w,h, text, 0 ); // background text
HudDrawText( fontid, absx+9,absy+48,w,h, text, 0 ); // background text
HudColor(0xffffff00); // set colour as yellow
HudDrawText( fontid, absx+8,absy+48,w,h, text, 0 ); // foreground text
}

In DoPickUpObject():

if(class==CLASS_COIN) // coins are to be collected
{
coins = PlayerGet(P_COINS)+1;
ObjSet(idx, O_DISABLE, 1); // make disabled (picked up)
PlayerSet(P_COINS,coins); // store coins counter
SamplePlay(FX_COIN);

//floaty message
coinhash= ObjGet(idx,O_MAP)*1000+ObjGet(idx,O_MAP+1);
if(coinhash==0){
text="+1 coin";
} else if(coinhash==16000){
text="+1 ankh";
} else if(coinhash==16){
text="+1 pearl";
} else if(coinhash==16016){
text="+1 diamond";
} else {
text="+1 treasure";
}
x = PlayerGet(P_X) - HudGetTextWidth(text)/2;
y = PlayerGet(P_Y) - 16;
MesFloatUp(x,y,text,15);
}

This works best if all coins are in the same tga file.

delta
31-10-09, 10:54 PM
nice. I see what you've done to center it above the player. :)

I also see what you've done to differentiate the different 'collect' items. However, i'd have done it a different way :p

in gamedef.gs, set the following:



#def O_COINTYPE 40 // coin type for setting messages


then change your DoPickUpObject() code to the following:



if(class==CLASS_COIN) // coins are to be collected
{
coins = PlayerGet(P_COINS)+1;
ObjSet(idx, O_DISABLE, 1); // make disabled (picked up)
PlayerSet(P_COINS,coins); // store coins counter
SamplePlay(FX_COIN);

//floaty message
coinhash= ObjGet(idx,O_COINTYPE);
if(coinhash==1) { text="+1 coin"; }
if(coinhash==2) { text="+1 ankh"; }
if(coinhash==3) { text="+1 pearl"; }
if(coinhash==4) { text="+1 diamond"; }
if(coinhash==5) { text="+1 treasure"; }
x = PlayerGet(P_X) - HudGetTextWidth(text)/2;
y = PlayerGet(P_Y) - 16;
MesFloatUp(x,y,text,15);
}


then i'd use object property 40 to set in the map which collectible 'type' each one is.

hmmm, looking at it now, it actually requires a bit more work in the map than your way (albeit not much). The advantage is that this way doesn't rely on all the collectibles being on the same tile, and you could even swap them all around at a later date and the code wouldn't be affected.

ah well, if your way works fine for you, then that's cool :)

Queex
01-11-09, 10:37 PM
I thought about doing it that way, but I then decided I didn't trust myself to not forget a coin somewhere. That would have been embarrassing.

The difficult part was in working out how to display the text and updating in the right place. I let you do all of that :v2_dizzy_tongue2:

I guess an even better solution would have separate calls for by map position and by screen position, but I've got what I wanted and I'll never get anything else done if I keep tinkering. The text is still painted and floats up when menus are open- a thorough solution wouldn't do that, I guess. I hope it doesn't break on a save and reload, but it's difficult to test.

delta
02-11-09, 05:02 AM
there shouldn't be a problem on save/reload. test it just to be sure though.

you can also use the 'select' feature in the map editor to search for tiles that are class=coin, and specific tile/mapping properties. I don't see that there's any real need for specific calls by map/screen position.