-
Notifications
You must be signed in to change notification settings - Fork 13
3. Critter tasks and animation
- Genesis. From the idea to the design
- Architecture and framework
- Critter tasks and animation
- Level design and display
- Minification and size constraints
Weasels. Special attention was paid to both their behaviour and their graphical appearance, as they are central to the game. Even though they are the foes and their life expectancy should be severely limited once the slaughter begins.
The use cases defined the activities that the critters were able to undertake. A few adjustments were made later on.
- walking : moving forward, default activity as long as there is a ground underneath.
- falling : obeying gravity, default when there is no ground.
- floating : still going down, but with a parachute to slow down the fall (automatically opens after a few pixels down)
- balloonning : going up in the air. Needs a balloon.
- flying : being airborne because of a shockwave. Gravity applies, the weasel will follow a parabolic path, unless it bounces on a wall or ceiling.
- blocking : staying in place, forcing other weasels to turn around
- building : creating a flight of stairs to bridge a gap.
- digging : digging down. Removed from the final release for lack of space.
- excavating : digging sideways (left or right). Removed from the final release for lack of space.
The implementation in the Critter class features a state machine representing the current activity. Several factors can trigger a transition between two activities, either decided by the weasel AI or caused by an external event :
- fallers or floaters will resume walking upon touching the ground
- exploding mines will send nearby critters gliding
- picking up a balloon will make a weasel fly
- popping the ballon with the shotgun causes them to fall (obviously)
- weasels can decide on their own to let the balloon go
- weasels reaching the edge of a cliff can continue forward and fall down, or react and turn into blockers or builders
- after a short delay, falling weasels will open an umbrella and turn into floaters
Results of pixel-perfect collision tests are used as input by the game logic to decide on a critter's next move. The pixels from the scenery tested depend on the activity currently undertaken by the weasel. The scenery is stored as a raw bitmap, extracted from the canvas using getImageData()
.
Pixels with alpha value above 128 are considered as solid for collision purposes. However, all functions editing the scenery always make sure that all pixels are either fully opaque or fully transparent, which was the norm on 16-bit computers.
Walkers
Weasel progress at one pixel every computed frame (40 ms). Their walk involves several phases and three collision checks for each frame : one below, one in front at foot height, and one in front full size. First, the presence of a solid ground right underneath its paws is checked. A single pixel over the sprite's total width is enough to support it. Otherwise, the walk is interrupted and the activity changes to a fall. If there is a solid ground, the process continues. Two pixels are tested in front of the critter, right above the ground. A positive test results in the weasel being lifted by a couple pixels, which lets it walk up gentle slopes. (the code is buggy, the detection line is 2 pixels high, while the lift is 3 pixels). A last test is then performed : if any pixel right before the weasel, at any height up to its head, is solid, then the path is blocked and the critter will turn around. The encounter with a blocker, resulting in the same outcome, is performed at another point in the code. In both cases, the walker is allowed to fall by a couple pixels without changing its activity, to compensate for an uneven ground.
Edge of a cliff
An extra collision test is performed at the beginning of each walk cycle. A bounding box extending five pixels below is used to detect an edge before the weasels performs a base jump. If the test is positive, the AI kicks in and the weasel is given a chance to do something smart. The decision tree is quite simple : if the nearest exit is the other way, the weasel becomes a blocker, to orient others in the right direction. If the exit is located that way, but higher, it turns into a builder. Otherwise it hopes that down is the right way out.
Fallers and floaters
The entry point for fallers is the same as for walkers. If a ground is found, the weasel will resume walking, otherwise the fall continues at a rate of 4 pixels every frame (a collision test is performed for each pixel). Floaters behave the same way but at the slower pace of only one pixel a frame.
Flyers
Flyers are given an initial momentum by an exploding mine, and they stay airborne until gravity brings them back to the ground. This is achieved through a discrete acceleration : a constant value is added to their vertical speed each frame. Their motion is divided into steps equal to the greatest value between horizontal (x) and vertical (y) speeds. Each step, the weasel moves by no more than one pixel in each direction, then several collision tests are performed on the rounded coordinates. The sides (left or right, depending on the direction of travel) are tested first. If a collision is detected, the weasel bounces sideways, losing part of its horizontal speed upon hitting the scenery. If the weasel is travelling up, the pixels above are tested. If a ceiling is hit, the vertical speed is reduced to zero, but the flight continues. Last, the pixels below are tested, only if going down. Once the ground is reached, the fall is broken, resulting in damage to the critter, which then resumes walking.
The animation of the Lemmings was definitely a source of inspiration, I wanted something with the same spirit, yet different. Weasels should be taller and more slender, not bulky like the lemmings were.
The weasel walk is 10 frames long, and each image is displayed twice. At 25 FPS, the cycle is completed in 20/25=0.8 seconds, which is identical to that of a lemming (8 frames shown at 10 FPS).
Note that the animation frame is tied to the game loop (using the member variable Critter.timer
), not the rendering loop.
Other activities may have shorter cycles, for instance the floater uses only 5 images, with a ping-pong animation lasting 8 frames. Objects such as balloons and umbrellas are drawn separately.
- Genesis. From the idea to the design
- Architecture and framework
- Critter tasks and animation
- Level design and display
- Minification and size constraints