S01E03 – Making Willy Walk

Displaying Miner Willy

So, we want to make Miner Willy move then? Well, we can’t do that unless he’s shown on the screen. In our previous episode, we created our game loop, and so we need to make an adjustment to that loop in order to call our display routine for Miner Willy.

repeat
    gosub @DisplayMinerWilly
    gosub @WaitVBL
until false

For our subroutine, we will keep it simple for the moment and just place Miner Willy at a specific location on our screen.

@DisplayMinerWilly
    sprite 1,100,100,1
    return

We have already learned about the label and the return command in previous episodes, but what else are we doing here? We’ve introduced a new command:

CommandDescription
sprite 1,100,100,1This is the official definition of the sprite command as written in the STOS manual:

SPRITE n,x,y,p
Displays sprite number "n" on the screen at coordinates "x" and "y".

"n" is the number of the sprite, which can range from 1 to 15. It is this number that will be used to identify the sprite in any subsequent calls to the MOVE and the ANIM instructions.

"x" and "y" are the coordinates of the point on the screen where the sprite is to be drawn. Unlike normal screen coordinates, these can take NEGATIVE values. The "x" coordinate can vary from -640 to +1280, and the "y" coordinate from -400 to +800. This allows you to move a sprite off the screen without causing an error.

"p" specifies which of the images in bank 1 is to be used for a particular sprite. The only limit to the number of these images is the amount of available memory.

Each sprite has an invisible handle through which it can be manipulated, called a "Hot Spot". Whenever we draw a sprite, we always specify its coordinates in terms of the position of this point on the screen. As a default, the hot spot is always set to the top left-hand corner of the image, but this can readily be changed using a special option from the sprite definer accessory.


Phew! There is quite a bit to take in there, but essentially what it is saying is that we have a total of 15 sprites that STOS can deal with. When we create a sprite, we have to tell STOS which number of the 15 sprites we are creating: “n”, as it calls it in the manual. We have assigned sprite number 1 to Miner Willy.

Next, we are specifying where to show Miner Willy on the screen—his x and y coordinates. We have just chosen to show Miner Willy at 100,100 on the screen.

Finally, we specify which of the images we want to show—in other words, which of the image numbers from the sprite bank we have loaded. We are using 1 here, which is Miner Willy facing left.

Let’s transpile our program, load it into STOS, and run it. We should simply get Miner Willy, frame 1, at position 100,100 on the screen. It works! But now we need to make him move.

A Moving Willy

We need to define some form of mechanism to make Miner Willy move. In the ZX Spectrum version of Manic Miner, this was done purely with keyboard input. We could do the same for our game, but for ease, I’ve decided to use the joystick to control our hero. We therefore need to read some input from the joystick, which STOS has some handy routines to deal with.

Before we can do anything in terms of joystick input, we need to do some setup so we can control the position of Miner Willy. We always need to know his current x and y positions on the screen so that we can increment them as required for walking and jumping. We need to do this in a series of variables, which we will need to initialise before trying to display Miner Willy. There’s also some additional setup that we will do too but won’t use for the moment; this is to cater for the fact that we are not going to use either the sprite movement or animation routines that are built into STOS.

Let’s create a new subroutine for initialising everything we need for Willy.

@InitMinerWilly
        MWD = 0 : MWWS = 1 : MWWC = 2 : MWWCC = MWWC : MWXPOS = 16 : MWYPOS = 80 : MWSPD = 0

There are a fair few variables there! I’ve tried to use logical naming conventions for them, but let’s go through them one by one and describe what they are used for:

VariableDescription
MWDMiner Willy Direction

Will be used to identify the direction that Miner Willy is currently facing. MWD with a value of 0 will mean that Willy is facing to the right, and -1 will mean he is facing to the left. We use 0 and -1 for a reason, which will become clear later on.
MWWSMiner Willy Walk Speed

This value will be used to identify the number of pixels that Miner Willy will move with each frame of animation when walking.
MWWCMiner Willy Walk Counter

This value is an interesting one. Miner Willy is only a small chap and doesn’t walk very fast. If we were to move and animate him a single pixel every 50th of a second, he would be running the 100-metre dash across the screen. So we need to slow him down a little bit. This is where the walk-counter comes in. Let me explain a bit:

When the joystick is pushed to the right, we move Willy to the right by the number of pixels as defined by MWWS and change his animation frame. If this is happening at 50 frames per second, then it would take 6 seconds for Willy to cross the entire screen, and the animation frames 1-4 would happen 12 times in just a single second—it would be super fast.

Therefore, to slow him down, we need to introduce a counter. We can use this to control the speed by decrementing the counter and then only moving and animating when it reaches zero. At which point we reset the counter back to its maximum value. MWWC holds the default value of the counter that can be used to change the speed and reset our main counter (see below).
MWWCCMiner Willy Walk Counter Current

This is the current value of the walk counter. This value is decreased at each VBL, and when it reaches zero, we will update Miner Willy’s position and current animation frame. We then reset this value back to the default setting (MWWC) for it to happen all over again.
MWXPOSMiner Willy X POSition

Holds Miner Willy’s current X position on the screen. It is this value that is increased to move him to the right and decreased to move him to the left. We’re giving him a default X position of 16 to start with.
MWYPOSMiner Willy Y POSition

Holds Miner Willy’s current Y position on the screen. It is this value that is increased to move him down the screen and decreased to move him up. We’re giving him a default Y position of 80 to start with.
MWSPDMiner Willy SPeeD

We will use this variable to hold the current speed that Miner Willy is moving horizontally. More about this later.

We now need to make a call to our initialisation routine and also update our sprite command so that we are not forcing Willy to always be at position 100,100 on the screen. So we do that after we have loaded our assets. We may need to move this later on, but it serves it’s purpose here for the moment.

gosub @LoadAssets
gosub @InitMinerWilly

Now, we need to update our sprite command so that we are using the newly created MWXPOS and MWYPOS variables.

@DisplayMinerWilly
    sprite 1,MWXPOS,MWYPOS,1
    return

OK, let’s transpile our program, load it into STOS, and see what we have. You should now see that Miner Willy has moved positions. He’s further to the left and higher up the screen. That means that our sprite positioning with MWXPOS and MWYPOS is working, and we can begin to manipulate those with the joystick input.

Joystick Input

To get Miner Willy to move, we need to read the joystick data stream. STOS provides joystick input commands, so we will use them. Let’s go ahead and create a new function that we can call whenever we want to read the joystick.

@ReadJoystick
    JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
    return

Let’s breakdown the calls that are being used:

CommandDescription
JL = jleftHere we are reading the value of the jleft command and storing it in our variable JL. If the player is pushing the joystick left, then JL will be TRUE, otherwise, it will be FALSE.
JR = jrightAs with the jleft command, we are reading if the player is pushing the joystick to the right. Again, TRUE or FALSE will be returned into JR.
JF = fireYou guessed it, we’re reading the status of the fire button. JF will be TRUE if fire is being pressed, and FALSE if not. We are going to use fire to ultimately make Miner Willy perform a jump.
JSTCK = (JL or JR)This is an interesting one. What we are doing here is creating a third variable that identifies if the joystick is being pushed either left (JL) or right (JR). The variable JSTCK will therefore contain TRUE if we are pushing the joystick in any horizontal direction. With this variable set, we can then start to perform operations that should only be performed when Miner Willy is moving.

The above is simple enough, but why are we storing the state of the joysticks in variables? Well, we do this for a couple of reasons. Essentially, the input from the joystick is a continual stream of data being thrown at STOS. If we used the direct joystick commands throughout our source code, we could end up with some strange results as the joystick states may be different whilst our code is executing. For example, making Miner Willy do something at a point in which he shouldn’t be doing it. In addition to this, we would force STOS to perform additional reads and therefore consume more processor time at each call. By storing the values within variables, we only read the joystick once per VBL and therefore save a bunch of processing power.

Now we need to make sure that we call the routine to get the values from the joystick. We must do this within the game loop, so add the following code:

repeat 
    gosub @ReadJoystick 
    gosub @DisplayMinerWilly 
    gosub @WaitVBL 
until false

Applying Movement to Willy

We’ve got Miner Willy all set up now, and we have our variables available for changing his X and Y positions on the screen. We’ve also started to feed joystick input into our JL, JR and JF variables. Now that we’ve got that in place, we need to make Miner Willy move. Earlier, we created the MWSPD variable to hold the current speed at which Miner Willy is walking. It is this variable that we manipulate in order to signal that movement is to occur. We do this within the @ReadJoystick section of code.

@ReadJoystick
    JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
    if not(JSTCK) then MWSPD = 0 : else MWSPD = MWWS : if JL then MWD = -1 : else MWD = 0
    return

Let’s break down that new line of code that we have just added in:

CommandDescription
if not(JSTCK)What we are doing here is testing that the JSTCK variable is set to FALSE. This could be done in a couple of ways. I have chosen to use “not”, which inverses the content of the variable. If you remember earlier in the series we spoke about how all conditions return TRUE or FALSE. Well, that what this does, if the value of JSTCK is FALSE the “not” command inverts it and therefore the tested condition is TRUE, and vice-versa if JSTCK was TRUE.

We could also have done if JSTCK = false and again, that creates a condition that returns TRUE or FALSE.

Hope that makes sense!
then MWSPD = 0If JSTCK is FALSE, then we set the current speed of Miner Willy to zero. There is no input on the joystick, and therefore Miner Willy is not moving and his movement speed is zero.
else The else clause of the if statement tells us what to do when the if condition does not equate to TRUE. Here we are saying, if JSTCK is TRUE (i.e. we have joystick input), then we need to execute the following code.
MWSPD = MWWSWe set the current walking speed of Miner Willy to the default walking speed that has been defined.
if JL then MWD = -1Is the joystick being pushed to the left? If so, then set the walking direction (MWD) to be -1.
else MWD = 0Else, the joystick must be pushed to the right, so set the walking direction to 0. We can make this assumption here as the initial check on the JSTCK variable told us that either left or right was being pushed. Therefore, if it’s not left (JL) as per the if check, then it can’t be anything but being pushed to the right.

We now know in which direction Miner Willy is facing (MWD) and at what speed he is moving (MWSPD). This information can now be used to update the MWXPOS variable accordingly. We’ll add this code into the @DisplayMinerWilly section.

@DisplayMinerWilly
    if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
    sprite 1,MWXPOS,MWYPOS,1
    return
CommandDescription
if MWD = 0 thenFirstly, we check the direction that Miner Willy is facing, remember, 0 is to the right and -1 is to the left.
MWXPOS = MWXPOS + MWSPDMWD does equal zero, so add the current speed to Miner Willy’s X position. In other words, increase the X position, thus moving him to the right.
else MWXPOS = MWXPOS – MWSPDMWD does not equal zero, so subtract the current speed from Miner Willy’s X position, thus moving him to the left.

Transpile the code and see what happens. You should have a Miner Willy that now moves left and right on the screen using the joystick.

That’s all for this episode, folks. In the next episode, we will look at animating Miner Willy using a very interesting technique!

In the meantime, you can find the various episode source codes and assets on the Manic Miner Github page.

         

About author View all posts Author website

Neil Halliday

Neil started coding in STOS in 1989 just after it was released in the UK.

During those 31 years he has written numerous demo screens, routines, games and extensions, most of which are now lost due to a massive hard disk crash. What remains on floppy disk is still being discovered and posted on the STOS Coders website and stored in the cloud for everybody to enjoy (or laugh at).

Neil is the author of the GBP Extension which added some pretty cool commands to STOS, along with the "Development" extension that enabled enhanced STE functionality, including probably one of the simplest hardware scrolling routines around.

Along with Bruno Azzara, Geoff Harrison and Mike Halliday we had loads of fun back in the day trying to push STOS to it's limits. We are all now enjoying bringing our knowledge to a new generation of STOS Coders.

Leave a Reply

Your email address will not be published. Required fields are marked *