What are tiles and scrolls?

[This post is a prologue to an upcoming post about parallax scrolling in Street Fighter 2]

Most games of the SF2 era were tile-based, meaning the graphics weren’t drawn in individual pixels, but with square tiles of pixels. This helped reduce the RAM that would be required to store individual pixels (often in multiple layers) and also reduce work for the CPU to update animations.

The Capcom Play System hardware which SF2 ran on uses three different tile sizes: 8×8, 16×16, and 32×32. Background graphics are displayed these tiles in three layers, each using one of these tile sizes, and the layers can be arranged in any order from foreground to background; transparent areas in one layer reveal the layers beneath it. These are called scrolls.

On top of these three scroll layers is the object layer, where sprites such as the playing characters, projectiles, some stage animations, as well as the energy gauge and score display. Tiles in the object layer are 16×16 pixels.

Let’s take a look at a diagnostic screen in SF2 which lets us look at individual tiles:

Some of Ryu’s tiles, each 16×16 pixels, numbered with a unique ID. I’ve displayed them all separated by gaps so you can see how big one tile is.

Often, the tiles are stored in ROM in a natural order as they are above so it’s easy to visualise the fully assembled image, but they don’t need to be. As far as the graphics hardware is concerned, each tile is selected by its identifier, and can be placed next to any other tile.

Many of the tiles are reused for different animation frames. For example, if Ryu’s legs look the same whether he’s standing or throwing a short punch, each of those tiles can be reused for that frame. This saves space in the tilemap ROMs. Background graphics especially are designed to exploit this, usually feature large areas of the same tile, or group of tiles, repeating.

Each pixel in a tile is assigned one of sixteen indexed colors, with the last color always being transparent. The graphics hardware looks this indexed color up in a palette, which determines the red/green/blue components of the pixel color, which is then finally drawn on-screen. In this way, tiles can be reused in different colors. This is used a lot for Ryu and Ken, who differ only in tiles containing their faces, and the color of their uniform.

Let’s take a look at that:

Some tiles of Ryu throwing a Hadouken. We can also some tiles of Ken’s head, but they don’t look right, because we’re displaying these tiles with Ryu’s palette.
Exactly the same tiles, but with Ken’s palette. Now Ryu’s head doesn’t look right, but those tiles won’t be used with this palette.

SF2 Championship Edition, which allowed fights between two of the same character also used this effect so each of them could be distinguished.

Additionally, each tile can be flipped horizontally or vertically. For example, a player facing left uses exactly the same tiles and if they were facing right, only flipped.

This is why Sagat’s eye-patch changes from one eye to the other when he turns around, and why a Shoryuken is thrown with a left arm when facing left, and vice versa. To do otherwise would have required a different set of tiles depending on which way the player was facing, which would have complicated the animation engine also.

Now let’s talk about the scroll layers used for the background. One limitation with these is that each layer is arranged in a single large grid. Tiles drawn this way can’t be moved around by individual pixels like the sprites can, so there’s a limit to what kind of animations that can be used there.

In SF2, animations in the background layers are constrained to simple sequences of a few repeating frames, such as these elephants, which move their trunks up and down, but don’t otherwise move around relative to the other tiles in the same layer. If they did, it’d need be by a multiple of 8, 16, or 32 pixels depending on what layer they were in.

However, each of the scrolls themselves can be moved with one-pixel resolution. The most noticeable use of this is seen as we move left and right along the stages, which – apart from the bonus stages – are wider than the screen.

But a more subtle effect (well, it was subtle for me, whose attention was mostly drawn to fighting, and usually losing) involves moving the scrolls by a different amount relative to each other, to achieve parallax scrolling and create the illusion of depth, or 2.5D:

Both players are near the middle of the stage, note the three elephants
Near the extreme left, note the elephants are starting to overlap each other

Another example of parallax can be seen on the floor. Note the orange rug changes perspective as we move across the room. This effect can’t be achieved with tiles alone, or at least not without creating lots of them for all of the different perspective angles.

This is a graphics effect on the CPS1 called line-scrolling (or row-scrolling), which involvs each individual row of pixels being translated left/right by a certain amount, and will be the topic of my next post. Until then!

What are tiles and scrolls?

Guile’s World Warrior bugs

One of the first things I went looking for was to see what the cause of Guile’s Invisible Throw bug (and others). This is why I started with the earliest, buggiest version, SF2UA, date 910206.

A ‘move’ in Street Fighter is encoded using three variables, one to signal whether the move a standing / crouching / jumping attack, another for punch / kick, and an ID. Another variable tracks whether they are standing, walking, attacking, or executing a power move. These are used in finite state machines which are called each frame to process the animations.

The bugs result from still checking for power move input after an ordinary attack has been selected (because players are allowed to charge back while still executing other moves), and a nasty side effect of the button decoder setting the Punch/Kick variable directly without even know the player’s context.

First, we’ll look at the ‘Stance’ bug. You get it by being close enough to your opponent to do the upside-down kick (within 70 pixels), charging back, pressing roundhouse to begin the kick, and then doing a sonic boom before the kick completes. You have to press fierce after the roundhouse, and within the same window that a Sonic Boom would be allowed. Guile executes his upside-down kick, but instead of returning to a standing position, he gets stuck on the last frame of the upside down kick animation. He can be hit out of this, but the AI players don’t usually do this as they think he’s still doing some sort of kick that seems to last forever. Vega gets very confused and does his slide attack repeatedly.

The upside-down kick is Standing, Kicking move #6.

What happens is the standing kick gets selected, and the animation begins normally. The Sonic and Blade state machines keep running throughout attacks; this is necessary to allow players to charge back/down while still executing a move. At this stage, because we’ve charged back for long enough, and are now pressing forward, the Sonic Boom state machine is now checking for a button press, and when it detects one it calls the button decoder.

The button decoder (you’ll find it at address 0x3296 in the SF2UA ROMs) is basically a chain of ‘if’ statements that test a bit for each button, and set the corresponding Punch/Kick and Strength, bailing out at the first match. ┬áThe last condition, a strong kick, isn’t even tested for, it’s returned by default is no other buttons are detected. It only makes sense to call the subroutine if it’s already known a button is down.

Punches are tested before kicks, and when we press our punch button the player Punch/Kick variable gets set to ‘Punch’, so our player’s state is now Standing, Punching move #6 which actually represents one of Guile’s throws. The animation doesn’t change, but the state machine for the throw runs instead. This is at 0x2f922.

Where the state machine for the kick simply waits for the animation to finish and then sets the player’s state and sprite to a normal standing pose, the state machine for the throw is different. It looks for a special flag in the animation that signals that the character being thrown should now be released, but this never comes so Guile gets stuck in this state.

All of Guile’s bugs centre around this setting of one of his kicks, which then get turned into punches with the same ID by this bug in the button decoder, and were fixed in the next release (sf2ub, sf2eb, and sf2jb).

Guile’s World Warrior bugs

SF2 AI – does it cheat?

I’ve seen a few questions floating around about whether the AI can cheat – specifically whether it needs to charge Bladekicks or Sonic Booms like a human player does.

No, it doesn’t.

The built-in AI programs are simple lists of moves that aren’t subject to the normal human rules of needing to charge back a certain amount of time before performing a move like a Sonic Boom, and the computer controlled opponent can execute them without these requirements.

For some moves, such as E.Honda’s Hundred-Hand-Slap, Chun-Li’s Thousand-Foot-Kick, etc. the AI scripts include a couple of moves to make it look like they are subject to the same rules as human input (and also a clue to new players how to execute the moves), but the AI machine is capable of launching into any power move as easily and instantly as any ordinary attack.

SF2 AI – does it cheat?

Reverse engineering Street Fighter 2 – The World Warrior

Years ago (2005!) I started reverse engineering the ROMs for Street Fighter 2 – The World Warrior in my spare time, and rewriting the whole thing (yes, the whole game) in C.

Since I can’t likely share my source or binary without infringing Capcom’s copyright, I decided I should at least start a blog to share with the world what I’ve found from the code, for whatever reason you might be interested.

I’ll likely never be able to release my full code or binary, so I want to share as much of my findings as I (legally) can so if for some reason I can’t continue, not too much of this goes to waste.

I also wrote a graphics engine to emulate the CPS’s tile graphics hardware using OpenGL. Because this code is mine, I’ll be sharing parts of it with you.

Reverse engineering Street Fighter 2 – The World Warrior