So I’ve just come back to my point and click experiment and I’ve managed to get it working (an issue with setting base.cam rather than base.camera).
My code appears to work just fine if I set PATHFINDING to zero (Ralph walks straight to the destination), but if I use the pandaai pathfinding code it doesn’t seem to work very well.
My main two issues are:
- Ralph doesn’t seem to ever make it to the destination - he gets close but not quite there. I’m guessing this is a result of the A* pathfinding mechanism using a matrix of tiles.
- More importantly as Ralph to a destination on a hill he seems to fall through and doesn’t track the terrain. I can’t see why though, it works fine without the pathfinding code and I’m adjusting Ralphs height after pandaai has updated his position.
Can anyone help?
(run this code with the models from the Roaming-Ralph example)
# Author: Richard Cooper ... based on Ryan Myers original
# Models: Jeff Styers, Reagan Heller
# Last Updated: 2/4/2011
#
# This tutorial provides an example of creating a character
# and having it walk around on uneven terrain, as well
# as implementing a fully rotatable camera.
import direct.directbase.DirectStart
from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionRay
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from panda3d.core import Vec3,Vec4,BitMask32, GeomNode
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from direct.showbase.ShowBase import Vec3, Point3
import random, sys, os, math
#for Pandai
from panda3d.ai import *
SPEED = 0.5
PATHFINDING = 1
class World(DirectObject):
def __init__(self):
self.walking = Vec3()
self.isMoving = False
self.dest = None
base.win.setClearColor(Vec4(0,0,0,1))
# Set up the environment
# This environment model contains collision meshes. If you look
# in the egg file, you will see the following:
#
# <Collide> { Polyset keep descend }
#
# This tag causes the following mesh to be converted to a collision
# mesh -- a mesh which is optimized for collision, not rendering.
# It also keeps the original mesh, so there are now two copies ---
# one optimized for rendering, one for collisions.
self.environ = loader.loadModel("models/world")
self.environ.reparentTo(render)
self.environ.setPos(0, 0, 0)
# Create the main character, Ralph
ralphStartPos = self.environ.find("**/start_point").getPos()
self.ralph = Actor("models/ralph",
{"run":"models/ralph-run",
"walk":"models/ralph-walk"})
self.ralph.reparentTo(render)
self.ralph.setScale(1)
ralphStartPos = Vec3(-98.64, -20.60, 0)
self.ralph.setPos(ralphStartPos)
# click to move
self.accept("mouse1", self.findDest)
# Set up the camera - be carefully to always set base.camera; if you set base.cam the click
# to move doesn't work properly - read up on the differences between base.cam and base.camera
base.disableMouse()
base.camera.setPosHpr(17.79,-87.64,90.16,38.66,325.36,0)
#base.camera.setPosHpr(-98, -20, 5, 0, 0 , 0)
# We will detect the height of the terrain by creating a collision
# ray and casting it downward toward the terrain. One ray will
# start above ralph's head, and the other will start above the camera.
# A ray may hit the terrain, or it may hit a rock or a tree. If it
# hits the terrain, we can detect the height. If it hits anything
# else, we rule that the move is illegal.
self.cTrav = CollisionTraverser()
self.ralphGroundRay = CollisionRay()
self.ralphGroundRay.setOrigin(0, 0, 1000)
self.ralphGroundRay.setDirection(0, 0, -1)
self.ralphGroundCol = CollisionNode('ralphRay')
self.ralphGroundCol.addSolid(self.ralphGroundRay)
self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
self.ralphGroundHandler = CollisionHandlerQueue()
self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)
self.clickTrav = CollisionTraverser()
self.pickerNode = CollisionNode('mouseRay')
self.pickerNP = base.camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(BitMask32.bit(0))
self.pickerNode.setIntoCollideMask(BitMask32.allOff())
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.pickerHandler = CollisionHandlerQueue()
self.clickTrav.addCollider(self.pickerNP, self.pickerHandler)
# Uncomment this line to show a visual representation of the
# collisions occuring
self.cTrav.showCollisions(render)
self.clickTrav.showCollisions(render)
# Create some lighting
ambientLight = AmbientLight("ambientLight")
ambientLight.setColor(Vec4(.3, .3, .3, 1))
directionalLight = DirectionalLight("directionalLight")
directionalLight.setDirection(Vec3(-5, -5, -5))
directionalLight.setColor(Vec4(1, 1, 1, 1))
directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
render.setLight(render.attachNewNode(ambientLight))
render.setLight(render.attachNewNode(directionalLight))
self.setAI()
def setAI(self):
self.AIworld = AIWorld(render)
self.AIchar = AICharacter("ralph",self.ralph, 60, 0.05, 25) # mass, force maxforce
self.AIworld.addAiChar(self.AIchar)
self.AIbehaviors = self.AIchar.getAiBehaviors()
self.AIbehaviors.initPathFind("models/navmesh.csv")
# find the place to walk to
def findDest(self):
# First we check that the mouse is not outside the screen.
if base.mouseWatcherNode.hasMouse():
# This gives up the screen coordinates of the mouse.
mpos = base.mouseWatcherNode.getMouse()
# This makes the ray's origin the camera and makes the ray point
# to the screen coordinates of the mouse.
self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
self.clickTrav.traverse(render)
self.pickerHandler.sortEntries()
# walk through collision until we find one with the terrain - allows us to click behind things
for i in xrange(0, self.pickerHandler.getNumEntries()):
pickedObj = self.pickerHandler.getEntry(i).getIntoNode()
#print pickedObj.getName()
if pickedObj.getName() == 'terrain':
#print pickedObj
self.dest = self.pickerHandler.getEntry(i).getSurfacePoint(render)
taskMgr.remove("moveTask") # stop any previous move
if not PATHFINDING:
self.ralph.lookAt(self.dest.x, self.dest.y, self.ralph.getZ())
self.ralph.setHpr(self.ralph.getH() - 180, self.ralph.getP(), self.ralph.getR())
else:
self.AIbehaviors.pathFindTo(self.dest, "addPath")
taskMgr.add(self.aimove, "moveTask")
break
def aimove(self, task):
retVal = task.cont
self.AIworld.update()
retVal = self.move(task)
if ( retVal == task.done or
self.AIbehaviors.behaviorStatus("pathfollow") == "done"):
self.ralph.stop()
self.ralph.pose("walk", 5)
self.isMoving = False
return task.done
if self.isMoving is False:
self.ralph.loop("run")
self.isMoving = True
return retVal
def move(self, task):
retVal = task.cont
# save ralph's initial position so that we can restore it,
# in case he falls off the map or runs into something.
startpos = self.ralph.getPos()
if not PATHFINDING:
# If ralph is moving, loop the run animation.
# If he is standing still, stop the animation.
if self.dest:
self.walking = Vec3(self.dest.x - self.ralph.getX(),
self.dest.y - self.ralph.getY(),
0)
if self.walking.length() > 1:
self.ralph.setY(self.ralph, -25 * globalClock.getDt())
else:
retVal = task.done
# Now check for collisions.
self.cTrav.traverse(render)
# Adjust ralph's Z coordinate. If ralph's ray hit terrain,
# update his Z. If it hit anything else, or didn't hit anything, put
# him back where he was last frame.
self.ralphGroundHandler.sortEntries()
for i in xrange(0, self.ralphGroundHandler.getNumEntries()):
entry = self.ralphGroundHandler.getEntry(i).getIntoNode()
if entry.getName() == "terrain":
self.ralph.setZ(self.ralphGroundHandler.getEntry(i).getSurfacePoint(render).getZ())
break;
self.ralph.setP(0) # make sure the head is always upright - before we try colliding
return retVal
w = World()
run()