Game Development Community

Text Control Linked to Player Health and Energy

by John Eckhardt · 06/18/2009 (8:47 pm) · 6 comments

Introduction:
Old games, new games and middle-aged games all have numbers for their health levels. So it's time for Torque Game Engine to have the same. This is a guiTextControl with the added twist that you can assign it to sync to a player-specific variable, like their health or their energy for starters.

Engine:
Developed and tested in TGE 1.5.2. I don't have TGEA yet, much less T3D. I hope that one day I can get one of those engines, or that somebody in the meantime will give me their old, unused, dusty copy. I don't know if there's any difference in the architecture as far as GuiTextCtrls or GameConnection methods go, but those would be the places that would cause problems if they're different.

Network Considerations:
This is the real reason I decided to create this control. If you have a variable tracking points or health or energy or some other ingame thing that changes constantly, you would have to schedule a script event for every 100ms or so. Depending on how you kept track of the variable, you might even have the server call a client side function for each client, and when you add up the clients, that's a lot of extra data traffic. At best, you call the update every time the value changes (e.g. onDamaged), and for something that changes a lot, that adds up.

With this, you already have the variables in place and they're being mirrored over to the client already through the engine. So it puts the client to work to simply display them as a text control. Now, I am no expert in network code, but it would seem that this implementation would be much faster and more efficient.

The Code:

  1. Add this as guiVarTextCtrl.cc to the engine project.
  2. //-----------------------------------------------------------------------------
    // Torque Game Engine
    // Copyright (C) GarageGames.com, Inc.
    // guiVarTextCtrl.cc copyright John Eckhardt, written June, 2009
    //-----------------------------------------------------------------------------
    
    #include "gui/controls/guiTextCtrl.h"
    #include "game/gameConnection.h"
    #include "game/shapeBase.h"
    
    
    class GuiVarTextCtrl : public GuiTextCtrl
    {
    
       typedef GuiTextCtrl Parent;
    
    
    public:
       GuiVarTextCtrl();
    
       void onPreRender();
       
        enum dataOptions
        {
    		health,		///< the player's health
    		energy		///< the player's energy
        };
        S32 mVariable;	// what stat we're checking to display on the bar.
    
    	static void initPersistFields();
    	DECLARE_CONOBJECT(GuiVarTextCtrl);
    };
    
    
    
    //-----------------------------------------------------------------------------
    
    IMPLEMENT_CONOBJECT( GuiVarTextCtrl );
    
    GuiVarTextCtrl::GuiVarTextCtrl()
    {
       mVariable = health;
    }
    
    
    static EnumTable::Enums dataEnums[] =
    {
    	{ GuiVarTextCtrl::health, "Health"  },
    	{ GuiVarTextCtrl::energy, "Energy"  }
    };
    static EnumTable gDataLocTable(2, &dataEnums[0]);
    
    
    
    void GuiVarTextCtrl::initPersistFields()
    {
       Parent::initPersistFields();
    
       addGroup("Misc");
    	   addField("data", TypeEnum, Offset( mVariable, GuiVarTextCtrl), 1, &gDataLocTable);
       endGroup("Misc");
    }
    
    
    
    //------------------------------------------------------------------------------
    void GuiVarTextCtrl::onPreRender()
    {
       
       GameConnection* conn = GameConnection::getConnectionToServer();
       if (!conn)
          return;
       ShapeBase* control = conn->getControlObject();
       if (!control || !(control->getType() & PlayerObjectType))
          return;
       
       S32 val;
    
       //switch between health and energy, and get the right variable
       switch (mVariable) 
       {
       case health:
    	   val = (S32)(control->getDamageMax() - control->getDamageNum());
    	   break;
       case energy:
    	   val = (S32)(control->getEnergyLevel());
    	   break;
       default:
    	   val = 35505;   
       }
    
       //convert the s32 to something more texty
       char buf[256];   
       dSprintf(buf,sizeof(buf), "%d", val);   
       setText(buf);
    
    }

  3. Define these two function in shapeBase.h right above getDamageValue():
  4. F32 getDamageNum() { return mDamage; }
       F32 getDamageMax();
    
       /// Returns the damage percentage.
       ///
       /// @return Damage factor, between 0.0 - 1.0
       F32  getDamageValue();

  5. In shapeBase.cc directly below getDamageValue() declare this function:
  6. F32 ShapeBase::getDamageValue()
    {
       // Return a 0-1 damage value.
       return mDamage / mDataBlock->maxDamage;
    }
    
    F32 ShapeBase::getDamageMax()
    {
    	return mDataBlock->maxDamage;
    }
  7. Compile, add a GuiVarTextCtrl to your playGui, get hurt and watch your lucky number!


Next Steps:
I added an option dropdown box so that you can easily switch back and forth between energy and health. You can extend it in the engine to keep track of whatever other variables you're using in your game, and add new fields in the enum and use it for everything!

In my game, you're going to have gold which will be displayed on the main playGui, the shopgui, the pause menu and many other places. It will be increasing at a constant rate in multiplayer, it will go up when you do damage to enemies, it will go up when you get a treasure chest. So I didn't want to keep track of 3 different locations that I had to update. Now I can drop one of these in and forget about it.



#1
06/18/2009 (8:51 pm)
nice, thanks!.
#2
06/18/2009 (8:57 pm)
If you're interested, here is a similar control that can be formatted, display values (with a label), etc.

www.garagegames.com/community/resources/view/16287
#3
06/18/2009 (9:11 pm)
Ah, where was that control a few days ago...
#4
06/18/2009 (9:43 pm)
Cool, nice to see someone else's take on numerical HUDs. Believe it or not but there are already several such resources -- although this one seems to be nice, clean, and simple :D So good job for that.

I've found Jaimi's resource to be a godsend on many levels, but my personal favorite was Plastic Gem #41 (just because it works in TGE, TGEa, and Torque 3D)
#5
06/19/2009 (1:59 pm)
Hey, those are cool other resources there, but neither of them would easily fit what I'm trying to do. That's a relief, I thought I did all that two hours of work for nothing. :P
#6
09/18/2009 (11:57 pm)
update for tgea 1.8.1

replace this:
GameConnection* conn = GameConnection::getConnectionToServer();
   if (!conn)
      return;
   ShapeBase* control = conn->getControlObject();
   if (!control || !(control->getType() & PlayerObjectType))
      return;

to

// Must have a connection and player control object
   GameConnection* conn = GameConnection::getConnectionToServer();
   if (!conn)
      return;
   ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
   if (!control || !(control->getType() & PlayerObjectType))
      return;

it's work.
thx