Task.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. from PandaModules import *
  2. from DirectNotify import *
  3. from PythonUtil import *
  4. exit = -1
  5. done = 0
  6. cont = 1
  7. def getTimeFrame():
  8. # WARNING: If you are testing tasks without an igloop,
  9. # you must manually tick the clock
  10. # Ask for the time last frame
  11. t = ClockObject.getGlobalClock().getTime()
  12. # Set the clock to have last frame's time in case we were
  13. # Paused at the prompt for a long time
  14. ClockObject.getGlobalClock().setTime(t)
  15. # Get the new frame count
  16. f = ClockObject.getGlobalClock().getFrameCount()
  17. return t, f
  18. class Task:
  19. def __init__(self, callback):
  20. self.__call__ = callback
  21. def setStartTimeFrame(self, startTime, startFrame):
  22. self.starttime = startTime
  23. self.startframe = startFrame
  24. def setCurrentTimeFrame(self, currentTime, currentFrame):
  25. # Calculate and store this task's time (relative to when it started)
  26. self.time = currentTime - self.starttime
  27. self.frame = currentFrame - self.startframe
  28. def doLater(delayTime, task, taskName):
  29. task.name = taskName
  30. # make a sequence out of the delay and the task
  31. seq = sequence(pause(delayTime), task)
  32. return seq
  33. def pause(delayTime):
  34. def func(self):
  35. if (self.time < self.delayTime):
  36. return cont
  37. else:
  38. # Time is up, return done
  39. TaskManager.notify.debug('pause done: ' + self.name)
  40. return done
  41. task = Task(func)
  42. task.name = 'pause'
  43. task.delayTime = delayTime
  44. return task
  45. def release():
  46. def func(self):
  47. # A release is immediately done
  48. TaskManager.notify.debug('release done: ' + self.name)
  49. return done
  50. task = Task(func)
  51. task.name = 'release'
  52. return task
  53. def sequence(*taskList):
  54. return make_sequence(taskList)
  55. def make_sequence(taskList):
  56. def func(self):
  57. # If we got to the end of the list, this sequence is done
  58. if (self.index >= len(self.taskList)):
  59. TaskManager.notify.debug('sequence done: ' + self.name)
  60. return done
  61. else:
  62. task = self.taskList[self.index]
  63. # If this is a new task, set it's start time and frame
  64. if (self.index > self.prevIndex):
  65. task.setStartTimeFrame(self.time, self.frame)
  66. self.prevIndex = self.index
  67. # Calculate this task's time since it started
  68. task.setCurrentTimeFrame(self.time, self.frame)
  69. # Execute the current task
  70. ret = task(task)
  71. # Check the return value from the task
  72. # If this current task wants to continue,
  73. # come back to it next frame
  74. if (ret == cont):
  75. return cont
  76. # If this task is done, increment the index so that next frame
  77. # we will start executing the next task on the list
  78. elif (ret == done):
  79. self.index = self.index + 1
  80. return cont
  81. # If this task wants to exit, the sequence exits
  82. elif (ret == exit):
  83. return exit
  84. task = Task(func)
  85. task.name = 'sequence'
  86. task.taskList = taskList
  87. task.prevIndex = -1
  88. task.index = 0
  89. return task
  90. def makeSpawner(task, taskName, taskMgr):
  91. def func(self):
  92. self.taskMgr.spawnTaskNamed(self.task, self.taskName)
  93. return done
  94. newTask = Task(func)
  95. newTask.name = taskName + "-spawner"
  96. newTask.task = task
  97. newTask.taskName = taskName
  98. newTask.taskMgr = taskMgr
  99. return newTask
  100. def makeSequenceFromTimeline(timelineList, taskMgr):
  101. timeline = []
  102. lastPause = 0
  103. sortedList = list(timelineList)
  104. sortedList.sort()
  105. for triple in sortedList:
  106. t = triple[0] - lastPause
  107. lastPause = triple[0]
  108. task = triple[1]
  109. taskName = triple[2]
  110. timeline.append(pause(t))
  111. timeline.append(makeSpawner(task, taskName, taskMgr))
  112. return make_sequence(timeline)
  113. def timeline(*timelineList):
  114. def func(self):
  115. # Step our sub task manager (returns the number of tasks remaining)
  116. lenTaskList = self.taskMgr.step()
  117. # The sequence start time is the same as our start time
  118. self.sequence.time = self.time
  119. self.sequence.frame = self.frame
  120. if (not self.sequenceDone):
  121. # Execute the sequence for this frame
  122. seqRet = self.sequence(self.sequence)
  123. # See if sequence is done
  124. if (seqRet == done):
  125. self.sequenceDone = 1
  126. # See if the timeline is done
  127. if (lenTaskList == 0):
  128. TaskManager.notify.debug('timeline done: ' + self.name)
  129. return done
  130. else:
  131. return cont
  132. else:
  133. return cont
  134. else:
  135. return cont
  136. task = Task(func)
  137. task.name = 'timeline'
  138. task.taskMgr = TaskManager()
  139. task.sequence = makeSequenceFromTimeline(timelineList, task.taskMgr)
  140. task.sequenceDone = 0
  141. return task
  142. class TaskManager:
  143. notify = None
  144. def __init__(self):
  145. self.running = 0
  146. self.taskList = []
  147. self.currentTime, self.currentFrame = getTimeFrame()
  148. if (TaskManager.notify == None):
  149. TaskManager.notify = directNotify.newCategory("TaskManager")
  150. #TaskManager.notify.setDebug(1)
  151. def spawnMethodNamed(self, func, name):
  152. task = Task(func)
  153. self.spawnTaskNamed(task, name)
  154. def spawnTaskNamed(self, task, name):
  155. TaskManager.notify.debug('spawning task named: ' + name)
  156. task.name = name
  157. task.setStartTimeFrame(self.currentTime, self.currentFrame)
  158. self.taskList.append(task)
  159. return task
  160. def removeAllTasks(self):
  161. # Make a shallow copy so we do not modify the list in place
  162. taskListCopy = self.taskList[:]
  163. for task in taskListCopy:
  164. self.removeTask(task)
  165. def removeTask(self, task):
  166. TaskManager.notify.debug('removing task: ' + `task`)
  167. try:
  168. self.taskList.remove(task)
  169. except:
  170. pass
  171. # TODO: upon death
  172. def removeTasksNamed(self, taskName):
  173. removedTasks = []
  174. TaskManager.notify.debug('removing tasks named: ' + taskName)
  175. # Find the tasks that match by name and make a list of them
  176. for task in self.taskList:
  177. if (task.name == taskName):
  178. removedTasks.append(task)
  179. # Now iterate through the tasks we need to remove and remove them
  180. for task in removedTasks:
  181. self.removeTask(task)
  182. # Return the number of tasks removed
  183. return len(removedTasks)
  184. def step(self):
  185. self.currentTime, self.currentFrame = getTimeFrame()
  186. for task in self.taskList:
  187. task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
  188. # Run the task and check the return value
  189. ret = task(task)
  190. if (ret == cont):
  191. continue
  192. elif (ret == done):
  193. self.removeTask(task)
  194. elif (ret == exit):
  195. self.removeTask(task)
  196. else:
  197. raise 'invalid task state'
  198. return len(self.taskList)
  199. def run(self):
  200. self.running = 1
  201. while self.running:
  202. try:
  203. self.step()
  204. except KeyboardInterrupt:
  205. self.stop()
  206. def stop(self):
  207. # Set a flag so we will stop before beginning next frame
  208. self.running = 0
  209. def __repr__(self):
  210. str = ''
  211. str = str + 'taskList\n'
  212. str = str + '--------------------\n'
  213. for task in self.taskList:
  214. str = str + ' ' + task.name + '\n'
  215. return str
  216. """
  217. import Task
  218. from ShowBaseGlobal import * # to get taskMgr, and run()
  219. # sequence
  220. def seq1(state):
  221. print 'seq1'
  222. return Task.done
  223. def seq2(state):
  224. print 'seq2'
  225. return Task.done
  226. t = Task.sequence(Task.pause(1.0), Task.Task(seq1), Task.release(),
  227. Task.pause(3.0), Task.Task(seq2))
  228. taskMgr.spawnTaskNamed(t, 'sequence')
  229. run()
  230. # timeline
  231. def keyframe1(state):
  232. print 'keyframe1'
  233. return Task.done
  234. def keyframe2(state):
  235. print 'keyframe2'
  236. return Task.done
  237. def keyframe3(state):
  238. print 'keyframe3'
  239. return Task.done
  240. testtl = Task.timeline(
  241. (0.5, Task.Task(keyframe1), 'key1'),
  242. (0.6, Task.Task(keyframe2), 'key2'),
  243. (0.7, Task.Task(keyframe3), 'key3')
  244. )
  245. t = taskMgr.spawnTaskNamed(testtl, 'timeline')
  246. run()
  247. # do later - returns a sequence
  248. def foo(state):
  249. print 'later...'
  250. return Task.done
  251. seq = Task.doLater(3.0, Task.Task(foo), 'fooLater')
  252. t = taskMgr.spawnTaskNamed(seq, 'doLater-fooLater')
  253. run()
  254. # tasks with state
  255. someValue = 1
  256. def func(state):
  257. if (state.someValue > 10):
  258. print 'true!'
  259. return Task.done
  260. else:
  261. state.someValue = state.someValue + 1
  262. return Task.cont
  263. task = Task.Task(func)
  264. # set task state here
  265. task.someValue = someValue
  266. t = taskMgr.spawnTaskNamed(task, 'funcTask')
  267. run()
  268. """