Health Bars (using DirectGUI ?)

I’m trying to add simple healthbars to my chars,
What i’m doing now is loading 2 images (healthframe and the healthbar) images into a DirectGui frame and attaching them to the correct chars.
Everything is working quite ok, the only problem is it seems impossible to position & scale the actual bar correctly so it fits decently into the frame

I can get the full bar sort of positioned correctly (using very ugly floats) but when I scale it (when health decreases) the scaling origin is in the middle of the image, I tried specifiing a frameSize manually to correct it but that didn’t seem to affact anything. and calculating the correct position offset for the bar isn’t working out either

anyone has a fix / better way to do this … I guess i’m not the first one to make healthbars in panda

here is my current code:

# in init
self.healtFrame=DirectFrame(relief=None, image="media/health_bar_empty.png", image_scale=(4,1,0.5))
    self.healtFrame.setPos(0, 0, 12)
    self.healtFrame.setBillboardPointEye()
    self.healtFrame.reparentTo(self.spiderActor)
    
    self.healtBar=DirectFrame(relief=None, image="media/health_bar.png", image_scale=(4,1,0.5))
    self.healtBar['image_pos'] = (0.1995, 0, 0)
    self.healtBar.setBillboardPointEye()
    self.healtBar.reparentTo(self.healtFrame)

# in updatetask:
 self.healtBar['image_scale'] = (4 * self.health / 100.0, 1, 0.5)
 self.healtBar['image_pos'] = (0.1995 - 4 * self.health / 200.0), 0, 0)

Try using a DirectWaitBar to implement your health bars. It does this sort of thing automatically.

David

Thx for replying drwx.

I allready tried this after ThomasEgi pointed me to it on the panda IRC channel, but while they work perfectly when I attach em to aspect2d, they allways are “blank” when I attach em to a node in the 3D scenemanager.

self.bar = DirectWaitBar(value=100, scale = 3)
self.bar.setPos(0, 0, 12)
self.bar.setBillboardPointEye()
self.bar.reparentTo(self.spiderActor)

So the problem is the actual bar isn’t showing up, made me remember a fix I used for directlabels:

self.nameBox.component('text0').textNode.setCardDecal(1)

but I wasn’t able to test this because I didn’t know the component name for the bar (and didn’t find it in the manual either)

Hmm, right, the DirectWaitBar is a little different in that it’s mostly implemented in C++, so there’s no component you can reach that you can set the decal property on.

But in general, the DirectGui objects are not really designed for parenting into the 3-D scene graph. Some of them (like DirectFrame) can work there anyway, while others (like DirectWaitBar) really don’t work at all.

If you need to have a health bar in the 3-D scene graph, you’re probably better off building your own with the CardMaker, or with your own custom-made geometry. For instance, you can do this:

class HealthBar(NodePath):
    def __init__(self):
        NodePath.__init__(self, 'healthbar')
        
        cmfg = CardMaker('fg')
        cmfg.setFrame(0, 1, -0.1, 0.1)
        self.fg = self.attachNewNode(cmfg.generate())

        cmbg = CardMaker('bg')
        cmbg.setFrame(-1, 0, -0.1, 0.1)
        self.bg = self.attachNewNode(cmbg.generate())
        self.bg.setPos(1, 0, 0)

        self.fg.setColor(1, 0, 0, 1)
        self.bg.setColor(0.5, 0.5, 0.5, 1)

        self.setHealth(0.5)

    def setHealth(self, value):
        self.fg.setScale(value, 1, 1)
        self.bg.setScale(1.0 - value, 1, 1)

The above example accepts a health value in the range 0 … 1. It creates two side-by-side cards, a red one and a gray one, and scales them so that as the health grows higher, the red card occupies more of the space and the gray card occupies less of the space.

The red card scales around (0, 0, 0), and the gray card scales around (1, 0, 0), because of their different offsets.

There are lots of variants on this idea possible. Feel free to use this code, or go creative with your own.

David

thx alot, its working like a charm!

when I make a healthbar with images later on I’ll place it onlien here or on some wiki

I’ve also found this useful, but made a modification for better-looing* colors. It goes from green to red.

color_value = 1
if value < .7:
  color_value = (value+.3)*(value+.3)
health_bar.setColor(1.3-color_value, color_value, 0, 1)
health_bar.setScale(value, 1, 1)
max_health_bar.setScale(1.0 - value, 1, 1)

There’s one problem I have with this. I can’t figure out a good way to attach it to a model that rotates without having to rotate the hp bar and use trig to recreate the positions. Anyone have an idea?

*guess what eyboard ey isn’t woring for me?

try to reparent it to the character, set the Z some higher than the character, and use the billboard effect to have it face the camera.
panda3d.org/manual/index.php/Billboard_Effects

The K? :unamused:

small update, this is the bar class I’m using atm. I changed the one of drwr so the origin of the bar is in the center of it, because when I attached drwr’s version to the nodePath of a player and applied a billboard effect on it, the bar was not centered above the player but positioned somewhat to the right of it.

Since the offset parameter of the setBillboard functions didn’t seem to be helping I just adapted the bar class. Probably there are better (cleaner) solutions but oh well …

from pandac.PandaModules import CardMaker
from pandac.PandaModules import NodePath

class Bar(NodePath): 
        def __init__(self, scale=1, value=1, r=1, g=0, b=0): 
                NodePath.__init__(self, 'healthbar') 

                self.scale = scale
                cmfg = CardMaker('fg') 
                cmfg.setFrame(- scale,  scale, -0.1 * scale, 0.1 * scale) 
                self.fg = self.attachNewNode(cmfg.generate()) 

                cmbg = CardMaker('bg') 
                cmbg.setFrame(- scale, scale, -0.1 * scale, 0.1 * scale) 
                self.bg = self.attachNewNode(cmbg.generate()) 

                self.fg.setColor(r, g, b, 1) 
                self.bg.setColor(0.2, 0.2, 0.2, 1) 

                self.setValue(value) 

        def setValue(self, value):
                value = min(max(0, value), 1)
                self.fg.setScale(value * self.scale, 0, self.scale) 
                self.bg.setScale(self.scale * (1.0 - value), 0, self.scale)
                self.fg.setX((value - 1) * self.scale * self.scale)
                self.bg.setX(value * self.scale * self.scale)

Sure. I would have just used the existing bar class, and parented it to a dummy node with an offsetting setPos() applied to it.

Don’t overlook the power of a scene graph. :slight_smile:

David