Game Development Community

dev|Pro Game Development Curriculum

Modified GuiShapeNameHUD

by Dreamer · 02/23/2006 (10:50 am) · 11 comments

On a lark I decided for fun I would see if I could get the GuiShapeNameHud to display damage values as well as names...
Well I took the relevant bits from GuiCrossHairHud and merged them in.
It works!

*update*
Added the ability to turn the damage view on and off, I also added the ability to change how far away you can see the players name and damage.

Just replace your current GuiShapeNameHud.cc file with this one and recomplie...

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "dgl/dgl.h"
#include "dgl/gNewFont.h"
#include "gui/core/guiControl.h"
#include "gui/core/guiTSControl.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "game/shapeBase.h"
#include "game/gameConnection.h"

//----------------------------------------------------------------------------
/// Displays name & damage above shape objects.
///
/// This control displays the name and damage value of all named
/// ShapeBase objects on the client.  The name and damage of objects
/// within the control's display area are overlayed above the object.
///
/// This GUI control must be a child of a TSControl, and a server connection
/// and control object must be present.
///
/// This is a stand-alone control and relies only on the standard base GuiControl.
class GuiShapeNameHud : public GuiControl 
{
   typedef GuiControl Parent;

   // field data
   ColorF   mFillColor;
   ColorF   mFrameColor;
   ColorF   mTextColor;

   F32      mVerticalOffset;
   F32      mDistanceFade;
    // Visible distance info & name fading
   F32      mVisDistance;
   bool     mShowFrame;
   bool     mShowFill;
   bool	mShowDamage;
   ColorF   mDamageFillColor;
   ColorF   mDamageFrameColor;
   Point2I  mDamageRectSize;
   Point2I  mDamageOffset;

protected:
   void drawName( Point2I offset, const char *buf, F32 opacity);
   void drawDamage(Point2I offset, F32 damage, F32 opacity);
public:
   GuiShapeNameHud();

   // GuiControl
   virtual void onRender(Point2I offset, const RectI &updateRect);

      // object selection additions
   virtual void onMouseDown(const GuiEvent &evt);
	
   static void initPersistFields();
   DECLARE_CONOBJECT( GuiShapeNameHud );
};


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

IMPLEMENT_CONOBJECT(GuiShapeNameHud);

/// Default distance for object's information to be displayed.
static const F32 cDefaultVisibleDistance = 500.0f;

GuiShapeNameHud::GuiShapeNameHud()
{
   mFillColor.set( 0.25, 0.25, 0.25, 0.25 );
   mFrameColor.set( 0, 1, 0, 1 );
   mTextColor.set( 0, 1, 0, 1 );
   mShowFrame = mShowFill = true;
   mShowDamage = true;
   mVerticalOffset = 0.5;
   mDistanceFade = 0.1;
   mVisDistance = gClientSceneGraph->getVisibleDistance();
   mDamageFillColor.set( 0, 1, 0, 1 );
   mDamageFrameColor.set( 1, 0.6, 0, 1 );
   mDamageRectSize.set(50, 4);
   mDamageOffset.set(0,32);	
}

void GuiShapeNameHud::initPersistFields()
{
   Parent::initPersistFields();
   addGroup("Colors");
   addField( "fillColor",  TypeColorF, Offset( mFillColor, GuiShapeNameHud ) );
   addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiShapeNameHud ) );
   addField( "textColor",  TypeColorF, Offset( mTextColor, GuiShapeNameHud ) );
   endGroup("Colors");

   addGroup("Misc");
   addField( "showFill",   TypeBool, Offset( mShowFill, GuiShapeNameHud ) );
   addField( "showFrame",  TypeBool, Offset( mShowFrame, GuiShapeNameHud ) );
   addField( "verticalOffset", TypeF32, Offset( mVerticalOffset, GuiShapeNameHud ) );
   addField( "distanceFade", TypeF32, Offset( mDistanceFade, GuiShapeNameHud ) );
   addField( "visibleDistance", TypeF32, Offset( mVisDistance, GuiShapeNameHud ) );
   endGroup("Misc");

   addGroup("Damage");
   addField( "showDamage",   TypeBool, Offset( mShowDamage, GuiShapeNameHud ) );		
   addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiShapeNameHud) );
   addField( "damageFrameColor", TypeColorF, Offset( mDamageFrameColor, GuiShapeNameHud) );
   addField( "damageRect", TypePoint2I, Offset( mDamageRectSize, GuiShapeNameHud) );
   addField( "damageOffset", TypePoint2I, Offset( mDamageOffset, GuiShapeNameHud) );
   endGroup("Damage");		
}

//--------------------------------------------------------------------------
// object selection additions
//--------------------------------------------------------------------------
void GuiShapeNameHud::onMouseDown(const GuiEvent &evt)
{
   // Let's let the parent execute its event handling (if any)
   GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());

   if (parent)
      parent->onMouseDown(evt);
}


//----------------------------------------------------------------------------
/// Core rendering method for this control.
///
/// This method scans through all the current client ShapeBase objects.
/// If one is named, it displays the name and damage information for it.
///
/// Information is offset from the center of the object's bounding box,
/// unless the object is a PlayerObjectType, in which case the eye point
/// is used.
///
/// @param   updateRect   Extents of control.
void GuiShapeNameHud::onRender( Point2I, const RectI &updateRect)
{

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

   // Must be in a TS Control
   GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());
   if (!parent) return;

   // Must have a connection and control object
   GameConnection* conn = GameConnection::getConnectionToServer();
   if (!conn)
      return;

   ShapeBase* control = conn->getControlObject();
   if (!control)
      return;

   // Get control camera info
   MatrixF cam;
   Point3F camPos;
   VectorF camDir;
   conn->getControlCameraTransform(0,&cam);
   cam.getColumn(3, &camPos);
   cam.getColumn(1, &camDir);

   F32 camFov;
   conn->getControlCameraFov(&camFov);
   camFov = mDegToRad(camFov) / 2;

   // Visible distance info & name fading
   //F32 visDistance = gClientSceneGraph->getVisibleDistance();
   //F32 visDistanceSqr = visDistance / 2;
   F32 fadeDistance = mVisDistance * mDistanceFade;

   // Collision info. We're going to be running LOS tests and we
   // don't want to collide with the control object.
   static U32 losMask = TerrainObjectType | InteriorObjectType | ShapeBaseObjectType;
   control->disableCollision();

   // All ghosted objects are added to the server connection group,
   // so we can find all the shape base objects by iterating through
   // our current connection.
   for (SimSetIterator itr(conn); *itr; ++itr) 
   {
      if ((*itr)->getType() & ShapeBaseObjectType) 
      {
         ShapeBase* shape = static_cast<ShapeBase*>(*itr);
         if (shape != control && shape->getShapeName()) 
         {

            // Target pos to test, if it's a player run the LOS to his eye
            // point, otherwise we'll grab the generic box center.
            Point3F shapePos;
            if (shape->getType() & PlayerObjectType) 
            {
               MatrixF eye;

               // Use the render eye transform, otherwise we'll see jittering
               shape->getRenderEyeTransform(&eye);
               eye.getColumn(3, &shapePos);
            }
            else 
            {
                // Use the render transform instead of the box center
                // otherwise it'll jitter.
               MatrixF srtMat = shape->getRenderTransform();
               srtMat.getColumn(3, &shapePos);
            }

            VectorF shapeDir = shapePos - camPos;

            // Test to see if it's in range
            F32 shapeDist = shapeDir.lenSquared();
            if (shapeDist == 0 || shapeDist > mVisDistance)
               continue;
            shapeDist = mSqrt(shapeDist);

            // Test to see if it's within our viewcone, this test doesn't
            // actually match the viewport very well, should consider
            // projection and box test.
            shapeDir.normalize();
            F32 dot = mDot(shapeDir, camDir);
            if (dot < camFov)
               continue;

            // Test to see if it's behind something, and we want to
            // ignore anything it's mounted on when we run the LOS.
            RayInfo info;
            shape->disableCollision();
            ShapeBase *mount = shape->getObjectMount();

            if (mount)
               mount->disableCollision();
            bool los = !gClientContainer.castRay(camPos, shapePos,losMask, &info);
            shape->enableCollision();
            if (mount)
               mount->enableCollision();

            if (!los)
               continue;

            // Project the shape pos into screen space and calculate
            // the distance opacity used to fade the labels into the
            // distance.
            Point3F projPnt;
            shapePos.z += mVerticalOffset;
            if (!parent->project(shapePos, &projPnt))
               continue;
            F32 opacity = (shapeDist < fadeDistance)? 1.0:
               1.0 - (shapeDist - fadeDistance) / (mVisDistance - fadeDistance);

            // Render the shape's name
            drawName(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getShapeName(),opacity);
	   if(mShowDamage)	
	    	drawDamage(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getDamageValue(), opacity);
         }
      }
   }

   // Restore control object collision
   control->enableCollision();

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


//----------------------------------------------------------------------------
/// Render object names.
///
/// Helper function for GuiShapeNameHud::onRender
///
/// @param   offset  Screen coordinates to render name label. (Text is centered
///                  horizontally about this location, with bottom of text at
///                  specified y position.)
/// @param   name    String name to display.
/// @param   opacity Opacity of name (a fraction).
void GuiShapeNameHud::drawName(Point2I offset, const char *name, F32 opacity)
{
   // Center the name
   offset.x -= mProfile->mFont->getStrWidth((const UTF8 *)name) / 2;
   offset.y -= mProfile->mFont->getHeight();

   // Deal with opacity and draw.
   mTextColor.alpha = opacity;
   dglSetBitmapModulation(mTextColor);
   dglDrawText(mProfile->mFont, offset, name);
   dglClearBitmapModulation();
}

void GuiShapeNameHud::drawDamage(Point2I offset, F32 damage, F32 opacity)
{
   mDamageFillColor.alpha = mDamageFrameColor.alpha = opacity;

   // Damage should be 0->1 (0 being no damage,or healthy), but
   // we'll just make sure here as we flip it.
   damage = mClampF(1 - damage, 0, 1);

   // Center the bar
   RectI rect(offset, mDamageRectSize);
   rect.point.x -= mDamageRectSize.x / 2;

   // Draw the border
   dglDrawRect(rect, mDamageFrameColor);

   // Draw the damage % fill
   rect.point += Point2I(1, 1);
   rect.extent -= Point2I(1, 1);
   rect.extent.x = (S32)(rect.extent.x * damage);
   if (rect.extent.x == 1)
      rect.extent.x = 2;
   if (rect.extent.x > 0)
      dglDrawRectFill(rect, mDamageFillColor);
}

Enjoy!

#1
02/23/2006 (1:15 pm)
Nice job! This is definitely handy -- especially if you're doing some sort of RPG. One suggestion though: It could be useful to have a flag accessible from script to turn the damage view on/off. Reasons might include: Allowing the player to toggle it, or (back to the RPG) possibly there's a spell that can be cast by the player that allows him to view the health of nearby creatures for a limited period.
#2
02/28/2006 (7:53 pm)
Ok I added those features and a couple more, now rate this a 5 please ;)
#3
07/14/2006 (9:19 pm)
rated as requested -:)
#4
07/16/2006 (4:17 pm)
Very useful 5*
#5
08/02/2006 (3:10 pm)
Dreamer I am running into a snag here. Say I want different color names based on what type of player/AI/team/whatever. How would you go about say, having a blue name for players and a red name for AI units?
#6
08/02/2006 (4:15 pm)
Propegate the value, and change the color based on either the value or some mathematical representation of the value.
In the MMOKit we have modified it for instance to show the level difference between you and what your looking at.
Red for > 10 level Yellow for >5 white for equal etc.
It's not hard to do, and I may update this in the near future to show how it's done.

Regards,
Dreamer
#7
09/24/2006 (1:18 am)
Great resource!
#8
12/03/2006 (9:43 am)
@Dreamer and anyone else who may be able to help:

I'm a hair away from cracking the code for guiShapeNameHud to allow for custom colors for in-game shapes, but I'm missing something small I believe. Perhaps you could check what I am doing:

In shapebase.h (in the public area of ShapeBase)

//ADDED FOR CUSTOMIZING THE COLOR OF THE NAME

   char*   mTextColor;				/// The default color of the object's name

   void setTextColor(const char*);  // <== added to set name color
   const char* getTextColor();    // <== added to return name color

   void setShapeName(const char*);
   const char* getShapeName();
   void setSkinName(const char*);
   const char* getSkinName();

In shapeBase.cc

// ADDED TO SUPPORT CUSTOM SHAPENAME COLORS

void ShapeBase::setTextColor(const char* cColor)
{
	mTextColor = (char *)cColor;
}

const char* ShapeBase::getTextColor()
{
	return mTextColor;
}
ConsoleMethod( ShapeBase, getNameColor, const char*, 2, 2, "")
{
	return object->mTextColor;	
}

ConsoleMethod( ShapeBase, setNameColor, void, 3, 3, "Color(R G B A)")
{
	object->setTextColor(argv[2]);	
}
//

Also in shapeBase.cc, in the function ShapeBase::ShapeBase()

mTextColor = "1 0 0 1";   // initializes the color to red

Finally in guiShapeNameHud.cc:



in class GuiShapeNamedHUD : public GuiControl change

protected:
void drawName( Point2I offset, const char *buf, F32 opacity)

to

protected:
void drawName( Point2I offset, const char *buf, F32 opacity, const char *cColor);

in guiShapeNamedHud::onRender change:

drawName(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getShapeName(),opacity);

to

drawName(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getShapeName(),opacity, shape->getTextColor());

and then:

void GuiShapeNameHud::drawName(Point2I offset, const char *name, F32 opacity, const char *cColor)
{
   // Center the name
   offset.x -= mProfile->mFont->getStrWidth((const UTF8 *)name) / 2;
   offset.y -= mProfile->mFont->getHeight();
//
// CUSTOM COLOR - Added from here
   int i_red, i_green, i_blue, i_alpha;  

   dSscanf(cColor, "%d %d %d %d", &i_red, &i_green, &i_blue, &i_alpha  );
   mTextColor.set((const F32)i_red, (const F32)i_green, (const F32)i_blue, (const F32)i_alpha);
// CUSTOM COLOR - to here
//
   // Deal with opacity and draw.
   mTextColor.alpha = opacity;
   dglSetBitmapModulation(mTextColor);
   dglDrawText(mProfile->mFont, offset, name);
   dglClearBitmapModulation();
}

Now interestingly enough, everything works EXCEPT for the console method setNameColor. The names are rendered in RED regardless of what the guishapenamehud control's color is set to, which is excellent because that's the default.

If I get an object's ID in the game then type in the console "echo(%obj.getnamecolor());", it returns the correct value of "1 0 0 1".

However, if I try to set the color using the console method %obj.setnamecolor("0 1 1 0"), not only does it NOT change the color, but when I try to type "echo(%obj.getnamecolor());" in the console it will return as blank or will actually return the literal words "%obj.getnamecolor()".

I am obviously doing something wrong that is very simple but I need another set of eyes to take a look at my code. It's proving to be a stubborn box to unlock.

Thanks!
#10
09/09/2007 (4:55 am)
I used the faction resource and instead of modifying guiCrosshairHud.cc, I modified guiShapeNameHud.cc.
#11
01/08/2008 (5:37 pm)
@Robert Seeman: did you ever figure out your problem with this? I'm getting the same thing with your nice code.. :-)