Thanks for all the advice folks, I've actually clocked over 3000 hours practice in Fusion over the last few years (yes I log it) so there's a fair bit I can do now, but I'm getting to the stage where I'm trying to tie it altogether into a properly thought out and structured approach rather than just hot shotting ideas when they come to me.
I'm feeling like effective coding doesn't come down to being able to do loads of fancy clever tricks, but rather how good you are at pulling together the basics in a consistent, disciplined, logical fashion?
OK, well, you've had plenty of experience, so my comments about 'getting your feet wet' clearly don't apply. I totally agree with you about the importance of a disciplined and thoughtful approach.
For what it's worth, I can give you a peek at how I've done things in my game. I don't know if it qualifies as a state machine (probably not, or at least not entirely) and I don't know if it would meet the approval of any programming theorist. I never studied programming in any proper way, and I'm basically self-taught. Which means I made the dubious choice to pick a teacher who literally didn't know what they were doing.
But Steam says I've spent over 19,000 hours in Fusion, and 95% of that has been on Spryke. In that time, I've (eventually) done a good job of making an engine that works effectively and is easy for me to understand. In the first couple of years I was more haphazard, and by about year 3 I'd ended up with spaghetti code, a mountain of bugs, and had to redo a lot from scratch. Since then, I've been very disciplined with keeping things modular and well-organised. I'm pleased to say that even if I revisit pieces of the engine I made 5 years ago, I'll generally still find my way around pretty comfortably.
I can't overstate how important I believe it is to keep everything about your MFA organised and clear. Not just the code, but the frame editor, the icons, the naming conventions, the general workflow, and of course you should comment extensively. If you allow yourself bachelor habits, what starts as a few dirty socks in the corner today will eventually become a labyrinth of lost belongings and mysterious smells. It's the same with an MFA.
I keep my variables organised in relevant objects, and I give those objects 50x50px icons that are easy to read in both the Frame Editor and the Event List Editor. For example, this is my Spryke States object:
Please login to see this picture.
Inside it are all the different state-related variables of the player character. I use a clock icon to differentiate those that are timer-based variables, and a flag icon for flags, so that I immediately know what I'm dealing with when I see it in an expression. I group them logically and use other altVals as dividers and headings:
Please login to see this picture.
I can keep track of many of these variables during runtime in my debugging panel (the blue section):
Please login to see this picture.
In my code, I first set a bunch of parameters for the physics of the movement engine. For example, gravity. The gravity changes depending on various factors in the game. When Spryke jumps normally, it's one value, when she jumps on a special trampoline platform, it momentarily becomes weaker. While she floats in a bubble, it becomes weaker still...and so on. I set all these different gravity values early on, in my "config" section:
Please login to see this picture.
Lower down in my code is a group called Spryke States where all the various things that can determine or affect the player's state are processed:
Please login to see this picture.
For example, if the player is not on the ground, and is moving downwards, then I determine that the player is falling, which will activate the IF: falling = 1 group (this group is continually closed earlier in the code, so it will only run if it's turned on here, because falling=1):
Please login to see this picture.
In this group, I set up various things that need to be set up. For example, I cancel out certain states/variables that clearly don't apply any more (eg. set jumping and isStopped to 0). Some things will be set no matter what (event #5654), while others must only be set if the player is not on a wall (event #5648). I also set the gravity (how much the player will be pulled downwards per frame) and the fallCap (how fast the player is allowed to drop before further acceleration is stopped):
Please login to see this picture.
However, some of the things that were set here may yet be overridden. For example, lower down is the Floating section of the code. And if in this section the player has been determined to be floating, then a bunch of different code will execute. Including different gravity and fallCap values that will overide what was set earlier:
Please login to see this picture.
Once all of the player's internal states have been processed, we then process various mechanics that can affect the player externally:
Please login to see this picture.
For example, if the player is standing on a vertical platform, we make a note of it in the Spryke States object by setting vertFloater to 1, and we record how fast that platform is moving in YmovingPlatMovement:
Please login to see this picture.
Notice that the player object hasn't actually been moved in any way yet. There have been no set X(player) to... or set Y(player) to... actions thus far. All I've done is set a bunch of variables. Once the entire Spryke States and Movement-related Mechanics groups play out, the variables will have been set, cancelled out, and overidden as needed, and we arrive at the final states that the player is in. Only then is it time to actually process the movement:
Please login to see this picture.
So, for example, we now apply the gravity (insofar as the fallCap will allow). As we saw above, what those values actually are will depend on various factors. But by the time we get to this point, it's all been worked out, and they are what they need to be, depending on the various player states:
Please login to see this picture.
After applying gravity to the Y speed, we then adjust it for various other things that may be affecting Spryke. For example, the vertical platform platform we registered earlier. Notice that YmovingPlatMovement will actually be added to Spryke's Y movement every single frame, no matter what. Most of the time, YmovingPlatMovement will be 0 so won't have any effect. But whenever the player is indeed on a vertical platform, YmovingPlatMovement will be set appropriately above, and will be applied here.
Please login to see this picture.
We then consolidate everything into a final value yMovePixelPerFrame (second-from-bottom action in above pic) that will be used to actually move the player:
Please login to see this picture.
Finally, once the movement has been completed, we reset a bunch of stuff, and perform a battery of tests to see where the player has now ended up (eg. is she now touching a wall? if so, what type of wall?), some of which are shown below.
Please login to see this picture.
Spryke has been moved, and the code is totally up to date with her various states. Now, and only now, we ensure the appropriate animation is playing, depending on her various states:
Please login to see this picture.
So that's, broadly speaking, the process. Everything happens in due course, in an appropriate part of the code. All the different physics and movement parameters are stored at the top, where I can easily see and modify them. They will then be applied as appropriate by subsequent groups. First we determine the player's various states, then we check whether she's being affected by any external mechanics. Only once we know all this information do we perform any movement. Finally, once we've moved the player and re-measured her relationship with the environment, we update her animation. Here's a snapshop of my Event Manager window:
Please login to see this picture.
You will have noticed that I use a lot of graphic characters in my group names. Keeping things clearly labelled and visually segregated helps remove cognitive friction so you can spend more mental energy problem-solving and less on trying to remember what does what. I posted a big list of useful characters Please login to see this link.. I have dedicated buttons on my streamdecks/macropads for my most commonly used ones, and I use a pop-up clipboard manager called Phrase Express for the rest:
Please login to see this picture.