Search Unity

Question how to use variables as events

Discussion in 'Scripting' started by MilitaryG, May 9, 2023.

  1. MilitaryG

    MilitaryG

    Joined:
    Apr 26, 2021
    Posts:
    28
    can this code be simplified to use variables as events and not functios as in my case?

    what I'm doing atm is if mob is in range agressive music starts and once all mobs go away from me passive music starts

    all works well only question is can this be simplified.
    Code (CSharp):
    1.  
    2. public event Action onAnotherMOB;
    3.     public Status status = Status.passive;
    4.  
    5.     public void MOBInArea(){
    6.         if(onAnotherMOB == null){
    7.             MusicType(Status.aggressive);
    8.         }
    9.         onAnotherMOB += ActionMusic;
    10.     }
    11.     public void MOBLeftArea(){
    12.         onAnotherMOB -= ActionMusic;
    13.         if(onAnotherMOB == null){
    14.             MusicType(Status.passive);
    15.         }
    16.     }
    17.     public void ActionMusic(){}
    18.  
    thanks in advance
     
  2. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    I don't really get your question about variable as events. You code is already pretty compact, I feel like it could be "simplified" using Getter-Setter and ternary operators. Note that here, simplified is only a matter of personal opinion, it's more readable and use only one path for the logic, but it is not really lighter.

    Code (CSharp):
    1. public event Action onAnotherMOB;
    2. public Status status = Status.passive;
    3.  
    4. int mobCount = 0;
    5.  
    6. public int MobCount {
    7.       get {
    8.         return mobCount;
    9.       }
    10.       set {
    11.         int previousValue = MobCount;
    12.  
    13.         // This line apply changes to mobCount and makes sure it can't go negative
    14.         mobCount = value <= 0 ? 0 : value;
    15.  
    16.         // This line apply your music status
    17.         MusicType(mobCount == 0 ? Status.passive : Status.aggressive);
    18.  
    19.  
    20.          if(previousValue < mobCount)
    21.              onAnotherMOB += ActionMusic;
    22.  
    23.          if(previousValue > mobCount)
    24.              onAnotherMOB -= ActionMusic;
    25.       }
    26.     }
    27.  
    You then increase or decrease the property depending if your mob enter or leave the zone

    Code (CSharp):
    1. MobCount++;
    2.  
    3. or
    4.  
    5. MobCount--;
    6.  
     
    Last edited: May 9, 2023
    MilitaryG likes this.
  3. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,006
    Well, I think you haven't really understood his abuse of the event as some sort of semaphore / unit-counter, which btw is a horrible usage of events ^^. It seems the event is only used to act as a "counter". So whenever the mob count goes above 0 the music type should be set to "aggressive" ONCE and as soon as all mobs have left the area the music type should be set back to "passive". It seems he's abusing the delegate / event just as a counter.

    Your code has several differences. He only calls MusicType whenever the count goes above 0 or when it drops to 0. You call it every time. So the behaviour is already different. You also overcomplicate things with your temporary "previousValue". This would have the same effect as the original code:

    Code (CSharp):
    1.         int mobCount = 0;
    2.         public int MobCount
    3.         {
    4.             get => mobCount;
    5.             set
    6.             {
    7.                 value = value <= 0 ? 0 : value;
    8.                 if (value == 0 && mobCount > 0)
    9.                     MusicType(Status.passive);
    10.                 else if (value > 0 && mobCount == 0)
    11.                     MusicType(Status.aggressive);
    12.                 mobCount = value;
    13.             }
    14.         }
    15.  
    So we only call MusicType whenever the count either goes from 0 to x+ or from x to 0. However any change above would not call MusicType. Maybe it doesn't matter when we call it each time. However since it may actually start playing music, calling it again for each mob added is probably not a good idea.

    Just to make this clear: Whenever you want to "add" a mob, you just have to do
    MobCount++;
    and when you want to remove a mob you have to do
    MobCount--;


    If you need the two add / remove methods (maybe because they are actually called by events) you can actually simplify the code to:

    Code (CSharp):
    1.         int mobCount = 0;
    2.         public void MOBInArea()
    3.         {
    4.             if (mobCount++ == 0)
    5.                 MusicType(Status.aggressive);
    6.         }
    7.         public void MOBLeftArea()
    8.         {
    9.             if (--mobCount == 0)
    10.                 MusicType(Status.passive);
    11.         }
    12.  
    Here we take advantage of the pre- / post- increment / decrement behaviour. The post increment operator will read the OLD value and then increase the value. So we only set the music type to aggressive when the mobCount is 0 (and is changed to 1). So any further call will just increment the mobCount but will not call MusicType again.

    Likewise when a mob leaves the area we use the pre-decrement operator. It will first decrement the value and then read the value for the check. So here we only set the music type to passive when be go from 1 to 0.

    Yes, I don't have a check if the mob count goes below 0. However this should never happen. If the add / remove calls are not matched, the system is screwed anyways since you have lost track. I would not recommend to clamp the value arbitrarily. Adding an assert in MOBLeftArea at the end that should check that mobCount is 0 or greater would be the best approach. So you actually get an exception if that happens.
     
    orionsyndrome likes this.
  4. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    @Bunny83 hehe, yeah I assumed he need that event tracking for non-shared snippet logic :p
     
  5. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    Honestly I'd do this slightly differently, by making a singleton MobTracker whose job is to know how many mobs there are.
    Code (csharp):
    1. public class MobTracker {
    2.  
    3.   static MobTracker _instance;
    4.   static public MobTracker Instance => _instance??= new MobTracker();
    5.  
    6.   public event EventHandler OnMobsGone;
    7.   public event EventHandler OnMobsNearby;
    8.  
    9.   int _mobs = 0;
    10.  
    11.   public void AddMob() {
    12.     if(_mobs++ == 0) OnMobsNearby?.Invoke(...); // EventsArgs.empty perhaps, can't remember
    13.   }
    14.  
    15.   public void RemoveMob() {
    16.     _mobs--;
    17.     Debug.Assert(_mobs >= 0, "whoopsie");
    18.     if(_mobs == 0) OnMobsGone?.Invoke(...);
    19.   }
    20.  
    21. }
    Of course this is very extensible to support any other logic, maybe even a true registration system, and making sure that a mob never gets registered twice, etc.
     
    Last edited: May 9, 2023
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,006
    But you will also call OnMobsNearby for every new mob, not just for the first one. I think you need the condition in your "AddMob" method. Though of course your approach is much more versatile so it can be used for other purposes as well. Though of course it requires extra setup as well to register the callbacks.
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    You're right, that's a mistake. Will amend this.
     
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    Yes but I can only see benefits down the dev path. You keep things in the same place, and you're likely to add more behaviors worth of tracking. And all interested parties can now join in and listen to various processed events.

    Code (csharp):
    1. public class MusicPlayer {
    2.  
    3.   static MusicPlayer _instance;
    4.   static public MusicPlayer Instance => _instance??= new MusicPlayer();
    5.  
    6.   // I prefer custom delegates btw
    7.   public delegate void MusicHandler(MusicStatus status);
    8.   public event MusicHandler OnMusicChange;
    9.  
    10.   MusicStatus _status = MusicStatus.Passive;
    11.  
    12.   private MusicPlayer() {
    13.     MobTracker.Instance.OnMobsNearby += (...) => PlayAggroMusic();
    14.     MobTracker.Instance.OnMobsGone += (...) => StopAggroMusic();
    15.   }
    16.  
    17.   public void PlayAggroMusic() {
    18.     if(_status != MusicStatus.Aggressive) {
    19.       _status = MusicStatus.Aggressive;
    20.       OnMusicChange?.Invoke(_status);
    21.     }
    22.   }
    23.  
    24.   ...
    25.  
    26. }