Help Me Structure Some Code Better In Heat Signature

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:

  1. PlayerMovementControls() checks oReticule.visible, finds that it’s false, and continues pointing straight at the cursor.
  2. THEN PlayerItemControls() finds an enemy to point at, and puts the reticule there and sets oReticule.visible = true.
  3. oReticule finds that visible = true, draws itself, and then sets oReticule.visible = false.
  4. 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:

  1. 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.
  2. 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.

Any thoughts?

Update: Solved!

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!

17 Replies to “Help Me Structure Some Code Better In Heat Signature”

  1. What Engine are you using? If this were Unity, I’d such the reticule to be a child of the gun (or aiming mechanism). Is there only 1 gun? Or can you control multiple guns like when you hijack a ship and have the turrets follow the mouse/reticule?

  2. As I’m understanding it you’re using the oReticule.visible variable to decide whether “auto aim” is active. Why do you turn it off each step always? Why not do something like once “auto aim” is active a “dead zone” is created around the object being aimed at. While the cursor is in the dead zone the “auto aiming” remains active. You might have to dynamically adjust the size of the dead zone so it’s not too sticky when there are multiple objects near each other.

  3. Loose thoughts – but they might be more useful than specific approaches:

    – it feels like separating out the visibility of the reticule object, and whether auto-aim should be engaged, might help you here. Ie: you need to work out what object the player is auto-aiming at, and separately, you need to work out whether to show the thing.

    – given that, feels like you need a separate ReticuleManager unattached to a visible object. Basically: anything that can turn the reticule on sends a message to this, such as “I am enemy #248 and I should be auto-aimed at”. That probably gets stored on something like a list, if you want to have many reticules, or just a single field if you want one. (Or: might be worth having a list, so you can re-prioritise later, though I think priority order might just be last on first off). Anyhow, then, when it comes to aiming, you ask the ReticuleManager “which object should I point at” – and separately, when it comes to rendering, you ask the reticule manager “entirely separately, which object do I draw the reticule on?”

    And then, at the end of your loop, you need something to tidy up – ie, rather than setting reticuleVisible=false immediately, you clear up after yourself at the end of a game loop.

    Is this making sense? It might not be what other people do, but what it feels like is you need something abstract to handle all the possible auto-aims and reticules, that you can message and later ask for canonical answers. That’d also help separate out render and logic.

  4. It’s unlikely that I’m a more experienced programmer but what I would do is probably let character always aim at the abstract target which may be on the mouse cursor but may also be somewhere else. Basically you always aim at the small reticle, whether its visible or not. No “aim at the mouse cursor except when xyz”.

  5. So, I think the problem you’re running into is that you’re commingling the reticule rendering and logic states. You’re using the same boolean value — oReticule.visible — to determine both whether:

    * The reticule should be shown (i.e. you’ve locked onto a target)
    * The reticule needs to (re-)paint itself to the screen.

    Instead, if you separated these values to something like:

    * oReticule.visible
    * oReticule.painted

    … then you can use .visible as you currently are for the player aiming logic, and use .painted to keep track of whether the object needs to repaint itself.

    Does that make sense, or have I misunderstood?

  6. Add a flag to your oReticule object called willDeactivate.

    When oReticule.visble is set to true. set oReticule.willDeactivate to false.

    When oReticule.visble would be set to true instead check oReticule.willDeactivate.
    If oReticule.willDeactivate is false set oReticule.willDeactivate to true
    else set oReticule.visble to false.

    some sudo code:
    if value:
    oReticule.visible = true
    oReticule.willDeactivate = false
    if oReticule.willDeactivate:
    oReticule.visble = false
    oReticule.willDeactivate = true

    This will make visible false after one loop in which nothing has called it but will stay visible until that happens

  7. Now I’m not anywhere near as experienced as you are.

    But could you potentially make a separate script that turns the reticle on, and while it’s on, everytime you call “IsReticleOn()” it checks for a certain condition, in your case: “Mouse no longer on enemy”, before returning the value?

    Kinda similar to James Vokes’ suggestion, actually

    Or alternatively, make the reticule ALWAYS drawn, but when it’s not autoaimed, make it’s position off-screen, yet still in the same direction as the mouse. Pretty easy vector math, no?

  8. someone compressed my white spacing :(

    if value
    oReticule.visible = true
    oReticule.willDeactivate = false
    if oReticule.willDeactivate:
    oReticule.visble = false
    oReticule.willDeactivate = true

  9. Maybe just treat the visible state as an integer, e.g. set myVisible=2 whenever you want the reticule to appear. Then, where you currently set visible=false, instead subtract 1 from myVisible and set visible=false when it hits zero. That way your visibiliry state will carry over 2 frames, technically leaving the player auto-aiming for one frame too long but that might not be a big problem.

  10. If you’re happy with your current architecture and just want to fix the ordering problem – then perhaps you could store the frame number that the reticule was turned on, and change the code that turns it off to only do that if the current frame is greater than the stored frame. That way it will prevent the reticule from turning itself off immediately on the same frame, and your other code will get a chance to see that it’s on and update accordingly before it does turn itself off the frame after.

  11. Wow, you got a lot of responses very fast! There might be some bikeshedding going on here :)

    Anyway, I think what Duncan Wintle and Frans Kasper are saying is the simple solution you can easily use. But I don’t like it.

    I think your problem is that you have both the “Several bits of code that can each turn the reticule on”, and the “None of these bits of code know what order they will execute in, on a given step” requirements. I’m guessing this means that if several bits of code want to turn the reticule on in the same step, it will be set to the place where the last bit of code wanted it to go. So even if you solve this problem now, you might run into a problem later, where every step those code paths run in different order, and your reticule will jump around between two places. And you make it a bit hard to set up any priority between the targeting.

    So I think long term what would be best would be to have one function which can decide whether to turn the reticle on and where to target it, and this function would run once each step. Of course this probably requires a larger rewrite of your code, so I understand if you don’t do it. But maybe later, when you are introducing some new “state” into the game, you could try doing it in a way where setting the state only happens in one place, generally that makes working with it less complicated.

  12. I think the problem is you want it to appear whenever but only stick around for one frame from when it appears. However you now have a problem that the time it should disappear is arbitrary.

    I think the cleanest solution is to let it stick around for one frame on from a designated point. Ideally this point occurs just after when it gets turned on, but at least this solution will never fail if it happens to be some time before this point. Then both the draw and the auto aim can happen if the reticle is on, and while they may last for one frame more than is ideal, they will never last for longer than that, and they will always be on for at least one frame.

  13. In terms of how you achieve this you need amIOn and stayOn. When you turn the reticle on both amIOn and stayOn need to be true. Then at this designated point if (stayOn)
    stayOn = false;
    amIOn = false;

  14. When all you have is OOP, everything looks like an object. But it isn’t. Time to go back to imperative. Let me try to explain (I hate writing English about Code, it’s even harder than writing Code about English).

    So you have all this functionality distributed around these “objects”. But they aren’t actual independent entities – you’re actually just doing a single action (aim a gun) and a single blob of logic. So splitting things around the objects is hurting you, and you’re having to add a bunch of book-keeping – stuff that doesn’t make decisions, it just tried to keep all the objects in sync.

    I would move all the actual thinking logic into a single place. Doesn’t matter where, but probably PlayerMovementControls(). It will do:

    1. Find enemy to point at.
    2. Point player at enemy.
    3. Tell gun to aim itself that way.
    4. Tell reticule to draw itself there.

    All the other objects just do what this logic tells them. The information flows one way – from the decision-maker to the children. There’s no back-and-forth or multiple chunks of state. In this case the player object makes the choice, and then the gun and reticle do what they’re told. This will keep things so much simpler, modifiable and debuggable.

  15. However, the implemented solution only solves the problem if the execution order doee not change between frames.

    initially OnSignal=OnState=false
    # Frame 1
    Listener A checks OnState: false
    Something sets OnSignal = true
    OnState = OnSignal = true
    OnSignal = false
    # Frame 2
    OnState = OnSignal = false
    OnSignal = false
    Listener A checks OnState: false

    If Game Maker does not guarantee that an order is kept you might want to think about implementing something more robust.

Comments are closed.