Collision Optimization... or am I doomed?

Hello all and thanks for the help ahead of time! I’m really hoping that there is some error in my code (although I’m still willing to find a work around if there isn’t). I’m working on a game similar to Alien Swarm which was a mod in the UT2004 engine. It’s going to be a bit more like Diablo but with the use of arrow keys to move and mouse to aim a lazer gun. I wanted this code to run on a fairly basic machine and have been testing it on a 2ghz 2gig RAM ATI x600 notebook computer. My concern is that pstats is showing a framerate drop based on Collision. I’ve looked over the forums and made some changes to my code based on this (these forums are great), but am still not getting the framerate that I want at this point. I’ve broken my code up into separate py files as below:

#main.py
from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from direct.task import Task
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import *

from player import Player
from environment import Environ
from camera import mainCamera
from NPC import NPC

class World(DirectObject):
    def __init__(self):
        
        base.disableMouse()
        self.player = Player()
        self.environ = Environ()
        self.NPC = NPC()
        self.NPC2 = NPC()
        self.NPC3 = NPC()
        self.NPC2.prime.setX(3)
        self.NPC2.prime.setY(3)
        self.NPC3.prime.setX(4)
        self.NPC3.prime.setY(4)
        self.mainCamera = mainCamera(self.player.prime)
        self.player.prime.setH(90)

        taskMgr.add(self.player.movement, "movement")
        taskMgr.add(self.player.faceMouse, "faceMouse")
        taskMgr.add(self.NPC.chasePlayer, "chase", extraArgs=[self.player], appendTask=True)
        taskMgr.add(self.NPC2.chasePlayer, "chase", extraArgs=[self.player], appendTask=True)
        taskMgr.add(self.NPC3.chasePlayer, "chase", extraArgs=[self.player], appendTask=True)

World()
run()
#environ.py
from pandac.PandaModules import *
import direct.directbase.DirectStart
from collisionSystems import fromCol

class Environ:
    def __init__(self):
        self.wallsC = loader.loadModel("models/collisionWalls")
        self.wallsC.reparentTo(render)
        self.wallsC.setCollideMask(BitMask32(0x10))

#collisionSystems.py
from pandac.PandaModules import *

base.cTrav = CollisionTraverser()
base.cTrav.setRespectPrevTransform(1)

def fromCol(parent,handler,type,mask = BitMask32.allOn()):
        nodepath = parent.attachNewNode(CollisionNode('frmcol'))
        nodepath.node().addSolid(type)
        nodepath.node().setFromCollideMask(mask)
        nodepath.setCollideMask(BitMask32.allOff())
        base.cTrav.addCollider(nodepath,handler)
        handler.addCollider(nodepath,parent)


#camera.py
from direct.showbase.DirectObject import DirectObject
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import *


class mainCamera(DirectObject):
    def __init__(self,avatar1):
        self.avatar1 = avatar1
        self.loadCamera()

    def loadCamera(self):
        self.dum = render.attachNewNode("dum")
        self.dum.reparentTo(self.avatar1)
        self.dum.node().setEffect(CompassEffect.make(render))
        base.camera.reparentTo(self.dum)
        base.camera.setPos(0, 0, 15)
        base.camera.lookAt(self.avatar1)
#player.py
from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.task import Task
from direct.actor import Actor
from direct.fsm import FSM
from collisionSystems import fromCol
import sys, math


class Player(FSM.FSM):
    """set up the main player"""
    def __init__(self):
        FSM.FSM.__init__(self,'player')

        self.prime=NodePath("eve")
        self.playerActor=Actor.Actor("models/eve", {"run":"models/eve_run"})
        self.playerActor.setScale(.2)
        self.playerActor.reparentTo(self.prime)
        self.actorNeck = self.playerActor.controlJoint(None, 'modelRoot', 'Neck')
        self.prime.reparentTo(render)
        self.request('Run')

        self.prime.setCollideMask(BitMask32.allOff())
        fromCol(self.prime,CollisionHandlerPusher(),CollisionSphere(0,0,.7,.3),0X10)

        self.accept("escape", sys.exit)
        self.keys = {"moveLeft" : 0,
                     "moveRight" : 0,
                     "moveUp" : 0,
                     "moveDown" : 0}

        self.accept("arrow_left", self.setKey, ["moveLeft" , 1])
        self.accept("arrow_left-up", self.setKey, ["moveLeft" , 0])

        self.accept("arrow_right", self.setKey, ["moveRight" , 1])
        self.accept("arrow_right-up", self.setKey, ["moveRight" , 0])

        self.accept("arrow_up", self.setKey, ["moveUp" , 1])
        self.accept("arrow_up-up", self.setKey, ["moveUp" , 0])

        self.accept("arrow_down", self.setKey, ["moveDown" , 1])
        self.accept("arrow_down-up", self.setKey, ["moveDown" , 0])


        self.direction = 0
        self.up = 0
        self.down = 0
        self.left = 0
        self.right = 0
        self.speed = 3 #moving speed
        self.prime.setZ(0)


    def movement(self, task):
        if self.keys["moveLeft"]:
           self.prime.setX(self.prime.getX()-.04)
           self.left = 5
        else:
           self.left = 0

        if self.keys["moveRight"]:
           self.prime.setX(self.prime.getX()+.04)
           self.right = 11
        else:
           self.right = 0

        if self.keys["moveUp"]:
           self.prime.setY(self.prime.getY()+.04)
           self.up = 3
        else:
           self.up = 0
        if self.keys["moveDown"]:
           self.prime.setY(self.prime.getY()-.04)
           self.down = 7
        else:
           self.down = 0
        return direct.task.Task.cont

    def faceMouse (self, task):
        if base.mouseWatcherNode.hasMouse():
           bodyOffset = 90
           self.direction = self.left+self.up+self.right+self.down

           if self.direction == 11:
              directionOffset = 0
              self.prime.setH(0 + bodyOffset)
           elif self.direction == 14:
              directionOffset = 45
              self.prime.setH(45 + bodyOffset)
           elif self.direction == 3:
              directionOffset = 90
              self.prime.setH(90 + bodyOffset)
           elif self.direction == 8:
              directionOffset = 135
              self.prime.setH(135 + bodyOffset)
           elif self.direction == 5:
              directionOffset = 180
              self.prime.setH(180 + bodyOffset)
           elif self.direction == 12:
              directionOffset = 225
              self.prime.setH(225 + bodyOffset)
           elif self.direction == 7:
              directionOffset = 270
              self.prime.setH(270 + bodyOffset)
           elif self.direction == 18:
              directionOffset = 315
              self.prime.setH(315 + bodyOffset)
           else:
              directionOffset = 270
              self.prime.setH(270 + bodyOffset)

           mpos = base.mouseWatcherNode.getMouse()
           curH = self.prime.getH()
           rSquare = mpos.getX()*mpos.getX() + mpos.getY()*mpos.getY()
           angle = math.acos(mpos.getX()/math.sqrt(rSquare))

           if mpos.getY() < 0:
              self.actorNeck.setP((180 + (180 - math.degrees(angle))) - directionOffset)
           if mpos.getY() >= 0:
              self.actorNeck.setP(math.degrees(angle) - directionOffset)

        return direct.task.Task.cont




    def setKey(self, key, val):
        self.keys[key] = val

    def enterRun(self):
        self.playerActor.loop("run")
    def exitRun(self):
        self.playerActor.stop("run")
#NPC.py
from pandac.PandaModules import *
from direct.actor import Actor
import direct.directbase.DirectStart
from direct.fsm import FSM
from collisionSystems import fromCol

class NPC(FSM.FSM):
    def __init__(self):
        FSM.FSM.__init__(self,'NPC')
        self.prime=NodePath(PandaNode("ralph"))
        self.npcActor=Actor.Actor("models/ralph",{"run":"models/ralph-run","walk":"models/ralph-walk"})
        self.npcActor.setScale(.2)
        self.npcActor.reparentTo(self.prime)
        self.prime.reparentTo(render)
        self.prime.setX(2)
        self.prime.setY(2)
        self.prime.setZ(0)
        self.prime.setCollideMask(BitMask32.allOff())

        fromCol(self.prime,CollisionHandlerPusher(),CollisionSphere(0,0,.7,.3),0x10)
        self.request('Run')

    def chasePlayer (self, player, task):
        self.prime.lookAt(player.prime)
        self.prime.setP(0)

        self.prime.setH(self.prime.getH()-180)
        self.target = player.prime.getPos()
        if self.prime.getY() < self.target.getY():
              self.prime.setY(self.prime.getY() + .03)
        if self.prime.getY() > self.target.getY():
              self.prime.setY(self.prime.getY() - .03)

        if self.prime.getX() < self.target.getX():
              self.prime.setX(self.prime.getX() + .03)
        if self.prime.getX() > self.target.getX():
              self.prime.setX(self.prime.getX() - .03)

        return direct.task.Task.cont


    def enterRun(self):
        self.npcActor.loop("run")
    def exitRun(self):
        self.npcActor.stop("run")

My collision is going up against an object specified with the collide tag that is made of 106 tris. If it’s a matter of needing to divide my level into collision areas or even set up a 2D system I’d be willing to do that. I just wanted to see if there was something I did that is causing so much slowdown before I start to optimize any further. With collisions turned off, I get 60 fps. with collisions on, it drops to 28. It still stays at 60 if I divide my level down to 30 tris it stays at 60.

Thanks for any advice!

106 tris is certainly not that much, and your collisions shouldn’t cost too much. Are you running your measurements with “sync-video 0” in your Config.prc file? If you leave video sync enabled, then the tiniest hit can change you from 60fps to 30fps.

David

Thanks for the tip. Oddly enough, the frame rate is the same for v-sync both on and off when I have collisions on (all the way down to 28 fps), then it jumps to 280 with collisions off.

i can hardly belive testing 4 spheres against a 160 tris mesh to take that much computation time. even in worst case it should be a lot faster. can you enable collision-visualsation and post a screenshot so we can get a glimpse on what’s going on?

I’ve posted the game files to my site:

brothersgrim.us/gameFiles.zip

I’ve tried to use the model from the ball in maze… thinking that it may be something to do with the exporter but got the same results. I can run the ball in maze sample just fine, along with roaming ralph. The collisions seem to be showing up a bit out of range, but I’m unsure if this is the issue as even with things pushed out a bit I get the same results. I can post some screenshots if need be. Just a bit unsure of where to go from here.

I think I figured out the problem with my files. In the collision egg there was a line that read Dart{1}. I removed that and now everything is perfect! I read a bit in the forums that this is for compressing geometry egg files but never really saw what it would be used for. Can anyone enlighten me? I noted that my collisions are now showing up at the right time as well. Before, they were off by a bit. Is it rounding down a float or something like that?

That’s for animated egg files, e.g. Actors. If it appears in an egg file that’s intended for static geometry or collisions, it’s usually due to setting the wrong parameters on collisions.

David