PDA

View Full Version : [Tutorial][Work in progress] Hud elements



IzNoGoD
19th October 2013, 12:03
You can create 2 kinds of hud elements: a global one, or a client-based one.

Global:


hud = newHudElem();


Client:


hud = newClientHudElem(client_entity);


Above code might look trivial, but remember to store your hud elems in some proper variable so you can destroy() them when you dont need them anymore. However, if you think you might reuse a hud elem, just make it insivible instead of destroying (iirc there is a max on creation of hud elements)

Now, if you have a hud element, you want to place it somewhere. You can mainly do this using .vertAlign and .horzAlign:



hud.vertAlign = "some_string";
hud.horzAlign = "some_string";


In which the "some_string" is one of these, for vertAlign:


top
bottom
middle
center_safearea
fullscreen
subtop
noscale
alignto480


The easiest of this bunch is probably the fullscreen one. This makes your hud element stretch from top to bottom, having its zero-origin-point in the middle (as a horizontal line, as it still depends on the .horzAlign) of your screen. This means .y can range from +240 to -240 for the hud to be visible. The downside here is that the hud will be scaled (from 480 pixels to whatever you are using) and will probably look ugly on new, non-4:3 screens. Plus side is that it will cover the entire screen.

The top alignment is so the engine knows it can use the top 480 pixels (? or does it scale according to your aspec ratio?) of your screen, with the zero-origin-point at the top of your screen. This means that, in order for the hud to become visible, you have to give it a negative .y coordinate. Plus side is that it will not be scaled, downside is that you can probably not reach the bottom of the screen (?).

Bottom alignment is exactly the opposite of top-alignment: the hud will start at the bottom, having its zer-origin-point exactly there and requiring a positive .y to be visible.

center_safearea uses the middle 640x480 pixels of your screen (might scale according to aspect ratio also, unsure) and will have your hud visible (at least partially) if you have a .y between 240 and -240

If the middle alignment exists (unsure) it will not give much advantages over center_safearea, although it might be that middle scales according to aspect ratio while center_safearea does not (citation needed)


For the .horzalign:


right
left
center
center_safearea
fullscreen
subleft
noscale
alignto640


Most of above alignments are comparable to their vertAlign counterparts: left will start at the left of your screen, requiring a positive .x, while right starts at the right, requiring a negative .x.

It is advisable to use either fullscreen/fullscreen for both, or use any other for both (so dont use fullscreen/left or bottom/fullscreen) unless you know what you are doing and know its intended effect. It is also advisable to not use fullscreen for any text, unless you want it to look goddamn ugly, and the same goes for any image you want to have placed somewhere while not being scaled unevenly.

[part about setvalue/settext/.label/setshader goes here]

[part about .sort and .foreground goes here]

[part about scaleovertime/fadeovertime+alpha goes here]

[part about destroying goes here]

[part with some practical examples with example images of usage of vertalign/horzalign goes here]

IzNoGoD
19th October 2013, 12:04
Feel free to write parts of this tutorial, i will then add them to the first post (or link them if you prefer) giving the proper credits.

Also, there are a few things in the tutorial i'm not entirely certain of. If you know any of these things, feel free to reply, will edit them and credit you.

pollo
12th September 2014, 18:19
Hey all, I know this is an old topic but I dont wan't to create a new topic for this question

Can you tell me guys what are these variables used for?

sort
archived

Eg.


level.hud.sort = 9999;
level.hud.archived=false;


There's no info about this on the inet!

Thanks in advance!

Pollo

IzNoGoD
12th September 2014, 19:12
sort is to indicate in what order the huds should be drawn (aka put before or behind other huds) (i dont know if higher number = foreground or lower number = foreground, try this yourself)

archived = visible on killcam yes/no. Also applies to spectator (if you set archived to false, spectators cant see that particular hud). Im uncertain what happens with a non-archived hud as a personal hud for a spec, archived only applies when you are speccing a player

guiismiti
15th September 2014, 06:44
So, I just made this today, it is a very basic hud based on eXtreme+ (only thing is, this is much more simple).
This is for displaying kill streak messages as images instead of strings with iprintlnBold.
I'm not sure if there are any errors, since it's the first hud I ever wrote, but it works fine.

I hope it helps anybody who's looking for help.

Do not forget to precache your shaders. Do it in Callback_StartGameType():


precacheShader("your_shader");




displayShader(shader, size, duration, attacker)
{
shaderhud = newClientHudElem(attacker);
shaderhud.vertAlign = "subtop";
shaderhud.horzAlign = "center";
shaderhud.x = size*(-0.5);
shaderhud.y = size*(0.5);

shaderhud.alpha = 0;
shaderhud setShader(shader, size, size);
shaderhud.alpha = 1;

wait(duration);

shaderhud fadeOverTime(1);
shaderhud.alpha = 0;
wait(1);

shaderhud destroy();
}


Where, naturally, shader is the name of your shader (string), size is the size, duration is the duration before it starts to fade and attacker is the player receiving the new hud.



added info that was here to first post - izno

guiismiti
22nd September 2014, 07:28
A couple useful information and another HUD example:

- These hud attributes will align your object to its origin position. I could have used it in the hud from my previous post, but I didn't know about it:
self.your_hud.alignx
which can be: "left", "center", "right"

self.your_hud.aligny
which can be: "top", "middle", "bottom"


And here's an example of a stopwatch. It gradually changes color from green (full time) to red (end time). Made by me, based on game type sd. Use thread to call this function, since there's a <duration> total wait time before it is finished.



gunshipStopwatch()
{
self endon("disconnect");
self endon("killed_player");

if(isdefined(self.hud_gunshipstopwatch))
self.hud_gunshipstopwatch destroy();

duration = 60;
increment = 0.5;

self.hud_gunshipstopwatch = newClientHudElem(self);
self.hud_gunshipstopwatch.x = 6;
self.hud_gunshipstopwatch.y = -24;
self.hud_gunshipstopwatch.horzAlign = "left";
self.hud_gunshipstopwatch.vertAlign = "middle";
self.hud_gunshipstopwatch setClock(duration, duration, "hudStopwatch", 48, 48);
self.hud_gunshipstopwatch.sort = 9999;

for(i = 1; i < duration + 1.01; i += increment)
{
gradientlevel = i / duration;
self.hud_gunshipstopwatch.color = (gradientlevel,(1 - gradientlevel),0);
wait(increment);
}

self.hud_gunshipstopwatch destroy();
}

pollo
22nd September 2014, 20:53
Hey :)

I had some trouble while setting the alpha value of some huds by using the "fadeovertime()" function (sometimes when 2 funcs change the alpha value of a same hud, it kinda "flashes", or become transparent/opaque in a short time), so I made this little one which seems to work fine.



//call the function like this:

//thread _alphahud(level.hud,true);

//hud = your hud
//show = boolean (true, hud fades to opaque; false, it fades to transparent)
//you can also add a time variable in the function to edit it at your own

_alphahud(hud,show)
{
self endon("disconnect");
for(i=0;i<20;i++)
{
if(show == true)
{
hud.alpha += 0.05;
if(hud.alpha == 1)
break;
}
else
{
hud.alpha -= 0.05;
if(hud.alpha == 0)
break;
}
wait .05;
}
return;
}

Tally
23rd September 2014, 08:30
Hey :)

I had some trouble while setting the alpha value of some huds by using the "fadeovertime()" function (sometimes when 2 funcs change the alpha value of a same hud, it kinda "flashes", or become transparent/opaque in a short time), so I made this little one which seems to work fine.



//call the function like this:

//thread _alphahud(level.hud,true);

//hud = your hud
//show = boolean (true, hud fades to opaque; false, it fades to transparent)
//you can also add a time variable in the function to edit it at your own

_alphahud(hud,show)
{
self endon("disconnect");
for(i=0;i<20;i++)
{
if(show == true)
{
hud.alpha += 0.05;
if(hud.alpha == 1)
break;
}
else
{
hud.alpha -= 0.05;
if(hud.alpha == 0)
break;
}
wait .05;
}
return;
}

As there is no loop function running continuously, do not use an endon(). This will add to the max number of script variables allowed, and is completely unnecessary unless you have a loop function which does more than count (as in the current method). Endon() is designed to kill continuous loops only. It will not end any other function such as a count.

Max script variables will crash a server, and is reached when the game counts ever single instance of endon(), waittill(), or wait(). Conserve your script variables at all costs. It is very easy to reach the ceiling with them.

IzNoGoD
23rd September 2014, 08:32
As there is no loop function running continuiously, do not use an endon(). This will add to the max number of script variables allowed, and is completely unnecessary unless you have a loop function which does more than count (as in the current method).

Max script variables will crash a server, and is reached when the game counts ever single instance of endon(), waittill(), or wait(). Conserve your script variables at all costs. It is very easy to reach the ceiling with them.

You either have to use endon or you have to check if self is defined, else you are trying to write to a client hud elem whilst the client is not defined anymore. The endon will be freed at the end of this loop, which only takes a second. I'd say, keep the endon.

Tally
23rd September 2014, 08:37
You either have to use endon or you have to check if self is defined, else you are trying to write to a client hud elem whilst the client is not defined anymore. The endon will be freed at the end of this loop, which only takes a second. I'd say, keep the endon.

In my opinion it is better to use a check for defined entity. An endon() will kill a for() count after the first loop through but if the player leaves the server before the 1st count is complete it is likely to throw an "undefined is not an entity" error anyway. I've had that happen to me many times. It would have been helpful if the endon() function killed for() counts during the loop, and not after it has run its course. But that is not the case.

I warn people about the mis-use of endon() functions as a matter of course because if you have a big mod it is very easy to hit the max number. The COD2 stock files already use a lot anyway. It doesn't give the modder much to play with.

IzNoGoD
23rd September 2014, 09:08
In my opinion it is better to use a check for defined entity. An endon() will kill a for() count after the first loop through but if the player leaves the server before the 1st count is complete it is likely to throw an "undefined is not an entity" error anyway. I've had that happen to me many times. It would have been helpful if the endon() function killed for() counts during the loop, and not after it has run its course. But that is not the case.

I warn people about the mis-use of endon() functions as a matter of course because if you have a big mod it is very easy to hit the max number. The COD2 stock files already use a lot anyway. It doesn't give the modder much to play with.

This might be the case in many other pieces of code, but as the wait is at the end of the loop, this will not happen, as cod2 runs on a frame-by-frame basis, and you cannot disconnect mid-frame.

Edit: above is true for checking for defined(), but not true for endon. Endon will work properly.

But like tally is saying, max variables might be an issue, especially on servers filled with players.

pollo
23rd September 2014, 14:35
Well I need to put an endon() because I have a func somewhere in my script which is continuously fading the hud (if players touch a trigger the hud starts to flash), so while I was testing I noticed that if a player disconnects when the hud func was running, the server crashed with the "undefined is not an entity" error.

But thank you for this advice, didn't know there was a max on that func. Atm there are like 10 endon in my script argh

guiismiti
24th March 2017, 17:16
Does anybody know if it is possible to change the position of the .label, from left to right?

IzNoGoD
24th March 2017, 17:24
Put a &&1 in your label.

hud.label = &"&&1 is on a killing spree";
hud setplayernamestring(player);

guiismiti
24th March 2017, 18:25
Looks like it works for player names only, not custom strings.
It will be useful for another purpose, though.

Anyway, I'm just gonna add a lot of spaces to the "Headshot:" string and add the "m" in the end, instead of adding "m" as a label of the distance value.

1303

IzNoGoD
24th March 2017, 18:37
You could also .value or setstring() it, but that didnt make sense given the stuff I typed in the example.

For your thing:

hud.label = &"Headshot: ^1&&1 ^7m";
hud.value = 36;

Might need to add that label as a localized string file, not sure if &&1 works without it. Try it i'd say.

guiismiti
24th March 2017, 19:08
I will try it later.

The other purpose I had with it was to print variable messages to everyone (iprintln):


iprintln(&"BATTLEFIELD_MONSTERKILL", player);


Localized string:


REFERENCE MONSTERKILL
LANG_ENGLISH "&&1 ^7- ^1MONSTER KILL !!!"
LANG_GERMAN "#same"
LANG_RUSSIAN "#same"


Edit #1: I posted because it worked without the need of setplayernamestring(player). I copied the way it is on meatbot.
Edit #2: I tried izno's suggestion and it seems to be the best thing to do because (1) I only need 1 hud and (2) it is now perfectly centered. Thank you very much one again.
Just one small corretion, change


hud.value = x;

for


hud setValue(x);


And here is the code for a simple hud for headshots.
1. You must create the localized string for the label:


REFERENCE HEADSHOT
LANG_ENGLISH "^7Headshot: ^1&&1 ^7m"

2. On player connect, set self.headshothudid to 0;
3. Call this function with the killer


displayHeadshot(distance, duration)
{
self.headshothudid += 1;
hudid = self.headshothudid;

if(isdefined(self.headshothud))
{
self.headshothud destroy();
}

self.headshothud = newClientHudElem(self);
self.headshothud.vertAlign = "top";
self.headshothud.horzAlign = "center";
self.headshothud.alignx = "center";
self.headshothud.y = 120;
self.headshothud setValue(distance);
self.headshothud.label = &"FILENAME_HEADSHOT";
self.headshothud.fontScale = 1.4;
self.headshothud.alpha = 1;
self.headshothud.sort = 5000;
self.headshothud.color = (1, 1, 1);

wait(duration);

// if the player performed another headshot, there will be a new hud, which should not be affected/deleted by the old hud function
if(self.headshothudid == hudid)
{
self.headshothud fadeOverTime(1);
self.headshothud.alpha = 0;

wait(1);

if(self.headshothudid == hudid)
{
if(isdefined(self.headshothud))
{
self.headshothud destroy();
}

self.headshothudid = 0;
}
}
}

Where FILENAME is the name of your localized strings file; duration is the duration of the hud (not counting the 1 second for fading); and distance(in meters) can be given as


distance = int(((distance(attacker.origin , self.origin)) * 0.0254) + 0.5);

(the +0.5 is because int will round, for example, 3.7 to 3, and I'd rather have it rounding it to the nearest int)

serthy
25th March 2017, 08:20
If you have libcod, you can also use Izno's unprecached setText() patch

IzNoGoD
25th March 2017, 21:54
Simpler version:



headshot(meters)
{
self notify("headshot");
self endon("headshot");
self endon("disconnect");
if(isdefined(self.headshothud))
self.headshothud destroy();

self.headshothud = newclienthudelem(self);
//set other stuff like .x etc here

self.headshothud.label = &"yourshitgoeshere";
self.headshothud setvalue(meters);
self.headshothud.alpha = 1;
wait 3;
self.headshothud.fadeovertime(3);
self.headshothud.alpha = 0;
wait 3;
self.headshothud destroy();
}

guiismiti
25th March 2017, 23:25
Change hud.fadeovertime for hud fadeovertime and it works fine.