moved from ../showbase

This commit is contained in:
Dave Schuyler 2004-08-07 01:07:43 +00:00
parent 432390df8c
commit 59606cc4ec
10 changed files with 2094 additions and 0 deletions

View File

@ -0,0 +1,196 @@
from direct.showbase.ShowBaseGlobal import *
#from DirectGui import *
#from PythonUtil import *
#from IntervalGlobal import *
from otp.avatar import Avatar
from direct.directnotify import DirectNotifyGlobal
#import GhostWalker
#import GravityWalker
#import NonPhysicsWalker
#import PhysicsWalker
#if __debug__:
# import DevWalker
from direct.task import Task
class ControlManager:
notify = DirectNotifyGlobal.directNotify.newCategory("ControlManager")
wantAvatarPhysicsIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
wantAvatarPhysicsDebug = base.config.GetBool('want-avatar-physics-debug', 0)
def __init__(self):
assert self.notify.debugCall(id(self))
self.enableJumpCounter = 1
self.controls = {}
self.currentControls = None
self.isEnabled = 1
#self.monitorTask = taskMgr.add(self.monitor, "ControlManager-%s"%(id(self)), priority=-1)
inputState.watch("forward", "arrow_up", "arrow_up-up")
inputState.watch("forward", "control-arrow_up", "control-arrow_up-up")
inputState.watch("forward", "alt-arrow_up", "alt-arrow_up-up")
inputState.watch("forward", "shift-arrow_up", "shift-arrow_up-up")
inputState.watch("reverse", "arrow_down", "arrow_down-up")
inputState.watch("reverse", "control-arrow_down", "control-arrow_down-up")
inputState.watch("reverse", "alt-arrow_down", "alt-arrow_down-up")
inputState.watch("reverse", "shift-arrow_down", "shift-arrow_down-up")
inputState.watch("turnLeft", "arrow_left", "arrow_left-up")
inputState.watch("turnLeft", "control-arrow_left", "control-arrow_left-up")
inputState.watch("turnLeft", "alt-arrow_left", "alt-arrow_left-up")
inputState.watch("turnLeft", "shift-arrow_left", "shift-arrow_left-up")
inputState.watch("turnLeft", "mouse-look_left", "mouse-look_left-done")
inputState.watch("turnRight", "arrow_right", "arrow_right-up")
inputState.watch("turnRight", "control-arrow_right", "control-arrow_right-up")
inputState.watch("turnRight", "alt-arrow_right", "alt-arrow_right-up")
inputState.watch("turnRight", "shift-arrow_right", "shift-arrow_right-up")
inputState.watch("turnRight", "mouse-look_right", "mouse-look_right-done")
inputState.watch("jump", "control", "control-up")
inputState.watch("jump", "alt-control", "alt-control-up")
inputState.watch("jump", "shift-control", "shift-control-up")
inputState.watch("slideLeft", "home", "home-up")
inputState.watch("slideRight", "end", "end-up")
inputState.watch("levitateUp", "page_up", "page_up-up")
inputState.watch("levitateDown", "page_down", "page_down-up")
inputState.watch("run", "shift", "shift-up")
# FYI, ghost mode uses jump for slide.
#inputState.watch("slide", "slide-is-disabled", "slide-is-disabled")
inputState.watch("slide", "mouse3", "mouse3-up")
#inputState.watch("slideLeft", "shift-arrow_left", "shift-arrow_left-up")
#inputState.watch("slideLeft", "control-arrow_left", "control-arrow_left-up")
#inputState.watch("slideLeft", "alt-arrow_left", "alt-arrow_left-up")
#inputState.watch("slideLeft", "shift-arrow_left", "shift-arrow_left-up")
#inputState.watch("slideLeft", "slide-is-disabled", "slide-is-disabled")
#inputState.watch("slideRight", "shift-arrow_right", "shift-arrow_right-up")
#inputState.watch("slideRight", "control-arrow_right", "control-arrow_right-up")
#inputState.watch("slideRight", "alt-arrow_right", "alt-arrow_right-up")
#inputState.watch("slideRight", "shift-arrow_right", "shift-arrow_right-up")
#inputState.watch("slideRight", "slide-is-disabled", "slide-is-disabled")
def add(self, controls, name="basic"):
"""
controls is an avatar control system.
name is any key that you want to use to refer to the
the controls later (e.g. using the use(<name>) call).
Add a control instance to the list of available control systems.
See also: use().
"""
assert self.notify.debugCall(id(self))
assert controls is not None
oldControls = self.controls.get(name)
if oldControls is not None:
print "Replacing controls:", name
oldControls.disableAvatarControls()
oldControls.setCollisionsActive(0)
oldControls.delete()
controls.disableAvatarControls()
controls.setCollisionsActive(0)
self.controls[name] = controls
def use(self, name="basic"):
"""
name is a key (string) that was previously passed to add().
Use a previously added control system.
See also: add().
"""
assert self.notify.debugCall(id(self))
controls = self.controls.get(name)
if controls is not None:
if controls is not self.currentControls:
if self.currentControls is not None:
self.currentControls.disableAvatarControls()
self.currentControls.setCollisionsActive(0)
self.currentControls = controls
self.currentControls.setCollisionsActive(1)
if self.isEnabled:
self.currentControls.enableAvatarControls()
#else:
# print "Controls are already", name
else:
print "Unkown controls:", name
def setSpeeds(self, forwardSpeed, jumpForce,
reverseSpeed, rotateSpeed):
assert self.notify.debugCall(id(self))
for controls in self.controls.values():
controls.setWalkSpeed(
forwardSpeed, jumpForce, reverseSpeed, rotateSpeed)
def delete(self):
assert self.notify.debugCall(id(self))
self.disable()
#self.monitorTask.remove()
def getSpeeds(self):
return self.currentControls.getSpeeds()
def deleteCollisions(self):
assert self.notify.debugCall(id(self))
for controls in self.controls.values():
controls.deleteCollisions()
def collisionsOn(self):
assert self.notify.debugCall(id(self))
self.currentControls.setCollisionsActive(1)
def collisionsOff(self):
assert self.notify.debugCall(id(self))
self.currentControls.setCollisionsActive(0)
def placeOnFloor(self):
assert self.notify.debugCall(id(self))
self.currentControls.placeOnFloor()
def enable(self):
assert self.notify.debugCall(id(self))
self.isEnabled = 1
self.currentControls.enableAvatarControls()
def disable(self):
assert self.notify.debugCall(id(self))
self.isEnabled = 0
self.currentControls.disableAvatarControls()
def enableAvatarJump(self):
"""
Stop forcing the ctrl key to return 0's
"""
assert self.notify.debugCall(id(self))
self.enableJumpCounter+=1
if self.enableJumpCounter:
assert self.enableJumpCounter == 1
self.enableJumpCounter = 1
inputState.unforce("jump")
def disableAvatarJump(self):
"""
Force the ctrl key to return 0's
"""
assert self.notify.debugCall(id(self))
self.enableJumpCounter-=1
if self.enableJumpCounter <= 0:
inputState.force("jump", 0)
def monitor(self, foo):
#assert(self.debugPrint("monitor()"))
#if 1:
# airborneHeight=self.avatar.getAirborneHeight()
# onScreenDebug.add("airborneHeight", "% 10.4f"%(airborneHeight,))
if 0:
onScreenDebug.add("InputState forward", "%d"%(inputState.isSet("forward")))
onScreenDebug.add("InputState reverse", "%d"%(inputState.isSet("reverse")))
onScreenDebug.add("InputState turnLeft", "%d"%(inputState.isSet("turnLeft")))
onScreenDebug.add("InputState turnRight", "%d"%(inputState.isSet("turnRight")))
return Task.cont

161
direct/src/controls/DevWalker.py Executable file
View File

@ -0,0 +1,161 @@
"""
DevWalker.py is for avatars.
A walker control such as this one provides:
- creation of the collision nodes
- handling the keyboard and mouse input for avatar movement
- moving the avatar
it does not:
- play sounds
- play animations
although it does send messeges that allow a listener to play sounds or
animations based on walker events.
"""
from direct.showbase.ShowBaseGlobal import *
from direct.directnotify import DirectNotifyGlobal
from direct.showbase import DirectObject
class DevWalker(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory("DevWalker")
wantDebugIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
# Ghost mode overrides this:
slideName = "slide-is-disabled"
# special methods
def __init__(self):
DirectObject.DirectObject.__init__(self)
self.speed=0.0
self.rotationSpeed=0.0
self.vel=Vec3(0.0, 0.0, 0.0)
self.task = None
def setWalkSpeed(self, forward, jump, reverse, rotate):
assert(self.debugPrint("setWalkSpeed()"))
self.avatarControlForwardSpeed=forward
#self.avatarControlJumpForce=jump
self.avatarControlReverseSpeed=reverse
self.avatarControlRotateSpeed=rotate
def getSpeeds(self):
#assert(self.debugPrint("getSpeeds()"))
return (self.speed, self.rotationSpeed)
def initializeCollisions(self, collisionTraverser, avatarNodePath,
wallCollideMask, floorCollideMask,
avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0):
assert not avatarNodePath.isEmpty()
self.cTrav = collisionTraverser
self.avatarNodePath = avatarNodePath
def setAirborneHeightFunc(self, getAirborneHeight):
pass
def deleteCollisions(self):
pass
def setCollisionsActive(self, active = 1):
pass
def placeOnFloor(self):
pass
def oneTimeCollide(self):
pass
def displayDebugInfo(self):
"""
For debug use.
"""
onScreenDebug.add("controls", "DevWalker")
def handleAvatarControls(self, task):
"""
Check on the arrow keys and update the avatar.
"""
# get the button states:
forward = inputState.isSet("forward")
reverse = inputState.isSet("reverse")
turnLeft = inputState.isSet("turnLeft")
turnRight = inputState.isSet("turnRight")
slideLeft = inputState.isSet("slideLeft")
slideRight = inputState.isSet("slideRight")
levitateUp = inputState.isSet("levitateUp")
levitateDown = inputState.isSet("levitateDown")
run = inputState.isSet("run") and 4.0 or 1.0
# Determine what the speeds are based on the buttons:
self.speed=(
(forward and self.avatarControlForwardSpeed or
reverse and -self.avatarControlReverseSpeed))
self.liftSpeed=(
(levitateUp and self.avatarControlForwardSpeed or
levitateDown and -self.avatarControlReverseSpeed))
self.slideSpeed=(
(slideLeft and -self.avatarControlForwardSpeed) or
(slideRight and self.avatarControlForwardSpeed))
self.rotationSpeed=(
(turnLeft and self.avatarControlRotateSpeed) or
(turnRight and -self.avatarControlRotateSpeed))
if self.wantDebugIndicator:
self.displayDebugInfo()
# Check to see if we're moving at all:
if self.speed or self.liftSpeed or self.slideSpeed or self.rotationSpeed:
# How far did we move based on the amount of time elapsed?
dt=ClockObject.getGlobalClock().getDt()
distance = dt * self.speed * run
lift = dt * self.liftSpeed * run
slideDistance = dt * self.slideSpeed * run
rotation = dt * self.rotationSpeed
# Take a step in the direction of our previous heading.
self.vel=Vec3(Vec3.forward() * distance +
Vec3.up() * lift +
Vec3.right() * slideDistance)
if self.vel != Vec3.zero():
# rotMat is the rotation matrix corresponding to
# our previous heading.
rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
step=rotMat.xform(self.vel)
self.avatarNodePath.setFluidPos(Point3(self.avatarNodePath.getPos()+step))
self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
messenger.send("avatarMoving")
else:
self.vel.set(0.0, 0.0, 0.0)
return Task.cont
def enableAvatarControls(self):
"""
Activate the arrow keys, etc.
"""
assert(self.debugPrint("enableAvatarControls"))
if self.task:
# remove any old
self.task.remove(self.task)
# spawn the new task
self.task = taskMgr.add(
self.handleAvatarControls, "AvatarControls-dev-%s"%(id(self),))
def disableAvatarControls(self):
"""
Ignore the arrow keys, etc.
"""
assert(self.debugPrint("disableAvatarControls"))
if self.task:
self.task.remove()
self.task = None
if __debug__:
def debugPrint(self, message):
"""for debugging"""
return self.notify.debug(
str(id(self))+' '+message)

View File

@ -0,0 +1,27 @@
"""
GhostWalker.py is for avatars.
A walker control such as this one provides:
- creation of the collision nodes
- handling the keyboard and mouse input for avatar movement
- moving the avatar
it does not:
- play sounds
- play animations
although it does send messeges that allow a listener to play sounds or
animations based on walker events.
"""
from direct.showbase.ShowBaseGlobal import *
from direct.directnotify import DirectNotifyGlobal
import NonPhysicsWalker
class GhostWalker(NonPhysicsWalker.NonPhysicsWalker):
notify = DirectNotifyGlobal.directNotify.newCategory("GhostWalker")
# Ghosts slide instead of jump:
slideName = "jump"

View File

@ -0,0 +1,614 @@
"""
GravityWalker.py is for avatars.
A walker control such as this one provides:
- creation of the collision nodes
- handling the keyboard and mouse input for avatar movement
- moving the avatar
it does not:
- play sounds
- play animations
although it does send messeges that allow a listener to play sounds or
animations based on walker events.
"""
from direct.showbase.ShowBaseGlobal import *
from direct.directnotify import DirectNotifyGlobal
from direct.showbase import DirectObject
from pandac import PhysicsManager
import math
class GravityWalker(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory("GravityWalker")
wantDebugIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
wantFloorSphere = base.config.GetBool('want-floor-sphere', 0)
# special methods
def __init__(self, gravity = -32.1740, standableGround=0.707,
hardLandingForce=16.0):
assert self.notify.debugStateCall(self)
DirectObject.DirectObject.__init__(self)
self.__gravity=gravity
self.__standableGround=standableGround
self.__hardLandingForce=hardLandingForce
self.mayJump = 1
self.jumpDelayTask = None
self.controlsTask = None
self.indicatorTask = None
self.falling = 0
self.needToDeltaPos = 0
self.physVelocityIndicator=None
self.avatarControlForwardSpeed=0
self.avatarControlJumpForce=0
self.avatarControlReverseSpeed=0
self.avatarControlRotateSpeed=0
self.getAirborneHeight=None
self.priorParent=Vec3(0)
self.__oldPosDelta=Vec3(0)
self.__oldDt=0
self.moving=0
self.speed=0.0
self.rotationSpeed=0.0
self.slideSpeed=0.0
self.vel=Vec3(0.0)
self.collisionsActive = 0
self.isAirborne = 0
self.highMark = 0
def delete(self):
assert self.notify.debugStateCall(self)
if self.doLaterTask is not None:
self.doLaterTask.remove()
del self.doLaterTask
#DirectObject.DirectObject.delete(self)
"""
def spawnTest(self):
assert self.notify.debugStateCall(self)
if not self.wantDebugIndicator:
return
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from toontown.coghq import MovingPlatform
if hasattr(self, "platform"):
# Remove the prior instantiation:
self.moveIval.pause()
del self.moveIval
self.platform.destroy()
del self.platform
self.platform2.destroy()
del self.platform2
model = loader.loadModelCopy('phase_9/models/cogHQ/platform1')
fakeId = id(self)
self.platform = MovingPlatform.MovingPlatform()
self.platform.setupCopyModel(fakeId, model, 'platformcollision')
self.platformRoot = render.attachNewNode("GravityWalker-spawnTest-%s"%fakeId)
self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 0.0, 1.0))
self.platformRoot.setHpr(base.localAvatar, Vec3.zero())
self.platform.reparentTo(self.platformRoot)
self.platform2 = MovingPlatform.MovingPlatform()
self.platform2.setupCopyModel(1+fakeId, model, 'platformcollision')
self.platform2Root = render.attachNewNode("GravityWalker-spawnTest2-%s"%fakeId)
self.platform2Root.setPos(base.localAvatar, Vec3(-16.0, 30.0, 1.0))
self.platform2Root.setHpr(base.localAvatar, Vec3.zero())
self.platform2.reparentTo(self.platform2Root)
duration = 5
self.moveIval = Parallel(
Sequence(
WaitInterval(0.3),
LerpPosInterval(self.platform, duration,
Vec3(0.0, 30.0, 0.0),
name='platformOut%s' % fakeId,
fluid = 1),
WaitInterval(0.3),
LerpPosInterval(self.platform, duration,
Vec3(0.0, 0.0, 0.0),
name='platformBack%s' % fakeId,
fluid = 1),
WaitInterval(0.3),
LerpPosInterval(self.platform, duration,
Vec3(0.0, 0.0, 30.0),
name='platformUp%s' % fakeId,
fluid = 1),
WaitInterval(0.3),
LerpPosInterval(self.platform, duration,
Vec3(0.0, 0.0, 0.0),
name='platformDown%s' % fakeId,
fluid = 1),
),
Sequence(
WaitInterval(0.3),
LerpPosInterval(self.platform2, duration,
Vec3(0.0, -30.0, 0.0),
name='platform2Out%s' % fakeId,
fluid = 1),
WaitInterval(0.3),
LerpPosInterval(self.platform2, duration,
Vec3(0.0, 30.0, 30.0),
name='platform2Back%s' % fakeId,
fluid = 1),
WaitInterval(0.3),
LerpPosInterval(self.platform2, duration,
Vec3(0.0, -30.0, 0.0),
name='platform2Up%s' % fakeId,
fluid = 1),
WaitInterval(0.3),
LerpPosInterval(self.platform2, duration,
Vec3(0.0, 0.0, 0.0),
name='platformDown%s' % fakeId,
fluid = 1),
),
name='platformIval%s' % fakeId,
)
self.moveIval.loop()
"""
def setWalkSpeed(self, forward, jump, reverse, rotate):
assert self.notify.debugStateCall(self)
self.avatarControlForwardSpeed=forward
self.avatarControlJumpForce=jump
self.avatarControlReverseSpeed=reverse
self.avatarControlRotateSpeed=rotate
def getSpeeds(self):
#assert(self.debugPrint("getSpeeds()"))
return (self.speed, self.rotationSpeed)
def setupRay(self, bitmask, floorOffset, reach):
assert self.notify.debugStateCall(self)
# This is a ray cast from your head down to detect floor polygons.
# This ray start is arbitrarily high in the air. Feel free to use
# a higher or lower value depending on whether you want an avatar
# that is outside of the world to step up to the floor when they
# get under valid floor:
cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
cRayNode = CollisionNode('GW.cRayNode')
cRayNode.addSolid(cRay)
self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
cRayNode.setFromCollideMask(bitmask)
cRayNode.setIntoCollideMask(BitMask32.allOff())
# set up floor collision mechanism
self.lifter = CollisionHandlerGravity()
self.lifter.setGravity(32.174 * 2.0)
self.lifter.addInPattern("enter%in")
self.lifter.addOutPattern("exit%in")
self.lifter.setOffset(floorOffset)
self.lifter.setReach(reach)
# Limit our rate-of-fall with the lifter.
# If this is too low, we actually "fall" off steep stairs
# and float above them as we go down. I increased this
# from 8.0 to 16.0 to prevent this
#self.lifter.setMaxVelocity(16.0)
self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath)
def setupWallSphere(self, bitmask, avatarRadius):
"""
Set up the collision sphere
"""
assert self.notify.debugStateCall(self)
# This is a sphere on the ground to detect collisions with
# walls, but not the floor.
self.avatarRadius = avatarRadius
cSphere = CollisionSphere(0.0, 0.0, avatarRadius, avatarRadius)
cSphereNode = CollisionNode('GW.cWallSphereNode')
cSphereNode.addSolid(cSphere)
cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
cSphereNode.setFromCollideMask(bitmask)
cSphereNode.setIntoCollideMask(BitMask32.allOff())
# set up collision mechanism
handler = CollisionHandlerPusher()
#handler.setInPattern("pusher_enter%in")
#handler.setOutPattern("pusher_exit%in")
handler.addCollider(cSphereNodePath, self.avatarNodePath)
self.pusher = handler
self.cWallSphereNodePath = cSphereNodePath
def setupEventSphere(self, bitmask, avatarRadius):
"""
Set up the collision sphere
"""
assert self.notify.debugStateCall(self)
# This is a sphere a little larger than the wall sphere to
# trigger events.
self.avatarRadius = avatarRadius
cSphere = CollisionSphere(0.0, 0.0, avatarRadius-0.1, avatarRadius*1.04)
# Mark it intangible just to emphasize its non-physical purpose.
cSphere.setTangible(0)
cSphereNode = CollisionNode('GW.cEventSphereNode')
cSphereNode.addSolid(cSphere)
cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
cSphereNode.setFromCollideMask(bitmask)
cSphereNode.setIntoCollideMask(BitMask32.allOff())
# set up collision mechanism
handler = CollisionHandlerEvent()
handler.addInPattern("enter%in")
handler.addOutPattern("exit%in")
self.event = handler
self.cEventSphereNodePath = cSphereNodePath
def setupFloorSphere(self, bitmask, avatarRadius):
"""
Set up the collision sphere
"""
assert self.notify.debugStateCall(self)
# This is a tiny sphere concentric with the wallSphere to keep
# us from slipping through floors.
self.avatarRadius = avatarRadius
cSphere = CollisionSphere(0.0, 0.0, avatarRadius, 0.01)
cSphereNode = CollisionNode('GW.cFloorSphereNode')
cSphereNode.addSolid(cSphere)
cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
cSphereNode.setFromCollideMask(bitmask)
cSphereNode.setIntoCollideMask(BitMask32.allOff())
# set up collision mechanism
handler = CollisionHandlerPusher()
#handler.setInPattern("pusherFloor_enter%in")
#handler.setOutPattern("pusherFloor_exit%in")
handler.addCollider(cSphereNodePath, self.avatarNodePath)
self.pusherFloor = handler
self.cFloorSphereNodePath = cSphereNodePath
def initializeCollisions(self, collisionTraverser, avatarNodePath,
wallBitmask, floorBitmask,
avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0):
"""
floorOffset is how high the avatar can reach. I.e. if the avatar
walks under a ledge that is <= floorOffset above the ground (a
double floor situation), the avatar will step up on to the
ledge (instantly).
Set up the avatar collisions
"""
assert self.notify.debugStateCall(self)
assert not avatarNodePath.isEmpty()
self.avatarNodePath = avatarNodePath
self.cTrav = collisionTraverser
self.setupRay(floorBitmask, floorOffset, reach )
self.setupWallSphere(wallBitmask, avatarRadius)
self.setupEventSphere(wallBitmask, avatarRadius)
if self.wantFloorSphere:
self.setupFloorSphere(floorBitmask, avatarRadius)
self.setCollisionsActive(1)
def setAirborneHeightFunc(self, unused_parameter):
assert self.notify.debugStateCall(self)
self.getAirborneHeight = self.lifter.getAirborneHeight
def getAirborneHeight(self):
assert self.notify.debugStateCall(self)
self.lifter.getAirborneHeight()
def setAvatarPhysicsIndicator(self, indicator):
"""
indicator is a NodePath
"""
assert self.notify.debugStateCall(self)
self.cWallSphereNodePath.show()
def deleteCollisions(self):
assert self.notify.debugStateCall(self)
del self.cTrav
self.cWallSphereNodePath.removeNode()
del self.cWallSphereNodePath
if self.wantFloorSphere:
self.cFloorSphereNodePath.removeNode()
del self.cFloorSphereNodePath
del self.pusher
# del self.pusherFloor
del self.event
del self.lifter
del self.getAirborneHeight
def setCollisionsActive(self, active = 1):
assert self.notify.debugStateCall(self)
if self.collisionsActive != active:
self.collisionsActive = active
# Each time we change the collision geometry, make one
# more pass to ensure we aren't standing in a wall.
self.oneTimeCollide()
if active:
if 1:
# Please let skyler or drose know if this is causing a problem
# This is a bit of a hack fix:
self.avatarNodePath.setP(0.0)
self.avatarNodePath.setR(0.0)
self.cTrav.addCollider(self.cWallSphereNodePath, self.pusher)
if self.wantFloorSphere:
self.cTrav.addCollider(self.cFloorSphereNodePath, self.pusherFloor)
self.cTrav.addCollider(self.cEventSphereNodePath, self.event)
self.cTrav.addCollider(self.cRayNodePath, self.lifter)
else:
self.cTrav.removeCollider(self.cWallSphereNodePath)
if self.wantFloorSphere:
self.cTrav.removeCollider(self.cFloorSphereNodePath)
self.cTrav.removeCollider(self.cEventSphereNodePath)
self.cTrav.removeCollider(self.cRayNodePath)
def getCollisionsActive(self):
assert(self.debugPrint("getCollisionsActive() returning=%s"%(
self.collisionsActive,)))
return self.collisionsActive
def placeOnFloor(self):
"""
Make a reasonable effor to place the avatar on the ground.
For example, this is useful when switching away from the
current walker.
"""
assert self.notify.debugStateCall(self)
self.oneTimeCollide()
self.avatarNodePath.setZ(self.avatarNodePath.getZ()-self.lifter.getAirborneHeight())
def oneTimeCollide(self):
"""
Makes one quick collision pass for the avatar, for instance as
a one-time straighten-things-up operation after collisions
have been disabled.
"""
assert self.notify.debugStateCall(self)
self.isAirborne = 0
self.mayJump = 1
tempCTrav = CollisionTraverser("oneTimeCollide")
tempCTrav.addCollider(self.cWallSphereNodePath, self.pusher)
if self.wantFloorSphere:
tempCTrav.addCollider(self.cFloorSphereNodePath, self.event)
tempCTrav.addCollider(self.cRayNodePath, self.lifter)
tempCTrav.traverse(render)
def setMayJump(self, task):
"""
This function's use is internal to this class (maybe I'll add
the __ someday). Anyway, if you want to enable or disable
jumping in a general way see the ControlManager (don't use this).
"""
assert self.notify.debugStateCall(self)
self.mayJump = 1
return Task.done
def startJumpDelay(self, delay):
assert self.notify.debugStateCall(self)
if self.jumpDelayTask:
self.jumpDelayTask.remove()
self.mayJump = 0
self.jumpDelayTask=taskMgr.doMethodLater(
delay,
self.setMayJump,
"jumpDelay-%s"%id(self))
def displayDebugInfo(self):
"""
For debug use.
"""
onScreenDebug.add("w controls", "GravityWalker")
onScreenDebug.add("w airborneHeight", self.lifter.getAirborneHeight())
onScreenDebug.add("w falling", self.falling)
onScreenDebug.add("w isOnGround", self.lifter.isOnGround())
#onScreenDebug.add("w gravity", self.lifter.getGravity())
#onScreenDebug.add("w jumpForce", self.avatarControlJumpForce)
onScreenDebug.add("w contact normal", self.lifter.getContactNormal().pPrintValues())
onScreenDebug.add("w mayJump", self.mayJump)
onScreenDebug.add("w impact", self.lifter.getImpactVelocity())
onScreenDebug.add("w velocity", self.lifter.getVelocity())
onScreenDebug.add("w isAirborne", self.isAirborne)
onScreenDebug.add("w hasContact", self.lifter.hasContact())
def handleAvatarControls(self, task):
"""
Check on the arrow keys and update the avatar.
"""
# get the button states:
forward = inputState.isSet("forward")
reverse = inputState.isSet("reverse")
turnLeft = inputState.isSet("turnLeft")
turnRight = inputState.isSet("turnRight")
#slide = 0 #hack -- was: inputState.isSet("slide")
slide = inputState.isSet("slide")
jump = inputState.isSet("jump")
# Determine what the speeds are based on the buttons:
self.speed=(forward and self.avatarControlForwardSpeed or
reverse and -self.avatarControlReverseSpeed)
# Should fSlide be renamed slideButton?
self.slideSpeed=slide and (
(turnLeft and -self.avatarControlForwardSpeed) or
(turnRight and self.avatarControlForwardSpeed))
self.rotationSpeed=not slide and (
(turnLeft and self.avatarControlRotateSpeed) or
(turnRight and -self.avatarControlRotateSpeed))
if self.needToDeltaPos:
self.setPriorParentVector()
self.needToDeltaPos = 0
if self.wantDebugIndicator:
self.displayDebugInfo()
if self.lifter.isOnGround():
if self.isAirborne:
self.isAirborne = 0
assert(self.debugPrint("isAirborne 0 due to isOnGround() true"))
impact = self.lifter.getImpactVelocity()
if impact < -30.0:
messenger.send("jumpHardLand")
self.startJumpDelay(0.3)
else:
messenger.send("jumpLand")
if impact < -5.0:
self.startJumpDelay(0.2)
# else, ignore the little potholes.
assert(self.isAirborne == 0)
self.priorParent = Vec3.zero()
if jump and self.mayJump:
# ...the jump button is down and we're close
# enough to the ground to jump.
self.lifter.addVelocity(self.avatarControlJumpForce)
messenger.send("jumpStart")
self.isAirborne = 1
assert(self.debugPrint("isAirborne 1 due to jump"))
else:
if self.isAirborne == 0:
assert(self.debugPrint("isAirborne 1 due to isOnGround() false"))
self.isAirborne = 1
self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
# How far did we move based on the amount of time elapsed?
self.__oldDt = ClockObject.getGlobalClock().getDt()
dt=self.__oldDt
# Check to see if we're moving at all:
self.moving = self.speed or self.slideSpeed or self.rotationSpeed or (self.priorParent!=Vec3.zero())
if self.moving:
distance = dt * self.speed
slideDistance = dt * self.slideSpeed
rotation = dt * self.rotationSpeed
# Take a step in the direction of our previous heading.
self.vel=Vec3(Vec3.forward() * distance +
Vec3.right() * slideDistance)
if self.vel != Vec3.zero() or self.priorParent != Vec3.zero():
if 1:
# rotMat is the rotation matrix corresponding to
# our previous heading.
rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
step=rotMat.xform(self.vel) + (self.priorParent * dt)
self.avatarNodePath.setFluidPos(Point3(
self.avatarNodePath.getPos()+step))
if 0:
# rotMat is the rotation matrix corresponding to
# our previous heading.
rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), self.lifter.getContactNormal())
step=rotMat.xform(self.vel) + (self.priorParent * dt)
self.avatarNodePath.setFluidPos(Point3(
self.avatarNodePath.getPos()+step))
if 0:
# rotMat is the rotation matrix corresponding to
# our previous heading.
rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
forward = Vec3(rotMat.xform(Vec3.forward()))
up = Vec3(rotMat.xform(self.lifter.getContactNormal()))
rotMat2=Mat3()
headsUp(rotMat2, forward, up)
#rotMat2=Mat3.rotateMatNormaxis(0.0, )
step=rotMat2.xform(self.vel) + (self.priorParent * dt)
if 1:
onScreenDebug.add("a getH()", self.avatarNodePath.getH())
onScreenDebug.add("a forward", forward.pPrintValues())
onScreenDebug.add("a up", up.pPrintValues())
onScreenDebug.add("a Vec3.forward()", Vec3.forward().pPrintValues())
onScreenDebug.add("a Vec3.up()", Vec3.up().pPrintValues())
onScreenDebug.add("a Vec3.right()", Vec3.right().pPrintValues())
onScreenDebug.add("a contactNormal()", self.lifter.getContactNormal().pPrintValues())
onScreenDebug.add("a rotMat", rotMat.pPrintValues())
onScreenDebug.add("a rotMat2", rotMat2.pPrintValues())
self.avatarNodePath.setFluidPos(Point3(
self.avatarNodePath.getPos()+step))
self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
else:
self.vel.set(0.0, 0.0, 0.0)
if self.moving or jump:
messenger.send("avatarMoving")
return Task.cont
def doDeltaPos(self):
assert self.notify.debugStateCall(self)
self.needToDeltaPos = 1
def setPriorParentVector(self):
assert self.notify.debugStateCall(self)
if __debug__:
onScreenDebug.add("__oldDt", "% 10.4f"%self.__oldDt)
onScreenDebug.add("self.__oldPosDelta",
self.__oldPosDelta.pPrintValues())
velocity = self.__oldPosDelta*(1.0/self.__oldDt)
self.priorParent = Vec3(velocity)
if __debug__:
if self.wantDebugIndicator:
onScreenDebug.add("priorParent", self.priorParent.pPrintValues())
def reset(self):
assert self.notify.debugStateCall(self)
self.lifter.setVelocity(0.0)
self.priorParent=Vec3.zero()
def enableAvatarControls(self):
"""
Activate the arrow keys, etc.
"""
assert self.notify.debugStateCall(self)
assert self.collisionsActive
#*#if __debug__:
#*# self.accept("control-f3", self.spawnTest) #*#
# remove any old
if self.controlsTask:
self.controlsTask.remove()
# spawn the new task
taskName = "AvatarControls-%s"%(id(self),)
self.controlsTask = taskMgr.add(self.handleAvatarControls, taskName, 25)
self.isAirborne = 0
self.mayJump = 1
if self.physVelocityIndicator:
if self.indicatorTask:
self.indicatorTask.remove()
self.indicatorTask = taskMgr.add(
self.avatarPhysicsIndicator,
"AvatarControlsIndicator-%s"%(id(self),), 35)
def disableAvatarControls(self):
"""
Ignore the arrow keys, etc.
"""
assert self.notify.debugStateCall(self)
if self.controlsTask:
self.controlsTask.remove()
self.controlsTask = None
if self.indicatorTask:
self.indicatorTask.remove()
self.indicatorTask = None
if self.jumpDelayTask:
self.jumpDelayTask.remove()
self.jumpDelayTask = None
if __debug__:
self.ignore("control-f3") #*#
if __debug__:
def debugPrint(self, message):
"""for debugging"""
return self.notify.debug(
str(id(self))+' '+message)

View File

@ -0,0 +1,94 @@
from direct.directnotify import DirectNotifyGlobal
from direct.showbase import DirectObject
class InputState(DirectObject.DirectObject):
"""
InputState is for tracking the on/off state of some events.
The initial usage is to watch some keyboard keys so that another
task can poll the key states. By the way, in general polling is
not a good idea, but it is useful in some situations. Know when
to use it :) If in doubt, don't use this class and listen for
events instead.
"""
notify = DirectNotifyGlobal.directNotify.newCategory("InputState")
def __init__(self):
self.state = {}
assert(self.debugPrint("InputState()"))
self.watching = {}
self.forcing = {}
def delete(self):
self.ignoreAll()
def watch(self, name, eventOn, eventOff, default = 0):
"""
name is any string (or actually any valid dictionary key).
eventOn is the string name of the Messenger event that will
set the state (set to 1).
eventOff is the string name of the Messenger event that will
clear the state (set to 0).
default is the initial value (this will be returned from
isSet() if a call is made before any eventOn or eventOff
events occur.
See Also: ignore()
"""
assert(self.debugPrint(
"watch(name=%s, eventOn=%s, eventOff=%s, default=%s)"%(
name, eventOn, eventOff, default)))
self.accept(eventOn, self.set, [name, 1])
self.accept(eventOff, self.set, [name, 0])
self.state[name] = default
self.watching[name] = (eventOn, eventOff)
def force(self, name, value):
"""
Force isSet(name) to return value.
See Also: unforce()
"""
self.forcing[name] = value
def unforce(self, name):
"""
Stop forcing a value.
See Also: force()
"""
del self.forcing[name]
def ignore(self, name):
"""
The opposite of watch(name, ...)
See Also: watch()
"""
eventOn, eventOff = self.watching[name]
DirectObject.DirectObject.ignore(self, eventOn)
DirectObject.DirectObject.ignore(self, eventOff)
del self.watching[name]
del self.state[name]
def set(self, name, isSet):
assert(self.debugPrint("set(name=%s, isSet=%s)"%(name, isSet)))
self.state[name] = isSet
# We change the name befor sending it because this may
# be the same name that messenger used to call InputState.set()
# this avoids running in circles:
messenger.send("InputState-%s"%(name,), [isSet])
def isSet(self, name):
"""
returns 0, 1, or None (if we're not tracking it at all)
"""
#assert(self.debugPrint("isSet(name=%s)"%(name)))
r = self.forcing.get(name)
if r is not None:
return r
return self.state.get(name)
def debugPrint(self, message):
"""for debugging"""
return self.notify.debug(
"%s (%s) %s"%(id(self), len(self.state), message))

View File

@ -0,0 +1,259 @@
"""
NonPhysicsWalker.py is for avatars.
A walker control such as this one provides:
- creation of the collision nodes
- handling the keyboard and mouse input for avatar movement
- moving the avatar
it does not:
- play sounds
- play animations
although it does send messeges that allow a listener to play sounds or
animations based on walker events.
"""
from direct.showbase.ShowBaseGlobal import *
from direct.directnotify import DirectNotifyGlobal
from direct.showbase import DirectObject
class NonPhysicsWalker(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory("NonPhysicsWalker")
wantDebugIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
# Ghost mode overrides this:
slideName = "slide-is-disabled"
# special methods
def __init__(self):
DirectObject.DirectObject.__init__(self)
self.collisionsActive = 0
self.speed=0.0
self.rotationSpeed=0.0
self.vel=Vec3(0.0, 0.0, 0.0)
self.stopThisFrame = 0
def setWalkSpeed(self, forward, jump, reverse, rotate):
assert(self.debugPrint("setWalkSpeed()"))
self.avatarControlForwardSpeed=forward
#self.avatarControlJumpForce=jump
self.avatarControlReverseSpeed=reverse
self.avatarControlRotateSpeed=rotate
def getSpeeds(self):
#assert(self.debugPrint("getSpeeds()"))
return (self.speed, self.rotationSpeed)
def initializeCollisions(self, collisionTraverser, avatarNodePath,
wallCollideMask, floorCollideMask,
avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0):
"""
Set up the avatar for collisions
"""
assert not avatarNodePath.isEmpty()
self.cTrav = collisionTraverser
self.avatarNodePath = avatarNodePath
# Set up the collision sphere
# This is a sphere on the ground to detect barrier collisions
self.cSphere = CollisionSphere(0.0, 0.0, 0.0, avatarRadius)
cSphereNode = CollisionNode('NPW.cSphereNode')
cSphereNode.addSolid(self.cSphere)
self.cSphereNodePath = avatarNodePath.attachNewNode(cSphereNode)
self.cSphereBitMask = wallCollideMask
cSphereNode.setFromCollideMask(self.cSphereBitMask)
cSphereNode.setIntoCollideMask(BitMask32.allOff())
# Set up the collison ray
# This is a ray cast from your head down to detect floor polygons.
# This ray start is arbitrarily high in the air. Feel free to use
# a higher or lower value depending on whether you want an avatar
# that is outside of the world to step up to the floor when they
# get under valid floor:
self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
cRayNode = CollisionNode('NPW.cRayNode')
cRayNode.addSolid(self.cRay)
self.cRayNodePath = avatarNodePath.attachNewNode(cRayNode)
self.cRayBitMask = floorCollideMask
cRayNode.setFromCollideMask(self.cRayBitMask)
cRayNode.setIntoCollideMask(BitMask32.allOff())
# set up wall collision mechanism
self.pusher = CollisionHandlerPusher()
self.pusher.setInPattern("enter%in")
self.pusher.setOutPattern("exit%in")
# set up floor collision mechanism
self.lifter = CollisionHandlerFloor()
self.lifter.setInPattern("on-floor")
self.lifter.setOutPattern("off-floor")
self.lifter.setOffset(floorOffset)
self.lifter.setReach(reach)
# Limit our rate-of-fall with the lifter.
# If this is too low, we actually "fall" off steep stairs
# and float above them as we go down. I increased this
# from 8.0 to 16.0 to prevent this
self.lifter.setMaxVelocity(16.0)
self.pusher.addCollider(self.cSphereNodePath, avatarNodePath)
self.lifter.addCollider(self.cRayNodePath, avatarNodePath)
# activate the collider with the traverser and pusher
self.setCollisionsActive(1)
def setAirborneHeightFunc(self, getAirborneHeight):
self.getAirborneHeight = getAirborneHeight
def deleteCollisions(self):
del self.cTrav
del self.cSphere
self.cSphereNodePath.removeNode()
del self.cSphereNodePath
del self.cRay
self.cRayNodePath.removeNode()
del self.cRayNodePath
del self.pusher
del self.lifter
def setCollisionsActive(self, active = 1):
assert(self.debugPrint("setCollisionsActive(active%s)"%(active,)))
if self.collisionsActive != active:
self.collisionsActive = active
if active:
self.cTrav.addCollider(self.cSphereNodePath, self.pusher)
self.cTrav.addCollider(self.cRayNodePath, self.lifter)
else:
self.cTrav.removeCollider(self.cSphereNodePath)
self.cTrav.removeCollider(self.cRayNodePath)
# Now that we have disabled collisions, make one more pass
# right now to ensure we aren't standing in a wall.
self.oneTimeCollide()
def placeOnFloor(self):
"""
Make a reasonable effor to place the avatar on the ground.
For example, this is useful when switching away from the
current walker.
"""
# With these on, getAirborneHeight is not returning the correct value so
# when we open our book while swimming we pop down underneath the ground
# self.oneTimeCollide()
# self.avatarNodePath.setZ(self.avatarNodePath.getZ()-self.getAirborneHeight())
# Since this is the non physics walker - wont they already be on the ground?
return
def oneTimeCollide(self):
"""
Makes one quick collision pass for the avatar, for instance as
a one-time straighten-things-up operation after collisions
have been disabled.
"""
tempCTrav = CollisionTraverser("oneTimeCollide")
tempCTrav.addCollider(self.cSphereNodePath, self.pusher)
tempCTrav.addCollider(self.cRayNodePath, self.lifter)
tempCTrav.traverse(render)
def displayDebugInfo(self):
"""
For debug use.
"""
onScreenDebug.add("controls", "NonPhysicsWalker")
def handleAvatarControls(self, task):
"""
Check on the arrow keys and update the avatar.
"""
if not self.lifter.hasContact():
# hack fix for falling through the floor:
messenger.send("walkerIsOutOfWorld", [self.avatarNodePath])
# get the button states:
forward = inputState.isSet("forward")
reverse = inputState.isSet("reverse")
turnLeft = inputState.isSet("turnLeft")
turnRight = inputState.isSet("turnRight")
slide = inputState.isSet(self.slideName) or 0
#jump = inputState.isSet("jump")
# Determine what the speeds are based on the buttons:
self.speed=(forward and self.avatarControlForwardSpeed or
reverse and -self.avatarControlReverseSpeed)
# Should fSlide be renamed slideButton?
self.slideSpeed=slide and (
(turnLeft and -self.avatarControlForwardSpeed) or
(turnRight and self.avatarControlForwardSpeed))
self.rotationSpeed=not slide and (
(turnLeft and self.avatarControlRotateSpeed) or
(turnRight and -self.avatarControlRotateSpeed))
if self.wantDebugIndicator:
self.displayDebugInfo()
# How far did we move based on the amount of time elapsed?
dt=ClockObject.getGlobalClock().getDt()
# Check to see if we're moving at all:
if self.speed or self.slideSpeed or self.rotationSpeed:
if self.stopThisFrame:
distance = 0.0
slideDistance = 0.0
rotation = 0.0
self.stopThisFrame = 0
else:
distance = dt * self.speed
slideDistance = dt * self.slideSpeed
rotation = dt * self.rotationSpeed
# Take a step in the direction of our previous heading.
self.vel=Vec3(Vec3.forward() * distance +
Vec3.right() * slideDistance)
if self.vel != Vec3.zero():
# rotMat is the rotation matrix corresponding to
# our previous heading.
rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
step=rotMat.xform(self.vel)
self.avatarNodePath.setFluidPos(Point3(self.avatarNodePath.getPos()+step))
self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
messenger.send("avatarMoving")
else:
self.vel.set(0.0, 0.0, 0.0)
return Task.cont
def doDeltaPos(self):
assert(self.debugPrint("doDeltaPos()"))
def reset(self):
assert(self.debugPrint("reset()"))
def enableAvatarControls(self):
"""
Activate the arrow keys, etc.
"""
assert(self.debugPrint("enableAvatarControls"))
assert self.collisionsActive
taskName = "AvatarControls-%s"%(id(self),)
# remove any old
taskMgr.remove(taskName)
# spawn the new task
taskMgr.add(self.handleAvatarControls, taskName)
def disableAvatarControls(self):
"""
Ignore the arrow keys, etc.
"""
assert(self.debugPrint("disableAvatarControls"))
taskName = "AvatarControls-%s"%(id(self),)
taskMgr.remove(taskName)
if __debug__:
def debugPrint(self, message):
"""for debugging"""
return self.notify.debug(
str(id(self))+' '+message)

View File

@ -0,0 +1,2 @@
"""PhysicsRoller is for wheels, soccer balls, billiard balls, and other things that roll."""

View File

@ -0,0 +1,741 @@
"""
PhysicsWalker.py is for avatars.
A walker control such as this one provides:
- creation of the collision nodes
- handling the keyboard and mouse input for avatar movement
- moving the avatar
it does not:
- play sounds
- play animations
although it does send messeges that allow a listener to play sounds or
animations based on walker events.
"""
from direct.showbase.ShowBaseGlobal import *
from direct.directnotify import DirectNotifyGlobal
from direct.showbase import DirectObject
from pandac import PhysicsManager
import math
#import LineStream
class PhysicsWalker(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory("PhysicsWalker")
wantAvatarPhysicsIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
useLifter = 0
useHeightRay = 0
# special methods
def __init__(self, gravity = -32.1740, standableGround=0.707,
hardLandingForce=16.0):
assert(self.debugPrint("PhysicsWalker(gravity=%s, standableGround=%s)"%(
gravity, standableGround)))
DirectObject.DirectObject.__init__(self)
self.__gravity=gravity
self.__standableGround=standableGround
self.__hardLandingForce=hardLandingForce
self.needToDeltaPos = 0
self.physVelocityIndicator=None
self.avatarControlForwardSpeed=0
self.avatarControlJumpForce=0
self.avatarControlReverseSpeed=0
self.avatarControlRotateSpeed=0
self.__oldAirborneHeight=None
self.getAirborneHeight=None
self.__oldContact=None
self.__oldPosDelta=Vec3(0)
self.__oldDt=0
self.__speed=0.0
self.__rotationSpeed=0.0
self.__slideSpeed=0.0
self.__vel=Vec3(0.0)
self.collisionsActive = 0
self.isAirborne = 0
self.highMark = 0
"""
def spawnTest(self):
assert(self.debugPrint("\n\nspawnTest()\n"))
if not self.wantAvatarPhysicsIndicator:
return
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from toontown.coghq import MovingPlatform
if hasattr(self, "platform"):
# Remove the prior instantiation:
self.moveIval.pause()
del self.moveIval
self.platform.destroy()
del self.platform
model = loader.loadModelCopy('phase_9/models/cogHQ/platform1')
fakeId = id(self)
self.platform = MovingPlatform.MovingPlatform()
self.platform.setupCopyModel(fakeId, model, 'platformcollision')
self.platformRoot = render.attachNewNode("physicsWalker-spawnTest-%s"%fakeId)
self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 3.0, 1.0))
self.platformRoot.setHpr(base.localAvatar, Vec3.zero())
self.platform.reparentTo(self.platformRoot)
startPos = Vec3(0.0, -15.0, 0.0)
endPos = Vec3(0.0, 15.0, 0.0)
distance = Vec3(startPos-endPos).length()
duration = distance/4
self.moveIval = Sequence(
WaitInterval(0.3),
LerpPosInterval(self.platform, duration,
endPos, startPos=startPos,
name='platformOut%s' % fakeId,
fluid = 1),
WaitInterval(0.3),
LerpPosInterval(self.platform, duration,
startPos, startPos=endPos,
name='platformBack%s' % fakeId,
fluid = 1),
name='platformIval%s' % fakeId,
)
self.moveIval.loop()
"""
def setWalkSpeed(self, forward, jump, reverse, rotate):
assert(self.debugPrint("setWalkSpeed()"))
self.avatarControlForwardSpeed=forward
self.avatarControlJumpForce=jump
self.avatarControlReverseSpeed=reverse
self.avatarControlRotateSpeed=rotate
def getSpeeds(self):
#assert(self.debugPrint("getSpeeds()"))
return (self.__speed, self.__rotationSpeed)
def setupRay(self, floorBitmask, floorOffset):
# This is a ray cast from your head down to detect floor polygons
# A toon is about 4.0 feet high, so start it there
self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
cRayNode = CollisionNode('PW.cRayNode')
cRayNode.addSolid(self.cRay)
self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
self.cRayBitMask = floorBitmask
cRayNode.setFromCollideMask(self.cRayBitMask)
cRayNode.setIntoCollideMask(BitMask32.allOff())
if 0 or self.useLifter:
# set up floor collision mechanism
self.lifter = CollisionHandlerFloor()
self.lifter.setInPattern("enter%in")
self.lifter.setOutPattern("exit%in")
self.lifter.setOffset(floorOffset)
# Limit our rate-of-fall with the lifter.
# If this is too low, we actually "fall" off steep stairs
# and float above them as we go down. I increased this
# from 8.0 to 16.0 to prevent this
#self.lifter.setMaxVelocity(16.0)
#self.bobNodePath = self.avatarNodePath.attachNewNode("bob")
#self.lifter.addCollider(self.cRayNodePath, self.cRayNodePath)
self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath)
else: # useCollisionHandlerQueue
self.cRayQueue = CollisionHandlerQueue()
self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue)
def determineHeight(self):
"""
returns the height of the avatar above the ground.
If there is no floor below the avatar, 0.0 is returned.
aka get airborne height.
"""
if self.useLifter:
height = self.avatarNodePath.getPos(self.cRayNodePath)
# If the shadow where not pointed strait down, we would need to
# get magnitude of the vector. Since it is strait down, we'll
# just get the z:
#spammy --> assert self.debugPrint("getAirborneHeight() returning %s"%(height.getZ(),))
assert onScreenDebug.add("height", height.getZ())
return height.getZ() - self.floorOffset
else: # useCollisionHandlerQueue
"""
returns the height of the avatar above the ground.
If there is no floor below the avatar, 0.0 is returned.
aka get airborne height.
"""
height = 0.0
#*#self.cRayTrav.traverse(render)
if self.cRayQueue.getNumEntries() != 0:
# ...we have a floor.
# Choose the highest of the possibly several floors we're over:
self.cRayQueue.sortEntries()
floorPoint = self.cRayQueue.getEntry(0).getFromIntersectionPoint()
height = -floorPoint.getZ()
self.cRayQueue.clearEntries()
if __debug__:
onScreenDebug.add("height", height)
return height
def setupSphere(self, bitmask, avatarRadius):
"""
Set up the collision sphere
"""
# This is a sphere on the ground to detect barrier collisions
self.avatarRadius = avatarRadius
centerHeight = avatarRadius
if self.useHeightRay:
centerHeight *= 2.0
self.cSphere = CollisionSphere(0.0, 0.0, centerHeight, avatarRadius)
cSphereNode = CollisionNode('PW.cSphereNode')
cSphereNode.addSolid(self.cSphere)
self.cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
self.cSphereBitMask = bitmask
cSphereNode.setFromCollideMask(self.cSphereBitMask)
cSphereNode.setIntoCollideMask(BitMask32.allOff())
# set up collision mechanism
self.pusher = PhysicsCollisionHandler()
self.pusher.setInPattern("enter%in")
self.pusher.setOutPattern("exit%in")
self.pusher.addCollider(self.cSphereNodePath, self.avatarNodePath)
def setupPhysics(self, avatarNodePath):
assert(self.debugPrint("setupPhysics()"))
# Connect to Physics Manager:
self.actorNode=ActorNode("physicsActor")
self.actorNode.getPhysicsObject().setOriented(1)
self.actorNode.getPhysical(0).setViscosity(0.1)
physicsActor=NodePath(self.actorNode)
avatarNodePath.reparentTo(physicsActor)
avatarNodePath.assign(physicsActor)
self.phys=PhysicsManager.PhysicsManager()
fn=ForceNode("gravity")
fnp=NodePath(fn)
#fnp.reparentTo(physicsActor)
fnp.reparentTo(render)
gravity=LinearVectorForce(0.0, 0.0, self.__gravity)
fn.addForce(gravity)
self.phys.addLinearForce(gravity)
self.gravity = gravity
fn=ForceNode("priorParent")
fnp=NodePath(fn)
fnp.reparentTo(render)
priorParent=LinearVectorForce(0.0, 0.0, 0.0)
fn.addForce(priorParent)
self.phys.addLinearForce(priorParent)
self.priorParentNp = fnp
self.priorParent = priorParent
fn=ForceNode("viscosity")
fnp=NodePath(fn)
#fnp.reparentTo(physicsActor)
fnp.reparentTo(render)
self.avatarViscosity=LinearFrictionForce(0.0, 1.0, 0)
#self.avatarViscosity.setCoef(0.9)
fn.addForce(self.avatarViscosity)
self.phys.addLinearForce(self.avatarViscosity)
self.phys.attachLinearIntegrator(LinearEulerIntegrator())
self.phys.attachPhysicalnode(physicsActor.node())
self.acForce=LinearVectorForce(0.0, 0.0, 0.0)
fn=ForceNode("avatarControls")
fnp=NodePath(fn)
fnp.reparentTo(render)
fn.addForce(self.acForce)
self.phys.addLinearForce(self.acForce)
#self.phys.removeLinearForce(self.acForce)
#fnp.remove()
return avatarNodePath
def initializeCollisions(self, collisionTraverser, avatarNodePath,
wallBitmask, floorBitmask,
avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0):
"""
Set up the avatar collisions
"""
assert(self.debugPrint("initializeCollisions()"))
assert not avatarNodePath.isEmpty()
self.cTrav = collisionTraverser
self.floorOffset = floorOffset = 7.0
self.avatarNodePath = self.setupPhysics(avatarNodePath)
if 0 or self.useHeightRay:
#self.setupRay(floorBitmask, avatarRadius)
self.setupRay(floorBitmask, 0.0)
self.setupSphere(wallBitmask|floorBitmask, avatarRadius)
self.setCollisionsActive(1)
def setAirborneHeightFunc(self, getAirborneHeight):
self.getAirborneHeight = getAirborneHeight
def setAvatarPhysicsIndicator(self, indicator):
"""
indicator is a NodePath
"""
assert(self.debugPrint("setAvatarPhysicsIndicator()"))
self.cSphereNodePath.show()
if indicator:
# Indicator Node:
change=render.attachNewNode("change")
#change.setPos(Vec3(1.0, 1.0, 1.0))
#change.setHpr(0.0, 0.0, 0.0)
change.setScale(0.1)
#change.setColor(Vec4(1.0, 1.0, 1.0, 1.0))
indicator.reparentTo(change)
indicatorNode=render.attachNewNode("physVelocityIndicator")
#indicatorNode.setScale(0.1)
#indicatorNode.setP(90.0)
indicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
indicatorNode.setColor(0.0, 0.0, 1.0, 1.0)
change.reparentTo(indicatorNode)
self.physVelocityIndicator=indicatorNode
# Contact Node:
contactIndicatorNode=render.attachNewNode("physContactIndicator")
contactIndicatorNode.setScale(0.25)
contactIndicatorNode.setP(90.0)
contactIndicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
contactIndicatorNode.setColor(1.0, 0.0, 0.0, 1.0)
indicator.instanceTo(contactIndicatorNode)
self.physContactIndicator=contactIndicatorNode
else:
print "failed load of physics indicator"
def avatarPhysicsIndicator(self, task):
#assert(self.debugPrint("avatarPhysicsIndicator()"))
# Velocity:
self.physVelocityIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
physObject=self.actorNode.getPhysicsObject()
a=physObject.getVelocity()
self.physVelocityIndicator.setScale(math.sqrt(a.length()))
a+=self.physVelocityIndicator.getPos()
self.physVelocityIndicator.lookAt(Point3(a))
# Contact:
contact=self.actorNode.getContactVector()
if contact==Vec3.zero():
self.physContactIndicator.hide()
else:
self.physContactIndicator.show()
self.physContactIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
#contact=self.actorNode.getContactVector()
point=Point3(contact+self.physContactIndicator.getPos())
self.physContactIndicator.lookAt(point)
return Task.cont
def deleteCollisions(self):
assert(self.debugPrint("deleteCollisions()"))
del self.cTrav
if self.useHeightRay:
del self.cRayQueue
self.cRayNodePath.removeNode()
del self.cRayNodePath
del self.cSphere
self.cSphereNodePath.removeNode()
del self.cSphereNodePath
del self.pusher
del self.getAirborneHeight
def setCollisionsActive(self, active = 1):
assert(self.debugPrint("collisionsActive(active=%s)"%(active,)))
if self.collisionsActive != active:
self.collisionsActive = active
if active:
self.cTrav.addCollider(self.cSphereNodePath, self.pusher)
if self.useHeightRay:
if self.useLifter:
self.cTrav.addCollider(self.cRayNodePath, self.lifter)
else:
self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue)
else:
self.cTrav.removeCollider(self.cSphereNodePath)
if self.useHeightRay:
self.cTrav.removeCollider(self.cRayNodePath)
# Now that we have disabled collisions, make one more pass
# right now to ensure we aren't standing in a wall.
self.oneTimeCollide()
def getCollisionsActive(self):
assert(self.debugPrint("getCollisionsActive() returning=%s"%(
self.collisionsActive,)))
return self.collisionsActive
def placeOnFloor(self):
"""
Make a reasonable effort to place the avatar on the ground.
For example, this is useful when switching away from the
current walker.
"""
self.oneTimeCollide()
self.avatarNodePath.setZ(self.avatarNodePath.getZ()-self.getAirborneHeight())
def oneTimeCollide(self):
"""
Makes one quick collision pass for the avatar, for instance as
a one-time straighten-things-up operation after collisions
have been disabled.
"""
assert(self.debugPrint("oneTimeCollide()"))
tempCTrav = CollisionTraverser("oneTimeCollide")
if self.useHeightRay:
if self.useLifter:
tempCTrav.addCollider(self.cRayNodePath, self.lifter)
else:
tempCTrav.addCollider(self.cRayNodePath, self.cRayQueue)
tempCTrav.traverse(render)
def handleAvatarControls(self, task):
"""
Check on the arrow keys and update the avatar.
"""
if __debug__:
if self.wantAvatarPhysicsIndicator:
onScreenDebug.append("localAvatar pos = %s\n"%(base.localAvatar.getPos().pPrintValues(),))
onScreenDebug.append("localAvatar h = % 10.4f\n"%(base.localAvatar.getH(),))
onScreenDebug.append("localAvatar anim = %s\n"%(base.localAvatar.animFSM.getCurrentState().getName(),))
#assert(self.debugPrint("handleAvatarControls(task=%s)"%(task,)))
physObject=self.actorNode.getPhysicsObject()
#rotAvatarToPhys=Mat3.rotateMatNormaxis(-self.avatarNodePath.getH(), Vec3.up())
#rotPhysToAvatar=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
contact=self.actorNode.getContactVector()
# hack fix for falling through the floor:
if contact==Vec3.zero() and self.avatarNodePath.getZ()<-50.0:
# DCR: don't reset X and Y; allow player to move
self.reset()
self.avatarNodePath.setZ(50.0)
messenger.send("walkerIsOutOfWorld", [self.avatarNodePath])
# get the button states:
forward = inputState.isSet("forward")
reverse = inputState.isSet("reverse")
turnLeft = inputState.isSet("turnLeft")
turnRight = inputState.isSet("turnRight")
slide = 0#inputState.isSet("slide")
slideLeft = 0#inputState.isSet("slideLeft")
slideRight = 0#inputState.isSet("slideRight")
jump = inputState.isSet("jump")
# Determine what the speeds are based on the buttons:
self.__speed=(forward and self.avatarControlForwardSpeed or
reverse and -self.avatarControlReverseSpeed)
avatarSlideSpeed=self.avatarControlForwardSpeed*0.5
#self.__slideSpeed=slide and (
# (turnLeft and -avatarSlideSpeed) or
# (turnRight and avatarSlideSpeed))
self.__slideSpeed=(
(slideLeft and -avatarSlideSpeed) or
(slideRight and avatarSlideSpeed))
self.__rotationSpeed=not slide and (
(turnLeft and self.avatarControlRotateSpeed) or
(turnRight and -self.avatarControlRotateSpeed))
# How far did we move based on the amount of time elapsed?
dt=ClockObject.getGlobalClock().getDt()
if self.needToDeltaPos:
self.setPriorParentVector()
self.needToDeltaPos = 0
#self.__oldPosDelta = render.getRelativeVector(
# self.avatarNodePath,
# self.avatarNodePath.getPosDelta(render))
#self.__oldPosDelta = self.avatarNodePath.getRelativeVector(
# render,
# self.avatarNodePath.getPosDelta(render))
self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
self.__oldDt = dt
#posDelta = self.avatarNodePath.getPosDelta(render)
#if posDelta==Vec3.zero():
# self.priorParent.setVector(self.__oldPosDelta)
#else:
# self.priorParent.setVector(Vec3.zero())
# # We must copy the vector to preserve it:
# self.__oldPosDelta=Vec3(posDelta)
if __debug__:
if self.wantAvatarPhysicsIndicator:
onScreenDebug.add("posDelta1",
self.avatarNodePath.getPosDelta(render).pPrintValues())
if 0:
onScreenDebug.add("posDelta3",
render.getRelativeVector(
self.avatarNodePath,
self.avatarNodePath.getPosDelta(render)).pPrintValues())
if 0:
onScreenDebug.add("gravity",
self.gravity.getLocalVector().pPrintValues())
onScreenDebug.add("priorParent",
self.priorParent.getLocalVector().pPrintValues())
onScreenDebug.add("avatarViscosity",
"% 10.4f"%(self.avatarViscosity.getCoef(),))
onScreenDebug.add("physObject pos",
physObject.getPosition().pPrintValues())
onScreenDebug.add("physObject hpr",
physObject.getOrientation().getHpr().pPrintValues())
onScreenDebug.add("physObject orien",
physObject.getOrientation().pPrintValues())
if 1:
onScreenDebug.add("physObject vel",
physObject.getVelocity().pPrintValues())
onScreenDebug.add("physObject len",
"% 10.4f"%physObject.getVelocity().length())
if 0:
onScreenDebug.add("posDelta4",
self.priorParentNp.getRelativeVector(
render,
self.avatarNodePath.getPosDelta(render)).pPrintValues())
if 1:
onScreenDebug.add("priorParent",
self.priorParent.getLocalVector().pPrintValues())
if 0:
onScreenDebug.add("priorParent po",
self.priorParent.getVector(physObject).pPrintValues())
if 0:
onScreenDebug.add("__posDelta",
self.__oldPosDelta.pPrintValues())
if 1:
onScreenDebug.add("contact",
contact.pPrintValues())
#onScreenDebug.add("airborneHeight", "% 10.4f"%(
# self.getAirborneHeight(),))
if 0:
onScreenDebug.add("__oldContact",
contact.pPrintValues())
onScreenDebug.add("__oldAirborneHeight", "% 10.4f"%(
self.getAirborneHeight(),))
airborneHeight=self.getAirborneHeight()
if airborneHeight > self.highMark:
self.highMark = airborneHeight
if __debug__:
onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
#if airborneHeight < 0.1: #contact!=Vec3.zero():
if 1:
if (airborneHeight > self.avatarRadius*0.5
or physObject.getVelocity().getZ() > 0.0
): # Check stair angles before changing this.
# ...the avatar is airborne (maybe a lot or a tiny amount).
self.isAirborne = 1
else:
# ...the avatar is very close to the ground (close enough to be
# considered on the ground).
if self.isAirborne and physObject.getVelocity().getZ() <= 0.0:
# ...the avatar has landed.
contactLength = contact.length()
if contactLength>self.__hardLandingForce:
#print "jumpHardLand"
messenger.send("jumpHardLand")
else:
#print "jumpLand"
messenger.send("jumpLand")
self.priorParent.setVector(Vec3.zero())
self.isAirborne = 0
elif jump:
#print "jump"
#self.__jumpButton=0
messenger.send("jumpStart")
if 0:
# ...jump away from walls and with with the slope normal.
jumpVec=Vec3(contact+Vec3.up())
#jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec))
jumpVec.normalize()
else:
# ...jump straight up, even if next to a wall.
jumpVec=Vec3.up()
jumpVec*=self.avatarControlJumpForce
physObject.addImpulse(Vec3(jumpVec))
self.isAirborne = 1 # Avoid double impulse before fully airborne.
else:
self.isAirborne = 0
if __debug__:
onScreenDebug.add("isAirborne", "%d"%(self.isAirborne,))
else:
if contact!=Vec3.zero():
# ...the avatar has touched something (but might not be on the ground).
contactLength = contact.length()
contact.normalize()
angle=contact.dot(Vec3.up())
if angle>self.__standableGround:
# ...avatar is on standable ground.
if self.__oldContact==Vec3.zero():
#if self.__oldAirborneHeight > 0.1: #self.__oldContact==Vec3.zero():
# ...avatar was airborne.
self.jumpCount-=1
if contactLength>self.__hardLandingForce:
messenger.send("jumpHardLand")
else:
messenger.send("jumpLand")
elif jump:
self.jumpCount+=1
#self.__jumpButton=0
messenger.send("jumpStart")
jump=Vec3(contact+Vec3.up())
#jump=Vec3(rotAvatarToPhys.xform(jump))
jump.normalize()
jump*=self.avatarControlJumpForce
physObject.addImpulse(Vec3(jump))
if contact!=self.__oldContact:
# We must copy the vector to preserve it:
self.__oldContact=Vec3(contact)
self.__oldAirborneHeight=airborneHeight
moveToGround = Vec3.zero()
if not self.useHeightRay or self.isAirborne:
# ...the airborne check is a hack to stop sliding.
self.phys.doPhysics(dt)
if __debug__:
onScreenDebug.add("phys", "on")
else:
physObject.setVelocity(Vec3.zero())
#if airborneHeight>0.001 and contact==Vec3.zero():
# moveToGround = Vec3(0.0, 0.0, -airborneHeight)
#moveToGround = Vec3(0.0, 0.0, -airborneHeight)
moveToGround = Vec3(0.0, 0.0, -self.determineHeight())
if __debug__:
onScreenDebug.add("phys", "off")
# Check to see if we're moving at all:
if self.__speed or self.__slideSpeed or self.__rotationSpeed or moveToGround!=Vec3.zero():
distance = dt * self.__speed
slideDistance = dt * self.__slideSpeed
rotation = dt * self.__rotationSpeed
#debugTempH=self.avatarNodePath.getH()
assert self.avatarNodePath.getQuat().isSameDirection(physObject.getOrientation())
assert self.avatarNodePath.getPos().almostEqual(physObject.getPosition(), 0.0001)
# update pos:
# Take a step in the direction of our previous heading.
self.__vel=Vec3(
Vec3.forward() * distance +
Vec3.right() * slideDistance)
# rotMat is the rotation matrix corresponding to
# our previous heading.
rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
step=rotMat.xform(self.__vel)
physObject.setPosition(Point3(
physObject.getPosition()+step+moveToGround))
# update hpr:
o=physObject.getOrientation()
r=LRotationf()
r.setHpr(Vec3(rotation, 0.0, 0.0))
physObject.setOrientation(o*r)
# sync the change:
self.actorNode.updateTransform()
assert self.avatarNodePath.getQuat().isSameDirection(physObject.getOrientation())
assert self.avatarNodePath.getPos().almostEqual(physObject.getPosition(), 0.0001)
#assert self.avatarNodePath.getH()==debugTempH-rotation
messenger.send("avatarMoving")
else:
self.__vel.set(0.0, 0.0, 0.0)
# Clear the contact vector so we can tell if we contact something next frame:
self.actorNode.setContactVector(Vec3.zero())
return Task.cont
def doDeltaPos(self):
assert(self.debugPrint("doDeltaPos()"))
self.needToDeltaPos = 1
def setPriorParentVector(self):
assert(self.debugPrint("doDeltaPos()"))
print "self.__oldDt", self.__oldDt, "self.__oldPosDelta", self.__oldPosDelta
if __debug__:
onScreenDebug.add("__oldDt", "% 10.4f"%self.__oldDt)
onScreenDebug.add("self.__oldPosDelta",
self.__oldPosDelta.pPrintValues())
velocity = self.__oldPosDelta*(1/self.__oldDt)*4.0 # *4.0 is a hack
assert(self.debugPrint(" __oldPosDelta=%s"%(self.__oldPosDelta,)))
assert(self.debugPrint(" velocity=%s"%(velocity,)))
self.priorParent.setVector(Vec3(velocity))
if __debug__:
if self.wantAvatarPhysicsIndicator:
onScreenDebug.add("velocity", velocity.pPrintValues())
def reset(self):
assert(self.debugPrint("reset()"))
self.actorNode.getPhysicsObject().resetPosition(self.avatarNodePath.getPos())
self.priorParent.setVector(Vec3.zero())
self.highMark = 0
self.actorNode.setContactVector(Vec3.zero())
if __debug__:
contact=self.actorNode.getContactVector()
onScreenDebug.add("priorParent po",
self.priorParent.getVector(self.actorNode.getPhysicsObject()).pPrintValues())
onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
onScreenDebug.add("contact", contact.pPrintValues())
def enableAvatarControls(self):
"""
Activate the arrow keys, etc.
"""
assert(self.debugPrint("enableAvatarControls()"))
assert self.collisionsActive
if __debug__:
#self.accept("control-f3", self.spawnTest) #*#
self.accept("f3", self.reset) # for debugging only.
taskName = "AvatarControls-%s"%(id(self),)
# remove any old
taskMgr.remove(taskName)
# spawn the new task
taskMgr.add(self.handleAvatarControls, taskName, 25)
if self.physVelocityIndicator:
taskMgr.add(self.avatarPhysicsIndicator, "AvatarControlsIndicator%s"%(id(self),), 35)
def disableAvatarControls(self):
"""
Ignore the arrow keys, etc.
"""
assert(self.debugPrint("disableAvatarControls()"))
taskName = "AvatarControls-%s"%(id(self),)
taskMgr.remove(taskName)
taskName = "AvatarControlsIndicator%s"%(id(self),)
taskMgr.remove(taskName)
if __debug__:
self.ignore("control-f3") #*#
self.ignore("f3")
if __debug__:
def setupAvatarPhysicsIndicator(self):
if self.wantAvatarPhysicsIndicator:
indicator=loader.loadModelCopy('phase_5/models/props/dagger')
#self.walkControls.setAvatarPhysicsIndicator(indicator)
def debugPrint(self, message):
"""for debugging"""
return self.notify.debug(
str(id(self))+' '+message)

View File

View File