Game Development Community

dev|Pro Game Development Curriculum

GuiStatusCtrl - *Updated* Display Field Name, Values, Status Bar for any field or method on any named object. TGEA and (new! TGE 1.52)

by Jaimi McEntire · 02/04/2009 (2:15 pm) · 44 comments

GuiStatusCtrl

Displays Field Name and/or value and/or bar graph for any field or method of any named object, using TorqueML for customization.

The GuiStatusCtrl is a control that will monitor a field or method on any named object. This makes it easy to make status screens, etc. Also included is a GuiStatusController - a container that controls GuiStatusCtrls (more later on why this is useful).
The control can display the field name, value and the percentage of value as a progress bar. The field name and value can use TorqueML to customize the font, size, shadows, alignment, etc.

To use the GuiStatusCtrl, add the following files to your TGEA 1.7+ project, and recompile:

For TGEA 1.7+
Download Source Files for TGEA 1.7+

for TGE 1.5.2
Download Source Files for TGE 1.5.2

The GuiStatusCtrl has the following fields:

ObjectName:         This is the name of the object that you want to monitor
  FieldName:          This is the field that you want to monitor. New: Can also be a method on the class
  MaxFieldName:       If displaying a status bar, this is the field (or method) that holds the maximum value
  IgnoreController:   If set, this control will be ignored by any GuiStatusController objects
  FieldNameMLFormat:  The TorqueML that is used to display the Field's Name
  FieldValueMLFormat: The TorqueML that is used to display the Field's Value
  DisplayFieldName:   If true, then the field name is displayed to the left
  DisplayFieldValue:  If true, then the field value is displayed.
  DisplayBar:         If true, then a bar is displayed. The size is proportional to field/maxfield
  BarColor:           The color of the status bar.
  Label:              New: An alternate label to use instead of the FieldName.

The GuiStatusController is a container that is used to control any child GuiStatusCtrl objects. Lets say you have 25 GuiStatusCtrl controls all pointing to "PlayerBob", and you want them to show "PlayerJane" instead. Instead of manually coding the change to all 25 child controls, you instead just set the GuiStatusController ObjectName field, and it will update the child controls for you.

The GuiStatusController has all the fields that a GuiContainer has, with the addition of:

ObjectName:           The name of the object to monitor.

If you do not wish for a child control to be changed by the controller, then set the "IgnoreController" field on the child.

Example: Lets create three named objects for our fictional RPG "Supergalactic Megawars" - put this in a test.cs file, and then execute it:
new SimObject("MainPlayer");
new SimObject("PartyPlayer");
new SimObject("GameData");

GameData.GameName = "Supergalactic Megawars";
GameData.LevelName = "Orion Quadrant";

MainPlayer.PlayerName = "Big Bob";
MainPlayer.Strength = 18;
MainPlayer.Vitality = 20;
MainPlayer.Intellect = 30;
MainPlayer.Health = 50;
MainPlayer.MaxHealth = 65;

PartyPlayer.PlayerName = "Sue (female)"; // differentiate from any boys named Sue
PartyPlayer.Strength = 22;
PartyPlayer.Vitality = 18;
PartyPlayer.Intellect = 24;
PartyPlayer.Health = 20;
PartyPlayer.MaxHealth = 65;


Now, create a new gui, and put a GuiStatusController on it. Size it to hold 8 GuiStatusCtrl objects, and create and place the status controls in the controller.
Set the ObjectName on the top 2 to "GameData", and the field on the first one to "GameName" and the field on the second one to "LevelName". You should see the status control display the field names and values by default. Set "IgnoreController" to true for both controls. (you can also set the ML properties to apply formatting to the field name and value).

For the remainders, set the ObjectName to "MainPlayer", and the fieldnames to "PlayerName","Strength", "Vitality", "intellect", and "Health". On the Health Control, set "DisplayBar" to true, and set the MaxFieldName to "MaxHealth". You should now see all of the values displayed, and a proportional bar for health. (You can disable the field name and value on health if you don't want them printed).

To see how the controller works, set the Controller.ObjectName parameter to "PartyPlayer". now you are viewing her stats instead.

What's New

You can now use methods as well as field names. For example, to display "Health" on a player, you can add the following method to the Player.cs:
function Player::GetHealth(%this)
{
   return %this.dataBlock.MaxDamage  - (%this.dataBlock.MaxDamage * %this.getDamagePercent());
}

function Player::GetMaxHealth(%this)
{
   return %this.dataBlock.MaxDamage;
}

Now, instead of Field Names, use "GetHealth" for the Value and "GetMaxHealth" for the Max Value. Use the new "Label field" to override the display for the field name. (note: your Target object needs to be a "Player" for this to work. Or actually, any ShapeBase descended object)

Update
Here's a slightly more complicated version of the GetHealth function above, that shows how you can manipulate the control as part of the status function - we update the color and label that display based on how hurt the player is:

function Player::GetHealth(%this)
{
   %perc = %this.getDamagePercent();
   if (%perc < 0.25)
   {
      %newlabel = "Invincible";
      %BarColor = "128 0 0 192";
   }
   else if (%perc < 0.50)
   {
      %newlabel = "But a scratch";
      %BarColor = "160 0 0 192";
   }
   else if (%perc < 0.75)
   {
      %newlabel = "Just a flesh wound";
      %BarColor = "192 0 0 192";
   }
   else
   {
      // Careful, he'll bite your legs off.
      %newlabel = "Calling it a draw";
      %BarColor = "255 0 0 192";
   }
   MyHealthStatus.Label = %newlabel;
   MyHealthStatus.BarColor = %BarColor;
   return mFloor(0.99 + (%this.dataBlock.MaxDamage  - (%this.dataBlock.MaxDamage * %this.getDamagePercent())));
}


Sample Pic:
www.aztica.com/images/guistatusctrl.jpg
#21
02/09/2009 (6:58 pm)
I have to try this out, great resource!!!
Kudos for you for sharing with everyone.
#22
02/12/2009 (6:12 am)
Another great resource Jaimi!
#23
02/16/2009 (8:36 pm)
Great work :D i love it!
#24
02/20/2009 (7:21 pm)
Jami,thanks for the great resource.

BTW, according to your explanation, when we change the objectName in GuiStatusController, then it have to change all the children GuiStatusCtrls' objectName field. But I'm afraid it doesn't for me.

I took a look into GuiStatusController::onStaticModified(). And I found it is never called even when objectName field value is changed. As you made it overloaded, not using existing virtual method, I suppose you added this method somewhere in the simBase.cc. But as you didn't mentioned it, I'm wondering what's the problem with me.

Could you let me know if I missed something?
Edit: tested with TGE 1.5.2
#25
02/21/2009 (1:37 am)
@Jamin, instead of waiting for the answer, I changed existing TGE 1.5.2 onStaticModified method to accept two parameters, which was quite simple and make this resource work properly.

Thanks again for your contribution.
#26
03/04/2009 (6:26 am)
@Game4Rest - Sorry, this is apparently a difference between TGEA and TGE, changing the method is correct, it is supposed to be a simple override.
#27
04/03/2009 (11:25 am)
This is going straight into my RPG project, nice functionality there, kudos! (TGE 1.5.2)
#28
04/12/2009 (5:46 pm)
Great Resource. How did you get the drop shadow on the font in the sample pic?
#29
04/17/2009 (4:05 pm)
i can see a use for this in my project. will have to check it out. Thanks for sharing
#30
05/01/2009 (1:27 pm)
@Rob - It's also a GuiMLtextCtrl, so you can use TorqueML:

<shadow:x:y> Add a shadow to the text, displaced by (x, y).
<shadowcolor:RRGGBBAA> Sets the color of the text shadow.

For example: "<shadow:1:1><shadowcolor:000000FF>Health"

tdn.garagegames.com/wiki/GUI/TorqueML
#31
05/01/2009 (9:34 pm)
Ahhh. thanks for the info, Jaimi. Now I understand the earlier comment on TorqueML.

And just as I'm starting to look at Torque 3d beta. Now I have to look at what I can do there!
#32
06/13/2009 (8:04 am)
Just a quick note that the download links given are using an expired domain. I can't seem to download either file.

Can someone upload them somewhere for us to download or email the files to me and I'll put them up on my webserver and post links here. (Get my email from my profile.)

Much appreciated!

--Edit--
It seems I was pretty excited by this resource, enough to have downloaded the TGE 1.5.2 version prior to the domain expiring. I've uploaded it here. If anyone's got the TGEA version and can send it to me, I'll add it as well.

Cheers!
Great resource by-the-way!
#33
06/16/2009 (6:07 am)
I just forgot to renew the domain. It should be back up and running now.
#34
06/30/2009 (8:47 am)
Jaimi

This is exactly what I've been looking for.

Works in AFX for 1.5.2.

I got all the values in and they display the variables entered in test.cs.
#35
06/30/2009 (9:10 pm)
Jaimi

Here are some settings from my "test.cs" file:
MainPlayer.PlayerName = "a guy";  
 MainPlayer.Strength = 18;  
 MainPlayer.Vitality = 20;  
 
  MainPlayer.Health = GetHealth();  
 MainPlayer.MaxHealth = GetMaxHealth();

And here is what I have in my player.cs file:

function Player::GetHealth(%this) {
	return %this.dataBlock.MaxDamage  - (%this.dataBlock.MaxDamage * %this.getDamagePercent());  
}

and it's not working. The Gui pulls the static variables : (vitality, strength, etc.) but the functions are not returning anything, apparently, because the space is blank where the value should be in game.


Which leads me to my question: how could the GetHealth() function be accessing a GetHealth function which exists only the Player namespace? I'm confused.

Can someone shed some light on this? It doesn't look like it should work, and it doesn't, but maybe there's something I'm missing.

This will also help me gain some insight on how to access functions within namespaces like Player and GameConnection when you are not currently operating within those namespaces.

#36
07/01/2009 (7:44 am)
Okay - it's simpler than this. You don't have to assign the health and the max health, it will call the function on your player object at runtime to evaluate it.

When you create the health status control, set the ObjectName to "MainPlayer", and the FieldName to "GetHealth", and the label to "Health". For the MaxHealth status control, it's similar.


What's happening is that in the C++ internally, it checks to see if the field is a Method, and if so it evaluates the method using Con::executef

if (mTarget->isMethod(this->mStatusFieldName))
   {
     SetStatusValue(Con::executef(mTarget,1,mStatusFieldName));
   }

This version of executef will call a member function on the target type.


#37
07/01/2009 (7:28 pm)
I've updated the downloads above with some bug fixes and enhancements

Update TGE: Fixed bug as found by game4rest in TGE 1.5.2 version
Update TGEA: Added additional checks to prevent reflowing the ML when non-ML items changed.
#38
07/04/2009 (12:59 pm)
This resource works great for me on T3D, Beta 3 with one small change.

Use the TGEA 1.7+ source code provided. At the top of GuiStatusCtrl.cpp, do the following:
.
.
#include "gui/core/guiDefaultControlRender.h"
#include "gfx/gfxDrawUtil.h"  // <== insert this line to fix compile error
#39
09/20/2009 (9:59 am)
Ommm Domain broken agian :)
#40
09/20/2009 (1:37 pm)
I confirm I can't get the updated resource.

In case some are interested, I confirm that last sources was working in T3D beta 5.
I will test the updated ones when available.