Panda3D Manual: Tasks
  <<prev top next>>     

Tasks are special functions that are called once, each frame, while your application executes. They are similar in concept to threads, but in Panda, tasks are not separate threads; instead, all tasks are run cooperatively, one at a time, within the main thread. This design simplifies game programming considerably by removing the requirement to protect critical sections of code from mutual access.

When you start Panda3D by importing DirectStart, a handful of tasks are created by default, but you are free to add as many additional tasks as you like.

The Task Function

A task is defined with a function or class method; this function is the main entry point for the task and will be called once per frame while the task is running. By default, the function receives one parameter, which is the task object; the task object carries information about the task itself, such as the amount of time that the task has been running.

Your task function should return when it has finished processing for the frame. Because all tasks are run in the same thread, you must not spend too much time processing any one task function; the entire application will be locked up until the function returns.

The task function may return either Task.cont to indicate that the task should be called again next frame, or Task.done to indicate that it should not be called again. If it returns None (which is to say, it does not return anything), then the default behavior is to call the task function again.

You can check how long your task has been running by checking task.time in your task function. You can also check how many times the task function has been run by using task.frame.

The below example imports the Task module and shows a function used as a task.

from direct.task import Task

#This task runs for two seconds, then prints done
def exampleTask(task):
  if task.time < 2.0:
    return Task.cont
  print 'Done'
  return Task.done

The Do-Later Task

A useful special kind of task is the do-later: this is similar to a task, but rather than being called every frame it will be called only once, after a certain amount of time (in seconds) has elapsed. You can, of course, implement a do-later task with a regular task that simply does nothing until a certain amount of time has elapsed (as in the above example), but using a do-later is a much more efficient way to achieve the same thing, especially if you will have many such tasks waiting around.

taskMgr.doMethodLater(delayTime, myFunction, 'Task Name')

In this case myFunction must accept a task variable. If you wish to use a function that does not accept a task variable:

taskMgr.doMethodLater(delayTime, myFunction, 'Task Name', extraArgs = [variables])

Note: if you wish to call a function which takes no variables simply pass extraArgs = []

The Task Object

The task object is passed into all Task Functions. There are several variables accessible in the func object, these are:

Variable Returns
task.time A float that indicates how long this task function has been running since the first execution of the function. The timer is running even when the task function is not being executed.
task.frame An integer that counts how many times this task function has been executed. Count starts from 1.
task.id An integer that gives the unique id assigned to this task by the Task Manager.
task.name The task name assigned to the task function.

The Task Manager

All tasks are handled through the global Task Manager object, called taskMgr in Panda3D. The Task Manager keeps a list of all currently-running tasks. To add your task function to the task list, call taskMgr.add() with your function and an arbitrary name for the task. taskMgr.add() returns a Task which can be used to remove the task later on.

taskMgr.add(exampleTask, 'MyTaskName')

Additional task functions can be appended to existing tasks, allowing convenience for removing many task functions at the same time. Each task function in a task is independent from each other, meaning that a task function calling Task.done will not affect the other task functions in the task.

taskMgr.add(taskFunc,'Existing TaskName',appendTask=True)

A cleanup function can also be added to the task functions by using the uponDeath argument. Similar to task functions, the uponDeath function has a task object as a parameter.

taskMgr.add(exampleTask,'TaskName',uponDeath=cleanupFunc)

To remove the task and stop it from executing, call taskMgr.remove(). You can pass in either the name of the task, or the task object (which was returned by taskMgr.add(), above).

taskMgr.remove('MyTaskName')

To print the list of tasks currently running, simply print out taskMgr. Among your own tasks, you may see the following system tasks listed:

dataloop Processes the keyboard and mouse inputs
tkloop Processes Tk GUI events
eventManager Processes events generated by C++ code, such as collision events
igloop Draws the scene

Task timing

To see the specific timing information for each task when you print taskMgr, add the following line to your Config.prc file

task-timer-verbose #t

(see The Configuration File for config syntax)

Examples

Below are some detailed examples on how to use various aspects of tasks.

appendTask

#This function will run for 3 frames before stopping
def taskFunction1(task):
  print "This is Function 1, on task ID",task.id
  if task.frame > 2:
    print "Function 1 stops at frame",task.frame
    return Task.done
  return Task.cont

#This runs for 5 frames
def taskFunction2(task):
  print "This is Function 2, on task ID",task.id
  if task.frame > 4:
    print "Function 2 stops at frame",task.frame
    return Task.done
  return Task.cont

# Adds a task named "Task Tester", save the task reference to taskObj
taskObj = taskMgr.add(taskFunction1, "Task Tester")
# Append a function to "Task Tester" task
taskMgr.add(taskFunction2, "Task Tester", appendTask=True) 

uponDeath

taskAccumulator = 0

def cleanUp(task):
  print "Task func has accumulated",taskAccumulator
  #Reset the accumulator
  taskAccumulator = 0

#A task that runs forever
def taskFunc(task):
  taskAccumulator += 1
  return Task.cont

def taskStop(task):
  taskMgr.remove('Accumulator')

#Add the taskFunc function with an uponDeath argument
taskMgr.add(taskFunc,'Accumulator',uponDeath=cleanUp)
#Stops the task 2 seconds later
taskMgr.doMethodLater(2, taskStop, 'Task Stop')
  <<prev top next>>