Happy Sunday! I hope this video gives you some insights into the inner workings of the Unity Player Loop! We'll be working with this Improved Timer system more in future videos!
I think you're the only RUclipsr, or even the only person on the internet that's diving this deep into Unity that I've ever found. You're doing this community a world of good 🙏
Ok seriously though. I had no idea we could do this. I guess i had assumed that this was in some way protected by unity. This is really cool. We all use ticks for optimization, but putting it in the player loop...i had no idea. The optimization implications of player loop editing in general are really exciting. There is also a 100% chance i inadvertantly de-optimize the loop when start making my own changes to it.
This video seems to be getting a lot of views quickly. Hope it continues. This channel needs more views. If all of the subs actually viewed the video it would be great.
This is a priceless content for Game Developers. Thanks man for such content. Please never stop posting such content. Younger Game devs need teachers like you❤❤
Man, watching this video I see that I still have a long way to go in Unity. Discovering your channel was an incredible find, I have learned a lot in the last few months about how to improve the quality of my code.
As always content is very unique,deep and well structured. As a viewer I find ur videos straight to the point and covers lot of stuff in very short amount of time. Just out of curiosity wondering from ur system design videos it looks like you also have experience working with Enterprise business applications and those design knowledge you are applying to game domain.
@@git-amend More or less I'm on the same boat. Worked in Enterprise space and now looking to do something around gaming domain. Certainly the core challange is to getting/making art that looks gud and pro quality. How do you deal with Art challange..? Asset store or you also make of ur own..?
I try to get better at Art... and I'm a bit fan of 3D Tudor's blender tutorials... but I think it's just not ever going to be my specialty. So, generally I rely on Assets from the store, and I'm also starting to form some relationships in the community so that if needed I'll be able to work with people who are more skilled than I am! But who knows, maybe in 10 years I'll produce more than a low poly rock!
Aha! This is similar to the dependency injection frameworks like Vcontainer where we implement ITickable, IStartable and include them in player loop! Another great source from you. Thank you for your high efforts on this.
You have definitely been putting out great content, I hope you blow up fast. I wish I could support you more than a like, comment and subscribing. I've been super busy of late, hoping to see more of you!
This is great, extremly useful and well explained. There is so little documentation on this. I might try implementing the Day/Night Cycle with this method.
Just wondered if you tried using the updateFunction property of the PlayerLoopSystem. I tried using an IntPtr but Unity kept crashing. It supposedly is more performant?
No prob! Glad you watched anyway, it's good to take in things the are a bit beyond your level too, it will help your learning journey. Next week's video will be a bit more intermediate level.
I made a similiar timer system once that simply creates a invisible persistent singleton ticker monobehaviour after first scene is loaded and when a pure c# timer class is created it adds itself to the ticker singletons list of ITick. I also added a flag to the timers for switching between fixedUpdate, Update, LateUpdate, "EarlyUpdate" etc which then the ticker can tick accordingly to. I wonder what are the signficant differences as I feel like this approach was overengineered just for a timer system. By all means though, I would have never learned such a feat was possible and have a insight of the unitys internal structures if it wasn't for this video. Also on a different note, like many people some parts of this video also has gone over my head too. I love your advanced videos but wonder if you can recommend some mid or mid+ level youtubers or resources to learn about Unity and C# or both. Thank you.
Thanks for the comment. Your system sounds very good for many scenarios. You might consider the following things when comparing to the code demonstrated in the video: This example isn't tied to a specific scene or game object, making it usable across the entire project, no matter how many scenes there are. It's also beneficial for running background tasks. The functionality is encapsulated within the system, making it easily reusable in any project since it can be packaged and used without requiring the consumer to create, configure, or maintain anything like a manager singleton. It's designed in a decoupled way, making it easy to test and debug. Regarding over engineering, be careful not to conflate advanced programming with over engineering, which really means adding unnecessary features beyond your requirements. Over engineering is unrelated to skill level. And in regard to where to find more content that is not for beginners, it sounds like you have a good grasp of Unity already. You should check out Iain McManus's channel, he has very good videos that you might like.
Yes, there is, but it's way beyond the scope of this video or anything I can explain in a YT comment. If you look through the subsystem list, you can identify the one you want to replace or modify, and then your best bet is to investigate that particular system and determine a solution for whatever it is you need to do. You can replace entire subsystems if you need to, but you need to have a good understanding of what they are doing so you don't break anything. Or, you can your own subsystem execute immediately afterwards and modify the state as needed.
@@git-amend thanks for this comment, I’ll look into this. I’d probably need something like the inputsystem subsystem or something along those lines but this video definitely helps!
I get that the point is to showcase tapping into the Unity Lifecycle. But if you didn't wanna mess with the Player Loop, could you also make a MonoBehaviour Hook? Or even have the TimerManager be a MonoBehaviour Singleton that gets bootstrapped?
Yes of course, there are many approaches you could take to implement the same system. The impetus for this video was to minimize manual configuration and management on the part of the consumer so that it just works.
I wanted to point out, that if you use Unity ECS you have full control over the order that Systems update in. This reminds me a lot of that, just unsupported and for the old non-ECS loop. I'd actually be curious if the ECS systems are part of this "old loop" as I call it or work completely differently.
Am curious why you need your timer class to run every frame update? Most Unity sample projects i've explored prefer using corroutines for timers; and instead create a coroutine runner system to allow native classes to manage corroutine references and life-cycles. Regardless this is a neat tutorial into interfacing with unity low level APIs.
Thanks for watching! I'd have to see a specific coroutine runner system to really compare and contrast, but typically coroutines are designed to be run within the context of a MonoBehaviour because they still rely on the Unity engine's game loop to execute. It provides the necessary hooks into the game loop to allow coroutines to be scheduled and executed correctly. I would imagine that any coroutine runner solution likely extends MonoBehaviour in some way. The main benefit of this system is that it is completely independent of any scene or game object. In relation to coroutines specifically, it also avoids the overhead associated with coroutine scheduling and context switching - but that is likely minor in most cases.
@@git-amend The QuizU sample has a static CorroutineRunner utility script. It wraps the corroutine methods and exposes an Initialize() method that accepts a monobehaviour reference as the 'runner' object allowing native classes to access it and run corroutines without needing a monobehaviour passed to them in their constructor. Cleanup gets handled by the monobehaviour's lifecycle, so only issue is null reference excemptions. From my research into when to use update vs corroutine, it only really matters when there are many game objects. The time-sclicing of corroutines helps reduce unnesscary Update calls but if you go too far into nested corroutine hell then the scheduling and context switching overhead compromises performance gains making Update more performant. Still, Unity does say "Minimize code that runs every frame" in their best practices hub blog: unity.com/how-to/advanced-programming-and-code-architecture#minimize-code-runs-every-frame. They even recommend an 'Custom Update Manager' to reduce number of interop calls that can slow performance: github.com/Menyus777/Game-engine-specific-optimization-techniques-for-Unity
Glad it was helpful! The main benefit is that the system is not tied to any scene or game object. Beyond that, all the functionality is encapsulated in the system, making it easily portable between projects and can be packaged with zero setup by the consumer. It's also very easy to test and debug. It's also not really limited to the timer system, you could use this technique for any system that relies on regular updates during gameplay such as AI logic, data binding or some other calculations.
It may be a stupid question but, i ll ask anyways. Inside TimerManager there is a method called Clear() that just does timers.clear(). That method does not dispose of the timers, so wouldnt it be neccesary to do this first -> for (int i = 0; i < timers.count; i++) { timers[i].Dispose() }? I started learning c# directly inside unity so i dont have much knowledge about Garbage Collection, Cancellation Tokens and such, cause MonoBehaviours do it automatically.
The clear method is there to clear statics. Since we keep references to all the timers in a static List, that's why we are doing that. Garbage collection upon exiting play mode will take care of the rest. Refer to this article in the docs about this issue regarding domain reload and static variables: docs.unity3d.com/Manual/DomainReloading.html
The reason I like your lectures is that there are results that I am sure your lectures are at a real professional level. However, this video is too difficult for me to understand, my feeling is that I use it like an 'event', so why not use an event?
This is a very advanced topic. I'm not sure this really relates to events. The main reason we want to hook into the Player Loop is so that we don't have to perform manual updates on the Timer system ourselves. If we had a a game with 1000 timers on abilities, cooldowns and so on, you would have to manually write code to 'Tick' every single one yourself. This is the same technique used by advanced some advanced systems you can buy on the Asset Store to reduce the amount of boilerplate code and manager classes you have to create and maintain.
Pure coincidence tbh I just happened to hopen my phone, saw the time had changed exactly from 759 to 8. I thought to myself "let's beat a man at a race he didn't know he was in"
@git-amend I'm wondering, do you think it would be good practice to handle commands with this system? I'm using commands for pathfinding and combat, for example, and I was looking for a command manager to handle commands over time.
I think that I personally would not start building systems that are very specific to my own game's logic into the main player loop. Instead I would add systems that support my game in a more general sense like Timers or Bindings. In the example repository there is a FequencyTimer that will tick N times per second and fires an event every tick. You could use something like that to determine when to execute a command, and keep your commands out of the player loop.
@@git-amend I think I wasn't clear :o the word 'task' might be more appropriate. What I had in mind is as simple as this: public abstract class ThickCommand : ITimer //Can be either pathfinding command or an interaction... { event Action Completed; //callbacks for any listener void Execute() { TimerManager.Register(); } abstract void Thick(float t); //delegate the task void Complete(Status s) { TimeManager.Unregister(); Completed.Invoke(s); } void Dispose() { TimeManger.Unregister(); } //dispose pattern.. } I just wonder if it makes more sense to call Tick() with a task manager rather than by the "task owner" itself. Thanks for your time :)
This is really slick -- but I might want to offer just one word of advice for anyone looking at this, that a system like this may need a second injection on the Fixed Update Loop, and all of the accoutrements that come with that (a way to specific which manager you want to use, etc). This is especially if you're going to be using any timers in pause menus or for animations or tweening, or any contexts in which you set the TimeScale to zero (say, to pause a game), but may still want to rely on the Fixed Update Time for animations. Performing minor UI animations during pause menus is a common enough behavior, but wouldn't be supported by this kind of system. Either way, this is a super nifty solution.
Is there any significance to skipping index 0 in your for loops due to using ++i instead of i++? Thanks for these videos. As Lisa Simpson would say: "you didn't dumb down!", and that is priceless IMO.
Correct - semantically they are the same in a C# for loop. However, it might be worth noting that with some compilers ++i can avoid the need for a temporary copy of the incremented value, making it slightly more efficient, especially in tight loops or with complex types. In this video though, it makes absolutely no difference at all. I read a joke once that if C++ was called ++C, most people would opt to write ++i in their loops haha
At 11:00, when you remove the system from the playerLoopSystemList, wouldn't removing an item from the list have an effect on the next indexed element of the running for loop? Usually when I see removals in a for loop the developer writes the loop starting from the end of the list and advancing toward zero to prevent it.
When you call RemoveAt to remove an item, the remaining items in the list are renumbered to replace the removed item. For example, if you remove the item at index 3, the item at index 4 is moved to the 3 position. In addition, the number of items in the list (as represented by the Count property) is reduced by 1. This means during the next iteration, Count will be one less than it was before and the loop will execute one less time. Give it a try for yourself iterating a list of 10 numbers and remove every even number while looping, but base your loop on the Count property. Add some debug statements so you can see what the Count and the index are each iteration.
@@git-amend Right, that's what I mean. The item at index 4 is moved to the 3 position, so on the next iteration of the loop the index increases and the item now at index 3 is skipped. It works with odds/evens just by coincidence. var x = new List { 1, 2, 3, 4 }; for (int i = 0; i < x.Count; ++i) { if (x[i] == 2 || x[i] == 3) { x.RemoveAt(i); } } Debug.Log(string.Join(", ", x)); // output: 1, 3, 4 Somewhere over the years I picked up implementing it this way for safety: for (int i = x.Count - 1; i >= 0; i--) { if (x[i] == 2 || x[i] == 3) { x.RemoveAt(i); } } Debug.Log(string.Join(", ", x)); // output: 1, 4 It's a trivial and common bug and hardly worth mentioning, but I figured I'd point it out for others in case they run into it. Thank you for the amazingly high quality of content on this channel. I'm moving into C# and Unity dev and your videos are top notch and exactly what I was looking for.
@@davearvelo I see what you are getting at. This could indeed be an issue if we had more than one system to remove, so that is an oversight on my part. However, there will only be one system to remove, so I'll add an early return so we can exit the recursion as soon as the work is done. Thanks for pointing that out.
Have you tested to see if the performance of: 1) accessing Time.deltaTime in every timer separately (which you did in the video) versus 2) accessing Time.deltaTime once, in the TimerManager, and then providing that value to all timers? I haven't tested it myself.
I have not, but Time.deltaTime is a very lightweight operation. The performance overhead of accessing it multiple times per frame is usually negligible. Perhaps if there were thousands of timers it might have a small impact. I also don’t want timers coupled to a specific format… some Timers might use Time.unscaledDeltaTime for example.
I tested running 10,000 mono behavioirs vs 10 of these timers and the difference is around 60-80% based on the profiler resulta and fps@@SystemOfATool
I wonder why you made the concrete Tick() check if it IsRunning, instead of insulating the loop callback and having Tick() only be called when IsRunning. Is there any concrete Timer type which would benefit from getting Tick() when not IsRunning?
One scenario where you might want a Timer's Tick method to be called every frame would be a timer that needs to keep track of how long it has been paused, in addition to its usual functionality. It's an implementation choice that ensures that the Timer class can be extended and customized to meet the consumers requirements without being constrained by the TimerManager's design.
By inserting the Timer system to the Update loop system, does that mean the Timer got updated just as fast as a MonoBehaviour receive the Update message? i.e. if the game runs at 20 fps, does the Timer tick every 50 ms too?
I didn't know you can clear the statics to prevent duplication in Play Mode I had problems with Singletons that refuse to work properly in Enter Play Mode option. I guess your method can solve this problem(?)
Depends what you are doing, but often just doing a clean up when coming back into Editor mode will solve a lot of problems like that when it comes to statics. Check this out: docs.unity3d.com/Manual/DomainReloading.html
Related to the video: I want to have a tick system that just ticks every n seconds not per frame. I want to have the game ai to update every tick, but doing it every frame is overkill. What would be a way to do this?
I think you could still take a similar approach, but instead of working with deltaTime you would work with real Time. No matter what, the system managing the timers still needs to execute some logic every frame to see if enought time has passed, and if so then it should tick all your timers. In this case, maybe your Timers just tick up by one, or some other value.
I think Most people do this by creating an ontick event that is invoked in a a method using time.deltatime to keep track. There are some improvements to this model, but what GA is doing in this video is basically that on steroids. I'm on my phone here so I'm not going to type code, but if you are in the discord drop the question in programming questions someone will give you an example. if no one helps you with it I can give you some code examples when I am on my pc tonight.
When you remove the system upon exiting play mode, do you really need to remove the system, or would it be enough to set it back to the default player loop? If multiple systems were injected, then this would remove all of them right?
It would remove all of them. Be careful when resetting back to the default player loop, it's generally not advised because I have heard of people doing this and then suddenly their 3rd party tools stop working creating an invisible bug because the 3rd party tool was adding a system they didn't know about and suddenly it's being removed silently.
Hi. I have followed this tutorial precisely, and the timer manager is being inserted into the player loop just fine. However, when leaving play mode i get a stack overflow error. I have made sure that all of the code is exactly the same as in the video.
A stack overflow is often caused by an error in your recursion. I suggest you review your code in the methods that add and remove a system from the Player Loop.
@@git-amend Ah thank you, knew it something like this. In the HandleSubSystemLoopForRemoval() method, instead of passing loop.subSystemList[i] into RemoveSystem(), I was passing loop.
my question is... why not just make a delegate that gets called inside an update of an object, and just sub anything you want to that delegate? Why hacking your way into unity codebase...
It's not really about performance. It's about decoupling a system from any scene or game object. Not only does this encapsulate all the functionality into a stand alone system, it makes it extremely easy to package and reuse in any project with zero setup, management or maintenance on the part of the consumer.
Happy Sunday! I hope this video gives you some insights into the inner workings of the Unity Player Loop! We'll be working with this Improved Timer system more in future videos!
I think you're the only RUclipsr, or even the only person on the internet that's diving this deep into Unity that I've ever found. You're doing this community a world of good 🙏
Thanks for the encouraging words!
Ok seriously though. I had no idea we could do this. I guess i had assumed that this was in some way protected by unity. This is really cool. We all use ticks for optimization, but putting it in the player loop...i had no idea. The optimization implications of player loop editing in general are really exciting. There is also a 100% chance i inadvertantly de-optimize the loop when start making my own changes to it.
Haha! Yes, exercise some caution when doing this... test thoroughly!
@@git-amend I will...right after you make a "testing and optimization in your unity project" video.
lol coming up
You seriously need more subscribers, man!..glad I've found you..great video!!
Thanks! Glad to have you here!
Every monobehaviour you avoid it's a small victory... Great video!
Yes! Thank you!
I like this way better than assigning script execution order numbers.
Indeed, for some usecases this will be helpful in that regard.
This is very nice! It's rare to see advance concepts like this on YT. Very useful! Thanks!
You're very welcome!
It's crazy how much this went over my head xD
One day at a time my friend!
So happy to see some non-beginner unity videos.
Glad you liked it! More on the way!
This video seems to be getting a lot of views quickly. Hope it continues. This channel needs more views. If all of the subs actually viewed the video it would be great.
Yes, very busy today ! Might even be a record!
if that is not the most important tip I ever take in my 10 years career I don't know what it will be, thanks for the great tutorials.
Thank you for the kind compliment! Made my day!
This is a priceless content for Game Developers. Thanks man for such content. Please never stop posting such content. Younger Game devs need teachers like you❤❤
Much appreciated! Thanks for the comment!
Man, watching this video I see that I still have a long way to go in Unity. Discovering your channel was an incredible find, I have learned a lot in the last few months about how to improve the quality of my code.
Glad to hear it! Keep going one day at a time, the learning never ends!
As always content is very unique,deep and well structured. As a viewer I find ur videos straight to the point and covers lot of stuff in very short amount of time. Just out of curiosity wondering from ur system design videos it looks like you also have experience working with Enterprise business applications and those design knowledge you are applying to game domain.
Thanks for the comment! You are correct, my career has been in enterprise application development, something I still do today!
@@git-amend More or less I'm on the same boat. Worked in Enterprise space and now looking to do something around gaming domain. Certainly the core challange is to getting/making art that looks gud and pro quality. How do you deal with Art challange..? Asset store or you also make of ur own..?
I try to get better at Art... and I'm a bit fan of 3D Tudor's blender tutorials... but I think it's just not ever going to be my specialty. So, generally I rely on Assets from the store, and I'm also starting to form some relationships in the community so that if needed I'll be able to work with people who are more skilled than I am! But who knows, maybe in 10 years I'll produce more than a low poly rock!
Aha! This is similar to the dependency injection frameworks like Vcontainer where we implement ITickable, IStartable and include them in player loop!
Another great source from you. Thank you for your high efforts on this.
Thanks for sharing! I’ll have to take a look at Vcontainer!
@git-amend Thank you for including the frequency timer in the source code. Very helpful!😃😎
Thanks goes to you for the great comment / suggestion!
You have definitely been putting out great content, I hope you blow up fast. I wish I could support you more than a like, comment and subscribing. I've been super busy of late, hoping to see more of you!
I appreciate that! Thanks for watching!
This is great, extremly useful and well explained. There is so little documentation on this. I might try implementing the Day/Night Cycle with this method.
Glad it was helpful! That's probably a great use case!
Just wondered if you tried using the updateFunction property of the PlayerLoopSystem. I tried using an IntPtr but Unity kept crashing. It supposedly is more performant?
I’ve thought about doing this before but didn’t think it was possible. Thanks for the great video!
No problem 👍
Booting up Unity to test this out NOW! Really good vid as always ❤
Great! Thanks for the comment!
And without Reflections!? I'm flabbergasted!
Ty for this gem!
Thanks! Glad you enjoyed it!
My brain is currently too fried, but watching later. This seems extremely useful...
Amazing! I didn't know we could do that.
Awesome! Glad to show something new!
Awesome video once again! This one went bit over my head unfortunately because I've only started using Unity since January 😅 Thanks!
No prob! Glad you watched anyway, it's good to take in things the are a bit beyond your level too, it will help your learning journey. Next week's video will be a bit more intermediate level.
Godot community needs these high quality technical videos as well 😢
Wish I could help ya!
I've never seen this before🤯
Nice! Always great to see something new, isn't it!
I made a similiar timer system once that simply creates a invisible persistent singleton ticker monobehaviour after first scene is loaded and when a pure c# timer class is created it adds itself to the ticker singletons list of ITick.
I also added a flag to the timers for switching between fixedUpdate, Update, LateUpdate, "EarlyUpdate" etc which then the ticker can tick accordingly to.
I wonder what are the signficant differences as I feel like this approach was overengineered just for a timer system.
By all means though, I would have never learned such a feat was possible and have a insight of the unitys internal structures if it wasn't for this video.
Also on a different note, like many people some parts of this video also has gone over my head too. I love your advanced videos but wonder if you can recommend some mid or mid+ level youtubers or resources to learn about Unity and C# or both.
Thank you.
Thanks for the comment. Your system sounds very good for many scenarios. You might consider the following things when comparing to the code demonstrated in the video: This example isn't tied to a specific scene or game object, making it usable across the entire project, no matter how many scenes there are. It's also beneficial for running background tasks. The functionality is encapsulated within the system, making it easily reusable in any project since it can be packaged and used without requiring the consumer to create, configure, or maintain anything like a manager singleton. It's designed in a decoupled way, making it easy to test and debug.
Regarding over engineering, be careful not to conflate advanced programming with over engineering, which really means adding unnecessary features beyond your requirements. Over engineering is unrelated to skill level. And in regard to where to find more content that is not for beginners, it sounds like you have a good grasp of Unity already. You should check out Iain McManus's channel, he has very good videos that you might like.
I knowed this before, but you are propably first person on yt that mention it.
You might be right! Thanks for the comment!
This is actually crazy. Great video!
Thanks!
Every video makes me want more... more ... more
Thanks for watching again! Always a pleasure to read your comments!
Awesome video as always!
I’ve been wondering, is there a way to do controller/input rebinding?
Yes, there is, but it's way beyond the scope of this video or anything I can explain in a YT comment. If you look through the subsystem list, you can identify the one you want to replace or modify, and then your best bet is to investigate that particular system and determine a solution for whatever it is you need to do. You can replace entire subsystems if you need to, but you need to have a good understanding of what they are doing so you don't break anything. Or, you can your own subsystem execute immediately afterwards and modify the state as needed.
@@git-amend thanks for this comment, I’ll look into this. I’d probably need something like the inputsystem subsystem or something along those lines but this video definitely helps!
I get that the point is to showcase tapping into the Unity Lifecycle. But if you didn't wanna mess with the Player Loop, could you also make a MonoBehaviour Hook? Or even have the TimerManager be a MonoBehaviour Singleton that gets bootstrapped?
Yes of course, there are many approaches you could take to implement the same system. The impetus for this video was to minimize manual configuration and management on the part of the consumer so that it just works.
I like that we don't have to set a list of timers the biggest upgrade.
Yeah that's for sure! Next week we'll turn it into a package and make it even easier!
I wanted to point out, that if you use Unity ECS you have full control over the order that Systems update in. This reminds me a lot of that, just unsupported and for the old non-ECS loop.
I'd actually be curious if the ECS systems are part of this "old loop" as I call it or work completely differently.
Thanks for pointing that out. It's my understanding that these are 2 totally separate loops.
Absolutely great video! Keep it up man!
Thanks! Will do!
Another certified banger
Thank you!
Am curious why you need your timer class to run every frame update?
Most Unity sample projects i've explored prefer using corroutines for timers; and instead create a coroutine runner system to allow native classes to manage corroutine references and life-cycles.
Regardless this is a neat tutorial into interfacing with unity low level APIs.
Thanks for watching! I'd have to see a specific coroutine runner system to really compare and contrast, but typically coroutines are designed to be run within the context of a MonoBehaviour because they still rely on the Unity engine's game loop to execute. It provides the necessary hooks into the game loop to allow coroutines to be scheduled and executed correctly. I would imagine that any coroutine runner solution likely extends MonoBehaviour in some way.
The main benefit of this system is that it is completely independent of any scene or game object. In relation to coroutines specifically, it also avoids the overhead associated with coroutine scheduling and context switching - but that is likely minor in most cases.
@@git-amend
The QuizU sample has a static CorroutineRunner utility script. It wraps the corroutine methods and exposes an Initialize() method that accepts a monobehaviour reference as the 'runner' object allowing native classes to access it and run corroutines without needing a monobehaviour passed to them in their constructor. Cleanup gets handled by the monobehaviour's lifecycle, so only issue is null reference excemptions.
From my research into when to use update vs corroutine, it only really matters when there are many game objects. The time-sclicing of corroutines helps reduce unnesscary Update calls but if you go too far into nested corroutine hell then the scheduling and context switching overhead compromises performance gains making Update more performant.
Still, Unity does say "Minimize code that runs every frame" in their best practices hub blog: unity.com/how-to/advanced-programming-and-code-architecture#minimize-code-runs-every-frame. They even recommend an 'Custom Update Manager' to reduce number of interop calls that can slow performance: github.com/Menyus777/Game-engine-specific-optimization-techniques-for-Unity
That was an amazing video, I learned alot Thank you!
Yet I am still not sure what exactly the benefits of adding a the timer to the player loop?
Glad it was helpful! The main benefit is that the system is not tied to any scene or game object. Beyond that, all the functionality is encapsulated in the system, making it easily portable between projects and can be packaged with zero setup by the consumer. It's also very easy to test and debug. It's also not really limited to the timer system, you could use this technique for any system that relies on regular updates during gameplay such as AI logic, data binding or some other calculations.
Ahh I see, wow that seems like a really good tool to have 😄.
Also a timed metrics system could be a good use of it as well
It may be a stupid question but, i ll ask anyways. Inside TimerManager there is a method called Clear() that just does timers.clear(). That method does not dispose of the timers, so wouldnt it be neccesary to do this first -> for (int i = 0; i < timers.count; i++) { timers[i].Dispose() }? I started learning c# directly inside unity so i dont have much knowledge about Garbage Collection, Cancellation Tokens and such, cause MonoBehaviours do it automatically.
The clear method is there to clear statics. Since we keep references to all the timers in a static List, that's why we are doing that. Garbage collection upon exiting play mode will take care of the rest. Refer to this article in the docs about this issue regarding domain reload and static variables: docs.unity3d.com/Manual/DomainReloading.html
The reason I like your lectures is that there are results that I am sure your lectures are at a real professional level.
However, this video is too difficult for me to understand, my feeling is that I use it like an 'event', so why not use an event?
This is a very advanced topic. I'm not sure this really relates to events. The main reason we want to hook into the Player Loop is so that we don't have to perform manual updates on the Timer system ourselves. If we had a a game with 1000 timers on abilities, cooldowns and so on, you would have to manually write code to 'Tick' every single one yourself. This is the same technique used by advanced some advanced systems you can buy on the Asset Store to reduce the amount of boilerplate code and manager classes you have to create and maintain.
@@git-amend Thank you. Thank you for always sharing your knowledge. I should study with your code and video a little more.
@@git-amend I'm not good at understanding, so can I use it similar to JOB system?
Beating git amend to first comment on his own video -priceless
Well done sir! I salute you!
Pure coincidence tbh I just happened to hopen my phone, saw the time had changed exactly from 759 to 8. I thought to myself "let's beat a man at a race he didn't know he was in"
@@Fitz0fury lol
Great and insightful! But sadly too advanced for Unity newbies like me.
Thanks for watching! You can always circle back to this one in a bit. Next week's video will be a bit more intermediate level.
Great video
Ok I miss the new key word from the manager foreach iteration, my bad!
Thanks! Glad you figured out the answer to your Q!
@git-amend I'm wondering, do you think it would be good practice to handle commands with this system? I'm using commands for pathfinding and combat, for example, and I was looking for a command manager to handle commands over time.
I think that I personally would not start building systems that are very specific to my own game's logic into the main player loop. Instead I would add systems that support my game in a more general sense like Timers or Bindings. In the example repository there is a FequencyTimer that will tick N times per second and fires an event every tick. You could use something like that to determine when to execute a command, and keep your commands out of the player loop.
@@git-amend
I think I wasn't clear :o the word 'task' might be more appropriate. What I had in mind is as simple as this:
public abstract class ThickCommand : ITimer //Can be either pathfinding command or an interaction...
{
event Action Completed; //callbacks for any listener
void Execute() { TimerManager.Register(); }
abstract void Thick(float t); //delegate the task
void Complete(Status s) { TimeManager.Unregister(); Completed.Invoke(s); }
void Dispose() { TimeManger.Unregister(); } //dispose pattern..
}
I just wonder if it makes more sense to call Tick() with a task manager rather than by the "task owner" itself. Thanks for your time :)
This is really slick -- but I might want to offer just one word of advice for anyone looking at this, that a system like this may need a second injection on the Fixed Update Loop, and all of the accoutrements that come with that (a way to specific which manager you want to use, etc). This is especially if you're going to be using any timers in pause menus or for animations or tweening, or any contexts in which you set the TimeScale to zero (say, to pause a game), but may still want to rely on the Fixed Update Time for animations. Performing minor UI animations during pause menus is a common enough behavior, but wouldn't be supported by this kind of system. Either way, this is a super nifty solution.
Is there any significance to skipping index 0 in your for loops due to using ++i instead of i++?
Thanks for these videos. As Lisa Simpson would say: "you didn't dumb down!", and that is priceless IMO.
I just verified that it doesn't matter in a for loop. All these years 😅.
Correct - semantically they are the same in a C# for loop. However, it might be worth noting that with some compilers ++i can avoid the need for a temporary copy of the incremented value, making it slightly more efficient, especially in tight loops or with complex types. In this video though, it makes absolutely no difference at all. I read a joke once that if C++ was called ++C, most people would opt to write ++i in their loops haha
@@git-amend I believe it!
At 11:00, when you remove the system from the playerLoopSystemList, wouldn't removing an item from the list have an effect on the next indexed element of the running for loop? Usually when I see removals in a for loop the developer writes the loop starting from the end of the list and advancing toward zero to prevent it.
When you call RemoveAt to remove an item, the remaining items in the list are renumbered to replace the removed item. For example, if you remove the item at index 3, the item at index 4 is moved to the 3 position. In addition, the number of items in the list (as represented by the Count property) is reduced by 1. This means during the next iteration, Count will be one less than it was before and the loop will execute one less time. Give it a try for yourself iterating a list of 10 numbers and remove every even number while looping, but base your loop on the Count property. Add some debug statements so you can see what the Count and the index are each iteration.
@@git-amend Right, that's what I mean. The item at index 4 is moved to the 3 position, so on the next iteration of the loop the index increases and the item now at index 3 is skipped. It works with odds/evens just by coincidence.
var x = new List { 1, 2, 3, 4 };
for (int i = 0; i < x.Count; ++i) {
if (x[i] == 2 || x[i] == 3) {
x.RemoveAt(i);
}
}
Debug.Log(string.Join(", ", x)); // output: 1, 3, 4
Somewhere over the years I picked up implementing it this way for safety:
for (int i = x.Count - 1; i >= 0; i--) {
if (x[i] == 2 || x[i] == 3) {
x.RemoveAt(i);
}
}
Debug.Log(string.Join(", ", x)); // output: 1, 4
It's a trivial and common bug and hardly worth mentioning, but I figured I'd point it out for others in case they run into it. Thank you for the amazingly high quality of content on this channel. I'm moving into C# and Unity dev and your videos are top notch and exactly what I was looking for.
@@davearvelo I see what you are getting at. This could indeed be an issue if we had more than one system to remove, so that is an oversight on my part. However, there will only be one system to remove, so I'll add an early return so we can exit the recursion as soon as the work is done. Thanks for pointing that out.
Have you tested to see if the performance of:
1) accessing Time.deltaTime in every timer separately (which you did in the video)
versus
2) accessing Time.deltaTime once, in the TimerManager, and then providing that value to all timers?
I haven't tested it myself.
I have not, but Time.deltaTime is a very lightweight operation. The performance overhead of accessing it multiple times per frame is usually negligible. Perhaps if there were thousands of timers it might have a small impact. I also don’t want timers coupled to a specific format… some Timers might use Time.unscaledDeltaTime for example.
@@git-amend Understood, thanks for the response.
I tested running 10,000 mono behavioirs vs 10 of these timers and the difference is around 60-80% based on the profiler resulta and fps@@SystemOfATool
I wonder why you made the concrete Tick() check if it IsRunning, instead of insulating the loop callback and having Tick() only be called when IsRunning. Is there any concrete Timer type which would benefit from getting Tick() when not IsRunning?
One scenario where you might want a Timer's Tick method to be called every frame would be a timer that needs to keep track of how long it has been paused, in addition to its usual functionality. It's an implementation choice that ensures that the Timer class can be extended and customized to meet the consumers requirements without being constrained by the TimerManager's design.
By inserting the Timer system to the Update loop system, does that mean the Timer got updated just as fast as a MonoBehaviour receive the Update message?
i.e. if the game runs at 20 fps, does the Timer tick every 50 ms too?
Because this system is inserted right before the Script Update subsystem, it will run right before every MonoBehaviour Update.
I didn't know you can clear the statics to prevent duplication in Play Mode
I had problems with Singletons that refuse to work properly in Enter Play Mode option. I guess your method can solve this problem(?)
Depends what you are doing, but often just doing a clean up when coming back into Editor mode will solve a lot of problems like that when it comes to statics. Check this out: docs.unity3d.com/Manual/DomainReloading.html
I see, thanks for the reply!
The link is super helpful, exactly what I was looking for. Thanks 😊👍
Related to the video: I want to have a tick system that just ticks every n seconds not per frame. I want to have the game ai to update every tick, but doing it every frame is overkill. What would be a way to do this?
I guess a coroutine would work.
I think you could still take a similar approach, but instead of working with deltaTime you would work with real Time. No matter what, the system managing the timers still needs to execute some logic every frame to see if enought time has passed, and if so then it should tick all your timers. In this case, maybe your Timers just tick up by one, or some other value.
I think Most people do this by creating an ontick event that is invoked in a a method using time.deltatime to keep track. There are some improvements to this model, but what GA is doing in this video is basically that on steroids. I'm on my phone here so I'm not going to type code, but if you are in the discord drop the question in programming questions someone will give you an example. if no one helps you with it I can give you some code examples when I am on my pc tonight.
@@git-amend Thanks!
Madman.
But seriously cool.
Indeed! Thank you!
When you remove the system upon exiting play mode, do you really need to remove the system, or would it be enough to set it back to the default player loop? If multiple systems were injected, then this would remove all of them right?
It would remove all of them. Be careful when resetting back to the default player loop, it's generally not advised because I have heard of people doing this and then suddenly their 3rd party tools stop working creating an invisible bug because the 3rd party tool was adding a system they didn't know about and suddenly it's being removed silently.
Ah ok thank you. I dont currently have any third party tools that do this, so ill debug a warning to let me know.
Hi. I have followed this tutorial precisely, and the timer manager is being inserted into the player loop just fine. However, when leaving play mode i get a stack overflow error. I have made sure that all of the code is exactly the same as in the video.
A stack overflow is often caused by an error in your recursion. I suggest you review your code in the methods that add and remove a system from the Player Loop.
@@git-amend Ah thank you, knew it something like this. In the HandleSubSystemLoopForRemoval() method, instead of passing loop.subSystemList[i] into RemoveSystem(), I was passing loop.
So this could be used for a more data-driven approach?
Possibly, I think it really depends what you need to achieve. It’s great for setting up data binding, and I’m sure there are plenty of other uses.
Programming in Unity without monobehaviour?? Hurray!!! for democracy
Absolutely-freedom from `MonoBehaviour` opens up a whole new level of creative control!
nice
Thanks
good vid
Thank you!
hah, 'pure C#'. I like this because it implies MonoBehaviours make C# impure
Been chuckling about this comment for half an hour now.
They do, since they cross boundaries with c++.
my question is... why not just make a delegate that gets called inside an update of an object, and just sub anything you want to that delegate? Why hacking your way into unity codebase...
I guess what I am getting at is - do you really get that much performance boost with your way? ease of use vs performance type of thing...
It's not really about performance. It's about decoupling a system from any scene or game object. Not only does this encapsulate all the functionality into a stand alone system, it makes it extremely easy to package and reuse in any project with zero setup, management or maintenance on the part of the consumer.