Game Development Community

Health and energy bars

by Richard O · in Torque Game Engine · 05/05/2005 (7:04 am) · 10 replies

This might seem like a really stupid question, but how are the health and energy bar huds controlled? how does the engine know which one to use for the health and which one for the energy? where in the code does it update the visual appearance of the hud? for example, when you get hit, the scripts call applyDamage(), and when you use a health pack it calls applyRepair(), but it doesn't say to update the hud, yet the health bar is updated to reflect your current health. when you add the gui elements, there doesn't seem to be a variable to determine which one is which, or maybe im blind.

the reason i ask, is that id like to have two huds, one displaying your players health, and the other one displaying the energy of the torch you're carrying, like a visual readout of a battery. so how do i go about making the energy bar display this instead of a players energy - or, failing that, how do i go about introducing a new one that will do what i want?

thanks

#1
05/05/2005 (7:33 am)
GuiHealthBarHud.cc is the particular file you're looking for...
If you want to take a look at the MMORPG series of tutorials, I have an article on Object Selection, which also creates a new type of hud called a TargetHud, that was based on the code for the guiHealthBarHud, this could easily be modified to show the energy of an item rather than of a target, just by changing a couple of lines.

Specifically this one would be most important
if (!conn || !conn->getSelectedObject())
      return;
   ShapeBase* control = conn->getSelectedObject();
   if (!control || !(control->getType() & PlayerObjectType))
      return;

This is all in about line 90-100 or so...

Change the conn->getSelectedObject(), to whatever object you need, to get information from.

Then create a new gui file with this added in
new GuiTargetBarHud(TargetEnergy) {
			profile = "GuiDefaultProfile";
			horizSizing = "right";
			vertSizing = "bottom";
			position = "10 40";
			extent = "80 10";
			minExtent = "8 2";
			fillColor = "0.000000 0.000000 0.000000 0.500000";
			frameColor = "0.000000 1.000000 0.000000 0.000000";
			damageFillColor = "0.000000 0.000000 0.800000 1.000000";
			pulseRate = "0";
			pulseThreshold = "0.3";
			flipped = "0";
			showFill = "1";
			showFrame = "1";
			displayEnergy = "1";
		};

The displayEnergy property is what determines if the gui element is going to show the objects health or energy, set it to 0 to show health and 1 to show energy.

Hope that helps!
#2
05/05/2005 (4:59 pm)
Pointers on how to modify that to work with vehicle energy?
I've poked around, but my code appears to be correct.

I got the health showing by fixing a line in GuiHealthBarHud.cc, but the energy isn't. (Added VehicleObjectType with PlayerObjectType in the if() to achieve that)
#3
05/11/2005 (6:02 pm)
Hi dreamer,
thanks for the pointers. kinda weird, but kinda handy thing happened. i didn't put in your object selection or any of that, cause i first wanted to see if i could make a weapon that uses energy in the first place. i modified the crossbow.cs file in starter.fps thus
datablock ShapeBaseImageData(CrossbowImage)
{
   // Basic Item properties
   shapeFile = "~/data/shapes/crossbow/weapon.dts";
  ....
  ....

   // Projectile && Ammo.
  [b] usesEnergy = true;
   minEnergy = 3;[/b]
   item = Crossbow;
   //ammo = CrossbowAmmo;
   projectile = CrossbowProjectile;
   projectileType = Projectile;

   // Images have a state system which controls how the animations
   // are run, which sounds are played, script callbacks, etc. This
   // state system is downloaded to the client so that clients can
   // predict state changes and animate accordingly.  The following
   // system supports basic ready->fire->reload transitions as
   // well as a no-ammo->dryfire idle state.

   // Initial start up state
   stateName[0]                     = "Preactivate";
   ....
   ....

   // Fire the weapon. Calls the fire script which does 
   // the actual work.
   stateName[3]                     = "Fire";
   [b]stateEnergyDrain[3]              = 50;[/b]
   stateTransitionOnTimeout[3]      = "Reload";
   stateTimeoutValue[3]             = 1;
   stateFire[3]                     = true;
   stateRecoil[3]                   = LightRecoil;
   stateAllowImageChange[3]         = false;
   stateSequence[3]                 = "Fire";
   stateScript[3]                   = "onFire";
   stateSound[3]                    = CrossbowFireSound;
#4
05/11/2005 (6:06 pm)
(cont...)
i turned off the recharge rate and spawned the player with 100% energy. the energy bar hud started showing the energy for the weapon without being changed at all. if i add values for runDrain and jumpDrain in player.cs, the the energy bar takes from the both of them.

naturally enough this will do me, as i don't need the player to lose any energy and can just do it this way without major engine modifications. i looked around in guiHealthBarHud.cc anyway, just to get a feel for how it's being called and drawn on screen and while i was there i looked into shapeImage.cc to see how energy weapons were used in the code. while looking i modified the shapeBase::updateImageState() function slightly to this:
// Energy management
   if (imageData.usesEnergy) {
      F32 newEnergy = getEnergyLevel() - stateData.energyDrain * dt;
      if (newEnergy < 0){
         newEnergy = 0;
        [b] imageData.lightTurnedOn = 0;[/b]
      }
   }

lightTurnedOn is a variable i introduced into the flashlight code so i could switch it on and off. this works grand tho iv a few questions. id like to affect the flashlight so the intensity is controlled by how much battery power it has left, ie: the less power, the dimmer the light. as you can see i tried to introduce a variable named currentBatteryLevel that i could access in my registerWeaponImageLights function (engine was adapted using robert browers flashlight tut), but it doesn't come up. so these are my questions:

1: how do you make a global variable in the c++ code so i can access the currentBatteryLevel variable at will?
2: failing that, do you think it's better to make a member field (i think that's what it's called) in the weapons datablock using initPersistFields, packData, unPackData, and the like. i think i know how to do that, and should be able to access it from the registerWeaponImageLights function
3: how do you use a function defined in script in the engine? what i want to do, is when the user clicks the mouse the flashlight toggles on and off. unfortunatly the engine registers rapid button clicks so the light switches off, then back on again straight away. to combat this, im thinking of having two weapon mountedImages, one of the flashlight turned on, the other of it turned off. then bind the mouse press to the use function (think its something like keybind(mouse1, use, flashlightOff) or something to that effect). so in the updateImageState() function in the engine id like it to do that instead of turning a bool on or off (then get rid of lightTurnedOn altogether), tho i don't know how to do that inside the engine.

if youve made it this far through my ramblings and can help, id appreciate it.
thanks

(ps: is there a block on posting c++ code or something? i got nothing but forbidden 403 errors until i deleted most of it bar the important parts)
#5
05/12/2005 (2:07 pm)
I sorted out the battery problem by creating a function in c++ and exposing it in the script in the weapon's datablock, so now the flashlight dims as the battery runs out. i also bound the toggle to switch it on and off to the mouse so there was no need for two weapon images, and that works perfectly now.

i'm trying to code it so that when the battery runs out the player dies, so in the shapeBase::updateImageState function i added this bit of code:
if(!imageData.lightTurnedOn){
   GameConnection * connection = GameConnection::getLocalClientConnection();
   ShapeBase* player = 0;
   if(connection){
      player = connection->getControlObject();
   }
   if(player->getDamageLevel() != 100){
      player->applyDamage(100);
      player->setDamageState("Dead");
      Con::executef(2, "onDeath", scriptThis());
      Con::printf("is player destroyed? %f", player->isDestroyed());
   }
}
when i apply the damage, the orc dies, but the camera doesn't detach and the player thus can't respawn, and is left staring at the floor unable to move. in game.cs there's a function gameConnection::onDeath() thats called in script if the player is killed. i think this is what i need, but i don't know how to access this function from the c++ code. i thought it was this line:
Con::executef(2, "onDeath", scriptThis());
but that's obviously coded wrong as it doesn't work. does anyone know the proper syntax for a call like this?
#6
05/12/2005 (6:12 pm)
You're calling the onDeath for the player object, not the connection. You probably need to do:

Con::executef(2, "onDeath", connection->scriptThis());
#7
05/13/2005 (9:39 am)
Hi ben,
thanks for the pointer, i got it working, though i decided to call the script function damage() instead of onDeath() as for some reason onDeath() was getting called twice when i did it that way. one question: when the battery's power runs out, the player dies, yet when i click to respawn him, he respawns and dies immediately. the shapeBase::updateImageState() function is written like this . (the reason is on a web page is cause for some inanely frustrating reason i can't post up code that has to do with the engine, even though it's in the private forum - i keep getting a forbidden 403 error. is it supposed to be like that, or is my posting ability broken? i'll take it down after youve seen it if you want me to)

what i think's happening is the if check is looking to see if the currentBatteryLevel is a 0, and currentBatteryLevel is getting it's value from the call to getEnergyLevel() which should return the level of the player. when i Con::printf it out, it returns 0, even though i spawn the player with 100 energy like so:
//the GameConnection::createPlayer() function in game.cs
{
   //nothing changed till this point...
   
   // Starting equipment
   %player.setInventory(Crossbow,1);
   %player.setInventory(CrossbowAmmo,10);
   %player.mountImage(CrossbowImage,0);
   [b]%player.setEnergyLevel(100);
   %flashlight = %player.getMountedImage(0);
   %flashlight.lightTurnedOn = 1;
   %flashlight.currentBatteryLevel = %player.getEnergyLevel();[/b]

   //...nothing changed after this
}
when i echo out %player.getEnergyLevel or %flashlight.currentBatteryLevel, i get 100, even though a con::printf of what should be the same thing returns 0. is the engine getting the player's energy value from the datablock or somewhere else? how do i force it to take the value i set in the createPlayer() function?
thanks

::Edit to make the link work
#8
05/13/2005 (11:28 pm)
The code as I gave it could get called on the client side, potentially resulting in two calls (one from the server instance of the object, one from the client instance).

Are you exposing the fields in question to script via initPersistFields? Are you getting any interesting console errors?
#9
05/14/2005 (6:06 am)
LightTurnedOn and currentBatteryLevel are both fields that iv exposed via initPersistFields. getEnergyLevel() is a function that was already there. i initialize the player with 100 energy cause iv set his rechargeRate to a minus number to simulate the effect of a draining battery.

im not getting any console errors, as i don't think there's a error in the code, the player is just respawning with 0 energy. one interesting thing: im using the starter.fps level as my base, and modified the crossbow to act as a flashlight. instead of firing when the mouse is clicked, the flashlight simply turns off, except when he's dead, then it reverts back to it's original state so:
function toggleFlashlight(%val)
{
   %conn = ClientGroup.getObject( 0 );
   %player = %conn.getControlObject();
   %weapon = %player.getMountedImage(0);
   if(%val){
      if(%player.mode $= "Corpse"){
         $mvTriggerCount0++;
      } else {
         %weapon.lightTurnedOn = !%weapon.lightTurnedOn;
         //if the lights on, the the battery's being drained
         if(%weapon.lightTurnedOn){
            %player.setRechargeRate(-0.1);
         } else {
            %player.setRechargeRate(0);
         }
      }
   }
}
when my player dies, and i click, i respawn and die immediately. however, when my player dies and i do nothing until he fades completely, then click, he respawns as i want except the trigger is permanently down and he's firing the crossbow constantly, until he dies again. where's the function that controls the $mvTriggerCount0++ var? i looked in game.cs and in the onDisabled function there is a call to a function startFade(), then a second later, delete(). is my problem then that my player is not getting deleted?

im a bit confused, as when my orc does the whole respawn, die immediately thing, and i echo out the different variables in question, he has a different id every time. would this not mean that he's getting deleted?
thanks
#10
05/16/2005 (9:38 am)
I got the respawning/dying immediately problem solved by changing the shapeBase::updateImageState() function to this:

and changing the code in default.binds to this:
function toggleFlashlight(%val)
{
   //get the player object
   //NOTE: if the player is dead, then the control object is the camera
   %conn = ClientGroup.getObject( 0 );
   %player = %conn.getControlObject();
   %weapon = %player.getMountedImage(0);

   if(%val){
      if([b]%player.getClassName() $= "Camera" && %player.mode $= "Corpse"[/b]){
         $mvTriggerCount0++;
      } else {
         %weapon.lightTurnedOn = !%weapon.lightTurnedOn;

         //if the lights on, the the battery's being drained
         if(%weapon.lightTurnedOn){
            %player.setRechargeRate(-0.1);
         } else {
            %player.setRechargeRate(0);
         }
      }
   }
}
now my only problem with this is that when my orc respawns, he's constantly firing. the toggleFlashlight function will still work, and the $mvTriggerCount0++ is definetly not calling from there. does anyone know where this is set, or what's happening here? i can change the code in crossbow.cs so that it doesn't call the fire state, and this works, only im pretty sure it's still trying to call it. we don't really have any weapons, and don't plan on bringing anything that you need to fire into the game, so changing crossbow.cs will do for us. the only thing is, is that aside from being bad programming, if i ever made another game where i was doing roughly the same thing, this problem would still be there and i still wouldn't know how to fix it. anybody any ideas?
thanks