How often do you use delegates and interfaces in your games?

I’ve been using unity for about 4 months now and I’m loving every second of it. I come from no coding background whatsoever but have picked up C# reasonably well up to now. However, I’ve yet to delve into interfaces and delegates. I have downloaded free templates on the asset store and seen developers write some pretty impressive games without ever using them.

My question is not whether I should bother to learn how to use them as I plan to delve into them this week (Also if anyone has any good resources, I’d appreciate it as I have tried before to understand these properly but struggled to a degree)

My question is how much did understanding these concepts and implementing them make a difference to what you were able to produce?

On a side note. I’ve recently started looking into json since watching a gamergrind video, This is new to me as I would typically put information directly into a script as opposed to using a text docmuent. My question regarding this is, As I progress I’d like to experiment with storing data on servers and wondered whether json was a sensible way to go as opposed to using something like XML(I’ve never used either in a project up to now)

Thanks for any answers

OK… you seem to be talking about 3 different topics at the same time.

Delegates - object identity for functions

Interfaces - contract definitions for the interface of an object (in this case interface is different from Interface… where interface is the public members of a type, and Interface is the contract… as in interfaces like you used it)

Serialization - representing data as text with either json or xml

So…

Delegates - yes, get used to them, they’re VERY useful. Sure you could get around using them, but you’ll probably end up using them indirectly. Especially with things like ‘UnityEvent’, if you use those.

Interfaces - not as important, but I use them a lot. Do you have any specific questions about them?

Json vs XML - you had one specific question… which is better. I find both of them useful, xml especially for its expressiveness. But json has the benefits of being more compact, less verbose, and supporting a more object oriented approach to its structure.

It’s really up to you which to use… I often go json or binary.

For server storage, you may want to consider a database instead. And use json for transferring your data from client to server.

Again… if you have more specific questions, ask away. But keep in mind having 3 distinct topics in one thread can make for some confusing conversation.

4 Likes

Thanks for the response lordofduct.

Yeah I probably shouldn’t have bundled it all into one question. Could you recommend any tutorials that you found particularly helpful? I often find that the code I’ve wrote, while doing the job I want it to do, just doesn’t seem as efficient as it could be and often put it down to the fact that I’m not using delegates or interfaces

I’ve watched a couple of tutorials on interfaces, and have to admit that I have struggled with them in regards to what benefit they would actually bring. If I’m right from what I’ve understood, they basically allow you to organise a class? Judging by what I’ve seen through tutorials, and I am probably completely wrong, I can’t help but feel that I am just essentially duplicating functions that don’t actually do anything?

I think I will probably look into json to begin with then as it seems probably better for a beginner to work with.

Absolutely use Interfaces and delegates; they’re essential for decoupling. They both allow you to interact with classes without being aware of the class itself. It’s not so much a programming efficiency thing (in fact, use of either is slower than a direct call) as well, a user efficiency thing (using them can dramatically decrease refactoring, and other benefits).

I tend to serialize as XML if I want something human-readable for testing, then switch my serialization to binary for final. Binary serialization is far faster, and will produce much smaller files.

Tutorials… no, I don’t use tutorials usually. Especially not for these concepts as I learned them many years.

I have noticed people struggle with interfaces, I myself had a bit of a battle with them myself when I was learning OOP concepts.

So ok… I’ll try to explain them in the way I understood them… which means I’m going to have to talk about something NOT to do with interfaces as you may see them in C#.

WARNING - HUGE POST AHEAD

OK… so OOP (object oriented paradigm) operates on some fundamental concepts:

Encapsulation, Abstraction, Polymorphism

and Inheritance, though the OOP community at large has mostly realized inheritance isn’t exactly perfect, and shouldn’t get the fundamental label so much anymore. Which honestly is where interfaces as you see them in C# sort of spawns from.

OK, so what are these things.

Encapsulation
This is the concept that objects should have 2 distinct parts. An interior and an exterior. These are called its ‘implementation’ and its ‘interface’ respectively.

I like to use the VCR as an example (I don’t know how old you are, but hopefully you remember what the VCR/VHS is). A VCR has a chassity to it with some inputs and outputs. There is the slot in which you slide the VHS tape, buttons for play/pause/rewind/fastforward/stop/eject, and some outputs like video and audio out.

This is its interface. It’s the controlled structure through which you’re allowed to interact with the VCR.

If you crack it open though, you’ll see all the gizmos and parts that make up the VCR. What actually occurs when you insert a tape, what actually occurs when you push play, so on and so forth.

This is its implementation. It’s the guts of the object that you as a user don’t give 2 shits about.

All you need to know is the intent of those buttons and input and outputs. We don’t care that play moves a lever that opens the flap of the tape and draws it around the read head of the VCR and then starts a motor spinning the tape. WE JUST CARE THAT IT MAKES VIDEO COME OUT!

Furthermore, this interface means that Sony/JVC/etc can all make their own VCR and change around the guts to make it cheaper, or higher fidelity, or whatever they decide… yet the interface looks the same to you the end user. Same play button, same slot to stick a VHS tape in, etc.

This is encapsulation.

We see this in classes with the access modifier. You create ‘public’ and ‘private’ members (fields/methods/properties/etc), the public members are those outward facing buttons that can be tinkered with. You could see them like this:

public class VCR
{

    public AVSignal Output { get; };

    public void Load(VHS tape);
    public void Play();
    public void Pause();
    public void ToggleFFW();
    public void ToggleRW();
    public void Stop();
    public void Eject();

    public void Record(AVSignal input);

}

I don’t show the code inside those functions/methods, because we don’t care about them. This is the interface of the class (not interface as you see it in C#, we’ll get to that… you screw a screw with a screwdriver).

Abstraction
So encapsulation has separated implementation from interface of a class.

Abstraction is the idea of separating concrete class from the idea of how you interact with the class… its interface.

So back to the VCR… for years disc like systems attempted to reliquish the VCR from its position as home media player. There was laser disc, and VCD, and so on… and of course the DVD which we all know and love from the late 90’s on until now where even BluRay has a hard time subverting it.

Now… there’s something you may notice about all of these things though. THEY HAVE THE SAME INTERFACE.

They all have a slot in which you load the media, they have play/pause/stop methods. Sure the DVD also comes with a ‘chapter forward’ and ‘chapter backward’ button… but they still maintaint he fastforward and rewind functionality of the VCR.

They have COMMON interfaces. And with purpose… it makes it easy for a consumer buying this new technology to understand how to use it. You get a DVD player, and it works similar to a VCR.

So you might see as a class:

public class DVDPlayer
{

    public AVSignal Output { get; };

    public void Load(DVD disc);
    public void Play();
    public void Pause();
    public void ToggleFFW();
    public void ToggleRW();
    public void Stop();
    public void Eject();

    public void ChapterForward();
    public void ChapterBackward();

}

Now you might notice, we no longer have a Record functionality, because DVD’s can’t be recorded to. But we’ve gained the ChapterFW and Back functions.

Furthermore the Load function accepts a different input.

But other than that the interface is VERY similar.

What if we could describe these things as related?

So now we get into the contention of inheritance… I’m going to show you this example with inheritance/abstract class, but now a days this is considered poor design. But keep in mind OOP has been around a long time and has grown… but yeah, this is why I removed inheritance from the fundamental concepts of OOP.

public class VideoMedia
{

}

public class DVD : VideoMedia
{

}

public class VHS : VideoMedia
{

}

public class VideoMediaPlayer
{

    public AVSignal Output { get; };

    public void Load(VideoMedia media);
    public abstract void Play();
    public abstract void Pause();
    public abstract void ToggleFFW();
    public abstract void ToggleRW();
    public abstract void Stop();
    public abstract void Eject();

}

public class VCR : VideoMediaPlayer
{

    public AVSignal Output { get; };

    public overloads void Load(VHS tape) : base.Load(tape);
    public void Play();
    public void Pause();
    public void ToggleFFW();
    public void ToggleRW();
    public void Stop();
    public void Eject();

    public void Record(AVSignal input);

}

public class DVDPlayer : VideoMediaPlayer
{

    public AVSignal Output { get; };

    public overloads void Load(DVD disc) : base.Load(disc);
    public overrides void Play();
    public overrides void Pause();
    public overrides void ToggleFFW();
    public overrides void ToggleRW();
    public overrides void Stop();
    public overrides void Eject();

    public void ChapterForward();
    public void ChapterBackward();

}

So now, we could be handed a VideoMediaPlayer, told it’s media is already loaded, and call Play/Pause/Stop/etc to our hearts content… never needing to concern ourselves with what sort of videomedia it plays. Which leads to…

Polymorphism

Polymorphism is the concept of how you can treat 2 objects similarly because their interfaces are similar.

It’s the direct result of abstraction.

Inheritance

So the big evil monster that is inheritance.

Inheritance comes with weird issues that may not be recognizable in the examples I posted above. But it all comes down to that ‘implementation’ part.

See ‘VideoMediaPlayer’ can contain implementation in it for whatever purpose. Maybe it sets up the AVSignal stuff, because that’s most the same from media player to media player… but wait, what if you do that, but then a NEW media video output standard comes about (HDMI?), this means we must retrofit it into the existing VideoMediaPlayer class… and what if that standard doesn’t mesh well with outdated mediums like VHS and LaserDisc, which are inherently analogue signals.

Another issue comes up is that what if you define a class that several classes inherit from. But you create one that is both a VideoMediaPlayer but ALSO an AudioMediaPlayer (like a CD-player, or mp3-player).

public class AudioMediaPlayer
{
    AudioSignal Output {get;}

    public void Play();
    public void Pause();
    public void Stop();
    public void Skip();
}

public class CDPlayer : AudioMediaPlayer
{

}

public class MP3Player : AudioMediaPlayer
{

}

public AdvancedDVDPlayer : VideoMediaPlayer, CDPlayer, MP3Player
{
    //so on so forth
}

If I call Play, what am I playing?

If MP3Player already has code implementing Play, and so does CD player… how do I tell the compiler which function I actually want called?

Everything is getting weird now…

This is beginning to border on what is called the ‘diamond problem’ in inheritance:
Multiple inheritance - Wikipedia

Multiple inheritance is proving to create quagmires… so what do we do? Ban multiple inheritance (which C# does), but how do we get to be a DVD/CD/MP3 player?

In steps a NEW concept that was introduced to OOP to replace Inheritance…

Composition
Composition is the idea that instead of inheriting from a type, you instead consume that part and make it accessible through the interface.

public class CDPlayer : AudioMediaPlayer
{

}

public class MP3Player : AudioMediaPlayer
{

}

public class DVDPlayer : VideoMediaPlayer
{

}

public AdvancedDVDPlayer
{
    public CDPlayer CD {get;}
    public MP3Player MP3 {get;}
    public DVDPlayer DVD {get;}
}

BUT, now we have a layer between us and each device. We can’t just hand around the AdvancedDVDPlayer… what if we want to flip functionality based solely on what type we want it to be at that time instead of having to reach inside of it like this (breaking encapsulation/abstraction, fundamental principals of OOP).

An explicitly defined Interface!

Interfaces - we screw a screw with a screwdriver

OK, so up to this point I’ve said the word ‘interface’ multiple times, but kept bringing up that it’s not the interface you see in C#.

That interface is the keyword used for explicitly defining interfaces.

At first you might think its weird they have the same name… but that’s why I say “we screw a screw with a screwdriver”. We often use the same word for different things and you determine which we’re talking about by context.

“we define an interface to abstract the interface of our class”

So now we can say this:

public interface IAudioMediaPlayer
{
    AudioSignal Output {get;}

    void Play();
    void Pause();
    void Stop();
    void Skip();
}

public interface ICDPlayer : IAudioMediaPlayer
{

}

public interface IMP3Player : IAudioMediaPlayer
{

}

public interface IVideoMediaPlayer
{

    AVSignal Output { get; };

    void Load(VideoMedia media);
    abstract void Play();
    void Pause();
    void ToggleFFW();
    void ToggleRW();
    void Stop();
    void Eject();

}

public interface IDVDPlayer : IVideoMediaPlayer
{

}

Note, we don’t define public/private… it’s an interface. Interfaces are implicitly public, it’s the public facing members of an object.

public class CDPlayer : ICDPlayer
{
    public AudioSignal Output {get;}

    public void Play();
    public void Pause();
    public void Stop();
    public void Skip();
}

public class MP3Player : IMP3Player
{
    public AudioSignal Output {get;}

    public void Play();
    public void Pause();
    public void Stop();
    public void Skip();
}

public class DVDPlayer : IDVDPlayer
{

    public AVSignal Output { get; };

    public overloads void Load(DVD disc) : base.Load(disc);
    public overrides void Play();
    public overrides void Pause();
    public overrides void ToggleFFW();
    public overrides void ToggleRW();
    public overrides void Stop();
    public overrides void Eject();

    public void ChapterForward();
    public void ChapterBackward();

}

public AdvancedDVDPlayer : ICDPlayer, IMP3Player, IDVDPlayer
{
    public ICDPlayer CD {get;}
    public IMP3Player MP3 {get;}
    public IDVDPlayer DVD {get;}


    public void ICDPlayer.Play() { CD.Play(); }
    public void IMP3Player.Play() { MP3.Play(); }
    //so on so forth

}

And now our AdvancedDVDPlayer can be passed around and treated as any of its 3 types, without any of the clashing that comes with multiple-inheritance.

You MIGHT ask, dafuq I now would have to write all these forwarding methods as well!?

Yeah… that’s a sucky parts about how interfaces work in C# (and Java as well, where C# stole it from back in the late 90’s). Interfaces at the time of inception were created before composition was fully embraced and was like a “best of both worlds” compromise… programming is an evolving trade… so it’s a little broken when it comes to multiple composition. But for single composition, or for multiple inheritance it’s great.

Think of the collection types like ‘IEnumerblae’ and ‘IList’. You can quickly turn any class into a collection by merely implementing these interfaces.

Newer languages learned from this flaw in the design and come up with solutions.

Such as ‘duck typing’, in languages like python due to how its interpretted and ran, you can do a thing called ‘duck-typing’. The idea is that if “it walks like a duck, quacks like a duck, then it’s a duck”. Basically if an object has a member with some name, then it can be treated like a type that has that member name. There are flaws here again with types that have intersecting method names… but it’s a way of dealing with it.

Another… which I think is BEAUTIFUL and I hope to god that the language gets adopted far more…

Golang

Golang does away with inheritance all together and instead embraces composition completely.

You don’t even really have classes really… instead we remove the class and return back to ‘struct’ (a data structure…). But you get interfaces as well. The interfaces are implemented implicitly. And then you can composite types by just making other structs a member of your defined type:

type shape struct {
    x,y float64
}

type rectangle struct {
    shape shape
    width,height float64
}

rectangle can now be treated like a shape.

It implicitly knows because you’ve included ‘shape’ as a member, that you can directly access it. You don’t have to say ‘myrect.shape.x’ you just say ‘myrect.x’.

BUT if rectangle happened to composite a type that ALSO has an ‘x’ property. You can say ‘myrect.shape.x’ to explitly clarify which x you’re talking about.

Interfaces in this regard are also implied. If you define an interface

type geometry interface {
    area() float64
}

Then you defined a function for rectangle called area

func (r rectangle) are() float64 {
    return r.width * r.height;
}

Now rectangle is implicitly a geometry. And ANY function that takes in a geometry, can accept rectangles polymorphicly.

Unfortunately golang isn’t getting as popular as I wish it would. Primarily because the direction languages are leaning these days are ‘functional’ paradigm by and large. This is the result of software scale shrinking for the most part. We’re moving towards a lot of small applications in the web that can be easily maintained in procedural and/or functional paradigms.

And larger software in the open-source community is embracing the ‘linux/gnu’ philosophy of many small programs that are good at doing one thing. Which honestly I think of as moving object oriented concept out of the language, and into the OS… which I find… maybe not the best idea. I mean think about it, these many programs are encapsulated in themselves, and often API standards are designed to make them interchangeable if you prefer another company/groups implementation. Thusly making them abstracted (the standard api) and polymorhpic (interchangeable).

But as a result, OOP is dying out, except for in the large enterprise field where Java/C# dominate. And when efficiency is wanted (like games) C++ is king because it’s baremetal, and no one wants to convert to golang there because it’d mean rewriting HUGE libraries with decades of history to them.

Anyways… I got on a bit of a tangent with golang there.

But yeah… THAT’S the purpose of ‘interface’.

And yes, it’s VERY powerful.

8 Likes

Got it, so BinaryFormatter is a lie :stuck_out_tongue:

On the topic of interfaces, they help you simplfy working with a set of objects with just very generic actions. see if this help you understand

For example think about all the unique types of vehicles out there. Cars, Trucks, Semi’s, Construction vehicles, tow trucks, powered lawnmowers, that barbie jeep toy. A huge majority of these vehicles have a common “interface” called a “steering wheel” if you know how to use a steering wheel in one vehicle then you likely know how to operate a steering wheel in another vehicle, even if you’ve never seen that vehicle before. The standard interface between the vehicle and the driver on how to steer makes adapting to a new vehicle second nature (at least in regards to steering).

you may not know exactly how a car turns the wheels under the hood. but that doesn’t matter to you cause you just mess with the steering wheel interface. it makes your job far easier when you have to work with different vehicles. if you know how to steer a Semi, you can steer a barbie jeep.

interfaces don’t make much sense in the sort term when only one object has the interface. but when you get a dozen or a hundred different objects all using the same interface, then that interface just makes the job a ton easier for the caller.

Thanks for the responses guys.

I’ve got a much better understanding of them now. Also thanks for taking the time to write such a in depth response lordofduct. I’ve read over it a couple of times to take it all in and it’s made a huge difference.

In response to Dameon, The benefits you mentioned are the main reasoning for me looking into delegates aswell. I often find I am having to change my methods multiple times when I decide add something new to my game.

I’ve just finished watching a video by Programming with mosh which has helped my understanding of them. Im going to start a basic project today and implement them this week. I tend to learn a lot more by doing, things seem clearer when I put it into practice.

Think of interfaces as ways to add different behaviors to your objects, which has especially clear benefits when you’re writing a game.

For example, on a turn-based strategy game, I have an IRandomEvent interface that describes a variety of random things that can happen in my game each turn – what the event is called, descriptive text, and different effects on other parts of the game. Some of those random things create events that persist for several turns, and when I write those classes I also add an IPersistentEvent interface. Others create enemies that must attack and defend and move, so on those classes I add the IEnemyOperations interface. Still others have instantaneous one-time effects and don’t need any additional interfaces.

This way, the manager that creates new random events and reports them to the player only needs to know about the IRandomEvent interface. Later on at the end of the turn, a different piece of code only needs to know about the IPersistentEvent interface. In the middle when combat is happening, that code only needs to know how to use the IEnemyOperations interface.

Examples of other common uses are to produce different behaviors or effects for weapons, spells, projectiles, and so on. I’ve used interfaces to produce a group of state-machine classes that advance through and report their states with a common interface but do completely different things internally.

In general, “behaviors” is a really good way to think about what interfaces bring to the table.

Cheers MV10,

In your example above, Am I right in thinking that I could set an event publisher in your random event manager and then use an interface which implements an event subscriber to which all classes deriving from said interface would use?

Classes don’t derive from an interface. A class will implement an interface. This is an important distinction, because derivation means that a type gets the implementation of its base class. Interfaces have no implementation.

In other words, an interface is a set of method signatures (and possibly event/property accessors) that your new class promises to accept and have its own implementation for.

On the other hand, deriving from a base class is a way to give your new class all of the implementation of that base class.

These are different concepts.

1 Like

Sorry I did understand this and have completely mixed my terminology up. Thanks for the clarification

There’s one other important thing worth mentioning (probably lots but one I’m thinking of). Since interfaces are contracts and not implementations, there’s no problem with implementing multiple interfaces. You can use multiple more broad interfaces to add functionality to your system, and interfaces can even implement other interfaces.

For example:

public interface IWeapon
{
    float Damage {get; set;}
    float Health {get; set;}
    int Weight {get; set;}
}

public interface IHammer : IWeapon
{
    float SwingSpeed {get; set;}
    void Swing();
}

public interface IProjectileShooter : IWeapon
{
    int Capacity {get; set;}
    int Range {get; set;}
}

So the example above defines a weapon contract which all weapons will have. Different weapon types can have additional properties. Any class that implements IHammer will automatically implement IWeapon as well so SwingSpeed, Damage, Health and Weight will all need to be implmented (interfaces can define methods as well, not just properties).

That’s fine and dandy because let’s say I have an inventory class with a collection of Weapons:

public class Inventory
{
    public List<IWeapon> Weapons {get; set;}
}

Since the collection is IWeapon, I could put an IHammer in it, or an IProjectileShooter in it since both of those are at their root IWeapons. In code I could check the types even:

    var weapon = inventory.Weapons[0];
   
    if(weapon is IHammer)
          (weapon as IHammer).Swing();

There are better and more efficient ways to work with your types, but this demonstrates polymorphism a bit. Additionally, you can use intefaces to augment classes with additional funcionality. For example, let’s say you have a common type of effect, like taking damage. You have lots of different types of things in your game… you have Players, you have Buildings, you have Props and you have environment objects. All of those objects may (or may not) take damage. You wouldn’t want them to inherit from the same base class because they’re all very different. You can, however, apply an interface.

public interface IDamageable
{
    void ApplyDamage(int amount);
}

public class ScaryTree : Tree, IDamageable
{
      private int health = 100;

      public void ApplyDamage(int amount) {
              health = Math.Max(health - Math.Abs(amount), 0);

              if(health == 0)
                  //Player is dead
      }
}

In the example above, ScaryTree is a special kind of Tree… it inherits Tree (it could just as easily implement an ITree) that is part of what defines it’s base functionality and features. But it also implements IDamageable so it can take damage.

This also demonstrates that you can use a mix of inheritance and implementation which is often desired. Some objects will have common functionality and you don’t want to code that in every class, so it can exist in the base class and you can use interfaces to define contracts for augmented features.

And lastly, multiple interface implementations don’t fall prey to the Diamond problem because they don’t define any of the method or properties, just the contract for them. So if you have three interfaces with ApplyDamage(int amount) it doesn’t care which one (in fact the class will resolve to any of the three).

However, one things to beware and careful of:

public interface IWidget
{
     Guid Id {get; set;}
}

public interface IItem
{
     int Id {get; set;}
}

In the above if you try to implement IWidget and IItem you’ll have a collision because IWidget and IItem have the same property with different return types, so something to be careful of.

3 Likes

I suppose you could (subject to kru’s clarification of implement vs derive) but – probably my use of the word “event” was unfortunate, as they don’t occur arbitrarily like, say, a mouse-click event. Replace the word “event” with “occurrence” in my earlier post, maybe.

Delegates are great for “real” event pub/sub though, and I use them heavily, too. Only one class in my game subscribes to Unity’s magic “void Update” (and if I need them, FixedUpdate, LateUpdate, etc – everything except Awake and OnDestroy). That class has a UnityUpdate delegate list to which other classes add/remove themselves. I also expose a bunch of other delegates – for example, pointer movement and button click (because my game supports both mouse and Xbox controller interactions – transparent to all other classes except this event manager). You do have to be very careful to ensure all subscribers remove themselves cleanly or you’re likely to create hard-to-find memory leaks.

I think you’re on the right track asking about these language features.

1 Like

@MV10

Interested in how this looks like with delegates (I have a 6502 assembler background and am slowly using more and more of the c# / oop features)

I also use a GameManager, the only class that subscribes to start and update. It then runs the start (i call it init) and the update (I call it run) methods of all other manager classes. I just call their methods directly.

I happened to post the code a few days ago:

https://forum.unity3d.com/threads/move-mouse-cursor-with-xbox-360-controller-ingame.156171/#post-2878539

Is that more efficient than left Unity call it aside from giving you more granular control?

My turn-based game doesn’t gain any (important) performance benefits but in a game where framerate counts, yes, you eliminate crossing the managed/unmanaged boundary. I think the idea gained widespread attention recently with that “10,000 updates” blog post from Unity late last year, but honestly I was already doing it for convenience reasons (and on one project where I needed to control the order of Update calls), so I only sort of skimmed the article.

https://blogs.unity3d.com/2015/12/23/1k-update-calls/

I’m just going to leave this here…

using System;
using System.Collections.Generic;
using UnityEngine;

namespace MonoThreading
{
    public class MonoThread : MonoBehaviour
    {
        public static MonoThread Instance { get; private set; }

        // Previously used for subscribers, but delegate-based subscription allocates, so an Interface-based solution was used.
        //        public event Action<float> ThreadHandler = (float deltaTime) => { };
        private Queue<IThreadingObject> _subscribersToAdd = new Queue<IThreadingObject>();
        private Queue<IThreadingObject> _subscribersToRemove = new Queue<IThreadingObject>();

        /// <summary>
        /// Subscribers are called every Update, and passed a single float, which is Time.deltaTime.
        /// </summary>
        private List<IThreadingObject> _subscribers;
        private int _subscriberCount;

        private Queue<Action> _queuedCallbacks = new Queue<Action>();
        /// <summary>
        /// Subscribers are called only once, at the next Update.
        /// </summary>
        public Queue<Action> QueuedCallbacks { get { return queuedCallbacks; } }

        private void Awake()
        {
            Instance = this;
            _subscribers = new List<IThreadingObject>(250);
        }

        private void Update()
        {
            while (QueuedCallbacks.Count > 0)
            {
                var action = QueuedCallbacks.Dequeue();
                if (action != null)
                    action.Invoke();
            }
            UnityEngine.Profiling.Profiler.BeginSample("Thread subscribers");
            float deltaTime = Time.deltaTime;
            var enumerator = _subscribers.GetEnumerator();
            while (enumerator.MoveNext() != false)
            {
                enumerator.Current.OnUpdate(deltaTime);
            }
            while (_subscribersToRemove.Count > 0)
            {
                _subscribers.Remove(_subscribersToRemove.Dequeue());
            }
            while (_subscribersToAdd.Count > 0)
            {
                _subscribers.Add(_subscribersToAdd.Dequeue());
            }
            UnityEngine.Profiling.Profiler.EndSample();
        }

        public void Subscribe(IThreadingObject subscriber)
        {
            _subscribersToAdd.Enqueue(subscriber);
        }

        public void Unsubscribe(IThreadingObject subscriber)
        {
            _subscribersToRemove.Enqueue(subscriber);
        }

        private void OnDestroy()
        {
            queuedCallbacks = null;
            Instance = null;
            _subscribersToAdd = null;
            _subscribersToRemove = null;
        }
    }
}
2 Likes

I need to go back and work in my pubsub system. I threw together one that’s event based and pretty cool but was allocatey as it enumerated a Dictionary. But it was super handy because it let me fire a message to all subscribers and those that were using the subscription component could decide whether to handle via fixed or late update and choose how many messages to keep in the queue on a per subscriber basis. So if they received 20 messages between execute cycles and only wanted 10 it would throw the first 10 out. Or you could choose to fire the handlers immediately instead of queuing or just subscribe and handle the messages without a component at all.

I also only do it out of habit, I saw that performance related blog as well, but that was not my motivation. I just find it strange not to be in full control of execution order, guess related to my assembler 6502 background. Lot’s of things feel strange in Unity :wink: In a good way, it’s my only chance to produce something nowadays, things got way more complicated + c# is fairly easy to grasp (at least the basic stuff)

Ps

@MV10 - thx a lot