Basic path handler

Hi all,

This is a basic path handler I wrote, since I didn’t feel like using the Motion Paths (which are loaded via .egg files). I wanted to have a system I could change within the game. I mainly wrote this as test for myself, and since I needed it. This is still very basic, and I’m going to update this atleast this evening again. Currently this includes:

  • Adding/inserting paths to a Queue
  • Start/stopping, pause/resume the path
  • Going to a POS and ROTATE the camera

Here is the sourcecode:

from collections import deque
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import *

'''
MovingPathHandler class 
Usage: ([] = required, <> = optional)
MovingPathHandler.addPath("[POS] [(X,Y,Z)] <DURATION> <?>") 
MovingPathHandler.addPath("[ROTATE] [DEGREES] <DURATION> <?>")
'''

class MovingPathHandler(deque):

		def __init__(self, nodepath):
			deque.__init__(self)
			self.nodepath = nodepath

		def addPath(self, path):
			self.append(path)

		def insertPath(self, path):
			self.appendleft(path)

		def stopAction(self):
			self.sequence.finish()

		def pauseAction(self):
			self.sequence.pause()

		def resumeAction(self):
			self.sequence.resume()

		def handlePath(self):
			try:
				action = self.popleft()
			except IndexError:
				#Queue is empty - resume.
				return
			if action.split()[0] == 'POS':
				#HEADING to a position
				position = action.split()[1]
				x,y,z = tuple(map(int, position[1:-1].split(',')))
				duration = 5
				if action.split()[2] == 'DURATION':
					duration = int(action.split()[3])

				self.sequence = Sequence(self.nodepath.posInterval(duration,Point3(x,y,z),startPos=self.nodepath.getPos()), 
											Func(self.handlePath)).start()

			if action.split()[0] == 'ROTATE':
				#ROTATING TO A POS
				degrees = int(action.split()[1])
				duration = 1
				if action.split()[2] == 'DURATION':
					duration = int(action.split()[3])

				self.sequence = Sequence(self.nodepath.hprInterval(duration,Point3(degrees, 0, 0)), 
											Func(self.handlePath)).start()

Here’s a tiny example:

from pandac.PandaModules import loadPrcFileData, KeyboardButton, MouseButton, WindowProperties, TextNode
from direct.gui.DirectGui import *
loadPrcFileData("", "window-title Qlade v0.01 - By wvd")
#loadPrcFileData("", """fullscreen 1 win-size 1024 768""")
import direct.directbase.DirectStart
from direct.task import Task
from direct.actor import Actor
import math

from MovingPathHandler import MovingPathHandler
from direct.particles.Particles import Particles
from direct.particles.ParticleEffect import ParticleEffect
 
#Load the first environment model
environ = loader.loadModel("models/environment")
environ.reparentTo(render)
environ.setScale(0.5,0.5,0.5)
environ.setPos(-8,42,0)
base.disableMouse()
base.camera.setPos(0,-100,20)

mp = MovingPathHandler(base.camera)
mp.addPath("POS (10,0,0) DURATION 5")
mp.addPath("ROTATE 45 DURATION 1")
mp.addPath("POS (10,10,0) DURATION 2")
mp.handlePath()
 
run()

I just updated it, I’ve changed to lists since I can also insert it now and do some other various operations on it. Instead of doing it with a message I’ve now included addPosition() and addRotation(). I’ve also left the old way open with addRawPath(). Next update I will include a way to load some paths via files using the addRawPath function.

If anyone has comments, improvements or any ideas, feel free to post.

from collections import deque
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import *

'''
MovingPathHandler class 
Usage: ([] = required, <> = optional)
MovingPathHandler.addPosition([POSITION],duration=<DURATION>)
MovingPathHandler.addRotation([DEGREES],duration=<DURATION>)
MovingPathHandler.addPath("[POS] [(X,Y,Z)] <DURATION> <?>") 
MovingPathHandler.addPath("[ROTATE] [DEGREES] <DURATION> <?>")
'''

class MovingPathHandler(list):

		def __init__(self, nodepath):
			list.__init__(self)
			self.nodepath = nodepath

		def addPosition(self, point, duration=2, right=False, index=0):
			"""Adds a position to walk to to the list. Duration is standard 2"""
			print point
			path = "POS %s DURATION %s" % (point, duration)
			self.addRawPath(path, right, index)

		def addRotation(self, degrees, duration=2, right=False, index=0):
			"""Adds a rotation to move to the list. Duration is standard 2"""
			path = "ROTATE %s DURATION %s" % (degrees, duration)
			self.addRawPath(path, right, index)

		def addRawPath(self, path, right=False, index=0):
			"""Adds a path to the list at the left side. If right is true it will add it counting from right."""
			if right:
				self.insert(-index,path)
			else:
				self.insert(index,path)

		def stopAction(self):
			self.sequence.finish()

		def pauseAction(self):
			self.sequence.pause()

		def resumeAction(self):
			self.sequence.resume()

		def handlePath(self):
			try:
				action = self.pop().replace(', ',',')
			except IndexError:
				#Listis empty - resume.
				return
			print action.split()
			if action.split()[0] == 'POS':
				#HEADING to a position
				position = action.split()[1]
				print position
				x,y,z = tuple(map(int, position[1:-1].split(',')))
				duration = 5
				if action.split()[2] == 'DURATION':
					duration = int(action.split()[3])

				sequence = Sequence(self.nodepath.posInterval(duration,Point3(x,y,z),startPos=self.nodepath.getPos()), 
											Func(self.handlePath))
				self.sequence = sequence.start()

			if action.split()[0] == 'ROTATE':
				#ROTATING TO A POS
				degrees = int(action.split()[1])
				duration = 1
				if action.split()[2] == 'DURATION':
					duration = int(action.split()[3])

				sequence = Sequence(self.nodepath.hprInterval(duration,Point3(degrees, 0, 0)), 
											Func(self.handlePath))
				self.sequence = sequence.start()

Version 3 is here.

This will be the last version I will post for now I think. This version includes 3 new options: PAUSE, RESUME, WAIT. I will explain everything fully in this post, so it’s all clear. I also included a option to create paths into a file (using the ‘raw’ protocol) and them load them into the list. The code might be a bit messy, so any comments/improvements or something other are always welcome. Thanks.

You can use this path handler in two ways: the raw format, and the easier format. If you’re creating paths inside your program, you should just use the functions. The raw format is used to be able to load files into the list.

[]-tags are required, <> are optional
Note: I didn’t include every param to the function - you should look it up in the source for more advanced options.

Raw format:

  • POS [(X,Y,Z)] DURATION : This moves the NodePath to the position in X seconds. If the duration isn’t set it will be 2.
  • ROTATE [DEGREES] DURATION : This rotates the NodePath X degrees which you specified. If you don’t specify the duration, it’s set to 2.
  • PAUSE : This pauses the current NodePath movement for X seconds. After that it will automatically continue.
  • WAIT : This stops the NodePath movement for X seconds after a action has been done. If you want to pause a movement use MovingPathHandler.addPause().

Functions:

  • MovingPathHandler.loadFile([FILENAME]): Loads a filename into the list using the raw format.
  • MovingPathHandler.addPosition([DESTINATION],duration=): This moves the NodePath to the position in X seconds. If the duration isn’t set it will be 2.
  • MovingPathHandler.addRotation([DEGREES],duration=): This rotates the NodePath X degrees which you specified. If you don’t specify the duration, it’s set to 2.
  • MovingPathHandler.addPause([DURATION]): This pauses the current NodePath movement for X seconds. After that it will automatically continue.
  • MovingPathHandler.addWait([DURATION]): This stops the NodePath movement for X seconds after a action has been done. If you want to pause a movement use MovingPathHandler.addPause().

Example with a file:

POS (10,0,0) DURATION 5
ROTATE 45 DURATION 1
WAIT 2
POS (10,10,0) DURATION 2

Source:

from collections import deque
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import *
from direct.task import Task

'''
MovingPathHandler class 
Usage: ([] = required, <> = optional)
MovingPathHandler.addPath("[POS] [(X,Y,Z)] <DURATION> <?>") 
MovingPathHandler.addPath("[ROTATE] [DEGREES] <DURATION> <?>")
'''

class MovingPathHandler(list):

		def __init__(self, nodepath):
			list.__init__(self)
			self.nodepath = nodepath
			
		def loadPath(self, filename):
			"""Loads a path via a file"""
			try:
				file = open(filename)
				for line in file:
					self.addRawPath(line.rstrip())
				print "Loaded %s operations into the list" % (len(self),)
			
			except IOError, NameError:
				print "Invalid file name. Please check your syntax."

		def addWait(self, seconds, right=False, index=0):
			wait = "WAIT %s" % (seconds,)
			self.addRawPath(wait, right, index)

		def addPause(self, seconds, right=False, index=0):
			pause = "PAUSE %s" % (seconds,)
			self.addRawPath(pause, right, index)

		def addPosition(self, point, duration=2, right=False, index=0):
			"""Adds a position to walk to to the list. Duration is standard 2"""
			path = "POS %s DURATION %s" % (point, duration)
			self.addRawPath(path, right, index)

		def addRotation(self, degrees, duration=2, right=False, index=0):
			"""Adds a rotation to move to the list. Duration is standard 2"""
			path = "ROTATE %s DURATION %s" % (degrees, duration)
			self.addRawPath(path, right, index)

		def addRawPath(self, path, right=False, index=0):
			"""Adds a path to the list at the left side. If right is true it will add it counting from right."""
			if right:
				self.insert(-index,path)
			else:
				self.insert(index,path)

		def stopAction(self):
			self.sequence.finish()

		def pauseAction(self, seconds=None):
			self.sequence.pause()
			if seconds is not None:
				taskMgr.doMethodLater(seconds, self.resumeTask, "Resume")

		def resumeAction(self):
			taskMgr.add(self.resumeTask)

		def resumeTask(self, task):
			self.sequence.resume()
			return Task.done

		def startPath(self, task):
			self.handlePath()
			return Task.done

		def handlePath(self):
			try:
				action = self.pop().replace(', ',',')
			except IndexError:
				#Listis empty - resume.
				return
			print action.split()
			if action.split()[0] == 'POS':
				#HEADING to a position
				position = action.split()[1]
				print position
				x,y,z = tuple(map(int, position[1:-1].split(',')))
				duration = 5
				if action.split()[2] == 'DURATION':
					duration = int(action.split()[3])

				sequence = Sequence(self.nodepath.posInterval(duration,Point3(x,y,z),startPos=self.nodepath.getPos()), 
											Func(self.handlePath))
				self.sequence = sequence.start()

			if action.split()[0] == 'ROTATE':
				#ROTATING TO A POS
				degrees = int(action.split()[1])
				duration = 1
				if action.split()[2] == 'DURATION':
					duration = int(action.split()[3])

				sequence = Sequence(self.nodepath.hprInterval(duration,Point3(degrees, 0, 0)), 
											Func(self.handlePath))
				self.sequence = sequence.start()

			if action.split()[0] == 'RESUME':
				self.resumeAction()

			if action.split()[0] == 'PAUSE':
				#PAUSE THE MOVEMENT
				try:
					time = int(action.split()[1])
					self.pauseAction(seconds=time)
				except IndexError:
					self.pauseAction()

			if action.split()[0] == 'WAIT':
				#PAUSE THE MOVEMENT
				try:
					time = int(action.split()[1])
					taskMgr.doMethodLater(time, self.startPath, "Wait")
				except IndexError:
					print 'Error with WAIT statement'

Another post, I just uploaded a video with a very basic example.

youtube.com/watch?v=lsUS-_SRhKw