Delegates are a programming language feature that allows to invert the dependencies in the classes of your program. Delegates are also referred as Events or Callbacks. The idea behind them is exactly the same as a subscription magazine. Anyone can subscribe to the service and they will receive the update at the right time automatically. This is a super powerful way of designing the architecture of your game to achieve the next level in code quality.
If you enjoyed my previous article about how to use Lerp, this is the second of many simple tutorials to learn how to use the full potential of Unity following what I found out are the best practises. Follow this easy examples you will have projects that are not a nightmare to maintain after a couple of weeks of development. :)
If you started to program in Unity as your first serious programming job, it may be even possible that you don't know what delegates, callbacks or observers are. Don't be scared, we will talk about everything in detail. Anyway I recommend you to take a look at the official tutorials here and here if you haven't done it already and come back here to answer the rest of your questions. :D
One call at the right time is better that 60 calls a second until they pick up. |
Why do I need delegates? Imagine a complex game with a PlayerController script and a bunch UI elements with the following conditions:
- Your player controller script cannot be responsible of updating every of the UI elements because that will prevent artists from changing the UI
- The UI elements cannot afford to ask for Updates every frame because that will ruin your efficiency.
In a nutshell, delegates are like a list of functions. You can call your delegate like a normal function and whenever this happens the delegate it will automatically call his functions for you. Like any dynamic list of elements you can add and remove new methods and this is called subscription.
How does a delegate looks like?
// This is a delegate, allows other people to subscribe // And be notified automatically // Allows to return values and input paramenters public delegate void OnSomethingHappened(); // This is an event, allows to protect delegates // from external modification making them safer public event OnSomethingHappened onSomethingHappened;
How does a delegate works?
// 1. Some method is subscribed to the delegate void OnEnable() { // We subscribe when we want onSomethingHappened += CallMeLater; } // 2. The delegate is eventually called void TimeToCall() { // Make sure we there is anyone subscribed if (onSomethingHappened != null) onSomethingHappened(); } // 3. This method will be called // It matches "OnSomethingHappened()" with // the exact same return value and input parameters public void CallMeLater(){ DoTheStuff(); } // 4. We unsubscibe when we don't want more calls void OnDisable () { // We remember to unsubscribe to avoid memory leaks! onSomethingHappened -= CallMeLater; }
This is all pretty theoretic stuff and may look to confusing if you don't know what is going on or what are we trying to achieve. I hope it will be easier to grasp with this example:
Coins, coins COINS! Strangely mesmerizing :)
Creadits for the art: Kenney.In this example there is CoinSpawner that spawn 7 Coins at the beginning of the game. The goal of this spawner is to have always 7 coins in the game. The problem is that this coins have a random life spawn and we don't want to constantly check if the coin I spawned is alive or dead. That's why at the moment of creation we subscribe for the event of death of the coin.
// Spawns so there is always the same amount of prefabs public class CoinSpawner : MonoBehaviour { // The prefab we will spawn public CoinController prefab; // The number of prefabs we will have at all time public int prefabsCount = 1; // Delegates to notify the UI public delegate void OnSpawn(); public event OnSpawn onSpawn; // Create the first coins so there is always // the same amount on the screen void Start () { for (int i = 0; i < prefabsCount; i++){ SpawnPrefab(); } } // Helper function to spawn prefabs and subscribe public void SpawnPrefab (){ // Spawn the coin CoinController newCoin; newCoin = (CoinController)Instantiate(prefab, transform.position, transform.rotation); // Subscribe so the other class handles notification automatically newCoin.onDied += OnCoinDied; // Notify if any UI is listening if (onSpawn != null) onSpawn(); } // Function that will be called automatically when the coin dies public void OnCoinDied (CoinController coinController){ // We unsubscribe to avoid memory leaks coinController.onDied -= OnCoinDied; // Spawn a new coin SpawnPrefab(); } }
When the Coin is created it will automatically assign itself a random lifespan and when the time comes it will notify to anyone listening. It's important to note that in this approach the coin doesn't know anything about the CoinSpawner, this is a very good result of the use of delegates. This kind of code with few dependencies is called modular and greatly increases the chances that you can reuse this exact code behaviour in a different area of your game or even in another game.
// Destroy self after a random time and notify public class CoinController : MonoBehaviour { // Life variables public float minLifeTime = 1.0f; public float maxLifeTime = 2.0f; // Physics variables public Vector2 minForce; public Vector2 maxForce; // Delegates to notify public delegate void OnDied(CoinController coinController); public event OnDied onDied; // Called automatically thanks to MonoBehaviour void Start () { // Automatic destroy after random time Invoke ("Died", Random.Range(minLifeTime, maxLifeTime)); Rigidbody2D myRigidbody = GetComponent< rigidbody2d>(); // Apply random force if possible if (myRigidbody != null){ // Random force at the position of the coin Vector2 randomForce; randomForce = new Vector2(Random.Range(minForce.x, maxForce.x), Random.Range(minForce.y, maxForce.y)); myRigidbody.AddForceAtPosition(randomForce, transform.position); } } // Called automatically when time is up thanks to Invoke void Died () { // Notify if anyone is listening if (onDied != null) onDied(this); // Destroy self Destroy(gameObject); } }
Additionally UI elements should be always using delegates whenever possible. Updating UI values every frame can be acceptable for your small game but do you think any of the great game you know was ever programmed in such a way? It's always good to learn from the best examples :)
// Label that shows the amount of coins spawned public class LabelCountController : MonoBehaviour { // The coin spawner that we will be counting public CoinSpawner coinSpawner; // Label that we are controlling public Text label; void Awake () { // If not assigned we find a random one if (coinSpawner == null) coinSpawner = GameObject.FindObjectOfType< coinspawner>(); // If we have an spawner we subscribe if (coinSpawner != null) // Subscribe for spawn events coinSpawner.onSpawn += IncreaseCount; // Get the label reference if (label == null) label = GetComponent< text>(); } void OnDestroy () { // We unsubscribe to avoid memory leaks coinSpawner.onSpawn -= IncreaseCount; } // Called automatically when a new coin is spawned public void IncreaseCount() { // Turn the string into a int, add and reconvert to string label.text = (int.Parse(label.text) + 1).ToString(); } }
This is the end of the tutorial. As you can see, using delegates and events can have a high learning curve, but once you will be used them you will see them everywhere. There are many creative ways of using them as well, like subscribing many times the same method to a delegate to create a sort of loop or using them to notify about animation or sound events. Additionally delegates can be static as well but I don't recommend it since it can easily lead to memory leaks.
Whenever you are using delegates remember!
- Use events together with delegates for safety.
- Always unsubscribe to avoid memory leaks and null references
- Always check before calling if anyone is subscribed to the event.
- Thanks to Vectrex for this tip: instead of the null check (and the potential bugs from forgetting it) you can initialize it like this: "event SimpleDelegate OnTestEvent = delegate {};"
- Last point is not recommended by SubZeroGaming as it will cause problems when you try to serialize/deserealize it.
King Aerys II was not good at delegating and you see what happened... |
Here is the link to the complete example posted on github.
Here are the links to the official documentation: delegates, events and Invoke.
Great tutorial on delegates, well explained and easy to follow.
ReplyDeleteThanks a lot! ^^
DeleteGreat tutorial! Thanks.
ReplyDeleteThank you! :D
DeleteThats some handy information! It was a little confusing to process until I actually implemented it, then it was like dude... why dont i do this *everywhere*?
ReplyDeleteYeah! I remember the exact same feeling! ^^
DeleteHowever don´t fall into the trap of delegates who call second delegates who call first delegates again... it gets evil... :D
Cheers! :)
Thanks for providing the code/examples from this tutorial on GitHub. I always appreciate being able to comb over someone else's code and (hopefully) snatch the treasures of learning from others. :)
ReplyDeleteThanks to you!
DeleteMore tutorials with code repositories are on the way! :)
Hi, Thanks for sharing. Let me add something, please correct me if I'm wrong or anything as I'm just trying to be constructive here :)
ReplyDeleteUsing delegates this way seems like a good way of "communication" for components within the same game object or for really small scale games/app. Or when there is an obvious parent/children relationship between 2 objects, but the need for callee to have a reference of the caller is not ideal (or am I missing something?). If 50 different objects of different type are supposed to generate events that affect the UI you woudn't want 50 explicits references to these objects in your UI scripts.
I personally use the Observer pattern or a more global event system when required.
Anyway, please share your thoughts. Really like your blog!
Hi, it is so great tutorial, thanks so much
ReplyDeleteI became a delegate to the UN after reading this post..
ReplyDelete:) :)
Great post bro! Thanks!
excellent tutorial, easy to read & understand. Thx man!
ReplyDeleteI have read this post. Collection of post is a nice one ios swift online training
ReplyDeleteIf "delegates" were simply called "callback lists", all would be clear from the get-go.
ReplyDeletelabelText.text = (int.Parse(labelText.text) + 1).ToString();
ReplyDeletecannot work. I have compare many times
Great Share! Thank you so much for sharing this wonderful information. Learn Database Online Courses at FolksIT.
ReplyDeleteBest tution classes in Gurgaon
ReplyDeleteclass 9 tuition classes in gurgaon
class 10 tuition classes in gurgaon
class 11 tuition classes in gurgaon
what is microsoft azure
azure free trial account
azure adf
azure data factory interview questions
azure certification path
azure traffic manager
Good content. You write beautiful things.
ReplyDeletesportsbet
korsan taksi
taksi
vbet
hacklink
mrbahis
vbet
mrbahis
hacklink
slot siteleri
ReplyDeletekralbet
betpark
tipobet
betmatik
kibris bahis siteleri
poker siteleri
bonus veren siteler
mobil ödeme bahis
KPEH85
mecidiyeköy
ReplyDeletesakarya
istanbul
kayseri
ordu
QZ1X0Y
sakarya
ReplyDeleteyalova
elazığ
van
kilis
5WNUQ7
van
ReplyDeletedüzce
mardin
elazığ
sakarya
5Lİ
elazığ
ReplyDeletegümüşhane
kilis
siirt
sakarya
X5V
whatsapp görüntülü show
ReplyDeleteücretli.show
RKMZ
0D771
ReplyDeleteorder sarms
buy boldenone
Bartın Evden Eve Nakliyat
Tunceli Evden Eve Nakliyat
Niğde Evden Eve Nakliyat
Mersin Evden Eve Nakliyat
buy masteron
buy clenbuterol
Sakarya Evden Eve Nakliyat
A9957
ReplyDeleteMaraş Parça Eşya Taşıma
Denizli Şehirler Arası Nakliyat
Bayburt Evden Eve Nakliyat
Aion Coin Hangi Borsada
Sincan Fayans Ustası
Mersin Parça Eşya Taşıma
Muş Parça Eşya Taşıma
Dxgm Coin Hangi Borsada
Kayseri Şehir İçi Nakliyat
C5171
ReplyDeleteburdur ücretsiz sohbet
sohbet odaları
elazığ yabancı sohbet
görüntülü sohbet kızlarla
Uşak En İyi Ücretsiz Görüntülü Sohbet Siteleri
Bilecik Yabancı Canlı Sohbet
en iyi ücretsiz görüntülü sohbet siteleri
mardin en iyi ücretsiz sohbet uygulamaları
artvin canlı sohbet siteleri
85C6E
ReplyDeleteCoin Üretme
Coin Çıkarma Siteleri
Sohbet
Bitcoin Nasıl Oynanır
Görüntülü Sohbet Parasız
Onlyfans Takipçi Satın Al
Coin Kazanma
Yeni Çıkan Coin Nasıl Alınır
Binance Para Kazanma
ttrEpM884H
ReplyDeleteشركة مكافحة بق الفراش بالاحساء S3k61juATw
ReplyDelete