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.
For our subroutine, we will keep it simple for the moment and just place Miner Willy at a specific location on our screen.
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:
|This is the official definition of the sprite command as written in the STOS manual:
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.
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:
|Miner 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.
|Miner 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.
|Miner 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).
|Miner 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.
|Miner 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.
|Miner 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.
|Miner 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.
Now, we need to update our sprite command so that we are using the newly created MWXPOS and MWYPOS variables.
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.
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.
JL = jleft : JR = jright : JF = fire : JSTCK = (JL or JR)
Let’s breakdown the calls that are being used:
|JL = jleft
|Here 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 = jright
|As 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 = fire
|You 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:
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.
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
Let’s break down that new line of code that we have just added in:
|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 = 0
|If 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.
|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 = MWWS
|We set the current walking speed of Miner Willy to the default walking speed that has been defined.
|if JL then MWD = -1
|Is the joystick being pushed to the left? If so, then set the walking direction (MWD) to be -1.
|else MWD = 0
|Else, 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.
if MWD = 0 then MWXPOS = MWXPOS + MWSPD : else MWXPOS = MWXPOS - MWSPD
|if MWD = 0 then
|Firstly, we check the direction that Miner Willy is facing, remember, 0 is to the right and -1 is to the left.
|MWXPOS = MWXPOS + MWSPD
|MWD 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 – MWSPD
|MWD 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.