Roaming Ralph Explosion Animation

Hey, I am trying to modify roaming ralph to hold a weapon and shoot from that weapon. I’m pretty far from that goal, but I started with trying to make a big ball (which will later be a small explosion) appear right at his hand when i press the fire key. My problem is, that the explosion ball, seems to not always spawn in the same place. If i move around it moves around his body some. Sometimes it appears in really odd places…still near him, but different x, y coordinates in relation to him. The z is always correct. Here’s the code for the fire function:

def fire(self):
pos = self.ralph.getPos()
#print “firing projectile at”, pos

    projectile = loader.loadModel("models/projectile1")
    projectile.reparentTo(render)
    projectile.setScale(.3)
    projectile.setPos(self.ralph, -5, 1, 2) #the numbers, (x, y, z)  x= left/right, y= front to back, z= up/down
    #print "the projectile is now at", projectile.getPos()

If anybody has any examples of a project where someone has done something similar, I’d be really interested in seeing it. But I’m going to continue trying to edit this no matter what. Thanks in advance!

If you need it, here is the total code.

import direct.directbase.DirectStart
from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionRay
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from panda3d.core import Vec3,Vec4,BitMask32
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
import random, sys, os, math

SPEED = 0.5
BULLET_LIFE = 2

Function to put instructions on the screen.

def addInstructions(pos, msg):
return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
pos=(-1.3, pos), align=TextNode.ALeft, scale = .05)

Function to put title on the screen.

def addTitle(text):
return OnscreenText(text=text, style=1, fg=(1,1,1,1),
pos=(1.3,-0.95), align=TextNode.ARight, scale = .07)

class World(DirectObject):

def __init__(self):
    
    self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, "cam-left":0, "select-target":0, "fire":0}
    base.win.setClearColor(Vec4(0,0,0,1))

    # Post the instructions

    self.title = addTitle("Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)")
    self.inst1 = addInstructions(0.95, "[ESC]: Quit")
    self.inst2 = addInstructions(0.90, "[Left Arrow]: Rotate Ralph Left")
    self.inst3 = addInstructions(0.85, "[Right Arrow]: Rotate Ralph Right")
    self.inst4 = addInstructions(0.80, "[Up Arrow]: Run Ralph Forward")
    self.inst5 = addInstructions(0.70, "[Down Arrow]: Ralph Runs Backwards")
    self.inst6 = addInstructions(0.65, "[S]: Select Target")
    self.inst7 = addInstructions(0.60, "[Space]: Fire Weapon")
    
    
    # Set up the environment
    #
    # This environment model contains collision meshes.  If you look
    # in the egg file, you will see the following:
    #
    #    <Collide> { Polyset keep descend }
    #
    # This tag causes the following mesh to be converted to a collision
    # mesh -- a mesh which is optimized for collision, not rendering.
    # It also keeps the original mesh, so there are now two copies ---
    # one optimized for rendering, one for collisions.  

    self.environ = loader.loadModel("models/world")      
    self.environ.reparentTo(render)
    self.environ.setPos(0,0,0)
    
    # Create the main character, Ralph

    ralphStartPos = self.environ.find("**/start_point").getPos()
    self.ralph = Actor("models/ralph",
                             {"run":"models/ralph-run",
                              "walk":"models/ralph-walk"})
    self.ralph.reparentTo(render)
    self.ralph.setScale(.2)
    self.ralph.setPos(ralphStartPos)
    
    #create bear1 target
    self.bear = Actor("models/osito")
    self.bear.reparentTo(render)
    self.bear.setScale(1)
    self.bear.setPos(5,25,3.5)
    self.bear.lookAt(180,0,0)
    
    #create bear2 target
   # self.bear = Actor("models/osito2")
   # self.bear.reparentTo(render)
    #self.bear.setScale(1)
    #self.bear.setPos(5,20,3.5)
    #self.bear.lookAt(180,0,0)
    
    

    # Create a floater object.  We use the "floater" as a temporary
    # variable in a variety of calculations.
    
    self.floater = NodePath(PandaNode("floater"))
    self.floater.reparentTo(render)

    # Accept the control keys for movement and rotation

    self.accept("escape", sys.exit)
    self.accept("arrow_left", self.setKey, ["left",1])
    self.accept("arrow_right", self.setKey, ["right",1])
    self.accept("arrow_up", self.setKey, ["forward",1])
    self.accept("arrow_down", self.setKey, ["backward",1])
    self.accept("a", self.setKey, ["cam-left",1])
    self.accept("s", self.setKey, ["select-target",1])
    self.accept("arrow_left-up", self.setKey, ["left",0])
    self.accept("arrow_right-up", self.setKey, ["right",0])
    self.accept("arrow_up-up", self.setKey, ["forward",0])
    self.accept("arrow_down-up", self.setKey, ["backward",0])
    self.accept("s-up", self.setKey, ["select-target",0])
    #self.accept("spacebar", self.setKey, ["fire",1])
    #self.accept("spacebar", self.setKey, ["fire", 0])
    self.accept("space", self.fire)

    taskMgr.add(self.move,"moveTask")

    # Game state variables
    self.isMoving = False

    # Set up the camera
    
    base.disableMouse()
    #base.camera.setPos(self.ralph.getX(),self.ralph.getY()+5,2)
    
    # We will detect the height of the terrain by creating a collision
    # ray and casting it downward toward the terrain.  One ray will
    # start above ralph's head, and the other will start above the camera.
    # A ray may hit the terrain, or it may hit a rock or a tree.  If it
    # hits the terrain, we can detect the height.  If it hits anything
    # else, we rule that the move is illegal.

    self.cTrav = CollisionTraverser()

    self.ralphGroundRay = CollisionRay()
    self.ralphGroundRay.setOrigin(0,0,1000)
    self.ralphGroundRay.setDirection(0,0,-1)
    self.ralphGroundCol = CollisionNode('ralphRay')
    self.ralphGroundCol.addSolid(self.ralphGroundRay)
    self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
    self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
    self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
    self.ralphGroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)

    self.camGroundRay = CollisionRay()
    self.camGroundRay.setOrigin(0,0,1000)
    self.camGroundRay.setDirection(0,0,-1)
    self.camGroundCol = CollisionNode('camRay')
    self.camGroundCol.addSolid(self.camGroundRay)
    self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
    self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
    self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)
    self.camGroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)

    # Uncomment this line to see the collision rays
    #self.ralphGroundColNp.show()
    #self.camGroundColNp.show()
   
    # Uncomment this line to show a visual representation of the 
    # collisions occuring
    #self.cTrav.showCollisions(render)
    
    # Create some lighting
    ambientLight = AmbientLight("ambientLight")
    ambientLight.setColor(Vec4(.3, .3, .3, 1))
    directionalLight = DirectionalLight("directionalLight")
    directionalLight.setDirection(Vec3(-5, -5, -5))
    directionalLight.setColor(Vec4(1, 1, 1, 1))
    directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
    render.setLight(render.attachNewNode(ambientLight))
    render.setLight(render.attachNewNode(directionalLight))

#Records the state of the arrow keys
def setKey(self, key, value):
    self.keyMap[key] = value

def fire(self):
    pos = self.ralph.getPos()
    #print "firing projectile at", pos
    
    projectile = loader.loadModel("models/projectile1")
    projectile.reparentTo(render)
    projectile.setScale(.3)
    projectile.setPos(self.ralph, -5, 1, 2) #the numbers, (x, y, z)  x= left/right, y= front to back, z= up/down
    #print "the projectile is now at", projectile.getPos()


# Accepts arrow keys to move either the player or the menu cursor,
# Also deals with grid checking and collision detection
def move(self, task):

    # save ralph's initial position so that we can restore it,
    # in case he falls off the map or runs into something.

    startpos = self.ralph.getPos()

    # If a move-key is pressed, move ralph in the specified direction.
    if (self.keyMap["select-target"]!=0):
        self.ralph.lookAt(self.bear)
        self.ralph.setH(self.ralph.getH()-180)
        
    if (self.keyMap["left"]!=0):
        self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt())
    if (self.keyMap["right"]!=0):
        self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt())
    if (self.keyMap["forward"]!=0):
        self.ralph.setY(self.ralph, -25 * globalClock.getDt())
    if (self.keyMap["backward"]!=0):
        self.ralph.setY(self.ralph, 10 * globalClock.getDt())
        
    #if (self.keyMap["fire"] != 0):
    #    self.bear = Actor("models/osito2")
    #    self.bear.reparentTo(render)
    #    self.bear.setScale(20)
    #    self.bear.setPos(5,25,3.5)
    #    self.bear.lookAt(180,0,0)
    
    # If ralph is moving, loop the run animation.
    # If he is standing still, stop the animation.

    if (self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0) or (self.keyMap["backward"]!=0):
        if self.isMoving is False:
            self.ralph.loop("run")
            self.isMoving = True
    else:
        if self.isMoving:
            self.ralph.stop()
            self.ralph.pose("walk",5)
            self.isMoving = False

    
    # Now check for collisions.

    self.cTrav.traverse(render)

    # Adjust ralph's Z coordinate.  If ralph's ray hit terrain,
    # update his Z. If it hit anything else, or didn't hit anything, put
    # him back where he was last frame.

    entries = []
    for i in range(self.ralphGroundHandler.getNumEntries()):
        entry = self.ralphGroundHandler.getEntry(i)
        entries.append(entry)
    entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                 x.getSurfacePoint(render).getZ()))
    if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
        self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())
    else:
        self.ralph.setPos(startpos)
   
   
   
   
    base.camera.reparentTo(self.ralph)
    base.camera.setPos(self.ralph, 0, 30, <img src="/uploads/default/original/1X/ae031e9834d19166598edd060b063d44f1829139.gif" width="15" height="15" alt="8)" title="Cool"/>
    base.camera.setHpr(0,180,180)
    
    
    
    return task.cont

w = World()
run()

If you want the projectile/ball to stay in position relative to Ralph then you have parent the projectile node to Ralph and not render; you can still set its position in relation to render “projectile.setPos(render, x, y, z)”, but I’m assuming that once it leaves his hand you no longer want it to follow him, so at that point you would reparent it to render.

Also please use the “code” button when posting large amounts of code, way easier to read and automatically collapses (and preserves indents):

import direct.directbase.DirectStart
from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionRay
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from panda3d.core import Vec3,Vec4,BitMask32
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
import random, sys, os, math

SPEED = 0.5
BULLET_LIFE = 2

# Function to put instructions on the screen.
def addInstructions(pos, msg):
return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
pos=(-1.3, pos), align=TextNode.ALeft, scale = .05)

# Function to put title on the screen.
def addTitle(text):
return OnscreenText(text=text, style=1, fg=(1,1,1,1),
pos=(1.3,-0.95), align=TextNode.ARight, scale = .07)

class World(DirectObject):

def __init__(self):

self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, "cam-left":0, "select-target":0, "fire":0}
base.win.setClearColor(Vec4(0,0,0,1))

# Post the instructions

self.title = addTitle("Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)")
self.inst1 = addInstructions(0.95, "[ESC]: Quit")
self.inst2 = addInstructions(0.90, "[Left Arrow]: Rotate Ralph Left")
self.inst3 = addInstructions(0.85, "[Right Arrow]: Rotate Ralph Right")
self.inst4 = addInstructions(0.80, "[Up Arrow]: Run Ralph Forward")
self.inst5 = addInstructions(0.70, "[Down Arrow]: Ralph Runs Backwards")
self.inst6 = addInstructions(0.65, "[S]: Select Target")
self.inst7 = addInstructions(0.60, "[Space]: Fire Weapon")


# Set up the environment
#
# This environment model contains collision meshes. If you look
# in the egg file, you will see the following:
#
# <Collide> { Polyset keep descend }
#
# This tag causes the following mesh to be converted to a collision
# mesh -- a mesh which is optimized for collision, not rendering.
# It also keeps the original mesh, so there are now two copies ---
# one optimized for rendering, one for collisions. 

self.environ = loader.loadModel("models/world") 
self.environ.reparentTo(render)
self.environ.setPos(0,0,0)

# Create the main character, Ralph

ralphStartPos = self.environ.find("**/start_point").getPos()
self.ralph = Actor("models/ralph",
{"run":"models/ralph-run",
"walk":"models/ralph-walk"})
self.ralph.reparentTo(render)
self.ralph.setScale(.2)
self.ralph.setPos(ralphStartPos)

#create bear1 target
self.bear = Actor("models/osito")
self.bear.reparentTo(render)
self.bear.setScale(1)
self.bear.setPos(5,25,3.5)
self.bear.lookAt(180,0,0)

#create bear2 target
# self.bear = Actor("models/osito2")
# self.bear.reparentTo(render)
#self.bear.setScale(1)
#self.bear.setPos(5,20,3.5)
#self.bear.lookAt(180,0,0)



# Create a floater object. We use the "floater" as a temporary
# variable in a variety of calculations.

self.floater = NodePath(PandaNode("floater"))
self.floater.reparentTo(render)

# Accept the control keys for movement and rotation

self.accept("escape", sys.exit)
self.accept("arrow_left", self.setKey, ["left",1])
self.accept("arrow_right", self.setKey, ["right",1])
self.accept("arrow_up", self.setKey, ["forward",1])
self.accept("arrow_down", self.setKey, ["backward",1])
self.accept("a", self.setKey, ["cam-left",1])
self.accept("s", self.setKey, ["select-target",1])
self.accept("arrow_left-up", self.setKey, ["left",0])
self.accept("arrow_right-up", self.setKey, ["right",0])
self.accept("arrow_up-up", self.setKey, ["forward",0])
self.accept("arrow_down-up", self.setKey, ["backward",0])
self.accept("s-up", self.setKey, ["select-target",0])
#self.accept("spacebar", self.setKey, ["fire",1])
#self.accept("spacebar", self.setKey, ["fire", 0])
self.accept("space", self.fire)

taskMgr.add(self.move,"moveTask")

# Game state variables
self.isMoving = False

# Set up the camera

base.disableMouse()
#base.camera.setPos(self.ralph.getX(),self.ralph.getY()+5,2)

# We will detect the height of the terrain by creating a collision
# ray and casting it downward toward the terrain. One ray will
# start above ralph's head, and the other will start above the camera.
# A ray may hit the terrain, or it may hit a rock or a tree. If it
# hits the terrain, we can detect the height. If it hits anything
# else, we rule that the move is illegal.

self.cTrav = CollisionTraverser()

self.ralphGroundRay = CollisionRay()
self.ralphGroundRay.setOrigin(0,0,1000)
self.ralphGroundRay.setDirection(0,0,-1)
self.ralphGroundCol = CollisionNode('ralphRay')
self.ralphGroundCol.addSolid(self.ralphGroundRay)
self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
self.ralphGroundHandler = CollisionHandlerQueue()
self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)

self.camGroundRay = CollisionRay()
self.camGroundRay.setOrigin(0,0,1000)
self.camGroundRay.setDirection(0,0,-1)
self.camGroundCol = CollisionNode('camRay')
self.camGroundCol.addSolid(self.camGroundRay)
self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)
self.camGroundHandler = CollisionHandlerQueue()
self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)

# Uncomment this line to see the collision rays
#self.ralphGroundColNp.show()
#self.camGroundColNp.show()

# Uncomment this line to show a visual representation of the 
# collisions occuring
#self.cTrav.showCollisions(render)

# Create some lighting
ambientLight = AmbientLight("ambientLight")
ambientLight.setColor(Vec4(.3, .3, .3, 1))
directionalLight = DirectionalLight("directionalLight")
directionalLight.setDirection(Vec3(-5, -5, -5))
directionalLight.setColor(Vec4(1, 1, 1, 1))
directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
render.setLight(render.attachNewNode(ambientLight))
render.setLight(render.attachNewNode(directionalLight))

#Records the state of the arrow keys
def setKey(self, key, value):
self.keyMap[key] = value

def fire(self):
pos = self.ralph.getPos()
#print "firing projectile at", pos

projectile = loader.loadModel("models/projectile1")
projectile.reparentTo(render)
projectile.setScale(.3)
projectile.setPos(self.ralph, -5, 1, 2) #the numbers, (x, y, z) x= left/right, y= front to back, z= up/down
#print "the projectile is now at", projectile.getPos()


# Accepts arrow keys to move either the player or the menu cursor,
# Also deals with grid checking and collision detection
def move(self, task):

# save ralph's initial position so that we can restore it,
# in case he falls off the map or runs into something.

startpos = self.ralph.getPos()

# If a move-key is pressed, move ralph in the specified direction.
if (self.keyMap["select-target"]!=0):
self.ralph.lookAt(self.bear)
self.ralph.setH(self.ralph.getH()-180)

if (self.keyMap["left"]!=0):
self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt())
if (self.keyMap["right"]!=0):
self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt())
if (self.keyMap["forward"]!=0):
self.ralph.setY(self.ralph, -25 * globalClock.getDt())
if (self.keyMap["backward"]!=0):
self.ralph.setY(self.ralph, 10 * globalClock.getDt())

#if (self.keyMap["fire"] != 0):
# self.bear = Actor("models/osito2")
# self.bear.reparentTo(render)
# self.bear.setScale(20)
# self.bear.setPos(5,25,3.5)
# self.bear.lookAt(180,0,0)

# If ralph is moving, loop the run animation.
# If he is standing still, stop the animation.

if (self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0) or (self.keyMap["backward"]!=0):
if self.isMoving is False:
self.ralph.loop("run")
self.isMoving = True
else:
if self.isMoving:
self.ralph.stop()
self.ralph.pose("walk",5)
self.isMoving = False


# Now check for collisions.

self.cTrav.traverse(render)

# Adjust ralph's Z coordinate. If ralph's ray hit terrain,
# update his Z. If it hit anything else, or didn't hit anything, put
# him back where he was last frame.

entries = []
for i in range(self.ralphGroundHandler.getNumEntries()):
entry = self.ralphGroundHandler.getEntry(i)
entries.append(entry)
entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
x.getSurfacePoint(render).getZ()))
if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())
else:
self.ralph.setPos(startpos)




base.camera.reparentTo(self.ralph)
base.camera.setPos(self.ralph, 0, 30, 8)
base.camera.setHpr(0,180,180)



return task.cont


w = World()
run()