No, these stupid post titles aren't going to end any time soon. Sorry, not sorry. Another decently productive week down; I managed to get aim functionality in pretty smoothly, all things considered. The blend spaces I set up last week to clean up my animation tree made plugging this stuff in pretty easy. For anyone who's curious, here's what the inside of a blend space looks like: Direction along the bottom represents which way the player is facing, and Aim represents where their gun is pointed vertically. If the player pressed up or down, I set the value to 1/-1, and if they press diagonally up or down I set it to 0.5/-0.5 instead. The zig-zag lines... really aren't important since this animation is sprite based. Since I already put placeholder nodes in, once I got the input reading functionality working under the hood I just had to swap out the animations at each node as needed. Of course, that involved setting up more animations, but I find that I've been getting better at the Godot animation workflow so it didn't end up being as time consuming as I feared. Nintendo really didn't skip on detail for this games animations though. Did you know that there are separate turning animations depending on whether you're currently aiming up or down? As part of the aiming functionality, I also hooked in the 'diagonal lock' input. For those of you that may not be as familiar with Metroid, it is as simple as the name sounds; you hold a button and Samus aims diagonally up or down, even if you're not currently pressing the D-pad. Super Metroid was set up to have two such buttons; one for aiming upward and one for aiming downward. There was a bit of an oddity in the design however, and the game did not allow you to re-assign these inputs to non-shoulder buttons. This might not have been a problem for most players, but if you're like me and you can't stand having the Sprint button assigned to a face button (it makes trying to run, jump, and shoot at the same time basically impossible), that meant you effectively lost the ability to aim in one of the directions while standing still. That being said, the GBA Metroid games had a solution for this, since the handheld had fewer buttons to work with. Holding the button locked you in a diagonal aim mode, and pressing up or down transitioned your aim in that direction. Pressing down while already aiming diagonally down would make Samus crouch while maintaining the same aim direction, and similar transition would happen if you pressed up while aiming diagonally up when crouching. I always preferred the feel of this setup over the one that they had in Super Metroid, and so I opted to use that implementation instead in this case. Anyway, with the directional aiming taken care of, I was able to finish implementing some more movement tech that is dependent on it. Specifically, the ability to break out of a spin jump by aiming vertically, and the ability to enter and exit morphball while in the air: Aiming downward while in the air is pretty interesting, as it's treated differently than aiming in any other direction. If the player releases the Down button, they don't actually stop aiming downward. Only pressing a different direction will break you out of it, so it's sorta like an airborne equivalent to the crouch state... though I didn't give it it's own state here, just some fancy logic to prevent Samus from returning to neutral from a downward aim and a bit more to prevent her from entering morphball when she isn't already pointing down.
I know for a fact that there's plenty more nuance to the work I did this week that I'm largely glazing over, but the end result is simple; you can point your gun at things now. The next task on my todo list is the obvious continuation from this one; make it so that you can actually shoot the gun. Projectiles in Super Metroid are not at all complicated, so I don't expect any issues there, but I imagine making sure the projectile spawns in the right spot and goes the right direction might be a little more tricky since there aren't animation bones I can use and the positioning of the cannon isn't consistent between different animation states. I think I have a few ideas on how to handle it, but we'll see how it goes. See you next time. Picking right up from last week, I added in the extra animation for moving forward during the neutral jump state. While at first I had assumed this would be a very simple task, after reviewing the game I found that the specifics of when this animation played were a bit tricky. I'll go over those in a second, but here's what we got working: There's a little hitch with the animation when it swaps over from the jump to fall state, but that's something I'll deal with later. Now, in the source game this animation starts playing when you perform a neutral jump and then start moving forward. That part is just as simple as it sounds, but what took a minute to figure out was what caused it to transition back to the normal jump animation, because I found that frequently in testing, after entering this "forward jump" animation, Samus would simply stay in that state even after I stopped moving forward. turns out there are a handful of requirements. First and most obvious is that the player has stopped forward movement. The second is also pretty straight-forward; the player cannot be firing or charging their beam. The third one was what took some experimentation; the player only transitions back to the normal jump animation if the player is holding down the jump button when they release the d-pad. A bit weird in my opinion, but I coded that bit in all the same. After this, I went to work setting up the crouching and morphball states. Nothing too complicated here, except that I added in functionality to change the size of the player's hitbox when they enter/exit a given state. We got through our main tasks for the week pretty quickly, so I moved on to a bit of clean up to round things out. First of all, I went through my scripts and removed any hard-coded strings for things like state names and animations, and instead set up a bunch of constant variables in a global class. easier to keep track of, easier to change something if I need to. Next I went through my animation tree and turned all my states into blend trees so that I didn't need separate nodes for the left and right version of each animation. While I was doing that, I learned something rather handy; I don't need to have states connected in the animation tree unless I want them to automatically transition from one to another. Those two adjustments cleaned things up quite considerably, here's the before and after: The blend states also have the added bonus of helping prep for my next big undertaking; directional aiming. Each animation is currently set up to take a facing direction and a vertical aim value, and I already switched up my input logic in code to include the vertical axis when I implemented crouching/morphball. Theoretically it should just be a matter of turning the input into an aim value, and then slotting in the different animation variations, but.... we'll see just how smoothly that part goes. There are alot of animations for me to plug in here after all. In any case, a pretty productive week I'd say, let's hope the momentum I'm gaining here keeps going strong.
See you next time. Bit of a busy week between Holiday plans and job search shenanigans, but we still managed to make a chunk of progress in between everything. My primary goal for the week was to sort out the jump state and the various mechanics attached to that. ie; Jump, spin jump, fall, and land. Setting up the states themselves and the animations was simple enough. just more of what we already did last week, but when it came to figuring out the specific jump physics I came to an interesting realization. For how many years I've been working as a gameplay programmer, I've never actually programmed a jump before that wasn't just a basic "apply upward force, add gravity until you fall" sort of thing. Anyone who has played Super Metroid (or any Nintendo platformer for that matter) knows that that's not how it works; jumps are dynamic, you go higher when you hold the button down longer and you can short-hop by just tapping the button briefly. So after setting up the basic framework, I had a bit of homework to do. Luckily this is a very well explored topic and I was able to find a GDC talk from 2016 going over exactly what I needed to know. Here's a link for anyone who's interested: https://www.youtube.com/watch?v=hG9SzQxaCm8 Pretty simple setup after do we our math magic; we apply an upward force right when we enter the jump state, and then apply one of two constant gravity values depending on whether or not the player is holding down the jump button. Basically, gravity gets a lot heavier when you let go of the button. Once the player's vertical velocity becomes negative, we swap to the falling state and then apply a different gravity value that's slightly heavier to give the jump a bit more weight. Once we hit the ground, we swap to the Landing state just until the animation finishes before going back to Standing. Now technically I'm not sure this is exactly how they handle jump momentum in Super Metroid, based on my observations they might literally just zero out the player's vertical velocity when they release the jump button, but I feel like this solution is probably more valuable for me in terms of future projects. Speaking of Super Metroid, I noticed an interesting quirk while I was testing the game's jump feel against my own; the game seems to differentiate between when the player falls after jumping and when they fall from a standing state. Specifically, it has a different animation and the maximum fall speed is different, so I set up my system to work similarly: Last up for the week was the spin jump. This is the state you enter when you jump while moving as opposed to standing still, and it comes with a few distinctions. First, you have a constant forward velocity while spin jumping, even if you stop pressing forward on your controller. Second, there is no turn animation for spin jumping; you instantly flip around and move in the opposite direction instead. And Third, the Spin jump has a slightly different animation when landing than a normal jump. There a few other notable differences as well, but those won't be important until I get to things like aiming and wall jumping. Because the rest of the jump physics are the same though, getting this state working was a pretty simple task. And that's about it for now. I need to adjust the normal jump slightly in order to add in the separate animation the plays when you start moving forward, but besides that my main plans for next week are to get crouching and morphball working, and maybe start on figuring out the aiming logic.
See you next time. So, our first full week of development is done. Where are we at? Well, most of the week was actually spent gathering assets and getting those assets into a usable state. The gathering went pretty smoothly; Spriters-Resource had a full collection of character and enemy sprites as well as fully ripped tile sets for me to use. Unfortunately, neither of those could be used in the states I found them in, and that's where most of my effort for the week ended up going. For sprites, I had to convert the sheets to have a transparent background, isolate each frame, and then recombine them into sheets with a standardized grid size, all while fixing any errors I ran into with positioning and framing. Fun fact; did you know that Samus has approximately 720 different animation frames in Super Metroid? Needless to say, this took awhile, especially since I ended up having to go through the whole lot of them twice. Tile sets were a little bit easier; I just had to go in and add mirrored versions for any sloped tiles, since they only included one direction for each tile type. After getting the art settled, I imported all of Samus' sprites and got the animations plugged in to an AnimationPlayer node, which I then started hooking into an AnimationTree. Since I'm just starting out and wanted to get something working on the screen, I only built out the basic idle, move, and turn states. Not the prettiest thing in the world, but we'll spend some time organizing it properly later. Need to think of a system so that this thing doesn't turn into a massive spaghetti pile with just how many different animation states are in this game. With that in place, we made a basic placeholder character object and went to work setting up the architecture for a state machine. I'm not quite used to Godot's practice of needing a separate "scene" for each and every script I want, but I'm sure it'll get easier to wrap my head around over time. Just like the Animation Tree, we focused on getting basic movement working first, and here's what our final progress point looks like for the end of the week: Certainly not bad, but there's still a lot of work to do even with what is here. We'll need to add in acceleration/deceleration for the running to make it more game accurate, make sure the Turning state can properly handle transitioning between crouching, jumping, and falling states, and eventually tackle the hell that will be dealing with all of the different "aiming" sub-states that Samus has. Before we get into the weeds there though, I'm going to try to fill out the rest of the basic movement set, so next up on the agenda will be jumping, crouching, and maybe morphball. Only after we've got the basics accounted for will we loop back around and start fine-tuning and adding more layers of complexity.
See you next time. |
Archives
April 2025
Categories
All
|