We're just over a month into it now, and at this point I think I can say the whole blogging strategy is working pretty well. Forcing myself to sit down at the end of the week and measure my progress has definitely made me feel like I'm getting more done, even in the cases where I'm not working at a noticeably faster pace. But, that feeling of accomplishment helps boost morale, which in turn does actually make me more productive. I feel like at this point my output is quite a lot higher than it has been in a good while. Now for the actual progress report; In this past week I have built a pooling system, got basic projectiles working, and threw together a weapon manager for the player. All that is to say, you can now jump and shoot. As a quick aside, I looked intro trying to fix that little leg twitch Samus has at the top of her jump, but there's simply no easy way to do it. I'd either need to redo half of my animation tree, write some incredibly specific code to try to force a behavior that doesn't want to happen, or tear the whole thing up and write my own custom animation manager. Needless to say, since the whole point of this project is to learn, dealing with that isn't exactly high on my priorities. I've learned enough at this point that if I needed to set up animations like this on another project, I could build the systems in a way that better suites my needs, and I'm satisfied with that. Now, object pooling.... For those not familiar with games programming, here's a quick breakdown. Whenever you need to create or destroy a new instance of an object, that takes up a decent chunk of time/processing power. When it happens often enough, or if you're trying to create new instances of particularly complicated objects, that can easily lead to framerate drops and game stutters. Object pooling is a solution to this; instead of creating a new object whenever you need one, you create a bunch of objects ahead of time to setup a "pool"(usually during a loading screen). The object in the pool are just turned off and hidden until they are needed, at which point you switch them on and put them where they need to be. When you're done with an object, you turn it back off and return it to the pool. This is a pretty standard technique in the industry, used for things that we would otherwise need to create and destroy with some frequency. A few common examples would be bullets, item drops, and sometimes even enemies. Now, this is usually a pretty simple thing to set up, but Godot's quirks forced me to do a bit of experimentation this time. Godot doesn't support Multiple-Inheritance, a programming concept that's a bit harder to explain for those not already in the know, but the simplest explanation for my situation here is that I can't have projectiles be both physics entities and pooled objects at the same time. That forced me to revise my implementation of pooled objects a bit, but I'm actually pretty satisfied with the results. Basically, instead of telling something that it is a pooled object, I made pooled objects something you attach to other objects. Once attached, the pooled object has the ability to hijack whatever its connected to and turn them on or off as the Pool needs. This means that any object I make can theoretically be pooled, not just ones that I specifically design to be. For the projectiles themselves, I had to wrestle with Godot's Rigidbody system a bit more than anticipated, but I eventually got everything working as desired. The bigger lift here was actually making sure the projectiles spawned where they were supposed to. Samus' arm cannon doesn't have consistent positioning between different animations, even if it's aiming in the same direction. In order to deal with this, I had to go into every single animation and key in a position for the projectile's spawn point. Tedious work to say the least, but hey; it's done. One other thing of note; while I was setting up the projectile sprites, I discovered that the sprite sheet I'm using is actually missing quite a bit of content. It has all the sprites for missiles and bombs, but it only has the basic power beam and the fully upgraded ice beam... not any of the combinations in between. I was unable to find any more complete sprite sheets for the Super Metroid projectiles, so if I end up taking this far enough to want to do more than one beam power up, then I might need to substitute in different projectile spites. I was able to find a complete sheet for the GBA Metroid games, so in that scenario I might switch over to using those instead.
That's it for this week though. Not 100% sure what the goal for next week is, trying to decide between making an enemy to shoot at or getting tilemaps set up so that I'm not standing in a white and grey void any more. See you next time. 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. |
Archives
April 2025
Categories
All
|