Task.py 10 KB

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