Update: Solved! See bottom of post for details.
Hello, more experienced programmers than me! I could use your advice. There’s a code-pattern I’ve been using for a while and I’ve just discovered a problem with it, but I can’t see a great alternative either. I will explain by example:
What I’m doing
Right now I’m trying to add a little auto-aim when you’re pointing a gun at people. If you’re holding a gun and the cursor is near an enemy, I want a little reticule to appear on the enemy, the player to point their gun at them, and if you fire, obviously, fire in that direction.
How I’m doing it
The reticule needs to be an object – that’s the only way to specify a particular depth (z-layer) for it to be drawn at). I called it oReticule.
The code that looks for enemies to aim at is in the PlayerItemControls() script – that handles anything specific to what you’re holding in your hands, and this is a feature of guns and possibly some other item types, but not others. I tell it: if there’s an enemy near the cursor, place oReticule on it and make oReticule.visible = true.
The code that decides which way the player should face is called PlayerMovementControls(). Normally, in this ‘moving around freely’ state, the player always faces the mouse cursor. For auto-aim, I’ve added an exception to say: if oReticule.visible = true, look at that instead of exactly at the mouse. The difference is slight but important: guns fire in the direction they’re facing, so you really do need to look exactly at the reticule.
Lastly, when oReticule has drawn itself, it sets oReticule.visible = false so that it won’t be drawn again unless the auto-aim code tells it to. I don’t want to have to add lines to all other player states telling the reticule to be off, I want it to be always off unless this one specific bit of code activates it.
What the problem is
Currently, the player movement code executes earlier in the step than the player item controls. So:
- PlayerMovementControls() checks oReticule.visible, finds that it’s false, and continues pointing straight at the cursor.
- THEN PlayerItemControls() finds an enemy to point at, and puts the reticule there and sets oReticule.visible = true.
- oReticule finds that visible = true, draws itself, and then sets oReticule.visible = false.
- PlayerMovementControls() checks oReticule.visible, finds that it’s false – etc.
So the player doesn’t look at the reticule, and never will – it’s checking before the item controls has a chance to turn it on, and after the reticule has turned itself off.
Obviously I can fix this by just moving ItemControls to execute before the MovementControls, but those are separate behaviours, and I don’t want them to be order-dependent. It’s perfectly possible that I might later have the same problem the other way around, and it’s almost certain I will forget that they have this hidden order-dependency.
I also often want a similar relationship where those two scripts would be on different objects entirely, and in that case the order in which their step events execute is unknown. (It can be known, but it depends on something about how you organise your objects in GM that I change regularly, so I can’t depend on it).
What I’ve tried
What I’ve done for now is to take the ‘visible = false’ bit out of the oReticule’s draw event and put it at the start of ItemControls. Now the player does look at the reticule as desired, but there are still two problems:
- If we leave this state at a time when the reticule is on, this code won’t execute again, so the reticule will never get turned off. I do have an OnExitState bit I could add a line to, but this feels messy. It’s also possible for the player to get destroyed unexpectedly, and since the reticule is a separate object it would continue to exist and be visible.
- It’s perfectly possible that I might want something else to also turn on the reticule.
So what I want is
- Several bits of code that can each turn the reticule on
- Several other bits of code that need to know whether the reticule is currently on
- If none of the bits of code that turn the reticule on execute, the reticule should be off
- None of these bits of code know what order they will execute in, on a given step
- It’s fine if the listening bits of code respond on the next step instead of this one
This in Game Maker Studio 1.4, which uses its own language GML. It doesn’t have delegates or coroutines or structs, that I know of. All functions are public and can be called from anywhere.
Thanks everyone! Lots of very different ideas, many of them involving big concepts I didn’t know about. As usual, finding out all the different ways other people tackle this problem clarifies where the issue is, and I found I could boil it down into something pretty simple and lo-fi: I just need two variables instead of one.
The two types of code I identified above are ones that can set the variable and ones that listen for it. If these two operate on the same variable, order will always be a problem. So instead we’ll call the ones that can set it Signallers, and the ones that listen for it Listeners. And we replace the single variable with:
OnSignal: anyone can set this, but it is never read by anything other than the object.
OnState: only the object can set this, and anything can read it.
So Signallers all set OnSignal, and Listeners all listen for OnState. Then in the object’s code, we say:
OnState = OnSignal
OnSignal = false
Anything that listens for OnState – including this object’s draw code – will trigger once for every step when OnSignal was triggered by anything (it may be the next step). And will stop triggering if nothing set it since last step.
Tested it: it works, is only 2 lines, and most importantly, doesn’t involve adding code where there wasn’t already code for this.
Thanks again for the ideas!