This is a simple way to keep a vehicle aligned to the Terrain it is on.
To get to the good part, skip to the collisionUpdate function.
#
# Tank.py
#
# Author: mss
#
#
# Imports
#
from direct.showbase import DirectObject
from panda3d.core import BitMask32
from panda3d.core import CollisionNode
from panda3d.core import CollisionSphere
from panda3d.core import CollisionRay
from panda3d.core import CollisionSegment
from panda3d.core import CollisionHandlerGravity
from panda3d.core import CollisionHandlerQueue
from panda3d.core import Vec3
from panda3d.core import Point3
from panda3d.core import rad2Deg
from direct.interval.IntervalGlobal import *
from Game import Game
#
# Tank Class
#
class Tank(DirectObject.DirectObject):
def __init__(self):
self.Reverse = False
self.Left = False
self.Right = False
self.Forward = False
self.Velocity = 0
self.Tank = Game.getLoader().loadModel("content/Tank.egg")
self.Tank.setPos(0,0,10)
self.Tank.reparentTo(Game.getRender())
self.Handler = CollisionHandlerGravity()
self.cQueue = CollisionHandlerQueue()
#
# Setup the CollisionRay.
# This is used for collisions against the Terrain.
# I change the bitmask so the ray doesn't collide with the polyset
# mesh below.
#
self.Ray = CollisionRay(0,0,1,0,0,-1)
self.RayCollider = self.Tank.attachNewNode(CollisionNode("CollisionRay"))
self.RayCollider.node().addSolid(self.Ray)
self.RayCollider.setCollideMask(BitMask32.allOff())
self.Handler.addCollider(self.RayCollider, self.Tank)
Game.getTraverser().addCollider(self.RayCollider, self.Handler)
Game.getTraverser().addCollider(self.RayCollider, self.cQueue)
#
# Setup the polyset Collision mesh created in Blender.
# Bitmask is all 1's to ignore collisions with the ray.
#
self.Collider = self.Tank.find("*/Collision")
self.Collider.node().addSolid(self.Ray)
self.Collider.setCollideMask(BitMask32.allOn())
self.Handler.addCollider(self.Collider, self.Tank)
Game.getTraverser().addCollider(self.Collider, self.Handler)
#
# Init Key Input
#
self.accept('w', self.wKey)
self.accept('w-up', self.wKeyUp)
self.accept('s', self.sKey)
self.accept('s-up', self.sKeyUp)
self.accept('a', self.aKey)
self.accept('a-up', self.aKeyUp)
self.accept('d', self.dKey)
self.accept('d-up', self.dKeyUp)
#
# Tasks
#
taskMgr.add(self.moveUpdate, "TankMoveUpdate")
taskMgr.add(self.collisionUpdate, "TankCollisionUpdate")
return
#
# Collision Task
#
def collisionUpdate(self, task):
if self.cQueue.getNumEntries() > 0:
#
# For now, the Tank only collides with the Terrain so I don't
# have to check to make sure this is the collision I want.
#
entry = self.cQueue.getEntry(0)
if entry.hasSurfacePoint():
np = entry.getIntoNodePath()
point = Vec3(entry.getSurfacePoint(Game.getRender()))
normal = Vec3(entry.getSurfaceNormal(Game.getRender()))
pos = Vec3(self.Tank.getPos(render))
#
# Align Tank To Terrain
#
# I use headsUp to orientate the tank to the slope of the
# terrain, however: I have to hack the heading so headsUp()
# doesn't mistakingly change it, which it does on "harder"
# angles.
#
# I have the Pitch and Roll move to the Pitch and Roll
# given by headsUp, over a set amount of time.
# LerpIntervals will not work because it will lerp all HPR
# values, constraining user heading changes.
#
# It gets pretty nasty on steep angles, but tanks shouldn't
# be going up them anyway..
#
fwd = Game.getRender().getRelativePoint(self.Tank, (0,1,0))
oldHpr = self.Tank.getHpr()
self.Tank.headsUp(fwd, normal)
newHpr = self.Tank.getHpr()
newHpr.setX(oldHpr.getX())
self.Tank.setHpr(oldHpr)
#
# The magic number.
# (How fast the tank changes Pitch and Roll over time)
#
magicNumber = 90.0
if self.Tank.getP() > newHpr.getY():
self.Tank.setP( self.Tank.getP() - magicNumber * Game.getClock().getDt())
if self.Tank.getP() < newHpr.getY():
self.Tank.setP( self.Tank.getP() + magicNumber * Game.getClock().getDt())
if self.Tank.getR() > newHpr.getZ():
self.Tank.setR( self.Tank.getR() - magicNumber * Game.getClock().getDt())
if self.Tank.getR() < newHpr.getZ():
self.Tank.setR( self.Tank.getR() + magicNumber * Game.getClock().getDt())
return task.cont
#
# KEYS
#
def wKey(self):
self.Reverse = False
self.Forward = True
def wKeyUp(self):
self.Forward = False
def sKey(self):
self.Forward = False
self.Reverse = True
def sKeyUp(self):
self.Reverse = False
def aKey(self):
self.Right = False
self.Left = True
def aKeyUp(self):
self.Left = False
def dKey(self):
self.Left = False
self.Right = True
def dKeyUp(self):
self.Right = False
#
# Move Task
#
def moveUpdate(self, task):
if self.Left:
hpr = self.Tank.getHpr()
hpr.setX(hpr.getX() + (100.0 * Game.getClock().getDt()) )
self.Tank.setHpr(hpr)
if self.Right:
hpr = self.Tank.getHpr()
hpr.setX( hpr.getX() - (100.0 * Game.getClock().getDt()) )
self.Tank.setHpr(hpr)
if self.Forward:
if self.Velocity < 0.1:
self.Velocity += 1.0 * Game.getClock().getDt()
if self.Reverse:
if self.Velocity > -0.1:
self.Velocity -= 1.0 * Game.getClock().getDt()
pos = self.Tank.getPos(self.Tank)
pos -= Vec3(0,self.Velocity,0)
self.Tank.setPos(self.Tank, pos)
if self.Velocity > 0:
self.Velocity -= 0.75 * Game.getClock().getDt()
if self.Velocity < 0.001:
self.Velocity = 0
if self.Velocity < 0:
self.Velocity += 0.75 * Game.getClock().getDt()
if self.Velocity > -0.001:
self.Velocity = 0
#print self.Velocity
return task.cont
#
# Instatiate Tank Object
#
Tank = Tank()