Digital Speedometer (code inside)
by Tim Heldna · in Torque Game Engine · 02/11/2007 (10:55 pm) · 17 replies
Here's a free digital speedometer for anyone who wants it. It will display speed in km/h if you are mounted in a vehicle. It works across a network.
- First download the bitmaps required by the display:
speedHud.zip (31 KB)
- Once downloaded, extract to "starter.fps/client/ui" (keep bitmaps inside the speedHUD folder, the full path to the bitmaps should be "starter.fps/client/ui/speedHUD/")
- Add this to the Armor::onAdd method inside of player.cs
- Add this to the Armor::onDisabled method inside of player.cs
- Add this to server/scripts/commands.cs
- Add this to client/scripts/client.cs
- Add this to the bottom of your playGui.gui file, before the last closing brace
That's it. Run your game, mount your vehicle and test it out.
Here's a video of it in action (heavily compressed, sorry about the poor quality)
speedHudVideo.zip (2.8 MB)

- First download the bitmaps required by the display:
speedHud.zip (31 KB)
- Once downloaded, extract to "starter.fps/client/ui" (keep bitmaps inside the speedHUD folder, the full path to the bitmaps should be "starter.fps/client/ui/speedHUD/")
- Add this to the Armor::onAdd method inside of player.cs
// Digital Speedometer GUI %obj.getSpeed(%obj); // Digital Speedometer GUI
- Add this to the Armor::onDisabled method inside of player.cs
// Digital Speedometer GUI cancel(%obj.speedHUDSchedule); // Digital Speedometer GUI
- Add this to server/scripts/commands.cs
//-----------------------------------------------------------------------------
// Digital Speedometer (Server)
//-----------------------------------------------------------------------------
function Player::getSpeed(%player)
{
%player.speedHUDSchedule = %player.schedule(100, getSpeed, %player);
if(%player.getControlObject())
{
commandToClient(%player.client, 'displaySpeedHUD', true);
commandToClient(%player.client, 'getSpeed');
}
else
commandToClient(%player.client, 'displaySpeedHUD', false);
}
//------------------------------------------------------------------------------ Add this to client/scripts/client.cs
//-----------------------------------------------------------------------------
// Digital Speedometer (Client)
//-----------------------------------------------------------------------------
function clientCmdGetSpeed()
{
%texPath = "starter.fps/client/ui/speedHUD/";
%vel = getControlObjectSpeed();
// convert from m/s to km/h
%cVel = mFloor(%vel * 3.6); // m/s * (3600/1000) = km/h
if(%cVel <= 9)
{
digit_1.setBitmap(%texPath @ "0");
digit_2.setBitmap(%texPath @ "0");
digit_3.setBitmap(%texPath @ %cVel);
}
if(%cVel >= 10)
{
%speed1 = getSubStr(%cVel, 0, 1);
%speed2 = getSubStr(%cVel, 1, 1);
digit_1.setBitmap(%texPath @ "0");
digit_2.setBitmap(%texPath @ %speed1);
digit_3.setBitmap(%texPath @ %speed2);
}
if(%cVel >= 100)
{
%speed3 = getSubStr(%cVel, 2, 1);
digit_1.setBitmap(%texPath @ %speed1);
digit_2.setBitmap(%texPath @ %speed2);
digit_3.setBitmap(%texPath @ %speed3);
}
}
function clientCmdDisplaySpeedHUD(%bool)
{
digitalSpeedHUD.setVisible(%bool);
}
//------------------------------------------------------------------------------ Add this to the bottom of your playGui.gui file, before the last closing brace
new GuiControl(digitalSpeedHUD) {
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "top";
position = "60 676";
Extent = "56 36";
MinExtent = "8 2";
Visible = "1";
new GuiClockHud(background) {
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "0 0";
Extent = "56 36";
MinExtent = "8 2";
Visible = "1";
showFill = "1";
showFrame = "1";
fillColor = "0 0 0 1";
frameColor = "1 1 1 1";
textColor = "0 0 0 0";
};
new GuiBitmapCtrl(digit_1) {
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "2 2";
Extent = "16 32";
MinExtent = "8 2";
Visible = "1";
bitmap = "./speedHUD/0";
wrap = "0";
};
new GuiBitmapCtrl(digit_2) {
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "20 2";
Extent = "16 32";
MinExtent = "8 2";
Visible = "1";
bitmap = "./speedHUD/0";
wrap = "0";
};
new GuiBitmapCtrl(digit_3) {
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "38 2";
Extent = "16 32";
MinExtent = "8 2";
Visible = "1";
bitmap = "./speedHUD/0";
wrap = "0";
};
};That's it. Run your game, mount your vehicle and test it out.
Here's a video of it in action (heavily compressed, sorry about the poor quality)
speedHudVideo.zip (2.8 MB)

About the author
#2
02/12/2007 (12:02 am)
Just a small note. None of this needs to be serverside code. It can all be on the client end and not using up bandwidth and server cpu.
#3
But it is very clean.
EDIT: Or do you mean there is a way to do this clientside only
02/12/2007 (2:20 am)
Zod, It does use a little. You're making a server call (to getSpeed in commands.cs). But it is very clean.
EDIT: Or do you mean there is a way to do this clientside only
#4
Right.
My numbers are probably a bit off, but it is close.
That's 116 kb / s (32 players within scope). I would not confuse that with "little" :)
02/12/2007 (4:18 am)
Quote:
Zod, It does use a little
Right.
UDP Header (64 bits[b]?[/b]) Protocol Packet Flag (1 bit) NetEvent Flag (1 bit) NetEvent Flag (1 bit) NetEvent Flag (1 bit) NetEvent Sequence (7 bits) NetEvent Class ID (32 bits) NetEvent Flag (1 bit) RPC CommandArgs (5 bits) Initial String (2048 bits) / Subsequent Tags (32 bits[b]?[/b])
My numbers are probably a bit off, but it is close.
That's 116 kb / s (32 players within scope). I would not confuse that with "little" :)
#5
Depending your need, one thing you can is lower the sceduling time to only updating every 500ms.
02/12/2007 (5:41 am)
Didn't know it was that much... Stefan, do you know of a better way with less network traffic?Depending your need, one thing you can is lower the sceduling time to only updating every 500ms.
#6
setBitmap () is also quite heavy for its use above, because it loads the bitmap over and over again (I am pretty sure that it does) and does not cache it like you would with a TextureHandle in C++.
I would use a regular GUI control (look at GuiSpeedometer or GuiHealthBarHud, they are all client side) but if you really want to use the above scripts then I would modify them, to cut down the amount of updates to only 2 once, instead of 10 every second.
Armor::onAdd method inside of player.cs, modify:
Add this to the Armor::onDisabled method inside of player.cs, modify:
I have not tested this as I use guiControls, but it should work and give the exact same results as the original script above.
Edit: You could even set the schedule time to 32 ms if you like, because it still won't send any data across the network, but the excessive calls to setBitmap () would probably stop you there, though I'm not sure.
02/12/2007 (6:24 am)
There should be no network traffic (at all) for a hud, because the client already knows what speed the control object is travelling at. No need to ask the server. I am even not sure what the server is doing in the above scripts.setBitmap () is also quite heavy for its use above, because it loads the bitmap over and over again (I am pretty sure that it does) and does not cache it like you would with a TextureHandle in C++.
I would use a regular GUI control (look at GuiSpeedometer or GuiHealthBarHud, they are all client side) but if you really want to use the above scripts then I would modify them, to cut down the amount of updates to only 2 once, instead of 10 every second.
Armor::onAdd method inside of player.cs, modify:
commandToClient(%obj.client, 'getSpeed', true);
Add this to the Armor::onDisabled method inside of player.cs, modify:
commandToClient(%obj.client, 'getSpeed', false);
function clientCmdGetSpeed(%bool)
{
digitalSpeedHUD.setVisible(%bool);
if (!%bool)
{
cancel ($speedHUDSchedule);
return;
}
%texPath = "starter.fps/client/ui/speedHUD/";
%vel = getControlObjectSpeed();
%cVel = mFloor(%vel * 3.6);
if(%cVel <= 9)
{
digit_1.setBitmap(%texPath @ "0");
digit_2.setBitmap(%texPath @ "0");
digit_3.setBitmap(%texPath @ %cVel);
}
if(%cVel >= 10)
{
%speed1 = getSubStr(%cVel, 0, 1);
%speed2 = getSubStr(%cVel, 1, 1);
digit_1.setBitmap(%texPath @ "0");
digit_2.setBitmap(%texPath @ %speed1);
digit_3.setBitmap(%texPath @ %speed2);
}
if(%cVel >= 100)
{
%speed3 = getSubStr(%cVel, 2, 1);
digit_1.setBitmap(%texPath @ %speed1);
digit_2.setBitmap(%texPath @ %speed2);
digit_3.setBitmap(%texPath @ %speed3);
}
$speedHudSchedule = schedule(100, "clientCmdGetSpeed", true);
}I have not tested this as I use guiControls, but it should work and give the exact same results as the original script above.
Edit: You could even set the schedule time to 32 ms if you like, because it still won't send any data across the network, but the excessive calls to setBitmap () would probably stop you there, though I'm not sure.
#7
The reason some code is on the server is to initiate the loop on player spawn and stop it on death. Also, one of the commandToClient's is only called if the player is mounted in a vehicle.
Having said that, I see now some areas where I can cut things down (thanks Stefan), I'll take a look at what I can do and repost.
02/12/2007 (4:01 pm)
Instead of using bitmaps you could use a font (guiFontControl). I choose bitmaps as they look nicer and the bitmaps are extremely small (32 x 64 @ 3kb per file).The reason some code is on the server is to initiate the loop on player spawn and stop it on death. Also, one of the commandToClient's is only called if the player is mounted in a vehicle.
Having said that, I see now some areas where I can cut things down (thanks Stefan), I'll take a look at what I can do and repost.
#8
So, when the control is pushed, it starts the ball rolling, and when it's popped, it stops.
*Edit - named the main function wrong, corrected.
02/12/2007 (7:47 pm)
How I currently do it plus your digits (nice touch btw)function digitalSpeedHUD::onWake(%this)
{
%this.updateSpeed();
}
function digitalSpeedHUD::onSleep(%this)
{
if( isEventPending( %this.speedCheck ) )
{
cancel (%this.speedCheck );
%this.speedCheck = "";
}
}
function digitalSpeedHUD::updateSpeed(%this)
{
if ( isEventPending( %this.speedCheck ) )
cancel(%this.speedCheck);
%texPath = "starter.fps/client/ui/speedHUD/";
%vel = getControlObjectSpeed();
%cVel = mFloor(%vel * 3.6); // m/s * (3600/1000) = km/h
if(%cVel <= 9)
{
digit_1.setBitmap(%texPath @ "0");
digit_2.setBitmap(%texPath @ "0");
digit_3.setBitmap(%texPath @ %cVel);
}
if(%cVel >= 10)
{
%speed1 = getSubStr(%cVel, 0, 1);
%speed2 = getSubStr(%cVel, 1, 1);
digit_1.setBitmap(%texPath @ "0");
digit_2.setBitmap(%texPath @ %speed1);
digit_3.setBitmap(%texPath @ %speed2);
}
if(%cVel >= 100)
{
%speed3 = getSubStr(%cVel, 2, 1);
digit_1.setBitmap(%texPath @ %speed1);
digit_2.setBitmap(%texPath @ %speed2);
digit_3.setBitmap(%texPath @ %speed3);
}
%this.speedCheck = %this.schedule(100, "updateSpeed");
}So, when the control is pushed, it starts the ball rolling, and when it's popped, it stops.
*Edit - named the main function wrong, corrected.
#9
All you would have to do is place a call in the Armor::onMount method to push the GUI and another call in the Armor::onUnmount method to pop the GUI. The function only loops when you're in a vehicle and stops looping on dismount, I like.
RE the bitmaps, they only need to be loaded into memory once (contradictory to what Stefan said). So using the setBitmap function isn't really a problem when you consider there are only 10 bitmaps in total and they are extremely small in resolution & file size. If it really bothers you it's extremely easy to use a font GUI in lieu of bitmaps.
I'm going to post this as a resource, I'll give credit where it's due.
02/12/2007 (8:35 pm)
That's a good solution Zod, thanks for sharing. All you would have to do is place a call in the Armor::onMount method to push the GUI and another call in the Armor::onUnmount method to pop the GUI. The function only loops when you're in a vehicle and stops looping on dismount, I like.
RE the bitmaps, they only need to be loaded into memory once (contradictory to what Stefan said). So using the setBitmap function isn't really a problem when you consider there are only 10 bitmaps in total and they are extremely small in resolution & file size. If it really bothers you it's extremely easy to use a font GUI in lieu of bitmaps.
I'm going to post this as a resource, I'll give credit where it's due.
#10
Digital Speedometer
I did things slightly differently than you Zod however it performs just the same.
02/12/2007 (9:44 pm)
Here's the resource:Digital Speedometer
I did things slightly differently than you Zod however it performs just the same.
#11
Perhaps you should actually check next time because your statement is incorrect. setBitmap () manually loads the resource even if it has been used before, then resizeParent () and setUpdate () before it renders again.
02/13/2007 (1:48 am)
Quote:
RE the bitmaps, they only need to be loaded into memory once (contradictory to what Stefan said).
Perhaps you should actually check next time because your statement is incorrect. setBitmap () manually loads the resource even if it has been used before, then resizeParent () and setUpdate () before it renders again.
#12
02/13/2007 (8:53 am)
I'm pretty sure it won't resize the control and parent unless you tell it too as a second bool value in the function call to setbitmap(texture, resize). As for it not clearing the last texture from memory before rendering the new one, I do not know.
#13
When you change bitmap, the extent of the control might change, and a parent resize is useful if your control is larger than its parent, in which case the parent will be resized to be able to contain its child (the control).
I am just pointing out eventual flaws with the above approach, no biggie.
02/13/2007 (9:38 am)
You're talking about a manual resize, which is not the same thing. When you change bitmap, the extent of the control might change, and a parent resize is useful if your control is larger than its parent, in which case the parent will be resized to be able to contain its child (the control).
I am just pointing out eventual flaws with the above approach, no biggie.
#14
I now see I misinterpreted what you were saying there.
02/13/2007 (3:54 pm)
Once setBitmap loads a texture into memory, it uses that texture (from memory) without loading it into memory again. That's all I was saying, my comment was in response to this:setBitmap () is also quite heavy for its use above, because it loads the bitmap over and over again
I now see I misinterpreted what you were saying there.
#15
I converted the bitmaps to a font and updated the resource accordingly (font downloadable from resource page).
It looks just as nice and the code has been cut back due to not having to set 3 bitmaps.
02/14/2007 (1:10 am)
Ahh curse you Stefan, you made me paranoid about using bitmaps! :PI converted the bitmaps to a font and updated the resource accordingly (font downloadable from resource page).
It looks just as nice and the code has been cut back due to not having to set 3 bitmaps.
#16
Btw, why not use a GuiControl in C++? The only tricky thing would be the bitmaps, but that's fun.
02/14/2007 (2:55 am)
There are two overloaded functions for setBitmap, one that loads the texture from a filename, and another which swaps the handle. The former is the one which is used when calling setBitmap () from script, while the other one is used within C++ when use give it a handle instead of a filename.Btw, why not use a GuiControl in C++? The only tricky thing would be the bitmaps, but that's fun.
#17
I will be, just felt like donating a scripted version for non SDK owners.
02/14/2007 (3:27 pm)
Quote:
Btw, why not use a GuiControl in C++?
I will be, just felt like donating a scripted version for non SDK owners.
Torque Owner Juha Eerola