Q Solutions |
In the second part of development when the decision was made to take the rough draft and turn the code into a complete game, I decided to make the animation system a separate component that could be used in other projects. While this makes it more complicated that it needed to be for this game, it is still only 150 lines of Tcl code for the whole system.
The interface between the game and the inimation system is via a display list (in this case called Efffects. This list is built by the animation system (somewhat inefficiently) and called by the Togl redraw proc to display the effects.
Traditional 2D animation systems rely on sprites. All we have to do is move rectangular bitmaps around the screne to create effects. With 3D we have to create objects and apply geometry transformations to those objects to create the effect we desire.
Broadly speaking there are four parts to the animation system.
When Initialised, the animation system allocates a number of display lists from the Scene manager which are placed in a free pool. A request to allocate a new animation will allocate a free list from the pool When the animation effect finishes the allocated display list is returned to the pool.
Whenever a animation is created or it ends, the display list ( in this case Effects) is rebuilt. Each active animation has it's display list added to a vector. This vector is passed to the openGL glCallLists call. This means with one openGL call we can render hundreds or even thousands of Animation effects.
To reduce display load, the scene is only redrawn once per animation cycle (currently 50mS).
Each animation cycle we rebuild the lists, but it is up to the Scene manager to decide when to redraw the display. The Animation loop makes a request for the scene to be redrawn at the end of each cycle. The Scene queues the redraw for when it best suit it.
In this way the animations can be stopped, slowed, or delayed but they will always remain in time sync with the game even if the display does not get redrawn.
Every cycle each animation procedure that has been registed is called. The procedure is passed parameters that include the allocated display list, various time information , and any other arguments that were present at the time of allocation.
The animation proc should rebuild the display list with the openGL drawing commands that are used to render the animation for when the Scene is next redrawn. Some animations may only require that the display list is generated once and then the animation proc idles for the rest of it's lifetime.
Both time and count are passed to the animation proc so that both time and step based animations can be performed.
Since rotation and scaling are common functions for a number of animations, we create a static animation proc that never ends to provide general rotation matrices.
We preallocate a number of display lists with well known names, including:
To start an animation effect we call the animation controller with the following information:
$Mine(Anim) alloc 3000 ShieldUp 500 $themine [~ POINTFIELD]This command starts the Shieldup effect for a period of 3 seconds. The procedure takes the following arguments:
Each effect is produced by a Tcl procedure. We prefix the name of the procedure with the text sfx/ as a convention which helps in locating code but for no other purpose. It does limit the potential for name collision within the game. Each sfx procedure is called with the following arguments:
proc sfx/ShieldUp {name dlist times delay mine shield} { foreach {x y z} [split $mine ,] {break} foreach {loop time maxtime} $times {break} set ratio [expr {double($time) / $delay}] if {$ratio > 1.5} {return} glNewList $dlist GL_COMPILE glPushMatrix glCallList [~ MoveToZero] glTranslatef $x $y -$z glScalef $ratio $ratio $ratio glCallList $shield glPopMatrix glEndList }Explanation
This is used to determine the size of the shield.
We then recreate the display list
placing the commands to position the shield, scale the shield (from 0 to 1.5 units) and then place a call to the supplied display list (which we assume will actually draw the shield effect).
After delay ms has elapsed the ratio gets bigger than 1.5 and the procedure exits without changing the display list. Thus the display list remains static for the period of time between delay and maxtime.
After maxtime the animation controller will zero the display list and place it back in the free pool, the display list will be removed from the glCallLists vector, and then
the main display list Effects will be rebuilt specifing that there is one less display list in
the vector.
The code to rebuild the main list is simply
glNewList [~ Effects] GL_COMPILE glCallLists $number GL_INT $vector glEndList
In the program we start this animation when we select the DeFuse bomb function. We pass the animation procedure the arguments:
Inside the POINTFIELD display list is:
glNewList [Scene name POINTFIELD] GL_COMPILE glDisable GL_LIGHTING glPushMatrix glPointSize 1 glColor3f 1 1 1 glCallList [~ AntiSlowRotateXYZ] makeWS 0.5 100 POINT glEnable GL_LIGHTING glPopMatrix glEndListWe can see that as well as making a sphere of 100x100 points we also call another display list called AntiSlowRotateXYZ. You may have guessed that this is one of the well known rotation display lists that are automatically maintained.
The above also demonstrates that the animation effects can be isolated from the actual drawing commands so that the effect can be used in diverse and unrelated situations.
For example, if in the above example we replace the shield argument with 'A Tree' display list, we could get a growing tree placed at the position specified without having to create a new special effect for it. Maybe of more use would be a display list that contained text. In any case , it goes to show that the system is very flexible.
While Tcl will struggle to do intensive partical system animations there is enough potential that with considerate coding we can get acceptable speed in pure Scripting for one of the most demanding PC applications, Gaming!
Copyright © ![]() |