How to Apply Velocity to a Model / Collision Help

Hi, I’m trying to make a small jumping system for my testing program. How can I give my model an upwards velocity so that it jumps? I have already successfully implemented gravity.

from direct.showbase.ShowBase import ShowBase

from direct.actor.Actor import Actor

from panda3d.core import loadPrcFileData
from panda3d.core import AmbientLight
from panda3d.core import VBase4
from panda3d.core import CollisionSphere
from panda3d.core import CollisionHandlerPusher
from panda3d.core import CollisionTraverser
from panda3d.core import CollisionNode
from panda3d.core import Vec3

loadPrcFileData("", "window-title Test Program")
loadPrcFileData("", "win-size 800 500")

class Test(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        self.z_speed = 0
        self.gravity = -9.81
        self.x = 0
        self.y = 0
        
        base.disableMouse()
        base.setBackgroundColor(0, 0, 0)
        
        light = AmbientLight("light")
        light.setColor(VBase4(0.4, 0.4, 0.4, 1))
        lightNP = self.render.attachNewNode(light)
        self.render.setLight(lightNP)

        env = self.loader.loadModel("models/egg/environment")
        env.reparentTo(self.render)
        env.setScale(0.25)
        env.setPos(-8, 42, 0)
        
        something = self.loader.loadModel("untitled")
        something.reparentTo(self.render)
        something.setPos(0, 10, 0)
        
        self.panda = Actor("models/panda-model", {"walk": "models/panda-walk4"})
        self.panda.reparentTo(self.render)
        self.panda.setScale(0.005)
        self.panda.setPos(0, 0, 50)
        
        self.camera.reparentTo(self.panda)
        self.camera.setY(3000)
        self.camera.setZ(700)
        self.camera.setH(180)
        
        self.walk = {"forward": False, "backward": False, "left": False, "right": False, "jump": False}
        
        self.accept("w", self.setKey, ["forward", True])
        self.accept("s", self.setKey, ["backward", True])
        
        self.accept("a", self.setKey, ["left", True])
        self.accept("d", self.setKey, ["right", True])
        
        self.accept("space", self.setKey, ["jump", True])
        
        self.accept("w-up", self.setKey, ["forward", False])
        self.accept("s-up", self.setKey, ["backward", False])
        
        self.accept("a-up", self.setKey, ["left", False])
        self.accept("d-up", self.setKey, ["right", False])
        
        self.accept("space-up", self.setKey, ["jump", False])
        
        colNode = CollisionNode("colNode")
        colSphere = CollisionSphere(self.panda.getX(), self.panda.getY(), self.panda.getZ() + 300, 250)
        colNode.addSolid(colSphere)
        colNP = self.render.attachNewNode(colNode)
        
        colNP.reparentTo(self.panda)
        
        self.cTrav = CollisionTraverser()
        self.cHan = CollisionHandlerPusher()
        
        self.cHan.addCollider(colNP, self.panda)
        self.cTrav.addCollider(colNP, self.cHan)
        
        taskMgr.add(self.jumpPanda, "Jump Panda")
        taskMgr.add(self.controlGravity, "Gravity Control")
        taskMgr.add(self.movePanda, "Panda Control")
        taskMgr.add(self.checkCollisions, "Collision Check")
        
    def jumpPanda(self, task):
        
        return task.cont
        
    def checkCollisions(self, task):
        
        self.cTrav.traverse(self.render)
        
        return task.cont
        
    def setKey(self, key, value):
        self.walk[key] = value
        
    def controlGravity(self, task):
        
        dt = globalClock.getDt()
        
        self.z_speed += self.gravity * dt
        self.panda.setZ(max(self.panda.getZ() + self.z_speed * dt, 0))
        
        return task.cont
        
    def movePanda(self, task):
        
        dt = globalClock.getDt()
        
        if self.walk["forward"]:
            self.panda.setY(self.panda, -1000 * dt)
            if self.panda.getCurrentAnim() != "walk":
                self.panda.setPlayRate(1, "walk")
                self.panda.loop("walk")
                
        if self.walk["backward"]:
            self.panda.setY(self.panda, 1000 * dt)
            if self.panda.getCurrentAnim() != "walk":
                self.panda.setPlayRate(-1, "walk")
                self.panda.loop("walk")
                
        if not self.walk["forward"]:
            if not self.walk["backward"]:
                if self.panda.getCurrentAnim() == "walk":
                    self.panda.setPlayRate(0, "walk")
                    self.panda.pose("walk", 0)
                    self.panda.stop("walk")
                    
        if self.walk["left"]:
            self.panda.setH(self.panda, 100 * dt)
            
        if self.walk["right"]:
            self.panda.setH(self.panda, -100 * dt)
            
        return task.cont

t = Test()
t.run()

The code might be a bit messy, but it’s just a testing program. Also, I don’t know much into collision detection yet, so if I am doing something wrong with the collisions, please let me know so I can learn. If I walk into my custom created model, the panda tends to glitch around and it drives the camera crazy. Also, I punzipped the environment model and put it in a custom egg folder in the models folder, as you can see in the path when I load the environment model. I edited the egg file to add the following to it:

<Collide> { Polyset keep descend }

The only thing in the entire model that detects collision is one of the trees, and it detects the collision without any bugs (the panda doesn’t walk through it). I did the same with my custom created model, but the panda and camera teleports back and forth inside it (a little difficult to explain, but I can put it as: crazy). Thanks!

Regarding jumping, would it not be simply a matter of subtracting a value from your object’s “self.z_speed” variable when the relevant event (such as a key-press) is fired? This should presumably be done once per event, rather than repeating while the button is held, and only become active again when the player can next jump (whether having landed on the ground or on activating a feature such as a “double-jump”).

Thanks! I successfully applied jumping to my model. Also, I figured out that there were multiple groups in the environment egg file, so I managed to get the ‘into’ collision objects into the whole environment model. Everything works fine, however I get a problem when I walk into my custom model: the panda goes crazy (it teleports around like crazy in the model and around the model, and it’s difficult to get out). Here are my codes:

from direct.showbase.ShowBase import ShowBase

from direct.gui.DirectButton import DirectButton

from direct.actor.Actor import Actor

from panda3d.core import loadPrcFileData
from panda3d.core import AmbientLight
from panda3d.core import VBase4
from panda3d.core import CollisionSphere
from panda3d.core import CollisionHandlerPusher
from panda3d.core import CollisionTraverser
from panda3d.core import CollisionNode
from panda3d.core import TextNode

import sys

loadPrcFileData("", "window-title Test Program")
loadPrcFileData("", "win-size 800 500")
loadPrcFileData("", "show-frame-rate-meter True")

class Test(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        self.z_speed = 0
        self.gravity = -20
        
        base.disableMouse()
        base.camLens.setFar(100000)
        base.setBackgroundColor(0, 0, 0)
        
        light = AmbientLight("light")
        light.setColor(VBase4(0.4, 0.4, 0.4, 1))
        lightNP = self.render.attachNewNode(light)
        self.render.setLight(lightNP)

        env = self.loader.loadModel("models/egg/environment")
        env.reparentTo(self.render)
        env.setScale(0.25)
        env.setPos(-8, 42, 0)
        
        something = self.loader.loadModel("untitled")
        something.reparentTo(self.render)
        something.setPos(0, 10, 0)
        
        self.panda = Actor("models/panda-model", {"walk": "models/panda-walk4"})
        self.panda.reparentTo(self.render)
        self.panda.setScale(0.005)
        self.panda.setPos(0, 0, 0)
        
        self.camera.reparentTo(self.panda)
        self.camera.setY(3000)
        self.camera.setZ(700)
        self.camera.setH(180)
        
        self.walk = {"forward": False, "backward": False, "left": False, "right": False, "jump": False}
        
        self.accept("w", self.setKey, ["forward", True])
        self.accept("s", self.setKey, ["backward", True])
        
        self.accept("a", self.setKey, ["left", True])
        self.accept("d", self.setKey, ["right", True])
        
        self.accept("space", self.setKey, ["jump", True])
        
        self.accept("w-up", self.setKey, ["forward", False])
        self.accept("s-up", self.setKey, ["backward", False])
        
        self.accept("a-up", self.setKey, ["left", False])
        self.accept("d-up", self.setKey, ["right", False])
        
        self.accept("space-up", self.setKey, ["jump", False])
        
        colNode = CollisionNode("colNode")
        colSphere = CollisionSphere(self.panda.getX(), self.panda.getY(), self.panda.getZ() + 300, 250)
        colNode.addSolid(colSphere)
        colNP = self.render.attachNewNode(colNode)
        
        colNP.reparentTo(self.panda)
        
        self.cTrav = CollisionTraverser()
        self.cHan = CollisionHandlerPusher()
        
        self.cHan.addCollider(colNP, self.panda)
        self.cTrav.addCollider(colNP, self.cHan)
        
        taskMgr.add(self.controlGravity, "Gravity Control")
        taskMgr.add(self.movePanda, "Panda Control")
        taskMgr.add(self.checkCollisions, "Collision Check")
        
        pandaName = TextNode("pandaName")
        pandaName.setText("Bob the Panda")
        pandaName.setAlign(TextNode.ACenter)
        
        pandaName.setFrameColor(0, 0, 1, 1)
        pandaName.setFrameAsMargin(0, 0, 0, 0)
        
        pandaName.setCardColor(0, 0, 0, 0.5)
        pandaName.setCardAsMargin(0, 0, 0, 0)
        pandaName.setCardDecal(True)
        
        self.pandaNameNP = self.render.attachNewNode(pandaName)
        self.pandaNameNP.setScale(0.5)
        
        self.pandaNameNP.setBillboardPointEye()
        
        taskMgr.add(self.updatePandaName, "Panda Name")
        taskMgr.add(self.jumpPanda, "Jump Panda")
        
        self.accept("escape", sys.exit)
        
        def teleportSpawn():
            self.panda.setPos(0, 0, 0)
            
        tpSpawn = DirectButton(text = "Teleport to Spawn Point",
                               scale = (0.1, 0.1, 0.1),
                               pos = (-1, 1, 0.8),
                               command = teleportSpawn)
        
    def updatePandaName(self, task):
        
        self.pandaNameNP.setPos(self.panda.getX(), self.panda.getY(), self.panda.getZ() + 3)
        
        return task.cont
        
    def jumpPanda(self, task):
        
        if self.walk["jump"]:
            if self.panda.getZ() == 0:
                self.z_speed = 7
        
        return task.cont
        
    def checkCollisions(self, task):
        
        self.cTrav.traverse(self.render)
        
        return task.cont
        
    def setKey(self, key, value):
        self.walk[key] = value
        
    def controlGravity(self, task):
        
        dt = globalClock.getDt()
        
        self.z_speed += self.gravity * dt
        self.panda.setZ(max(self.panda.getZ() + self.z_speed * dt, 0))
        
        return task.cont
        
    def movePanda(self, task):
        
        dt = globalClock.getDt()
        
        if self.walk["forward"]:
            self.panda.setY(self.panda, -1000 * dt)
            if self.panda.getCurrentAnim() != "walk":
                self.panda.setPlayRate(1, "walk")
                self.panda.loop("walk")
                
        if self.walk["backward"]:
            self.panda.setY(self.panda, 1000 * dt)
            if self.panda.getCurrentAnim() != "walk":
                self.panda.setPlayRate(-1, "walk")
                self.panda.loop("walk")
                
        if not self.walk["forward"]:
            if not self.walk["backward"]:
                if self.panda.getCurrentAnim() == "walk":
                    self.panda.setPlayRate(0, "walk")
                    self.panda.pose("walk", 0)
                    self.panda.stop("walk")
                    
        if self.walk["left"]:
            self.panda.setH(self.panda, 100 * dt)
            
        if self.walk["right"]:
            self.panda.setH(self.panda, -100 * dt)
            
        return task.cont

t = Test()
t.run()

The pastebin for the egg file code: pastebin.com/wfE7NRaR

Thanks!

Hmm… I haven’t looked at the egg file, but one thing that might be worth checking (and which might be easiest to check in your modelling program) is whether your environment model’s collision geometry has its normals all pointing in the right directions (and in particular, not inverted): incorrect normals might produce odd collisions, if I’m not much mistaken.

Hi, it seems that’s not fully the issue. I attempted to make another model, and the panda still glitches with any model I create. I checked to see if each polygon was flipped properly (which they are) and here’s what it looks like when I try to texture it:

imgur.com/26pfM34

That doesn’t seem to be right. Another problem is, you can see how steep that slope is, and the panda can’t even walk up it because it keeps getting pulled down by the gravity, which lowering the gravity (or, really, increasing it because it’s a negative value), from -20 to -9.81 doesn’t seem to allow the panda to even go up the slope any easier. All the gravity does for the panda is how fast the panda gets pulled down. Anyway, when the panda walks around the model (especially the raised middle) it glitches. Also, if I move the panda back down the ramp, it goes down incredibly fast.

In addition to that, the name tag above the panda glitches around when I walk also. It glitches even more when I jump. It sticks with the panda like it should, but it’s glitchy when I move the panda around.

I’m also trying out more to the collision system, and I found that I can use the CollisionHandlerFloor instead of a gravity system. I created another file to test it out and the panda goes down to the environment model, but once it reaches it, the panda teleports itself back to where it was when it started and it continues going down and teleports back up, goes down, etc. like a continuous loop, instead of just going to the ground and stopping.

from direct.showbase.ShowBase import ShowBase

from panda3d.core import CollisionTraverser
from panda3d.core import CollisionHandlerFloor
from panda3d.core import CollisionNode
from panda3d.core import CollisionRay

class World(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        base.disableMouse()
        
        self.camera.setPos(0, -24, 10)
        self.camera.lookAt(0, 0, 5)
        
        self.panda = self.loader.loadModel("models/panda-model")
        self.panda.reparentTo(self.render)
        self.panda.setScale(0.005)
        self.panda.setPos(0, 0, 5)
        
        node = CollisionNode("node")
        ray = CollisionRay(0, 0, 0, 0, 0, -1)
        node.addSolid(ray)
        
        self.pandaNP = self.panda.attachNewNode(node)
        self.pandaNP.show()
        
        env = self.loader.loadModel("models/egg/environment")
        env.reparentTo(self.render)
        env.setScale(0.25)
        env.setPos(-8, 42, 0)
        
        self.cTrav = CollisionTraverser()
        cHan = CollisionHandlerFloor()
        
        cHan.addCollider(self.pandaNP, env)
        self.cTrav.addCollider(self.pandaNP, cHan)
        
        taskMgr.add(self.checkCol, "Check Collisions")
        
    def checkCol(self, task):
        
        self.cTrav.traverse(self.render)
        
        return task.cont
        
w = World()
w.run()

I don’t know much about CollisionHandlerFloor, I’m afraid, so I won’t address that in this post, I think.

Going back to your previous version, one problem that I see is that your gravity code doesn’t appear to have the z-speed react to the model being on the floor. As you have it, your object’s z-speed continues to increase regardless of whether the object is on the ground or not–if you leave your object standing still, it’s z-speed will eventually be incredibly high, which may very well interfere with your physics. (You also don’t appear to have a terminal velocity for falls, by the way.)

As to your screenshot, it doesn’t really tell me whether the normals are correct or not: unless you have backface culling turned on (which isn’t evident in the screenshot) the normals could well be reversed. However, you do say that you’ve checked them yourself, so I’ll take it that they’re correct.

Actually, the CollisionHandlerFloor is in a separate file, so it is not being used in the same file that the gravity physics are. Also, blender doesn’t show whether the polygons are normal or not in the screenshot because my model was in texture paint mode. If it was in edit mode, I can see the shading the piece is slightly different if it is inverted, also if I turn the model at different angles, the pieces would appear black on the reverted side. I made sure every piece was not inside-out.

UPDATE: Another thing I forgot to mention: Take a look back at the code I posted in my last post. You notice how the camera is just set to be a certain position? Do you see it being reparented to any model, especially the panda, at all? Apparently it’s reparented to the panda… so when the panda falls to the ground and teleports back up in the bugged loop, the camera keeps going with it.

Erm, I realise that–as I said, I wasn’t addressing that approach. I was referring back to your earlier post, in which you had implemented gravity.

Have you abandoned that previous approach for one that uses CollisionHandlerFloor, then? If so, then my most recent post before this is likely not of much help; if not, then it may at least help with that previous approach.

(From your post, I gathered that you were experimenting with both approaches.)

Indeed; I was simply pointing out that there was little for me to gather from that image regarding the normals, which is what your post was discussing at the point at which you linked to it. I do also see that you used it as reference for the next point, regarding the slope.