Moving enemies in a tiled screen
- MoltS Xalats
- 5 minutes ago
- 6 min read

Introduction
In the article "Scrolling through a tiled screen", we saw how to create scrolling based on a tile screen. Now, in addition to moving the main character, we will create a series of enemies that appear from one of the four edges of the screen and detect collisions with the background. If they collide, they will bounce in the opposite direction.
MSX sprites can occupy 32 layers, meaning only 32 sprites can be on screen at the same time. Since this is a finite number and sprites are generated randomly, I thought about managing this resource using a stack, so that each time a sprite is removed from the screen, the next one can take the available layer number.
Another technique we’ll need is randomness, so the sprites appear in different positions.
What is a stack?
A stack is a linear data structure that follows the LIFO principle (Last-In, First-Out), which means that the last element added is the first one to be removed. You can imagine it like a stack of plates: you can only add or remove a plate from the top (or risk a catastrophic mess).
This structure is mainly defined by two basic operations:
Push: Adds a new element to the top of the stack.
Pop: Removes and returns the element at the top of the stack.
In our program, we define a Stack structure composed of an array to hold the data and a value indicating the position of the top of the stack:
typedef struct {
char data[MAX_PLANS_SPRITE];
char top;
} Stack;
With this structure, we reserve memory for an array of MAX_PLANS_SPRITE elements and have a variable that keeps track of how many elements are currently in the stack. Based on this structure, we can create the push function, which increments the top value and stores the new element at the corresponding array position:
void push(Stack *stack, char value) {
stack->top++;
stack->data[stack->top] = value;
}
And the pop function, which retrieves the last element from the stack and decreases top so it points to the new top element:
char pop(Stack *stack) {
char val_ret = stack->data[stack->top];
stack->top--;
return val_ret;
}
With these two functions, we can now manage the stack of sprite layers: adding an element when a sprite is removed and retrieving one when a new sprite is added.
Random numbers
To make the game more dynamic and ensure that each playthrough is different, we use random numbers.
A random number is one that is generated in such a way that its value cannot be predicted beforehand, and each possible value has a certain probability of appearing. Ideally, in a mathematical sense, this generation should be completely unpredictable and without any repetitive pattern.
However, in computing, this ideal is nearly impossible to achieve because computers are deterministic machines: given the same input, they always produce the same output. That’s why, in computer science, we talk about pseudorandom numbers—algorithms that generate sequences of numbers that appear random but are entirely deterministic. If you initialize the generator with the same seed, the sequence will be exactly the same. This allows for reproducibility, but also carries the drawback that some repetition patterns may appear.
In Fusion-C, there's a function called Rnd(unsigned char seed), but when I tried using it, I didn’t like the results (perhaps I didn’t use it properly), so I searched in the SDCC forums and found this function:
int rand_xor() {
num_llarg_aleatori = num_llarg_aleatori ^ (num_llarg_aleatori << 13);
num_llarg_aleatori = num_llarg_aleatori ^ (num_llarg_aleatori >> 17);
num_llarg_aleatori = num_llarg_aleatori ^ (num_llarg_aleatori << 5);
return (num_llarg_aleatori & 0x7fff);
}
It uses an unsigned long (4 bytes) to generate numbers through bit shifts and XOR operations. To initialize the pseudorandom number sequence, I use a seed based on the MSX's current time:
void rand_xor_init() {
num_llarg_aleatori = (_Time << 15) | (unsigned long)_Time;
}
This adds some variability to the random number generation. When debugging, variability might not be desirable, and repeatability becomes more useful—so it’s better to start with a fixed num_llarg_aleatori value in that case.
This implementation is known as XORShift, and it was proposed by George Marsaglia in 2003. Since we use 32 bits, the maximum period we can achieve is 2³². That is, we can generate up to 2³² numbers before the sequence repeats. This period depends on the initial number and the specific shift values used in the algorithm.
Adding Enemies to Scrolling with Tile-Based Movement
Once we understand how a stack works and how to generate random numbers, we can add enemies to the program from the article "Scrolling on a Tile-Based Screen".
The first step is to define the enemy structure, where we group together the variables needed to control each enemy. This structure will include:
x and y – the position of the enemy on the screen
speed_x and speed_y – the number of pixels the enemy moves on each axis
size – used to determine collisions with obstacles based on the enemy’s shape
eliminate – a flag to mark enemies that have gone off-screen and should be removed
draw – a flag indicating whether this enemy should currently be rendered
sprite_plane_num – which sprite layer should be used for drawing
After defining the structure, we set the maximum number of enemies allowed on screen using the constant NUM_ESQUIROLS and then reserve memory to hold an array of this structure.

We define the shape of the enemy sprite at line 146. Then, we define the stack using the structure explained earlier, setting MAX_PLANS_SPRITE as the maximum number of sprite layers that enemies will be allowed to use.
After the stack functions, we define the pseudorandom number generation functions:
rand_xor_init() initializes the random sequence using the MSX clock as the seed, and
rand_xor() returns a new 16-bit integer each time it’s called.

Another function we need to add is crea_velocitat_esquirol(char num_esquirol), which generates a random number between 0 and 2 for both movement axes of the enemy. It then checks that the result is not (0, 0), since in that case the enemy would remain static and not move.
Instead of regenerating new random numbers—which has a computational cost—we simply assign the values (-1, -1), which is a cheaper operation.

When initializing the screen, we need to add the initialization of the stack (lines 146–149), as well as the entire array of enemy structures with their corresponding initial values.

Since we need to move these enemies across the screen, we must calculate their new positions on each clock interrupt, just as we do with the main character.
To achieve this, we’ve defined the functions esParet_amunt, esParet_avall, esParet_dreta, and esParet_esquerra, which detect whether the tile coordinates passed to them correspond to a wall type or not.
These functions use the same logic as the one used for the main character in the article "Scrolling on a Tile-Based Screen". Since they perform the same task as the previous code written for the main character, we’ve also replaced that code with calls to these functions.

The function esquirol_col_lisiona_amb_paret translates the x and y coordinates and the enemy’s position within the enemy array, and—using the previously defined functions—determines whether it can move forward or not.

Finally, we have the function that sweeps through the entire enemy array and determines whether new enemies should be created, whether existing ones should disappear, or whether they have collided with a wall. All of this logic is defined in the function actualitza_pos_esquirols.
This function contains a loop that goes through all enemies. The first thing it checks is whether an enemy should be eliminated. If so, a new enemy is created, choosing at random which side of the screen it should spawn from, and generating a random speed for it as well. I’ve limited it to one of the four edges of the screen, since I didn’t like how they could suddenly appear in the middle of the screen.
Next, we check whether the position where the enemy was created corresponds to a wall tile. If so, the enemy wouldn’t be able to move and needs to be regenerated.
If the enemy does not need to be eliminated, we then calculate its movement based on its speed and the previously mentioned function, esquirol_col_lisiona_amb_paret, to determine where it will be in the next frame.
In addition to checking for collisions, we also need to verify whether its coordinates have taken it outside the camera view (i.e. the visible area of the screen). If that’s the case, we mark it to be removed and recreated on the next frame.


Conclusions
In this article, we’ve added a basic enemy management system with movement and collisions within a tile-based scrolling environment for the MSX. To handle the MSX’s limit of 32 sprite layers, we used a stack to track which sprite layers are available. While this may not be strictly necessary in practice—often it’s enough to simply reuse the layer of a removed sprite when creating a new one—the stack provides better control and serves as a good opportunity to learn and apply this data structure for other potential projects.
Regarding enemy generation, we initially decided that enemies should appear from one of the four edges of the screen. This choice was made to prevent them from suddenly appearing in the middle of the map. However, this could be revisited, as in reality, only two edges are relevant, given that there is continuity from one side of the camera view to the other.
These modifications can be found in our GitLab repository in the file enemScFo.c. And as always, you can see the example in action.
Click here to see the example in action.
Comments