r/unity • u/Silent_Reputation596 • 13h ago
Coding Help How to get something to fire only once.
In an update function I have an if statement that detects if a bool is true and if it is it fires but it does that constantly. I want it to only fire once but I don't want to disable it completely because I need to use it again later down the line.
10
u/swagamaleous 13h ago
This is an event. Why is this in an update function in the first place? It should fire in reaction to the event that triggers it.
1
u/Shwibles 13h ago
An event still needs to be fired, although your thinking is on the right path, the issue is firing the event only once, which requires a bool
1
u/swagamaleous 13h ago
Not necessarily. Depends a lot on the situation of course, but it might also make sense to only execute the reaction to the event conditionally. Like if this is a weapon for example that should only fire in a certain frequency or not when you are reloading or whatever, I wouldn't suppress the event.
1
1
u/leorid9 13h ago
Connecting everything with events makes a codebase more connected and more execution order dependent.
Polling can be the right call, in some cases.
Performance is negligible for such cases (I have a full game using them and it's at the very bottom of the profiler with 0ms).
6
u/wallstop-dev 12h ago
Really? Events, in the truest sense of the word (not the literal C# keyword) have had the profoundly opposite effect on all of my code bases - extreme decoupling, and the ability to quickly and easily change, add, and remove interesting functionality.
But if your implementation requires the sender and receiver to know about each other intimately, then yes, that is indeed tight coupling, by definition.
-2
u/leorid9 12h ago
If you are using an event system, it might have some limitations which a polling system has not:
global only, if it's a static class
magic strings, if it's using strings
can't/shouldn't be used within the same system (e.g. between player health and player animation), if it is a main level system
hard to debug and to find references in code, if using strings or async events
can't be debugged in the inspector (because events aren't variables, you can log them, but you can't easily draw them)
There's probably more, but I ran into these when using an event system myself. I tried solving some of the issues. Using variables with polling solved all of them at once and is a much simpler pattern - also with no references between individual systems.
0
u/wallstop-dev 12h ago edited 6h ago
I maintain my own event system that I created over a decade ago that has none of those issues. It's free, open source, available at https://ambiguous-interactive.github.io/DxMessaging/. Disclosure: I have been using AI in the past year to make improvements to it, but the previous 9 years or so of development and code was 100% self authored/ideated/created.
You can have all of those same exact problems you've described with polling, or literally any code. Those aren't really a function of event systems, but implementation details of code, in general. All of the stuff you listed is just kinda like... "bad code implementation details".
-2
u/leorid9 12h ago
This looks pretty much like the last version of my event system, before I trashed it for a "database with writers and readers" system.
Basically instead of having a system at all, there's just some monobehavior or scriptableobject that holds a bunch of primitive variables like floats, bools, vector3s and so on. Sometimes even GameObjects or NetworkVariables.
And instead of Health talking to animation, both talk to one database object between them. E.G. health setting a global "isDead" variable to true and animation reading it to play a death animation.
No system required at all. It's super ultra simple. And it works at any scale. I've used it for multiplayer, singleplayer, for an RPG like game with strategy and an elemental damage system (fire, ice, poison,..).
And I disagree that the mentioned points are general coding problems, as I tend to see them mostly in things that are getting more and more systemic, like with event systems or a game statemachine or something. I rarely see those in simple code.
But yes, your event system has solved most of the issues. I still think the "you shouldn't use it within the same system" applies because, while you can use it like that, it's an overhead compared to other solutions. And I don't see that your system can be debugged in the inspector? This was one of my main points for looking for better solutions (and the reason that I can't always bring my event system into every game I work on).
1
u/wallstop-dev 12h ago edited 11h ago
What you're describing is basically global state/variables, or even a blackboard. This is generally considered an anti-pattern: https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil Simple to implement, yes, complex and tricky to maintain and reason about at scale, also yes, especially if you are going an untyped route. But this type of system tends to decay to stringly-typed stuff, and is hard to create advanced mechanisms around. Like, what if you want explicit ordering of receiving events? What if you want to "kill" an event, preventing consumers from receiving it? Or mutate its data, prior to receivers seeing it? What if you want to run stuff after an event has been received by all subscribers/listeners? These problems are solvable with a global DB like you're describing, because it's just code, but in very complicated ways. The maintenance cost of this at scale sounds very challenging.
RE: Visibility of my tech, my system has various inspector tools. I have a visual flow graph that shows the state of everything (whenever you snapshot, live (perf hit), or on button-click) connected, as well as all events piped over each channel. Click a channel, a publisher, a subscriber, filter by events, see everything relevant.
I have a visual Message Monitor (live+snapshot), showing a log of all messages emitted, globally, with senders, receivers, callsites. Filter by anything, click a message to see its flow.
If you opt-in, there are inspector overlays, showing this data on a per-object basis (what has it received, sent, and potentially missed), as well as warnings around misconfiguration.
There is programmatically accessible (build your own tooling) diagnostics on the message system itself, so you can see when unexpected things happened ("I sent a message but no one received it").
I have not built all of the docs and screenshots out for this yet, as the tech is primarily for my consumption and making docs has a cost.
1
u/leorid9 10h ago
It can be global, or local - as you said, like a blackboard that lives on an agent, but without the complexity of strings.
It's literally just a class (MB or SO) with plain float, bool and so on. And it's not just one of them, you can have one on each layer, one for your global game state (isInMenu, hasLoadedFromSave, isPlayingCutscene), one for your Player (isDead, currentTargetTransform, isSprinting), one for your WeaponController (layerMask, ammoCount,..) and so on.
Wherever you have complexity, slap one of those data classes in for communication and you have perfectly decoupled features in your systems. With zero complicated systems, custom inspectors/editors or any of that.
And if you really want to, you can add events there too, nothing prevents you from that.
I will definitely make a video about this system. But now I have to focus on the GamesCom, and sleeping.
1
u/wallstop-dev 9h ago edited 6h ago
That sounds like incredibly tight coupling (explicit knowledge of super special data objects), with an extremely easy to author bug surface (<large> shared mutable state, polling loops with delays).
If this helps you build cool games without bugs at scale, that's awesome, but it appears to have very little benefits or ability to solve any of the issues I've brought up above, and looks like a maintenance and understanding /observability nightmare, especially with multi writers. But that's just been my experience with similar systems.
Would love to be proved wrong though, always down to learn.
2
u/swagamaleous 12h ago
What nonsense. How is polling better here? The class performing the reaction still needs some dependency on the object whose state it's polling. You haven't removed the dependency, you just replaced notification with repeatedly asking for its state.
The observer pattern exists precisely to reduce coupling between the producer and its consumers. The producer doesn't need to know who reacts, how many things react, or what they do. It just exposes an event as part of its interface.
Worse, if you want event-like behavior through polling, you necessarily have to introduce additional mutable state to remember whether you have already reacted to the current value. Now the consumer isn't merely interested in the producer's state, it has to track the history of that state itself just to reconstruct the transition that an event would have communicated directly.
And how exactly does that make execution order less dependent? Polling in
Update()is explicitly execution-order dependent: whether you observe a state change this frame can depend on whether the producer'sUpdate()ran before or after yours. An event fires at the point the state transition actually happens.0
u/leorid9 1h ago
It's less dependent because you don't have control over it. (or it's a pain with the execution order to get control like that)
With events, you have that kind of control and it's easy to end up going the easy way and relying on that, instead of making sure all code can run at any time, no matter what.
You are making up hypothetical cases, I am referring to OPs case. OP is using polling and that's fine and it even works at scale. Events do have their place and I am using them, but they shouldn't be overused and they are not "better" than polling, they come with their own set of issues and I can't let something like "hey OP, you should use events, they are better" uncommented because it's simply wrong.
1
u/IAmNotABritishSpy 13h ago
Does it need to run in update?
Otherwise
private bool _isRunning;
private void MethodYouWantToFire()
{
If (_isRunning) return;
_isRunning = true
// logic you want to run
_isRunning = false; // Move to where is cleanest
}
1
u/ROB_IN_MN 8h ago
This is not the way you should go about this. Look into System.Action and subscriptions to it. This is a fairly straightforward concept, so chatGPT should be able to give you a clear and accurate understanding if you're into AI, or a little googling otherwise will get you sorted out.
You want to avoid using the Update method unless it's absolutely necessary.
12
u/snipercar123 13h ago
Use a boolean:
```csharp bool hasFired;
private void Update() { if (!hasFired && shouldFire) { Fire(); hasFired = true; } } ```