====== Lab 6 - Avatars and HUD ====== ===== Useful Links ===== * [[http://www.ru.is/kennarar/hannes/useful/PQR2.3.html|Python Quick Reference (LOCAL)]] * [[http://www.ru.is/kennarar/hannes/useful/Python-Docs-2.5|Complete Python Documentation (LOCAL)]] //This is the latest 2.5 release, but new 2.5 features are well marked// * [[http://www.ru.is/kennarar/hannes/useful/PandaManual|Panda 3D Manual (LOCAL)]] * [[http://www.ru.is/kennarar/hannes/useful/PandaReference|Panda 3D Reference(LOCAL)]] ===== In-Class Excercises ===== ==== Before You Start ==== * Download and unzip the [[http://www.ru.is/kennarar/hannes/classes/ve2007/Lab6Assets.zip|Lab 6 Asset File]] into your working directory ==== Roaming Ralph with some HUDs ==== In this exercise you'll start with a fully working 3rd person world with Ralph as your avatar. You'll then add a couple of classic HUD features, namely the **health bar** and the **overhead map**. - Get the **advancedralph.py** running and explore the controls, making sure you understand how to control both Ralph himself and the camera (rotating and tilting by approaching the edge of the screen with your mouse; zooming with the mouse wheel). Have the application run in **Fullscreen** mode, simply by changing False to True in the call to **setFullscreen** near the bottom of the code. It is easier to control the camera with the mouse in this mode, but the windowed mode might be better while debugging. - The following code is a **HealthBar** object that can be attached to a scene graph. A health bar can display some fractional property in a graphical manner, such as the health or power of a character. Attach this bar to the Avatar so that the bar appears to be floating over the Avatars head. Make sure you can see it (but it may disappear when you start moving around - see below) class HealthBar(NodePath): """ Adapted from drwr's code on the Panda 3D forums """ def __init__(self): NodePath.__init__(self, 'healthbar') self.group = self.attachNewNode('group') self.group.setPos(-0.5,0,0) cmfg = CardMaker('fg') cmfg.setFrame(0, 1, -0.1, 0.1) self.fg = self.group.attachNewNode(cmfg.generate()) cmbg = CardMaker('bg') cmbg.setFrame(-1, 0, -0.1, 0.1) self.bg = self.group.attachNewNode(cmbg.generate()) self.bg.setPos(1, 0, 0) self.fg.setColor(0, 1, 0, 1.0) self.bg.setColor(0.5, 0.5, 0.5, 1.0) self.value = 0.8 self.update() def update(self): self.fg.setScale(self.value, 1, 1) self.bg.setScale(1.0 - self.value, 1, 1) def setHealth(self, value): self.value = value self.update() def decrease(self, delta): if (self.value - delta) >= 0: self.value -= delta self.update() - If your health bar rotates with your Avatar, you may notice that it fully disappears when you're looking at the Avatar from another angle. You can use the so called **Billboard** effect to ensure that the health bar always faces you, no matter how the Avatar is facing. You enable this effect on any nodepath by calling its **setBillboardAxis** method. Try using this to make your health bar always visible. - The healthbar calls a little too much attention to itself on the display. Make it semi-transparent so that it blends in better. Don't forget to enable transparency in the scene graph (HINT: setTransparency). - Now make the healthbar start decreasing **while** the Avatar is running and then gain full health when the Avatar stops running. HINT: For any FSM class, you can query the **state** member variable to see what state it is in. - The following code is an **OverheadMap** object that can be attached to a scene graph. A good place for it would be the 2D scene graph, since it should be rendered on top of the 3D scene. Add this object and have it show you where your avatar is located any given moment. It is good for you to know that the world dimensions in the x-y plane are approximately **(-128,-68,48,48)**. You may need to scale and reposition your map on the screen so that it doesn't occlude your view. Also, to enable transparency on the 2D scene graph, you'll need to call **setTransparency** again because it is a separate scene graph from **render**. Make sure this is all working fine. class OverheadMap(NodePath): def __init__(self, x1, y1, x2, y2): """ Create a square map with the given world corners in the x-y plane """ NodePath.__init__(self, 'overheadmap') cmbg = CardMaker('bg') cmbg.setFrame(0,1,0,1) self.prime = self.attachNewNode(cmbg.generate()) self.prime.setColor(0.5,0.5,0.5,0.5) self.setBoundary(x1,y1,x2,y2) cmav = CardMaker('am') cmav.setFrame(-0.02,0.02,-0.02,0.02) self.avatar = self.prime.attachNewNode(cmav.generate()) self.avatar.setPos(0,0,0) self.avatar.setColor(0,1,0,1) def addLandmarks(self, landmarks, color=P.Vec4(1,0,0,0.5)): """ Take in a list of (x,y) touples in world coordinates and place them as little dots on the map in the given color """ ## To be filled in... def setBoundary(self, x1, y1, x2, y2): """ Define the extent of the map in world coordinates. Create the proper offset and scaling values that transform world coordinates to map coordinates""" self.xoffset = x1 self.xscale = 1.0/(x2-x1) self.yoffset = y1 self.yscale = 1.0/(y2-y1) def setAvatarPos(self, x, y): """ Sets the position of the avatar marker to the given world coordinates """ self.avatar.setPos((x-self.xoffset)*self.xscale,-0.01,(y-self.yoffset)*self.yscale) - Now add some landmarks to your world and have them show up on your map as little red squares. You can use the **Models/box.egg** object. Initialize a few of these boxes by storing a list if (x,y) tuples and then iterate through the list to place them in the world. You should also be able to send the list directly into the map's **addLandmarks** method and have the locations of the boxes light up. You'll need to fill in the rest of that method. Test to see if this is all working. - If you feel adventurous, change this application into a game where the health bar starts to drop as time passes and the only way to regain full health is to touch a box, which then disappears. You win the game if you reach all the boxes. By randomizing the initial box locations, you'll really need the map to help you play this game.