Sprites in Basic II - Animation
As we move forward with game design and development the
focus shifts to game play and graphics and less on actual coding. Sure, coding
is the glue that makes everything run smoothly and that overall experience is
pleasant but the appearance is becoming more and more central point. I am not a professional by any means but I personally
actually enjoy this part of game design just as much as coding, which is
fortunate. Too often I see perfectly coded games (or other software and apps)
with mediocre graphics and design.
We will therefore first create graphics for our sprite and
then write the code after we know what we are working with.
The goal is very simple, let’s create a character that we
can move or walk around the screen. Let’s also minimize the amount of typing
and go for simple 16 x 16 sprite.
Sprite development
I am using several tools to draw pixel art, for more complex
stuff I use tablet pad and Krita but for simple stuff I will just use Paint.NET
and Aseprite for animation.
We will do a simple person, let’s call him “Sam” (After an underwater game I was working on but never finished "Scuba Sam"). At 16 x 16
pixels we can’t really shoot for realism so let’s make him cartoonish with head
occupying about half of the sprite. Therefore we have only few pixels remaining
for the rest of the body:
It is very simple really, the head is slightly oval as we
are watching from top down, legs and arms and two eyes.
For animation we will again simplify things and use four
frames for a full cycle. Again the only thing we need to do is animate legs and
to make a bit more real also some arm movement. For walk down the frames can be
as follows:
We will use standard palette to color our hero. We will give
him brown hair, blue jeans pants and bright orange shirt today. The above four
frame animation now looks like this:
For walking up the screen we can use the same outlines, we just need to cover the head with hear. To make it a bit less uniform we will add few darker strains of hair. The animation sequence looks like this:
Side view is usually a little harder for some reason, but I
think this will do just fine:
Before we move to coding let’s analyze the Sprite Sheet and try to identify redundancies with the goal to reduce memory space required and not affect the speed too much. This is very common dilemma in game development speed vs space and we will be dealing with it much more in the future when we will be working on more complex projects.
As we see we have sprites for 4 directions and 4 to perform
full walk cycle. However there are some clear duplicates:
- Every first and third sprite in sequence are the same – so we can eliminate 4 sprites
- Every second and fourth sprite are mirror images of each other. Since we have register for flipping sprite horizontally we can eliminate those 4 too
- And last the whole fourth row is mirror image of third row (walking left and right) so we can eliminate those as well. That is additional two sprites eliminated (we shouldn’t count two twice)
So we only need to define 6 sprites out of 16 to draw all
needed frames of animation. Of course that will require some code but it
shouldn’t be too complex. This will save precious Video RAM and speed up
initialization phase.
Transfer graphics into code
In part one of Sprites in Basic we stored actual numbers in
DATA section of the program and using simple loop were poking them into memory.
We will slightly improve on it. Instead of typing in color codes we will define
sprite in 16 strings with each character representing one pixel. Based on the
drawings above we will need:
“1” – Black
“2” – Brown
“3” – Dark Brown
“4” – Skin
“5” – Orange
“6” - Blue
Clearly we could use 16 color mode for this sprite and still
have 9 additional colors at our disposal but for the sakes of simplicity let’s
just use the same mode as in previous chapter. First frame of sprite can
therefore be defined as:
Clearly blanks are transparent pixels and other are based on
the color code we picked above. We can
fairly quickly type the definitions for remaining five sprites as there is a lot
of just copying and pasting. The code to transfer this into VPOKEs is very
simple:
In line 30 we initialize variable M which will be our offset
into VRAM for VPOKE.
Then we loop 6 times for 6 sprites and 16 times for 16 lines
of each sprite. Of course we could do this in single loop.
In line 60 we read string representing one row of pixels in
a sprite and then loop through each character.
In line 80 we extract each character and in lines 90-150
determine exact color from palette for that color code. This approach has a
very convenient benefit. It allows us to play with different colors and
experiment with what works best on certain background. Or we can change color
of our hero from level to level by just redefining him/her with different
colors. The negative side of this approach is speed. This does take few seconds
to process…
In line 140 we store the color index into VRAM starting at
address $4000 and increase our memory offset in line 170.
Only thing remaining is closing all the loops in lines
180-200.
Displaying the Animation
In lines 220 – 300 we turn on and initiate the sprite. The
process is identical as in Part 1 the only differences are:
- We are using Sprite 1 and not 0 so we use registers $5008 - $500F instead of $5000 - $5007
- We set default position at 160 x 100 so the sprite appears roughly at the center of 320x200 mode screen (SCREEN 0)
In lines 510 and 520 we switch to the 320x200 text mode, and
set the brown background. We can do that with three PRINT codes: CHR$(149) to
set color to brown, then change it to background with CHR$(1) and finally
clearing the screen to apply this new color setting to the whole screen.
In line 530 we set some basic variables. Current X and Y
position are pretty clear. In addition to those we are also declaring DIR for
direction and I for animation Index. Index will just be looping from 0 -> 1
-> 2 -> 3 -> 0 -> 1 and so on. For DIR we picked following values:
Value | Direction |
---|---|
0 | Up |
1 | Right |
2 | Down |
3 | Left |
In order to control all the possibilities we have to
organize them all in some way that is easy accessible without much calculation.
The last thing we want it so to have ton of IF statement to test for each
condition. One way to do is to prepare all options in advance and just pick the
right one during the runtime. One way is to use arrays. Since we have two
variables that change as we move across the screen and determine the appearance
of sprite I used two dimensional array to decide which sprite graphics to
use. We declare it with:
DIM O(4,4)
If we decide that we will use direction of movement for
first index and animation sequence as second we can fill in the following data.
By data of course I mean the offset for graphic pointer in Sprite data. First
one is at offset 0, second at offset 8, third at 16 and so on for all 6
different graphics. Remember each sprite takes uses 256 bytes (16*16) but since
5 lower bits are always 0 so we can address in 32 byte increments so 8 in
pointer means we are stepping up by 256 bytes.
Frame\Direction | 0 (UP) | 1 (RIGHT) | 2 (DOWN) | 3 (LEFT) |
---|---|---|---|---|
0 | 16 | 32 | 0 | 32 |
1 | 24 | 40 | 8 | 40 |
2 | 16 | 32 | 0 | 32 |
3 | 24 | 40 | 8 | 40 |
The above data is stored in array in lines 550-580.
We also have to take the V-flip into consideration and because
it is set in separate register it is more convenient to create another two
dimensional array instead of one three dimensional. We declared it also in line
540 as DIM F(4,4). With declaring it, BASIC will automatically fill the whole
array with zeros so we only need to change the ones that point to sprites that
actually need to be flipped. The values should be the following:
Frame\Direction | 0 (UP) | 1 (RIGHT) | 2 (DOWN) | 3 (LEFT) |
---|---|---|---|---|
0 | 0 | 0 | 0 | 1 |
1 | 0 | 0 | 0 | 1 |
2 | 0 | 0 | 0 | 1 |
3 | 1 | 0 | 1 | 1 |
Now we have everything to make our hero walk around the
screen.
In Line 610 we read Joystick which in Emulator from version
R35 also returns pressing the arrow keys. We could also use GET command but the
difference is that JOY does not rely on keyboard auto-repeat and is therefore
more appropriate for this purpose.
In lines 620-650 we simply check which direction we want to
go to and increase X and Y by 1. We could change this to 2 or even 3 to make
our hero move faster and animation would still look good. We only check if the
sprite is moving out of screen at the top and right because that would cause an
error. We allow him to walk off on the right and bottom.
In lines 660 and 670 we increase the animation frame index
and loop it back to beginning if needed.
Now all that is required to show Sam at the next position is
to make few updates:
Line 680 – Update X coordinate using formula from Part 1
Line 690 – Update Y coordinate using same formula
Line 700 – update the pointer to graphics using the above defined
array
Line 710 – updating the H-Flip flag making sure we don’t
change other attributes in the same register
And at the end go back to beginning of the “Game” loop.
One of the main drawbacks of this approach is slow loading
of Sprite graphics into Video RAM. In principle it should be possible to load
data directly into it and much faster but I have some difficulties making it
work. Not yet sure if I am doing something wrong or if the issue is with the current
version of emulator. Will update this post when I figure it out.
Very nice post! Looking forward to more. Merry Christmas!
ReplyDeleteThank you Indy, apprecaite the comment. Christmas is over so I wish you and your family all the best in New Year 2020.
Delete