Game Development Community

GuiGenericBarHud code

by Demolishun · in Torque Game Engine · 12/11/2005 (3:17 pm) · 15 replies

A couple of screenshots:
demolishun.com/images/GuiGenericBarHud1.jpgdemolishun.com/images/GuiGenericBarHud2.jpg
I made this Hud changable from script with two commands:
setValue
getValue

You can access the other parameters of the normal GuiHealthBarHud.
The reason I made this is to have a Generic bar that can be changed via script in both the game and in a regular Gui.

Would anyone like the code? I can post it, or if I get time, create a resource.

About the author

I love programming, I love programming things that go click, whirr, boom. For organized T3D Links visit: http://demolishun.com/?page_id=67


#1
12/11/2005 (3:49 pm)
I'd love to see the code to that. It would come in very handy for my game...
#2
12/11/2005 (4:00 pm)
Now you need FillTo(F32), DrainTo(F32), and SetFlash(bool)

;)
#3
12/11/2005 (4:08 pm)
1st half:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "dgl/dgl.h"
#include "gui/guiControl.h"
#include "console/consoleTypes.h"
#include "game/gameConnection.h"
#include "game/shapeBase.h"

//-----------------------------------------------------------------------------
/// A basic Generic bar control.
/// The gui can be set to pulse if the value
/// drops below a set value. 
class GuiGenericBarHud : public GuiControl
{
   typedef GuiControl Parent;

   bool     mShowFrame;
   bool     mShowFill;
   bool     mDisplayEnergy;
   bool     mFlipped;

   ColorF   mFillColor;
   ColorF   mFrameColor;
   ColorF   mDamageFillColor;

   S32      mPulseRate;
   F32      mPulseThreshold;

   F32      mValue;

public:
   GuiGenericBarHud();
   
   void setValue(F32 value){mValue = value;};
   F32 getValue(){return mValue;};

   void onRender( Point2I, const RectI &);
   static void initPersistFields();
   DECLARE_CONOBJECT( GuiGenericBarHud );
};


//-----------------------------------------------------------------------------

IMPLEMENT_CONOBJECT( GuiGenericBarHud );

GuiGenericBarHud::GuiGenericBarHud()
{
   mShowFrame = mShowFill = true;
   mFlipped = mDisplayEnergy = false;
   mFillColor.set(0, 0, 0, 0.5);
   mFrameColor.set(0, 1, 0, 1);
   mDamageFillColor.set(0, 1, 0, 1);

   mPulseRate = 0;
   mPulseThreshold = 0.3f;
   mValue = 0.2f;
}

void GuiGenericBarHud::initPersistFields()
{
   Parent::initPersistFields();

   addGroup("Colors");
   addField( "fillColor",       TypeColorF, Offset( mFillColor, GuiGenericBarHud ) );
   addField( "frameColor",      TypeColorF, Offset( mFrameColor, GuiGenericBarHud ) );
   addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiGenericBarHud ) );
   endGroup("Colors");

   addGroup("Pulse");
   addField( "pulseRate",       TypeS32, Offset( mPulseRate, GuiGenericBarHud ) );
   addField( "pulseThreshold",  TypeF32, Offset( mPulseThreshold, GuiGenericBarHud ) );
   endGroup("Pulse");

   addGroup("Misc");
   addField( "flipped",         TypeBool, Offset( mFlipped, GuiGenericBarHud ) );
   addField( "showFill",        TypeBool, Offset( mShowFill, GuiGenericBarHud ) );
   addField( "showFrame",       TypeBool, Offset( mShowFrame, GuiGenericBarHud ) );
   addField( "displayEnergy",   TypeBool, Offset( mDisplayEnergy, GuiGenericBarHud ) );
   endGroup("Misc");
}
#4
12/11/2005 (4:09 pm)
2nd half:
//-----------------------------------------------------------------------------
/**
   Gui onRender method.
   Renders a Generic bar with filled background and border.
*/
void GuiGenericBarHud::onRender(Point2I offset, const RectI &updateRect)
{
   // Must have a connection and player control object
   /*
   GameConnection* conn = GameConnection::getServerConnection();
   if (!conn)
      return;
   ShapeBase* control = conn->getControlObject();
   if (!control || !(control->getType() & PlayerObjectType))
      return;
  
   if(mDisplayEnergy)
   {
      mValue = control->getEnergyValue();
   }
   else
   {
      // We'll just grab the damage right off the control object.
      // Damage value 0 = no damage.
      mValue = 1 - control->getDamageValue();
   }
   */

   // Background first
   if (mShowFill)
      dglDrawRectFill(updateRect, mFillColor);

   // Pulse the damage fill if it's below the threshold
   if (mPulseRate != 0)
   {
      if (mValue < mPulseThreshold) 
      {
         F32 time = Platform::getVirtualMilliseconds();
         F32 alpha = mFmod(time,mPulseRate) / (mPulseRate / 2.0);
         mDamageFillColor.alpha = (alpha > 1.0)? 2.0 - alpha: alpha;
      }
      else
         mDamageFillColor.alpha = 1;
   }

   // Render damage fill %
   RectI rect(updateRect);
   if(mBounds.extent.x > mBounds.extent.y)
   {
      if(mFlipped)
      {
         S32 bottomX = rect.point.x + rect.extent.x;
         rect.extent.x = (S32)(rect.extent.x * mValue);
         rect.point.x = bottomX - rect.extent.x;
      }
      else
      {
         rect.extent.x = (S32)(rect.extent.x * mValue);
      }
   }
   else
   {
      if(mFlipped)
      {
         rect.extent.y = (S32)(rect.extent.y * mValue);
      }
      else
      {
         S32 bottomY = rect.point.y + rect.extent.y;
         rect.extent.y = (S32)(rect.extent.y * mValue);
         rect.point.y = bottomY - rect.extent.y;
      }
   }

   dglDrawRectFill(rect, mDamageFillColor);

   // Border last
   if (mShowFrame)
      dglDrawRect(updateRect, mFrameColor);
}


// Console methods for direct manipulation of bar values.
ConsoleMethod(GuiGenericBarHud, setValue, void, 3, 3, "Set the value of the bar.")
{
   object->setValue(dAtof(argv[2]));
}

ConsoleMethod(GuiGenericBarHud, getValue, F32, 2, 2,"Get the value of the bar.")
{
   return object->getValue();
}
#5
12/11/2005 (4:10 pm)
Here is the code.

Josh:
Have at it! ; )
#6
12/11/2005 (5:37 pm)
So this could be for like Lava or Tear Gas timer etc??
#7
12/11/2005 (7:42 pm)
Ummm, it could be for anything you want because it is not tied to any objects. You could have a whole ton of them for different things. It is programmable from script. For instance I needed to show a base being taken over like in Battlefront. I change colors, modify the visibility, enable the flashing (when base is contested), and change the value all using scripts. This is not a terribly complicated object. Most of the code is stock from the Health/Energy GUI. Go look at that object and see the differences I just thought I would share.
#8
12/12/2005 (9:54 am)
Frank, I took your base GuiGenericBarHud and added a few extra features:

* Abilility to set the min/max values via script rather than the hard coded 0.0 - 1.0 values.
* Ability to have the bar slowly fill/drain instead of instantly changing the current value.
* Ability to set the speed of the fill/drain via script.
* Ability to display the target value on a fill in a faded color. IE, you're at 2 health and you take a potion that will increase your health to 50, you'll see the bar filled to 50 in a very light green color, while the dark green showing your real health quickly increases until it reaches that 50 mark. Think of the health bar in Diablo.

I also have a few very simple scripts to how you can use GuiGenericBarHud to automatically track player health/engery, thus completely replacing the GuiHealthBarHud, or to use it as a simple timer hud that starts filled will slowly drain until empty in a user-defined number of seconds.

Is there any other feature anybody can think of that would be useful?

Frank, were you planning on releasing this as a resource? I think with these extra features it could be pretty useful to some people, but you came up with the idea/base code so I'll let you write a resource if you were already planning to. If not, I'd like to write one, giving you credit in the resource for the base code of course.
#9
12/12/2005 (8:50 pm)
I am good either way. It looks like you are putting way more work into than I did. I simply cut paste and hacked away the parts I did not need. I will definitely be interested in seeing the finished product. Go ahead and do the resource, I will follow it and add to it if I see anything really pressing to add. I am thinking about making a round version that creates triangles based on the upper number so the gradiant would be programmable.
I am not sure why you need to make the number go from anything else but 0 to 1 since you can just scale the number anyway. To each his own. I do like the slow transition, it sounds very cool. Is the rate going to be programmable? Also, how about sound effect hooks? Like ones that will manipulate pitch as the value increases and decreases similar to Battlefront. That is something I have attempting in script. Kind of works, but not in 3D.

Thanks for pitching in, it always is better when people go wild on this stuff,
Frank
#10
12/12/2005 (9:45 pm)
Stupid question ... but what's the point? Torque already has a GuiProgressBarCtrl which pretty much does what you've reimplemented. I'm not 100% sure if it does vertical bars, but if not then that would be a really easy fix that wouldnt take more then 30 seconds to do ;-)

T.
#11
12/12/2005 (10:01 pm)
True, GuiProgressBarCtrl basically can do what GuiGenericBarHud does, but it's missing some nice features of GuiHealthBarHud, such as pulsing the bar when the value is below a specified level, displaying a faded background color when the bar isn't full, displaying vertically (as you mentioned), etc.

Of course, none of these features are hard to implement... the GuiGenericBarHud in itself is a fairly simplistic control. But creating a new control that implements all these features means nobody else has to go through and add those to GuiProgressCtrl, or modify GuiHealthBarHud. Helpful for those who aren't experienced in C++, and less re-inventing of the wheel for those of us who are. ;)
#12
12/12/2005 (10:09 pm)
So, the question now becomes, why not subclass GuiProgressCtrl to add those features, rather then add yet another class that's essentially similar to existing classes? :)

If you do turn this into a resource, I'd suggest going that route for less wheel re-inventing.

T.
#13
12/12/2005 (10:24 pm)
Well, if I were to derive a class I'd derive it from GuiHealthBarHud, as it has more of the features needed in GuiGenericBarHud than GuiProgressCtrl does.

Of course, that class is fairly simplistic and the one main function it contains would be rewritten completely anyways. In the end there would be very little difference in file size between a derived class and a completely new one.

Another reason I may not derive, is that IMO the GuiGenericBarHud would completely replace the GuiHealthBarHud, or at least it will in my game. I can create a GuiGenericBarHud and have it do exactly what GuiHealthBarHud does (and more) just by adding 3 lines of script.

Unless I'm misunderstanding you, and you're suggesting to subclass via script? I haven't really looked to in depth in how that's done, but I always preferred the C++ class approach. It just seems a cleaner solution to me, and certainly faster as I'm much more familiar with C++ than TorqueScript.

But if you are suggesting subclassing via scripting, then I'd be interested in hearing the pluses over deriving a new C++ class.
#14
12/12/2005 (11:36 pm)
No, the inheritance tree I was thinking was something like ...

GuiBaseBar - Basic horizontal or vertical bar base class
 |- GuiProgressBar - Progress bar if anything specific needed (probably not needed)
 '- GuiFlashyBar - Adds the flashy stuff you mentioned you added to GuiGenericBarHud in an earlier post
     '- GuiHealthBar - Really simple, just gets the data from the health/energy rather then set/getValue

This would of course all be C++.

As an aside, you may find this useful:

At the top of GuiHealthBarHud::onRender(), change the code where it gets the value to:

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

   if(control->getControlObject())
      control = control->getControlObject();

   if(mDisplayEnergy)
   {
      mValue = control->getEnergyValue();
   }
   else
   {
      // We'll just grab the damage right off the control object.
      // Damage value 0 = no damage.
      mValue = 1 - control->getDamageValue();
   }

This makes the health bar work a little better when the player is mounted to vehicles. I dont have the original version as easily to hand and I dont remember exactly why I changed it, but it should be pretty easy to figure it out.

T.
#15
12/13/2005 (8:01 am)
Tom, I definately agree that the inheritence tree you laid out is the best route to take.

However, I don't plan on writing new versions of GuiProgressBarCtrl and GuiHealthBarHud so they can properly derive from a base bar class. If I needed to modify the progress and health bars then I'd go ahead and do this, but as it is I have no need to modify other classes to create the GuiGenericBarHud. It just wouldn't be within the scope of the resource, and wouldn't be useful to those who download the resource since it would add no additional functionality.

BTW, thanks for the GuiHealthBarHud::onRender() mod for vehicles! I don't plan on hard-coding this into GuiGenericBarCtrl, as I'm trying to keep it as generic as possible, but if I create a resource I plan on showing some script snippets to show different ways to use GuiGenericBarHud. I hadn't thought of showing a snippet that makes GuiGenericBarHud show player health when unmounted, and switch to vehicle health when mounted. Now that you showed me the changes required in GuiHealthBarHud I'll definately be showing the equivelant in TorqueScript for GuiGenericBarHud!