BRANDON FANNIN
  • Home
  • About
  • Résumé
  • Blog
  • Contact

Adventures in Game Development

Week 17/18 - Pausing

4/25/2025

 
Did not intend for these last bunch of updates to all be doubles, but that's how my free time ended up working out. Anyway, I managed to clear off the last two tasks off my list, so this will be my last update for this particular project. At least for a good while anyway, never know if I'll loop back around and finish the job later on. For now though, let's get into the details.

First up, I've got a pause menu now:
Picture
I had to tweak my viewport settings a bit and add some functionality to my camera manager to get the fade to work properly with UI layers, but besides that it's a pretty simple matter of setting the world timescale to pause, while setting the pause manager itself to only be active when the timescale is 0. Fun fact about Super Metroid's pause menu: the game is still running and accepting inputs while the fade to black is happening. As such, it is possible to cross the barrier to enter another room while in the middle of pausing. In this situation, the game cancels the pause sequence and instead transitions rooms, starting the fade at whatever value it was already at.  My system unfortunately can't support that sort of interaction without adding in quite a large chunk of functionality, so I elected to simply disable the door triggers while pausing.

I didn't end up getting the map screen fully functional, but I was satisfied that I had gained all the knowledge I needed for how to set up UI, so I elected to move on. 

Now, for the sound system... I can't exactly "show" the work I did here without recording actual videos, so I'll just do my best to explain what I did instead. The sound manager runs with two object pools, one for creating AudioStreamPlayer objects, which handle global sounds, and one for AudioStreamPlayer2D objects, which handle spatial sounds. When you want to create a sound, you pass the audio manager a reference to the file you want to play as well as whether or not it's a looping sound, where to play the sound from, and what object, if any, you want to attach it to. The manager gives you a reference to the sound emitter so that you can stop it manually if needed, and when the sound finishes playing, it uses the "Finished" signal to return itself to the proper pool. 

I also created an "AudioTrigger" script, which holds a dictionary of sound files that you can retrieve through name strings, and attached that to the object that I want to play sound effects (the player, in this case).  With the audio trigger present, I can do things like trigger footstep sounds from the player's animation whenever the sprite's foot hits the ground, or start and stop the spin jump sound effect whenever the player enters/exits the relevant state. 

Besides that, there's not much else to say.  I also set up music to play and made it so that it can transition between songs properly when changing rooms, as well as learned how to properly loop music rather than having it fade out/in whenever the song "ends".


And.... that's about it. at some point I will loop back around and clean up my code so that I can post this project as part of my work samples, but for now I have accomplished my goal of learning how to bend the Godot engine to my will. Current plans are to take a week or two to do some other studying for job search purposes, then I'll be back to start work on my first "real" Godot game. 

​See you next time. 

Week 15/16 - Cartography/Saving

4/11/2025

 
Didn't have time to write a blog post last week, so this week you get a double update. For the past two weeks I've been working on the map and saving systems. There's really not much to say about the save functionality, other than the fact that it employs Godot's resource system, which turns out to be very simple to use in that capacity. Basically I just made a resource class that contains all the data I want to keep track of, and then I copy that data from/into the actively running game to save or load. I had to rework a few systems to allow for mid-game loading, but that's about as far as the system goes. I can't really show it in action within the scope of my .gif maker either, so I suppose you'll just have to take my word that it works as intended.

Now as for the map... I decided that the best way for me to implement Metroid's specific style of map was to use tilemaps. Rather than just using a single tilemap and changing each cell based on whether the map tile has been revealed, explored, or still unseen, I went with several tilemaps stacked on top of each other. The "hidden" layer is on top, followed by "revealed", "explored", and "occupied". This makes the map itself a little bit of a pain to set up, since I have to draw several different versions of the same geography, but it does make things much simpler at runtime, allowing me to simply hide individual tiles on each layer as the player discovers more of the map. 

The map itself is currently hidden in game, and will remain that way until I can get the pause menu up and running, but I did also get the mini-map working. I considered just using a second camera pointing at the map proper and projecting it onto the HUD, but I found it difficult to maintain the proper visual with that approach. Instead, I made use of another small tilemap on the HUD.  This tilemap looks at the current state of the full-sized map and simply swaps out the tile sprites to reflect the map state in the player's immediate vicinity. 
Picture
A simple timer hooked up to a visibility toggle gets me the effect for the blinking tile on the player's current position. I ran into a little bit of trouble getting the map to play nice with the instant-load system I have hooked up for debug, but it turned out that I was accidentally using C# syntax for one of my for loops instead of GDScript. Turns out it's hard to drop 10 years worth of muscle memory, who would have guessed?

With these two features in a state that I'm satisfied enough with, we're just left with Sound and the Pause menu for implementation. Getting pretty close to the end here now, and my brain is already at work trying to churn up ideas for what comes next.

See you next time.

Week 10 - Heads Up

1/17/2025

 
Another week down with a good amount of progress made. Following up from last week, my primary goal was to finish the player's damage interaction, which meant I had to set up a few extra systems.  First up, we need a way to actually keep track of the player's health, so that means setting up some UI. I created a UI Manager script, defined a basic "UI Screen" to hold shared functionality, and then went about making the HUD from there.  Luckily, unlike many of my past experiences with the art assets I'd been able to find myself, the sprites for the UI chunks I had needed minimal amounts of work in order to get in to "game-ready" state. So, after looking up some basic guides on how to use the UI nodes in Godot, I threw together my replica of the Super Metroid HUD.
Picture
Not bad, eh? I'm not planning on implementing grapple beam, reserve tanks, or X-ray visor for this little project of mine, so I left those bits out of my setup. Next step was actually getting those values hooked up to real player stats, which... lead me to rearrange some of my architecture first. Previously, I just had the player, level, and enemies thrown under an empty node for organizational purposes, but I ended up finally making myself a real "Game Manager" script to exercise more control over how the program runs.  Instead of just using Godot's built in "_ready" function to set up values at game start, I introduced Initialization functions to key scripts and had the Game Manager trigger those in correct load order.  In simpler terms, I needed to be able to make sure certain values were set within the player instance before I could hook up my new "Player Status" script and properly connect the HUD to that.

Speaking of Player Status, that script is effectively just a data holder used to keep track of stats. At the moment it holds the player's max and current health, as well as max and current ammo counts for missiles, super missiles, and power bombs, but it will eventually hold data on what power-ups have been collected and what items are equipped. Basically, this thing will be what I eventually turn into save data once I start working on that system.  For now though, it has been properly hooked into the HUD, which updates the relevant fields as necessary. 
Picture
Energy tanks are shown/hidden based on what the player's max health is, and the icons for your various types of ammo are set to hidden if you can't hold any of that ammo yet. And of course, this all updates in real time as the player's stats change.
Picture
As you might be able to see from the clip above, I also got my i-frame flash working as intended. That one was thankfully as simple as I thought it would be.

For next week, current goals are to get enemy drops working so that there's actually a way to make those numbers go up instead of just down, and also finally maybe make proper hit and death effects for my poor enemies. 

See you next time.

    Archives

    April 2025
    March 2025
    February 2025
    January 2025
    December 2024
    November 2024
    October 2024

    Categories

    All
    AI
    Animation
    Attack Logic
    Camera
    Clean Up
    Enemies
    Game Development
    Godot
    Level Design
    Music
    Optimization
    Player Mechanics
    Projectiles
    Project Start
    Research
    Sound
    State Machines
    Super Metroid
    Tile Maps
    UI
    VFX

    RSS Feed

Proudly powered by Weebly
  • Home
  • About
  • Résumé
  • Blog
  • Contact