Search Unity

Events or References?

Discussion in 'Game Design' started by angel1058, Mar 27, 2023.

  1. angel1058

    angel1058

    Joined:
    Apr 15, 2021
    Posts:
    10
    Hi

    New to game dev but long term developer. Design question. Learning unity with a simple TD game. My game has a finite number of bullets the towers can use. Each time a tower fires, I reduce bullet count by one. Currently I have an event on each tower which my game manager ( who holds the bullet count ) subscribes to. As a tower fires, the event is triggered, the bullet count decreases. This works. A different approach was to have each tower hold a reference to the game manager and call a ‘decreaseBulletCount’ method directly. No events. Also works. I went with events as other ‘stuff’ could be interested in the fire event and it decouples my tower from my game manager. Not after help per se, but just views on the approach, performance, scaling to many towers, best practice?

    cheers

    malcolm.
     
  2. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    i dunno what performance implications are, but I'd favor events because like you mentioned, it maintains decoupling which will make it easier to refactor system as things change, and also makes it easier to extend as new classes can just subscribe to event.

    I dunno if there is a specific pattern but I tend to think of the different gameobjects in the world as pretty much stupid objects that just broadcast a fire-and-forget event when something important changes. It is up to some manager to interpret those broadcast and coordinate any sort of reactions.

    This setup makes less confusing code to me because don't need to manage class-to-class communications very much at all. Sort of a military style setup. General only talks to a few colonels, colonels only talk to a few captains, and so on. Rather than just having every private out there making important decisions and trying to maintain communications with 1,000 other entities.

    having one class call function on another is the worst kind of code IMO. Becomes hard to debug fast and everytime you open the project will take you a long time to refigure out how things work. Whereas if you just have a few managers with clear responsibility you wont waste time trying to figure out who talks to who and in what order.
     
    Last edited: Mar 27, 2023
    angel1058 likes this.
  3. angel1058

    angel1058

    Joined:
    Apr 15, 2021
    Posts:
    10
    Events it is then. It seems natural and until I hit any performance issue, I am not going to actually go looking for any :). I’m only still messing around with stuff. Thanks for the response - really appreciated.
     
  4. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I'd go events. This isn't something that's happening so often that it needs this level of optimisation, and it maintains flexibility.

    If you have so many towers and bullets that you need to generally optimise them (e.g. seitch away from OO to flat arrays or such) then that's when I'd look at optimising out the message passing. Otherwise, even if this stuff is "slow" chances are that other parts of your game will have a much bigger impact.

    (P.S. this is a scripting question.)
     
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,856
    See, I would have initially designed this the other way around, though I would've made this 'manager' it's own plain C# static class, something like 'BulletPoolManager', with its own small API that anything could reach out to and invoke ts methods (so need for a direct reference).

    Though I can definitely see the merit of events here, as a 'turret has fired' event is definitely something multiple other things could hook into for whatever reason.

    Many ways to achieve the same thing, as always.
     
  6. angel1058

    angel1058

    Joined:
    Apr 15, 2021
    Posts:
    10
    My 8 posts against your 15k+ :). I will get better at this, and as I trawl the forums it should become clearer where the best place to post is. And thank you for your response. I've always been of a mind to get it working first, then optimise if you have to, but I also figured there were proven / standard / right? ways of doing things. No doubt I will be back over the next few weeks! Thanks Again.
     
  7. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    You're welcome. Don't worry too much about the forum section thing, it's just that you're more likely to get responses to scripting stuff in the place where people talk about it all the time. ;)

    Some people will tell you that, but I'd be wary of anything those people say.

    There are plenty of poor approaches to things, which you can hopefully recognise and rule out with a fundamental understanding of the systems you're working with. Usually that'll still leave you with multiple potentially good ways to move forward, each with their own pros and cons. Different solutions will be more or less effective depending on how their pros and cons match up against the specific scenario at hand.

    And that's why I don't listen to people who tell me there's a "right" way - I've never yet found a single approach which is provably best in all scenarios. ;)

    In your case here, events have overheads but also provide flexibility. A reference based approach (regardless of how it's set) reduces the overheads, but loses some flexibility. As the events are unlikely to be in your "hot path" (i.e. the parts of your code which execute many times per frame) their overhead is likely to have no practical impact, where the flexibility they give you will probably make life easier which, in turn, lets you put a bit more effort into things which will make a difference. So that's where I'd lean first.

    After that first pass, don't guess - measure! Occasionally use the Profiler to see if there are any unexpected performance hot spots, and investigate when they show up.
     
    Last edited: Mar 30, 2023
    Ryiah, CodeRonnie and spiney199 like this.
  8. Mallaboro

    Mallaboro

    Joined:
    Sep 22, 2017
    Posts:
    19
    I'd just have
    public static int bulletCount { get; set; } = 0;


    Then you can increase it from wherever you fire from, and decrease it when you destroy them, where ever that may be.

    Seems sensible to me cause
     
  9. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    the thing that I would watch out for with that approach is that if you get a bug related to the bullet count, now you have quite a bit of searching around to do to find all of the places something related to it happens.

    and anybody new coming into your project (or yourself three months from now) might ask, "when does bullet count decrement? there is a bug, it is not decrementing". And you might say, the magazine decrements the bullet count. Because magazine is in charge of that sort of thing.
    Okay, reasonable.

    But then the other programmer says, "well that is pretty good but really it is the gun that decrements the bullet. It should handle that."

    so you change that because that programmer is a pain in the butt if they don't get their way and you want to avoid human issues. But now that the code was relying on a hard reference, you have a ton of things to go and change. Now you spend an hour doing that and you totally lost your train of thought for the actual task you had planned for the day.

    Comapred to an event where you only have two points of contact to worry about - where it is fired and where it is receieved. The senders and receivers can be changed willy nilly, it won't break anything. The annoying guy who wants to change where event is fired from can do it all he wants and your code that responds to the event doesn't care.


    Anyway, just repeating myself but it's good to see some real life example of problems instead of just read peoples conclusions. Maybe somebody knows some other ways to get around this sort of issue.
     
  10. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    This is what's called "global scope", and there's plenty of well documented reasons to avoid it by default. Broadly speaking, taking that approach quickly turns a code base to spaghetti, while also increasing the potential amount of code which can cause bugs, and must be looked through when solving any given bug.
     
    Ryiah likes this.
  11. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    618
    I'm a huge fan of the publish-subscribe pattern. Any publisher can send defined messages to a channel on a messaging hub and any subscriber can subscribe to any channel. The publishers are totally unaware of who might be subscribed and contains no reference to subscribers.