In fact, I should maybe add something about my view of path calculation :
I based it on memory cells to be able to compute AI layers :
- one for spatial reasoning
- another for influence map
- strategy (terrain analysis, line of fire for example)
This could correspond to the openness+occupancy+static cover layers described in the Paul Tozour paper in AI Game Wisdom 2.
As I am new to Panda, I thought the better should be to go from that Pygame A* Example, that is working very well and after could be reworked (smoothing the path for example).
For now, my Roaming Ralph reworked + draw.py patchwork taken from post here give me a litlle beginning
# Left click on the ground to move.
# Rotate the camera by moving the mouse pointer to the edges of the screen or
# with the left & right arrow keys.
# Zoom the camera with the mouse wheel or the up & down arrow keys.
import direct.directbase.DirectStart # Start Panda.
from pandac.PandaModules import * # Import the Panda Modules.
from direct.showbase.DirectObject import DirectObject # To handle Events.
from direct.task import Task # To use Tasks.
from direct.actor import Actor # To use animated Actors.
from direct.interval.IntervalGlobal import * # To use Intervals.
# We need to import this function for the player's rotation to work properly.
from direct.showbase.PythonUtil import closestDestAngle
from direct.gui.OnscreenText import OnscreenText
from direct.gui.OnscreenImage import OnscreenImage
import sys
from draw import *
class Controls(DirectObject):
def __init__(self):
base.disableMouse() # Disable default camera.
self.loadModels()
self.setupCollisions()
self.drawGrid()
# Declare variables.
self.position = None
self.playerMovement = None
self.movementSpeed = 6.0 # Controls how long it takes the player to
# move to the clicked destination.
self.speed = .10 # Controls the speed of the camera's rotation and zoom.
# Setup controls
self.accept("escape", sys.exit)
self.accept("player-stopped", self.stopWalkAnim)
self.accept("mouse1", self.moveToPosition)
self.accept("arrow_left", self.cameraTurn,[-1])
self.accept("arrow_right", self.cameraTurn,[1])
self.accept("arrow_up", self.cameraZoom,[-1])
self.accept("arrow_down", self.cameraZoom,[1])
self.accept("wheel_up", self.cameraZoom,[-1])
self.accept("wheel_down", self.cameraZoom,[1])
## self.accept("g",self.drawGrid)
textObject = OnscreenText(text = 'Du tExte', pos = (-1, 0.7), scale = 0.07)
taskMgr.add(self.edgeScreenTracking, "edgeScreenTracking")
# End __init__
def drawGrid(self):
# Create the static elements of the test environment. Use one Draw
# object for all the static elements.
d = Draw()
# Make a 30x30 grid centered at the origin, in Purple.
d.drawXYGrid(Vec2(-15.5,-15.2), numSquares=30,squareSize=1.03,color=(0.66,0,0.66,1))
# Draw a grey 100x100x100 cuboid, with the grid we previously drew as the floor of the cuboid.
d.drawCuboid(Vec3(-5,-5,0), Vec3(5,5,2.5), color=(0.2,0.3,0.9,1))
node = d.create() # A special GeomNode that draws the shapes.
np = NodePath(node)
np.reparentTo(render)
def loadModels(self):
# Load an environment
self.environ = loader.loadModel("models/terrain2.egg")
self.environ.reparentTo(render) # Place it in the scene.
self.environ.setPos(0, 0, 0)
self.environ.setHpr(0, 0, 0)
## texture = loader.loadTexture("models/rock03.jpg")
## self.environ.setTexture(texture)
# For the camera to rotate independently of the player a 'player dummy
# node' and a 'camera dummy node' need to be created. Both dummy nodes
# are then 'parented' to the 'player dummy node' making them "siblings"
# under the player dummy node. This means that any transformations
# performed on the dummy node will be inherited by the player model and
# the camera. Moving the player dummy node will move both the player
# model and the camera, but moving or rotating the player model itself
# won't effect the camera (because the camera isn't directly parented
# to it).
# Create the player's dummy node.
self.player_dummy_node = render.attachNewNode("player_dummy_node")
# Position the dummy node.
self.player_dummy_node.setPos(0, 0, 0)
self.player_dummy_node.setHpr(0, 0, 0)
# The terrain model was edited by hand to include a start position for
# the player. Use the Find command to locate it.
self.playerStart = self.environ.find("**/start_point").getPos()
# Now load the player model and its animations.
self.player = Actor.Actor("models/ralph",{"walk":"models/ralph-walk"})
# Set the player to the start position.
self.player.setPos(self.playerStart)
# Attach/parent the player model to the player dummy node.
self.player.reparentTo(self.player_dummy_node)
# The player model is too large, so scale it down by 50%.
self.player.setScale(.5)
# Now create the camera dummy node.
self.camera_dummy_node = render.attachNewNode("camera_dummy_node")
# Attach/parent the camera dummy node to the player dummy node.
self.camera_dummy_node.reparentTo(self.player_dummy_node)
# Attach/parent the main camera to the camera dummy node.
camera.reparentTo(self.camera_dummy_node)
# Position the main camera.
camera.setPos(0, -35, 18) # X = left & right, Y = zoom, Z = Up & down.
camera.setHpr(0, -25, 0) # Heading, pitch, roll.
# End loadModels
# Define a function to setup collision detection. We need two rays, one
# attached to the camera for mouse picking and one attached to the player
# for collision with the terrain. The rays must only cause collisions and
# not collide with each other so their Into bitMasks are set to allOff().
def setupCollisions(self):
# The terrain model was edited by hand to include the following tag:
# <Collide> Plane01 { Polyset keep descend }.
#Once we have the collision tags in the model, we can get to them using
# the NodePath's find command.
self.ground = self.environ.find("**/terrain")
# Set the model's Into collide mask to bit (0). Now only objects that
# have their From bitmask also set to (0) can collide with the terrain.
self.ground.node().setIntoCollideMask(BitMask32.bit(0))
# Create a CollisionTraverser for the picker ray. CollisionTraversers
# are what do the job of calculating collisions.
self.picker = CollisionTraverser()
# Create a handler for the picker ray
self.queue = CollisionHandlerQueue()
# Make a collision node for our picker ray
self.pickerNode = CollisionNode('mouseRay')
# Attach that node to the camera since the ray will need to be positioned
# relative to it.
self.pickerNP = camera.attachNewNode(self.pickerNode)
# Set the collision node's From collide mask. Now the ray can only cause
# collisions with objects that have bitMask(0) such as the terrain.
self.pickerNode.setFromCollideMask(BitMask32.bit(0))
# Set the collision node's Into collide mask to allOff so that nothing
# can collide into the ray.
self.pickerNode.setIntoCollideMask(BitMask32.allOff())
# Make our ray
self.pickerRay = CollisionRay()
# Add it to the collision node
self.pickerNode.addSolid(self.pickerRay)
#Register the ray as something that can cause collisions with the traverser
self.picker.addCollider(self.pickerNP, self.queue)
# Setup collision stuff to handle the player's collision with the terrain.
# Make a collision node for the player's ray.
self.groundCol = CollisionNode('playerRay')
# Make a collision ray for the player.
self.groundRay = CollisionRay()
# Attach the collision node to the player dummy node.
self.groundColNp = self.player_dummy_node.attachNewNode(self.groundCol)
# Set the height of the ray (7 units above the player's head)
self.groundRay.setOrigin(0, 0, 7)
# Set the rays direction (pointing down on the Z axis)
self.groundRay.setDirection(0, 0, -1)
# Add the collision node to the collision ray
self.groundCol.addSolid(self.groundRay)
# Set the collision node's From collide mask. Now the ray can collide
# with objects (like the terrain) that also have bitMask(0).
self.groundCol.setFromCollideMask(BitMask32.bit(0))
# Set the collision node's Into collide mask to allOff so that nothing
# can collide into the ray.
self.groundCol.setIntoCollideMask(BitMask32.allOff())
# Make a CollisionTraverser. This will be used in the correctPlayerZ
# function.
self.Zcoll = CollisionTraverser()
# Make a handler for the ground ray. This will be used in the
# correctPlayerZ function.
self.ZcollQueue = CollisionHandlerQueue()
# Register it as something that can cause collisions with the traverser.
self.Zcoll.addCollider(self.groundColNp, self.ZcollQueue)
# Uncomment this line to see the collisions
# self.Zcoll.showCollisions(render)
# Uncomment this line to see the collision rays
# self.groundColNp.show()
# End setupCollisions
# Define a task to monitor the position of the mouse pointer & rotate
# the camera when the mouse pointer moves to the edges of the screen.
def edgeScreenTracking(self,task):
# Check if the mouse is available
if not base.mouseWatcherNode.hasMouse():
return Task.cont
# Get the relative mouse position, its always between 1 and -1
mpos = base.mouseWatcherNode.getMouse()
if mpos.getX() > 0.99:
self.cameraTurn(1)
elif mpos.getX() < -0.99:
self.cameraTurn(-1)
return Task.cont
# End edgeScreenTracking
# Define the CameraTurn function.
def cameraTurn(self,dir):
self.camTurn = LerpHprInterval(self.camera_dummy_node, self.speed, Point3(self.camera_dummy_node.getH()-(10*dir), 0, 0))
self.camTurn.start()
# End cameraTurn
# Define the cameraZoom function.
def cameraZoom(self,dir):
self.camZoom = LerpPosInterval(camera, self.speed, Point3(camera.getX(), camera.getY()-(2*dir), camera.getZ()+(.8*dir)))
self.camZoom.start()
# End cameraZoom
# Define a function to correct the player's Z axis so that he follows the
# contours of the ground.
def correctPlayerZ(self, time):
startpos = self.player.getPos()
# Check for collisions
self.Zcoll.traverse(render)
#Gestion de la collision : Retir? au profit d'une d?claration d'obstacles
if self.ZcollQueue.getNumEntries > 0:
self.ZcollQueue.sortEntries()
point = self.ZcollQueue.getEntry(0).getSurfacePoint(self.environ)
self.player.setZ(point.getZ())
else:
self.player.setPos(startpos)
# End correctPlayerZ
# Define a function to get the position of the mouse click on the terrain.
def getPosition(self, mousepos):
self.pickerRay.setFromLens(base.camNode, mousepos.getX(),mousepos.getY())
# Now check for collisions.
self.picker.traverse(render)
if self.queue.getNumEntries() > 0:
self.queue.sortEntries()
# This is the clicked position.
self.position = self.queue.getEntry(0).getSurfacePoint(self.environ)
# Set its Z axis to remain on the ground.
self.position.setZ(0)
return None
# End getPosition
# Define a function to make the player turn towards the clicked position
# and then move to that position.
def moveToPosition(self):
# Get the clicked position.
self.getPosition(base.mouseWatcherNode.getMouse())
if self.position==None:
return
# Create a dummy node.
self.npLook = render.attachNewNode("npLook")
# Calculate its position.
self.npLook.setPos(self.player.getPos(render))
# Make it look at the clicked position.
self.npLook.lookAt(self.position)
# Prevent overturning or 'wrap-around' by adjusting the player's heading
# by 360 degrees.
reducedH = self.player.getH()%360.0
# Set the player's heading to that value.
self.player.setH(reducedH)
# Get the player's new heading.
currH = self.player.getH()
# Get the dummy node's heading.
npH = self.npLook.getH()
# Ralph was modeled facing backwards so we need to add 180 degrees to
# stop him walking backwards. If your model is not modeled backwards
# then delete the + 180.0.
newH = closestDestAngle(currH, npH + 180.0)
# Create a turn animation from current heading to the calculated new heading.
playerTurn = self.player.hprInterval(.2, Point3(newH, 0, 0))
# Calculate the distance between the start and finish positions.
# This is then used to calculate the duration it should take to
# travel to the new coordinates based on self.movementSpeed.
travelVec = self.position - self.player_dummy_node.getPos()
distance = travelVec.length()
# Create an animation to make the player move to the clicked position.
playerMove = self.player_dummy_node.posInterval((distance / self.movementSpeed), self.position)
# We create a LerpFunc Interval to correct the Z axis as we go along.
# So that the player stays on the ground.
playerPositionZ = LerpFunc(self.correctPlayerZ, duration=(distance / self.movementSpeed))
# Put the animations into a parallel sequence and set the doneEvent.
if self.playerMovement:
self.playerMovement.setDoneEvent("")
self.playerMovement = Parallel(playerTurn, playerMove, playerPositionZ)
self.playerMovement.setDoneEvent("player-stopped")
# Play the walk animation.
self.player.loop("walk")
self.playerMovement.start()
# End moveToPosition
def stopWalkAnim(self):
# This is called when the movement animation has finished.
# We can then stop the walk animation.
self.player.stop("walk")
self.player.pose("walk",17)
self.playerMovement = None
c = Controls()
run()
As I said this is just a learning approach no code is mine and I still try to understand how to put a cube for each cekk that will be a wall as also the possibility to toggle the camera to a top-view(editor-type).
David