Codetapper's Amiga Site

The whole truth about Games Programming Part 4: Aliens

DAVE JONES, the ace coder behind Psygnosis' hits Menace and Blood Money, spills more secrets.

In this and next month's articles I will detail the largest part of the Menace code: namely, the alien movement and control routines. There will be no source with this month's instalment because I will only be describing WHAT the routines accomplish — next month will cover HOW they are accomplished, along with the source.

Movement is Life

So, we want to have aliens flying about the screen in nice patterns, attacking your ship, launching missiles at you, tracking your movements and doing all the other things that aliens do best. All the basic ingredients of a good shoot-em-up. What should spring to mind to control all of the movements is a decent data structure. As I have said before, there is no substitute for sitting down with a pen and paper and having a good think about what has to be accomplished. The best idea is to start off with the basic data that would have to be stored about an alien flying about on screen. This would probably be the following:

  • Alien number
  • Alien X coordinate
  • Alien Y coordinate
  • Alien speed

There will also have to be some way of telling the alien what to do. The simplest form would be a coordinate for it to move to. Once it gets there it would need another coordinate to go to. It would do this repeatedly until we tell it to stop, or until is has been destroyed. First, then, we will define the basic structure to describe a particular alien:

             rsreset               ; reset the rs counter
X.Pos        rs.w    1             ; one word for the alien's x coord
Y.pos        rs.w    1             ; one word for the alien's y coord
Sprite.Num   rs.b    1             ; a byte to hold the alien number
Speed        rs.b    1             ; a byte to hold the alien speed

Following this in memory would be a table of x,y coordinates for the alien to move to in sequence. An example piece of source that moves an alien from left to right and back again could be defined as follows:

             dc.w    0,100         ; start alien at x=0, y=100
             dc.b    6,2           ; use alien number 6 with a speed of 2
             dc.w    320,100,0,100 ; move to 320,100 then to 0,100
             dc.w    $7fff         ; the $7fff is an end marker

This basic structure will provide only limited control of an alien, so it's time to add a few more features.

Figure one: An eight-animation Menace alien

Figure one: An eight-animation Menace alien

Most of the aliens are animated (Figure One shows an eight-animation Menace alien) so as well as defining the alien number, we also have to define its current animation number. Note that in games an animated object is usually given a number that relates simply to the base address of that alien. The number of animations it actually has is stored separately. This makes changing the number of animation frames very simple, because the alien numbers all remain exactly the same. So we will also have to store the number of animations the alien contains somewhere in the structure as well as its current animation number:

Anim.Num     rs.b    1             ; a byte to hold the current animation
Num.Anims    rs.b    1             ; a byte to hold the maximum anims

The basic idea, then, is that for each game cycle we increment the
Anim.Num (0,1,2,3,... etc) until it hits the value in Num_Anims when we would reset it back to zero. Straight away a small problem crops up in that we may not want to animate an alien every game cycle (1/25th of a second for Menace). In fact very few of the aliens animate every game cycle as this is a little fast, so we will also have to introduce an animation delay into the structure:

Anim_Delay   rs.b    1             ; a byte to hold the animation delay

The method of incrementing the Anim.Num until it hits a maximum then resetting it to zero is termed a WRAP animation as it wraps around to zero. In some cases we may want the animation numbers to increase to a maximum and then decrease to zero: this is termed an UPDOWN animation. To indicate whether or not to use a wrap or updown type of animation requires us to store a single bit of information. There are many occasions in the controlling of an alien that only requires a simple on/off definition. These tend to be grouped together in the data structure and called a MODE word or byte. This is simply a collection of bits used for general-purpose description of simple on/off controls. This is now added to the structure:

Mode         rs.b    1             ; single byte to hold eight flags
UpDown       equ     1<<3          ; define the updown flag as bit 3

Note the equate of which bit in the control byte reflects the animation type. NEVER use 'magic numbers' in your source such as the statement:

             btst    #3,Mode

This in no way tells you what bit 3 signifies: you may remember as you are working on the project, but what about in a year's time when you come back to re-use some of this source? The above should have been written as:

             btst    #UpDown,Mode

which is very clear and has the advantage that to change the actual bit number that the UpDown flag is set at requires only the EQU statement above to be changed. Always make the assembler do as much work for you as possible. Right, lecture mode OFF, let's introduce the nextelement into the structure.

Reason for Living is Dying

Most aliens' sole purpose for existing in a shoot-em-up is to be blasted. Do we want them to die after being hit once, though? For most, I think not. We will have to introduce a variable to define how many times we can fill an alien with laser shot before it explodes:

Hits_Num     rs.b    1             ; number of shots an alien can take
                                   ; $ff for infinite

As you can see a certain value has been introduced that makes some aliens indestructible. This is not used very often but is handy, say for some asteroids tumbling across the screen that you simply have to dodge and cannot destroy (anybody who reached Level 6 of Menace will know exactly what I mean!)

As far as describing the alien, that is about it. I have made no mention of the size of the alien in the structure as in Menace all aliens were 32 x 24 pixels in size. To make larger ones simply meant moving a few about as though they were joined together (see Figure Two). It certainly makes life simpler handling only one size of alien, though you do lose out a little on flexibility.

Figure two: Four sprites make one large sprite

Figure two: Four sprites make one large sprite.

Back to Motion

Now back to the movement of the aliens. The basic GOTO command was implemented simply by defining pairs of x,y coordinates for the aliens to head for — they continued until they were destroyed or until a special coordinate value told them to stop.

This is very inflexible for performing, say, a small circle movement. We would have to define goto points for many points on the circumference of the circle. This may take quite a while to work out, and would have to be recalculated for any other aliens at different x,y coordinates on the screen that wanted to perform a similar circle.

One simple way of overcoming this problem is to use a relative coordinate system whereby rather than interpreting the x,y values as goto coordinates, they are simply offsets that are added to the present x,y coordinate. This allows us to define a shape as a set of offsets from a base point, the base point being arbitrary. This should be able to be used in conjunction with the goto points so we can switch freely between absolute and relative movement at any time.

It is apparent that some form of control data has to be added into the coordinate list to switch between relative and absolute or one may be mistaken for the other. What basically happens in Menace is that movement always starts off in absolute mode, but at any time we can switch to and from relative mode by making one of the coordinate values a control word. For example to goto the middle of the screen, then move in a triangle shape, then return again may look something like:

             dc.w    160,100       ; goto 160,100
             dc.w    OffsetMode    ; switch to offset mode
             dc.w    2,2           ; move 2 along and 2 down a few times
             dc.w    2,2
             dc.w    2,2
             dc.w    -2,0          ; move 2 back a few times
             dc.w    -2,0
             dc.w    -2,0
             dc.w    2,-2          ; move 2 along and 2 up a few times
             dc.w    2,-2
             dc.w    2,-2
             dc.w    OffsetMode    ; toggle back to absolute mode
             dc.w    0,100         ; back to the start x,y

Control words have certain bits set that distinguish them from a goto coordinate (not much point in, say, having 32768 as a coordinate with a screen width of 352 pixels). What we end up with is a small language that describes the path aliens follow. We can go on adding control features that make the path data more compact yet more powerful. The offset mode will set a bit in the MODE byte to indicate when it is in offset mode.

Memory Economy

To cut down the amount of data required to describe a complex path (like a large ellipse formed from offsets) it would be ideal to be able to branch to some other path data, and then return after executing so many of its commands. The triangle path above, for instance, requires 40 bytes for the offset data. If we required 12 aliens to perform the triangle movement, but at different coordinates on the screen, we only want to define the offset data once, and have the rest of the aliens branch off to that data in-between executing their own path data. Menace has a GOSUB command in the path data for this purpose. The gosub command passes over to the new offset data, then at the end of the data is the equivalent of a RETURN command which returns control back to original path data. The above could be written as:

             dc.w    160,100         ; goto 160,100
             dc.w    Gosub,Newpath-* ; branch to some new data
             dc.w    0,100           ; back to the start x,y

NewData      dc.w    OffsetMode      ; switch to offset mode
             dc.w    2,2             ; move 2 along and 2 down a few times
             dc.w    2,2
             dc.w    2,2
             dc.w    -2,0            ; move 2 back a few times
             dc.w    -2,0
             dc.w    -2,0
             dc.w    2,-2            ; move 2 along and 2 up a few times
             dc.w    2,-2
             dc.w    2,-2
             dc.w    OffsetMode      ; toggle back to absolute mode
             dc.w    Return

If we had 12 similar paths to the above the memory required would now be drastically reduced.

More Memory Economy

Another useful memory saver is a FOR type construct that allows any part of the path data to be repeated a set number of times. Imagine trying to move a series of aliens in a 'stepping' motion whereby they move, say, along 16 pixels then down 16 pixels, achieved by:

             dc.w    OffsetMode
             dc.w    4,0
             dc.w    4,0
             dc.w    4,0
             dc.w    4,0
             dc.w    0,4
             dc.w    0,4
             dc.w    0,4
             dc.w    0,4

The above performs one step, but if we wanted to repeat this 10 times, it would normally mean repeating the data 10 times or using many GOTO statements — but, yes you've guessed it, a loop function was implemented: simply appending the command:

             dc.w    DoLoop

to the end of the data will cause the last xx bytes of path data to be repeated so many times. This caused two new items to be added to the alien structure, namely:

Loop_Offset  rs.b    1             ; the number of bytes to loop back 
Loop_Count   rs.b    1             ; how many loops to perform

This limits the loop command to only allowing one size of loop in each individual path, although this restriction was not noticed as multiple loops were never required.

Other Tweaks

A PAUSE function was added that allowed an alien to pause at a specific point for any length of time. A simple function but used quite often:

             dc.w    Pause,PauseValue   ; the Pause value is in 1/25 seconds

A SEEK mode was added that allowed an alien, at any time, to start to track your ship and try to collide with it to reduce your shield. This is worked similarly to the pause function with a count specifying how long to seek your ship before carrying on with the rest of the path data:

             dc.w    Seek,SeekCount     ; seek ship for 1/25 * SeekCount secs.

Related to the seek command above is the FIRESEEK command which allowed one alien to start another path. This was used to allow aliens to fire missiles that used the seek mode:

             dc.w    FireSeek,SeekCount ; start a seek path from this alien

To finish off the command set some additional simple commands that came in useful were added. These are:

  • ChangeSprite — allowed you to change the sprite number at any time. Useful for transforming aliens into other types.
  • ChangeSpeed — quite obvious this one. Slow moving sprites can be given a quick turbo boost to attack the ship.
  • ChangeAnim — allows manual changing of the animation number. Used to mimic, say, turning a corner at any point on the screen.

And that, basically, is that. Table 1, below, lists the full data structure for an alien path. Hopefully you can see the benefit of designing such a control system for moving objects about. It need not necessarily only be used in a shoot-em-up style game but can relate to many styles of game. It soon becomes quite simple to add more powerful commands to the code, which results in the code being useful in many more projects.

Now you know what the commands do — next month I will present the source and explain how they are accomplished.

Table 1: Full data structure

             rsreset
Next.Path    RS.W    1     offset to the next path
X.Pos        RS.W    1     current x position
Y.Pos        RS.W    1     current y position
Kills.What   RS.W    1     kills others aliens if dead (0-11)
Table.Offset RS.W    1     the current table offset
Sprite.Num   RS.B    1     sprite number
Anim.Num     RS.B    1     animation number
Anim.Delay   RS.B    1     delay in l/25th sees, dynamic copy
Anim.Delay2  RS.B    1     static copy to refresh the above
Speed        RS.B    1     speed in pixels
Pause.Count  RS.B    1     dynamic pause counter
Mode         RS.B    1     flags, see below
Loop.Offset  RS.B    1     loop offset in bytes (-ve)
Loop.Count   RS.B    1     dynamic loop count
Hits.Num     RS.B    1     number of hits to kill
Num.Anims    RS.B    1     number of animations
Seek.Count   RS.B    1     dynamic seek count
Table.Size   RS.B    0

* This is followed by x,y bytes to move to (always even) with command 
* codes inserted to alter the control and movement. 

* Mode bits 

Offset.Mode  equ     1<<0  set when in offset mode 
Seek.Mode    equ     1<<1  set when in seek mode 
UpDown       equ     1<<3  set when updown animation 
AnimUpDown   equ     1<<4  set to animate up, reset down 
HeatSeekPath equ     1<<5  set to signify a heatseeker

Downloads

Menace source code and coverdisk data files

Series Links

Part 1 / Part 2 / Part 3 / Part 4 / Part 5 / Part 6 / Part 7

Post your comment

Comments

No one has commented on this page yet.

RSS feed for comments on this page | RSS feed for all comments

You may enjoy these articles...

The whole truth about Games Programming Part 4: Aliens

Comedy

If you look inside many Amiga games, secret messages have been hidden by the programmers. Richard Aplin was the king of hiding messages in the startup-sequence file, and his Line of Fire and Final Fight startup-sequences have become legendary! The Sensible Software team were also prolific at hiding messages in their games.

The whole truth about Games Programming Part 4: Aliens

Interviews

A collection of technical interviews with Amiga programmers that worked on commercial software in the glory days of the Amiga (late 1980s to early 1990s!)

The whole truth about Games Programming Part 4: Aliens

Maptapper

The Ultimate Amiga Graphics, Level and Map Ripper!

The whole truth about Games Programming Part 4: Aliens

Random Rants

A random assortment of rants relating to the Amiga!

The whole truth about Games Programming Part 4: Aliens

Sprite Tricks

An explanation of how many famous Amiga games utilised sprites in weird and interesting ways