Shield Cat's Work Between the Screens (Patreon)
Content
Howdy! Today I wanted to write a bit about what Shield Cat does behind the scenes when moving between screens, stages, and the start of the game. People seemed really curious about it, so let's talk about it!
- Between Screens
First, let's talk about what happens between screens. If you've ever played Shield Cat, you'll know that the game pauses for a second before moving you to the next screen. It may seem like a neat callback to older Zelda games, but just like those games, Shield Cat is also working on making the next area playable.
Aligning the Camera
First, let's talk about camera alignment! The game will align the camera using a 3 step process:
- First, the camera is aligned into position so that it can scroll into the next screen. It does this so it doesn't accidentally reveal parts of the stage that are considered “out of bounds,” as often other completely unrelated screens (such as the insides of houses) will be just outside of the playable area you're in, and it wouldn't be good for you to just be able to see that!
- Next, the screen smoothly scrolls over into the next screen using linear interpolation. Fun fact: for the SAGE 2020 version of the game, this was handled with an object leading to inconsistent timing on the transition as well as the possibility of it never actually reaching the destination. Fortunately, the new version of the game is put together much better than this.
- Finally, when the camera is in the new screen, it settles into it's “natural” position. What this means is that it aligns to where Lance is, instead of being locked into its scroll position from before (where it wouldn't show out of bounds stuff.)
Now the camera is in the next screen! But, what happened during all of that?
Generating Grass
You probably have noticed that you can walk into tall grass in the game and Lance will be depth sorted appropriately. In order to do this, a custom 3D object is generated when stepping into each new screen. Parts of the stage collision are marked for where tall grass should show up, and the new area is subsequently scanned to see where tall grass should be and then is placed there. This custom 3D object is one object for all the grass in the current screen, and I'll probably write about it in another post.
Fun fact: In the SAGE 2020 version of the game, all the grass was generated when you entered the stage. I found this pretty slow and also not optimal if you stepped into a stage and then decided to leave, so in the new version of the game, I use this much less expensive method.
If you're curious: no, grass doesn't exist outside of the current playable area you're in! The grass data is retained so that all grass you cut will still be cut when you go back, but that 3D object no longer exists as soon as you step into a new screen. Speaking of which...
Lance Fake?
Did you know that the Lance you're looking at during screen transitions is a fake Lance? That's right! When going between screens, the actual Lance is deactivated and replaced with a placeholder object showing his exact state as he was when you stepped into the new screen. This is done because Lance has a ton of variables, functions, etc and halting them all to have him involved in the scene would be too cumbersome, so he just has this replacement instead. However, he's not the only one who gets replaced during screen transitions...
Replicating Objects
Aside from Lance, many other objects are either replicated or told to stop animating during the transition (and also when pausing, during some cutscenes, etc.) For the replicated objects, the actual object is deactivated and then replaced with a placeholder that has exactly how that object looked before initializing the transition. Other objects (like bumpers, or objects with complex drawing routines like collectibles) are simply told that they should be “frozen” and each object reacts accordingly to this.
What about objects that are outside of the current playable area? Well, those don't exist at all! To optimize speed and performance, only the objects that are in your immediate play area are enabled and processed.
Update Enemies and Collectible Positions
When you step back into a screen you've been in before, the game will reset the positions of the enemies in that screen. This helps prevent an unfair situation where an enemy chased you to the edge of the screen and is still there waiting for you. It also keeps track of any Pretty Petals you left behind in the previous screen, so if you accidentally step out of the screen, you can step back and fetch them. The game only remembers the last area you were in, so if you want those petals, you better go back for them fast!
Updating the Minimap
When moving between screens, the minimap is updated to show what's going on in the new area, though this can also happen by doing certain things (getting a collectible, opening a gate, hitting a lever etc.)
Autosaving
The game also takes this time to write your progress to disk, though it doesn't do this for every screen you go through. It has a maximum threshold of about 2 minutes. However, if you go to a new stage, progress will always be saved.
Parse Dialogue for NPCs
If it hasn't happened already (ie. You've been to the screen before on the same stage) NPC dialogue and interactions are processed. This is done when you first visit a screen instead of when you first enter the stage for the same reason grass isn't generated for the entire stage when stepping into it: There's no point in processing the whole stage if you're only gonna be in a little bit of it!
Rest Your Hands and Mind
The convenient thing about all this work is that the game gives you time to relax. Input processing is not done between screens, so you can ease off the gamepad or keyboard for a minute. You also get some time to see what's going on in the new screen before it becomes playable, allowing you time to react to what's happening in the new screen.
- Between Stages
That's all well and good about going between screens, but what about going between stages? Well, there's a fair amount more work involved, so let's get into that!
Partial Load vs Full Load
First, I should talk about how the stage is loaded. If you're just stepping into the stage from a completely different stage (ie. Going to the deep woods from the village) the entire stage is loaded from the ground up. However, if you're just going from indoors to outdoors, like stepping into a house in the village, a lot of the stuff mentioned below is skipped to reduce the load times. This also allows stuff like cut grass to stay cut (as this information is discarded when you go to a new stage.)
Transition Is Aesthetics
When going to a new stage, the game will show Lance walking as well as your collectible totals for the new stage. Most of this is just fluff, and the game isn't actually loading anything or doing anything (this is why you can press a button to skip this.) However, it makes a nice transitionary sequence to the new stage and allows you to clear your mind for the new area.
New Stage, New Lance
When going between stages (or even going indoors/outdoors) you may be surprised to learn that Lance is regenerated from the ground up. This is because of the aforementioned ton of variables attached to him, and directly moving him between stages just proved to be too cumbersome because of variable and state caching. This is why there's currently a bug where he recovers his health when going to a new stage or indoors/outdoors, because the game stores this information right on Lance's object. In the future, I'll be fixing this though, so enjoy the free recovery while it lasts. (You can always just visit Willow for free recovery.)
Calculating Collectibles
When going to a new stage, the game will calculate all the collectibles in that stage. What this means is that Fish Scales will be assigned their IDs from data included with the game, and then the game will figure out which collectibles you've already gotten and mark them as “collected.” You can always pick them up again for a few extra Pretty Petals! But yea, if you ever wondered if I assign the IDs of Fish Scales by hand, the answer is “no I do not.” The game keeps track of this information automatically.
Initializing NPCs and Events
The game also takes this time to check all NPCs in the new stage to make sure they should even be present and what dialogue they should use, as well as setting levers to having been hit previous, gates opened, etc. Pretty standard stuff you'd expect honestly.
Loading Language Strings
The game also loads in language strings for the new area into RAM (these are actually pre-loaded during the transition that shows your collectibles for the new stage – Like I said, it's mostly fluff.) The language strings are fragmented like this to prevent them from taking up a load of RAM.
Generating the Minimap
The game also takes this time to generate minimap data based on the stage you're going into and how much of it you've seen or not, and then stores this result to a texture to display. When going between screens, the minimap is updated, but only the relevant parts of it (not the whole thing.)
Swapping Out Overlays
If you read my blog post about how the game is drawn, you'll know that the game uses textures that are stretched over the play field and influence stuff like lighting and color values. When stepping into a stage, this is when those overlays are loaded into RAM.
Fade out Music and Sound
Music and sound is faded out during this time as well. Fun fact: To fade out environment sounds such as the waterfall, I just move the camera far away from the stage. Since the game has a 2D orthographic perspective, you'd never know that I was doing this. It's simple, but effective.
- Start of Game
What about when you turn on the game? What does the game do? Let's talk a bit about that.
Preload INIs
At the time of writing this, I'm currently switching a bunch of code in the game to use Async operations to enhance the speed and performance. One of these is to read all the relevant INI files into RAM (don't worry, they're small!) All read operations to the INI files are now done from RAM instead of reading the file from disk, and writing is done in batches instead of just doing it as soon as you change something. This has the added benefit of removing most of the stuttering from the pause menu as the game reloads all of the configuration data. I was never fond of the way the game was handling this data, so I'm more than happy to have switched it. You'll be able to see the improvements in the next build.
Load in 3D Objects
During this time, the game also preloads 3D objects (of which there are currently two: the shield, and the gear.) How this used to work was that, whenever Lance was loaded into the stage or one of the rotating gears was loaded in, the game would load the file and convert it into a 3D object for every gear or every time Lance was loaded in. This wasn't terribly efficient, so now the game does this just once at the start. This leads to a small reduction in the amount of RAM the game uses, but also helps the game to load faster because it's not doing all this work loading in 3D objects (it can be slow to read .obj files!)
Please note, the river challenge has its own loading routine which I may cover in another blog post, but for now I'll just mention that, on my own computer, I pre-converted the 3D objects necessary for the stage and now you can get into the stage a lot faster than before.
You'll be able to see these changes in the next build.
Load Input Configuration
It is during this time that all input configurations for the keyboard, mouse, and any input devices are processed. Before the introductory sequence (when the game says “Presented by CyanSorcery”) the game actually has no input configuration and won't read any inputs at all! This information is also updated if you plug in/unplug a gamepad during gameplay, which is why you may see the game pause for a brief moment if you do this.
- Conclusion
Hopefully next time you play the game, you'll keep some of this stuff in mind and know what's going on without you even knowing about it because now you know about it! If you have any questions, please let me know. See you next time!