[Tutorial] Text draw tips

A quick guide to text_draw

Showing a text draw

Text draw's are very powerful in that they are really limited only by your imagination. That said they can be tricky to get right. If you’re a beginner to text draw's I recommend you first start with the basics. Create the text draw (you can define these anywhere in your script), show it and then build on it from there.

pawn Code:
new Text:txt;
txt = TextDrawCreate(10.0, 100.0, "Sample text draw under the chat box");
TextDrawUseBox(txt, 1);
TextDrawShowForPlayer(playerid, txt);
The text you show or pass to TextDrawCreate must not be an empty string. Also, don't forget that you will need to hide/destroy the text draw eventually for it to be removed from a players screen.

For example, the following code will show a server welcome text for new players that enter your server...

Add this to the top of your script:
pawn Code:
new Text:gTextDraw;
In the OnGameModeInit() callback add:
pawn Code:
gTextDraw = TextDrawCreate(10.0, 100.0, "Welcome to my server!");
TextDrawUseBox(gTextDraw, 1);
In the OnPlayerConnect(playerid) callback add:
pawn Code:
TextDrawShowForPlayer(playerid, gTextDraw);
In the OnPlayerRequestSpawn(playerid) callback add:
pawn Code:
TextDrawHideForPlayer(playerid, gTextDraw);
Your now well on your way to mastering text draw's. With practice creating something like this is easy:

Changing the text of a text draw

In SA:MP 0.2.2 we were blessed with the native "TextDrawSetString(Text:text, string[]);". Reference the Text variable and pass a new string to update a text draw's text (all existing text draw properties are kept).

Checking if a text draw is valid

You may have noticed there is a invalid text draw define:
pawn Code:
However you cannot simply compare a text draw against this define to determine if its valid.

For example, the following will give a tag mismatch warning at compile time:
pawn Code:
new Text:gTextDraw;
if(gTextDraw == INVALID_TEXT_DRAW) {
  // blah, do code here
To get arround the tag mismatch, you have two options.
pawn Code:
new Text:gTextDraw;
if(_:gTextDraw == INVALID_TEXT_DRAW) { // _: removes the tag from gTextDraw
  // blah, do code here
or ...
pawn Code:
new Text:gTextDraw;
if(gTextDraw == Text:INVALID_TEXT_DRAW) { // adds the "Text" tag to INVALID_TEXT_DRAW
  // blah, do code here
I prefer the first method, however if you like the second method its probably easier to just redefine INVALID_TEXT_DRAW in your gamemode. To do this, add this to the top of your gamemode:
pawn Code:
Text draw native fuctions explained

native Text:TextDrawCreate(Float:x, Float:y, text[]);
Use: Creates a text draw area in memory (does not display to the screen).
Notes: The x,y coordinate is the top left coordinate for the text draw area based on a 640x480 "canvas" (irrespective of screen resolution). If you plan on using TextDrawAlignment with alignment 3 (right), the x,y coordinate is the top right coordinate for the text draw. Do not pass an empty string to this function (eg. where text = ""). In 0.2.2 the max text draw's you can create is 1024.

native TextDrawDestroy(Text:text);
Use: Destroys a text draw.
Notes: None.

native TextDrawLetterSize(Text:text, Float:x, Float:y);
Use: Sets the width and height of the letters.
Notes: Use with TextDrawSetProportional if you want even spacing.

native TextDrawTextSize(Text:text, Float:x, Float:y);
Use: When used with TextDrawUseBox it changes the size of the box.
Notes: When used with TextDrawAlignment of alignment 3 (right), the x and y are the coordinates of the left most corner of the box. For alignment 2 (center) the x and y values need to inverted (switch the two) and the x value is the overall width of the box. For all other alignments the x and y coordinates are for the right most corner of the box.

native TextDrawAlignment(Text:text, alignment);
Use: Aligns the text in the draw area.
Notes: Alignments... left = 0 or 1 (perhaps one of these is justified), centre = 2 and right = 3.

native TextDrawColor(Text:text, color);
Use: Defines the text colour.
Notes: Color is in hexidecimal format.

native TextDrawUseBox(Text:text, use);
Use: Adds or removes a box (shadow area) behind the text.
Notes: Show a box with 'use' set to 1, or hide a box (if previously set) with 'use' set to 0.

native TextDrawBoxColor(Text:text, color);
Use: Adjusts the text box colour (only used if TextDrawUseBox 'use' parameter is 1).
Notes: The color opacity is set by the alpha intensity of colour (eg. color 0x000000FF has a solid black box opacity, whereas 0x000000AA has a semi-transparent black box opacity).

native TextDrawSetShadow(Text:text, size);
Use: Adds a black shadow to the lower right side of the text.
Notes: The shadow font matches the text font. The shadow can be cut by the box area if the size is set too big for the area.

native TextDrawSetOutline(Text:text, size);
Use: Adds a black outline to the text.
Notes: The size parameter defines the thickness of the outline. The outline colour cannot be changed unless TextDrawBackgroundColor is used.

native TextDrawBackgroundColor(Text:text, color);
Use: Adjusts the text draw area background colour.
Notes: If TextDrawSetOutline is used with size > 0, the outline colour will match the color used in TextDrawBackgroundColor. Changing the value of color seems to alter the color used in TextDrawColor.

native TextDrawFont(Text:text, font);
Use: Changes the text font.
Notes: There four font styles as shown below. A font value greater than 3 does not display, and anything greater then 16 crashes the client.

native TextDrawSetProportional(Text:text, set);
Use: Seems to scale the text spacing to a proportional ratio.
Notes: Useful when using TextDrawLetterSize to ensure the text has even character spacing.

native TextDrawShowForPlayer(playerid, Text:text);
Use: Shows a text draw for a specific player.
Notes: Text will remain on the players screen until TextDrawHideForPlayer, TextDrawHideForAll or TextDrawDestroy is used or if the gamemode is changed.

native TextDrawHideForPlayer(playerid, Text:text);
Use: Hides a text draw for a given player.
Notes: None.

native TextDrawShowForAll(Text:text);
Use: Shows a text draw for all players.
Notes: Text will remain on all players screens until TextDrawHideForPlayer, TextDrawHideForAll or TextDrawDestroy is used or if the gamemode is changed.

native TextDrawHideForAll(Text:text);
Use: Hides a text draw for all players.
Notes: None.

TextDrawSetString(Text:text, string[]);
Use: Changes the text displayed by the text draw.
Notes: None.

And I made an exaple of using it. It just draws to all players time how long you are running your server.

So lets start with global variables and forward...:
pawn Code:
new gSeconds = 0; // counts how many seconds server is running
new Text: gText; // global "Text"

forward TimeServerRunning();
In OnGameModeInit we need to add the timer which calls TimeServerRunning which draws the time:
pawn Code:
SetTimer("TimeServerRunning", 1000, true); // call function every second
And the function that timer calls:
pawn Code:
public TimeServerRunning() {
    new string[16];
    format(string, sizeof string, "%s", TimeConvert(gSeconds));
    text1 = TextDrawCreate(10.0, 100.0, string);
    TextDrawUseBox(gText, true);
    TextDrawTextSize(gText, 50.0, 20.0);
TimeConvert() function - converts time (seconds) into string. e.g.: 92 -> "1:32"
pawn Code:
TimeConvert(time) {
    new minutes;
    new seconds;
    new string[256];
    if(time > 59){
        minutes = floatround(time/60);
        seconds = floatround(time - minutes*60);
        else format(string,sizeof(string),"%d:0%d",minutes,seconds);
        seconds = floatround(time);
        else format(string,sizeof(string),"0:0%d",seconds);
    return string;

I hope it will give some idea how it works. It is not as difficult as it looks like.

Moved details to first post.

Why it doesn't work?
Beginning of file:
new speedtimer;
new Float:newpos[MAX_PLAYERS][3];
new Float:oldpos[MAX_PLAYERS][3];
new Text: gText;
OnGameModeInit Callback:
speedtimer = SetTimer("speedofplayer",500,1);
End of file:
public speedofplayer()
	new Float:speed[MAX_PLAYERS];
	new speedmessage[256];
	for (new i = 0; i < MAX_PLAYERS; i++)
		speed[i] = floatsqroot(floatpower(newpos[i][0]-oldpos[i][0],2)+floatpower(newpos[i][1]-oldpos[i][1],2))/2.25/0.5*3.6;
		format(speedmessage,sizeof(speedmessage),"%d km/h",floatround(speed[i],floatround_round));
        gText = TextDrawCreate(10.0, 100.0, speedmessage);
		TextDrawShowForPlayer(i, gText);
		oldpos[i][0] = newpos[i][0];
		oldpos[i][1] = newpos[i][1];

Try to use array for gText. new gText[100];

Change: TextDrawDestroy(gText); => TextDrawDestroy(gText[i]);
Change: gText = TextDrawCreate.... => gText[i] = TextDrawCreate...
Change: TextDrawShowForPlayer(i, gText) => TextDrawShowForPlayer(i, gText[i]);

NOTE: I didn't test...

PS. Sometimes when I try to compile mode which contain TextDrawBackgroundColor(), warning pops up: tag mismatch, but when it compile without any warns, errors it doesn't really change its color in-game.

Once a text draw is created you cannot change the text, so you had the right idea in creating a new text draw per player. Unfortunately, you are using a global Text for the text draw (gText) which you destroy for each connected player (actually, you don't but you should - use IsPlayerConnected). So as you display the text draw for one player, you destroy it for all others. To get around this, simply make gText an array so you use a unique text draw per player. However there is a catch in that you can only create 96 individual text draw's at once, so keep this in mind if you want to use a text draw anywhere else in your gamemode or if you expect to have > 96 connected players.

I have a question. Is the limit of 96 max TextDraws player-specific, or server-specific? In other words, can't I create 96 TextDraws on the screen of every player?

I placed the following code into OnPlayerConnect().

	for(new i = 0; i < 9; i++)
		for(new j = 0; j < 10; j++)
			new Float:x = 10.0 + 40.0 * j;
			new Float:y = 310.0 + 7.0 * i;
			new index = i * 10 + j;
			new s[24];
			format(s, sizeof(s), "%d", index);
			new Text:txt = TextDrawCreate(x, y, s);
			TextDrawFont(txt, 1);
			TextDrawColor(txt, 0x003300FF);
			TextDrawSetShadow(txt, 0);
			TextDrawShowForPlayer(playerid, txt);
			gPlayerTexts[playerid][index] = txt;
//texts are destroyed in OnPlayerDisconnect(), but it's not important here.
It creates only 64 text blocks and only for the first connected player, thus 64 seems to be the SERVER's limit. If so, it's extremely bad. Even if making some "streaming" for TextDraws, there are too few of them available. It would be great, if the limit of 96\64 were per player, or if the server's limit were raised up to about 1024.

How works TextDrawSetProportional(Text:text, set);?? And what should i put on "set"??

I'm curious as to why it uses floats for positioning of text. Is the x & y in pixels (curious because I don't have 2.5 pixels?) or some other PC unit?

I'm assuming the letter size is done as a scale (e.g. 1.0 = normal, 2.0 = double size, 0.5 = half).. can anyone confirm this?

Thanks for this topic, it helps me understand how this text drawing works .

I have a problem with my text draws. Whenever I useTextDrawDestroy(text), it destroys ALL text that I have on the screen. Is there any way to get around this?

Do i understand it correctly?

I can create 64 global (or 96, whatever) texts for example in OnGameModeInit, and can show and hide them at so many players as i want? (i show 64 texts 5 players, then are 320 text blocks displayed, but only 64 different)
Or can i only 64 texts show at the same time? (That would be bad)


I think it is number of texts. If you use the show to all (text), then that will count as one, I believe. Need someone to confirm this.

Y_Less do we need to create this texts on GameModeInit?
I tought tehre is a way to make them dynamic

Ohh good, but will max x and max y for text coord be same as resolution? (eg. : 1024*768 > maxx = 1024 maxy = 76

The screen resolution according to text_draw is 640x480 regardless of the actual resolution you use (it scales accordingly). That said there is nothing stopping you from using coordinates that exceed the 640x480 area, but obviously there is no point in doing this.

Heres a little function which will display text for an amount of milliseconds and then destroy it for you.

pawn Code:
forward DestroyTextTimer(Text:text);

TimeTextForPlayer(playerid, Text:text, time)
  SetTimerEx("DestroyTextTimer",time,0,"i",_:text); // _: from Simon

public DestroyTextTimer(Text:text)
Function: TimeTextForPlayer(playerid, Text:text, time).
text = the text you want to display
time = amount of time, in milliseconds

It will give you a tag mismatch warning because of the Text label on the variable, but it doesnt matter because in the end its still just an integer.

Example usage:
pawn Code:
new Text:text = TextDrawCreate(20.0, 300.0,"I like pie");
    TextDrawUseBox(text, 1);
    TextDrawBoxColor(text, 0x000000AA);
    TextDrawFont(text, 1);
EDIT: thx Simon, updated.

Remove the tag mismatch by doing this;

pawn Code:
TimeTextForPlayer(playerid, Text:text, time)
  SetTimerEx("DestroyTextTimer",time,0,"i",_:text); // Force the _: tag to the text variable.
It removes the tag mismatch for me on a blank script .

Hi, i had an old speedometer, and i replaced the GameTextForPlayer(i, strings, 1500, 5); for
//GameTextForPlayer(i, strings, 1500, 5);
gText = TextDrawCreate(10.0, 100.0, strings);
TextDrawShowForPlayer(i, gText);
oldpos[i][0] = newpos[i][0];
oldpos[i][1] = newpos[i][1];
it works fine on my screen, but when a new player is connected, he get the speedometer, and mines dissappear
then when a new player connects, that player will get the speedo, and the old player's speedo meter dissapear..

did i make a stupid mistake?


This topic is very useful, thanks for the guide.

Forum Jump:

Users browsing this thread: 1 Guest(s)