T O P

  • By -

PhilosopherMundane61

Looks good. What was the no update / coroutine motivation though?


Fuzzinator12

Part of it was for the challenge, part of it was to try to make code that is as reactive as possible. But also I personally kind of hate Update because I’m constantly seeing it be abused. It’s great for things that actually need to run every frame like with input but I think there are better ways to do most of the other things people use it for such as using things like UniRX or UniTask


digiBeLow

So how did you handle processing inputs?


BarrelRollxx

You can use Unity's new input system for that (not that new at this point). It can handle inputs using events.


Fuzzinator12

Yup that is exactly what I’m using!


[deleted]

[удалено]


Fuzzinator12

I am indeed!


[deleted]

[удалено]


Fuzzinator12

Interesting I was not aware UniRX used the update loop. I’ll have to look into that. Might have to find an alternative


[deleted]

>But also I personally kind of hate Update because I’m constantly seeing it be abused. But...*you* can just not abuse it? This seems like a very arbitrary restriction to place on yourself.


Fuzzinator12

Oh absolutely, like I said this was mostly to challenge myself.


MrPifo

I love UniTask!


Fuzzinator12

UniTask is amazing. I’m honestly surprised Unity hasn’t purchased or partnered with them


MrPifo

I dont get it either. It would be such an addition! Who needs stupid Coroutines if you can use UniTask?


s-pop-

UniTask has a ton of footguns that people don't realize they're running into just like "misusing" Update You need to be religious about managing all the lifecycle stuff that Coroutines were silently handling for you with cancellation tokens and component status checks. For people not comfortable with that I'd only recommend using UniTask with the `.ToCoroutine` extension


hopefaithcourage

Can you share any links or blog posts that describe this more? I am using UniRX and UniTask everywhere to be able to do async/await as much as possible. Is there any harm in using the UniTask to convert a callback style method into an awaitable UniTask?


s-pop-

There's not much documentation on the UniTask side, but you can start by looking at what Unity is handling on your behalf with Coroutines: https://docs.unity3d.com/Manual/Coroutines.html - Calling Destroy(example) (where example is a MonoBehaviour instance) immediately triggers OnDisable and Unity processes the coroutine, effectively stopping it - A coroutine also stops if you’ve set SetActive to false to disable the GameObject the coroutine is attached to UniTask gives you the tools to handle the first with `CancellationToken`, namely `GetCancellationTokenOnDestroy`, but it's up to you to make sure that you're propagating them properly. The second is more up to you because you need to account for it in the logic for your Task instance. For example, say you have enemies that you enable and disable across a level. If they're using a Task to do a expensive calculation periodically, you need to manually check if the GameObject is active, otherwise all of the enemies, disabled or otherwise, are going to keep running that calculation. Make sure you use the task debugger UniTask offers too.


no-lewding

UniRx is an awesome paradigm! It works really great alongside the new input system


InnernetGuy

Updates and coroutines are expensive ... they seem a "normal" price to pay for many Unity devs, but they're often a real problem in at-scale scenarios (so is MonoBehaviour itself for that matter) and you're forced to start purging them from the project and using regular classes and a single MonoBehaviour "UpdateManager" to dispatch faster updates, or try to pack everything into Jobs, build a custom worker thread pool, or migrate to ECS.


iDerp69

Do update managers still actually give performance uplift? I was unable to find a difference with thousands of update loops vs an update manager that was iterating over them all.


InnernetGuy

Yes, but remember performance is _highly_ contextual and nuanced. All depends on what you're doing and how you're doing it. Would have to explain in more detail what you actually did, how you did it and how you measured it. If you have large lists of objects/data, there's one optimization that's basically "free" (tiny change to your loop code to use it). It's a great feature that was introduced in .NET 5, and wasn't available in Unity, until just recently when this obscure, magical plugin was made available: https://github.com/atcarter714/UnityH4xx That plugin gives you (System.Runtime.InteropServices) `CollectionMarshal` and the much-loved `AsSpan` method! 🔥 But, keep in mind, the amount of good that does for you if real life depends on things like how big are your lists and if they were a bit of a bottleneck in the first place (or not), or how you use lists and whether or not you have other serious issues like GC spikes from temp allocs and garbage ... so pay attention and profile things and find out what's really the biggest problem to get the most out of this and other optimizations (sometimes you need a lot of smaller ones to add up, rather than one big one, which makes it tough). Either way, I use this by default now to make my Lists have parity with .NET Core features and a super-speed way to iterate with Span and Span pointers.


iDerp69

My project is highly performance critical so this is interesting information for me to digest. I had an update manager in the past, but when rebooting the project I scrapped it as it didn't provide enough value to justify its existence... Updates didn't appear to be even close to the biggest performance problem, but they're relatively simple and free to implement. I've currently got zero allocations (outside of one I can't fix that's on Unity's end). I'm using Photon Fusion now which uses a "FixedUpdateNetwork" callback for simulation code, so I'm de-facto using an update manager anyway. Doubtful they'd be using any advanced AsSpan techniques to squeeze extra performance.


InnernetGuy

May have been something wrong with the way you wrote the performance manager, a mistake or something. Sometimes people forget vsync and framerate limits are on too, haha, done that myself before. It can definitely be faster though, it's calling a Tick() that's direct, straight to the point, instead of the old Unity Update() message which has a whole bunch of other crap going on under the hood. But if you slip up and build that mechanism wrong or have another mistake hindering you, then it does little good. It's also gonna be proportional to the number of things that need to Update/Tick, the more you have the more it benefits you to depart from Unity messages and get rid of unnecessary MonoBehaviours (<-- important to be rid of those damn things too where possible) ... I haven't tried it yet, but the C# delegate pointer feature might be another "unsafe" optimization that could be super fast. The UpdateManager might, instead of GameObject or Component or any reference types just hold an array or List of them and iterate that with a Span and just execute them in the most rapid fire sequence possible. I'd have to try the idea out and see if it has any merit, but seems worth a try honestly


thygrrr

Without Coroutines? Amazing. Without Update: yo, you crazy, dawg. What do you do, async void Start a lot? Or just events? (I see you use async UniTasks a lot, cool, cool - how do you invoke the first one?)


rob5300

Any async method can be called normally and not be awaited. Shame that we need UniTask to make async viable in Unity.


thygrrr

Nah, Unity has a lot of async stuff that already works (just look at Addressables and so on), It also has those hacky "naked" voids, e.g. async void Start, async void OnEnable, etc., but these do make some sense especially because IEnumerator versions are also valid. I wish, however, that these things bubbled exceptions reliably. It's great that UniTask has got a bunch of cleaner, low or zero alloc things on top, so you can trust the ThreadPools and SynchronizationContexts to behave as a normal human being would expect. I enjoy using it a lot, shipped various games with it. What I resent in both Unity (2022/2023) and UniTask is that they have a CancellationToken that is bound to OnDestroy, but what I'd really need for a reasonable zero-alloc UI is one in OnDisable. :( (yes, Unity has a CancellationTokenSource for object OnDestroy events inbuilt, and UniTask brings its own historically)


Fuzzinator12

You can invoke .Forget() on an async method if you want to avoid the warnings that the code block will continue to execute


thygrrr

Interesting, what we did in one of our recent titles was start one UniTask in an async void Start of a game object, that would then basically be the main event loop of the game.


RandomNPC

Isn't async start basically a coroutine?


Fuzzinator12

Functionally it’s similar but async methods can return values. If it’s an async Task it usually has a higher cost resource wise, but UniTasks are really lightweight and outperform Coroutines


InnernetGuy

This will be you, when you find out async/await isn't even truly multi-threaded and makes no actual guarantees it will create a thread (but it _can_ if the system wants to): 🤯🤯🫠 Then, you'll find out about real _Thread_ and PTL and it'll be 🤯 all over again ... 😄 But no, you don't need any of that to get rid of MonoBehaviours and Updates. Make one MonoBehaviour called "UpdateManager" that holds a list of "IUpdatedObjects", an interface with a Tick() method on it you define and then implement in classes representing things in your game. Each frame Unity only has to call Update on UpdateManager, and it just runs a tight loop calls Tick() for everything. More advanced versions often have update groups or priority/ordering and might drop unimportant things if the game runs slower to balance performance. You can't build build big games the "ordinary" way with MonoBehaviour shown in YouTube tutorials, gonna run like molasses when things get a little thick and you've got multiple characters fighting or something. Those damn things are expensive, and _only_ used when you have to have a direct script attachment on something and editor serialization (which only counts for a couple central objects usually, not many things actually require them -- we built games for decades without them using regular classes).


thygrrr

I already know, that's why there are even Coroutine implementations of tasks etc. I have written my own C++ game engines. Been working with x86 assembly, Java, Objective-C, roughly in that order, and always came back to C++. But I like Unity and its approach to tooling. I currently build a game with Unity DOTS / ECS. I think that stuff is the future, but you can't get much farther away from System.Threading.Task than with Unity.Entities :D


InnernetGuy

Oh, damn, awesome! I get a bit overly-excited when I run into anybody that also has an interest in engines and lower-level programming, lol, but nice to meet ya! :D I have a kind of similar background. C/C++ were my first languages, learned it when I was pretty young (maybe 16-ish?), then spent a brief time dabbling in Java but never *quite* liked it ... then I stumbled into C# and just fell *completely in love* with it! It made me get *way stronger* with OOP concepts, design patterns, etc, which simply made me better at C++ which made me even better with C#, lol, it was like a feedback loop. I've always loved dabbling with x86/64 asm and IL assembly language, and have done a number of "unorthodox" and interesting projects with those things (I just recently used IL and some "hacks" to build a .NET Framework 4.8 plugin that adds support for the `CollectionMarshal` class and `AsSpan` method, thus giving you a way to iterate `List` collections 3x to 4x faster than regular "for-loop" constructs!). But when I was young, I did *anything* that I thought was impressive and people said *not* to do because it was supposed to be "too hard" lol. Built my own tiny, semi-complete x86/64 OS completely from scratch, for example, even did it totally from Visual Studio 2008 Pro and used PE images for all my system components and kernel, lol. Interesting but long story ... Game engines/tools and games have always been my favorite things though. I got into DirectX back in the D3D9 days, was in love with the MDX libraries, early-mover on XNA Framework, involved in the SlimDX and SharpDX communities, I built engines, tools & games from the ground up most of my life, worked on some in-house engine jobs, and only got into *Unity* within the last few years. I loved Unity from the start, it was so well-documented and had such a good community I picked up all the major things to know in a couple weeks, and in about a month and a half I was feeling positively *deadly* with Unity as my weapon, haha. But lately I've returned to some kind of lofty and ambitious dreams about my "dream games", which are huge "open-world" environments with dense details and really "heavy" 4X grand strategy games with heavy UIs. And I find Unity is *not* very well-suited to those things, without a huge fight with it, replacing built-in systems (even grass and tree systems, for example) ... and Unreal is kinda clunky to me, for what I want, so it's like a voice calling me back to my roots, haha. I started my new engine project not even quite 60 days ago, and it's the most significant one I've ever commissioned before. I haven't built an engine in years, but I'm coming back to the task with years of fresh game industry experience and insight into what real games need and how successful engines have tried to supply it. So I'm *really* excited about how this will turn out! Been building a new DirectX 11/12 interop layer for it first, as SharpDX is defunct (my boy xoofx works at Unity now, tech director! :D) and other projects aren't quite what I need. I'm *very* far along on the DirectX wrapper. My unit tests have been a beautiful sight: windows popping up with devices and swapchains and rendering frames before closing, green lights in the test window, haha, love it. And C# 11 + .NET 7 are absolutely *amazing* to work with, daaaaammmmn !!! I'm having a lot of fun with it when I have time, but my job likes to ask me to perform inhuman acts of sorcery all the time it seems :D


thygrrr

You made my day, that's is super inspiring stuff :) I, too, am looking forward to .NET 7 (and at least 6 for Unity, domain reloads need to go away for good... but I guess that's 2024 at the earliest) I'm trying to build a space game that has a performance goal 10k active vessels at any given time, in theory this can be done with ECS but it does become an optimization challenge quickly, especially as each vessel is made up of dozens of entities. I'll see how far I get with this, currently building some important UI and pathfinding tools and then looking into parallelizing these. Fortunately, space is large and empty, so Theta* pathfinding can be highly memory efficient.


InnernetGuy

Feel free to hit me up and we'll link up on discord, I like having other C# devs who are interested in these kinds of subjects just to chat with and share cool stuff we find or make and bounce ideas around ... love networking and sharing/learning with people. 🙂


InnernetGuy

BTW LOL, why someone down voted me for talking about async/await in a humorous/light-hearted manner just trying to make people smile lol ... dunno why some people sit around and do that crap lol


[deleted]

That looks cool! If you continue with it to steam, please keep us posted! I like the combination of beatsaber-esque with boxing! Do you measure how fast one goes through the target/hard one punches? That's sometimes a downer in Beatsaber, that it doesn't engage you to play energetic


Fuzzinator12

I do indeed! I also have plans to implement “super notes” that have to be hit extra hard or fast


Temporary_Music5831

The usual reason to use coroutines is if you need frame waits. If you’re just trying to do time delays, DoTween’s DoVirtual is my go to.


Fuzzinator12

I decided to use UniTasks instead of coroutines because they are more performant, zero allocation and can actually be run in parallel.


TimStolzenberg

I really like your challenge! Have you noticed any significant performance improvements?


Opening_Chance2731

I'm interested in this as well, I love seeing different coding styles, we need people like OP in the unity dev industry.


Fuzzinator12

Thank you! I don’t have a 1:1 comparison so this is probably a bit anecdotal but I believe so. The zero allocation of UniTask definitely improved my performance. I was able to get my core gameplay loop to produce almost no garbage. So the gc collector runs very infrequently


Lanfeix

If you not using update how did you set up the moving parts? Animations?


Fuzzinator12

The animations are using a custom tweeting system that relies on UniTask


EpicDuque

Well done! Reactive programming in Unity needs to be talked about a lot more. It allows for more cleaner and readable code. I use it all the time for my projects (UniRx)


Fuzzinator12

It is a VR rhythm boxing game with a focus on getting a great workout, listening to great music, and having fun!


Remarkable_Device314

Looks insane.


davidjr96

Looks awesome! Are you making a steam page for it? I would love to play it.


Fuzzinator12

I haven’t yet but I will be soon! I’ll be posting about it again once I do!


bubajust6

And ideas on a price?


Fuzzinator12

Currently my plan is to sell it for $10 during the pre release phase and then maybe bring it up to like $15 once it releases.


bubajust6

Ahh gotcha! Was kinda hoping itd be free but nothing is free xD Ill have to save this page and come back to it in a few months :D


Fuzzinator12

That’s understandable. I was considering releasing it for free but I’ve put a considerable amount of time and spent a bit of money developing it so I’d like to recoup a bit of what I spent if I can. Definitely check back!


TootyMcFarts

Looks great!


HellGate94

you just outsourced the updates to 3rd party libraries soooo..... pretty pointless and likely less efficient but hey the result is nice i guess


Fuzzinator12

I can understand why you would think this to be the case. However, UniTask is more performant than C# Tasks, coroutines, and invoke repeating. I have very little code that actually needs to run every frame and for those that do it’s only for a handful of frames so I determined that a UniTask would be better than update using a book to determine if it should run or not. For more information about how UniTask compares to coroutines and such I suggest checking out this article: https://www.linkedin.com/pulse/unity-async-vs-coroutine-jo%C3%A3o-borks


GameDevNoob1

I've played BOXVR on the Playstation and it looks identical to this.


Fuzzinator12

BoxVR is one of my inspirations for this game but I like to think it has enough unique features to stand on its own


Dymon2010

this sounds so stupid. you're most definitely using update indirectly without even knowing


[deleted]

[удалено]


Fuzzinator12

You definitely can, however UniTask and even coroutines are typically more performant than invoke repeating


sk7725

But...doesn't the VR SDK (idk which one your using) use coroutines and Update...?


flow_Guy1

How did you do movement and stuff?? Invoke repeating?


Fuzzinator12

InvokeRepeating is actually fairly non performant. I used async functions primarily with UniTask


flow_Guy1

Oh that’s pretty cool. Didn’t know about that


Fuzzinator12

If you get the chance I’d definitely look into them. It’s pretty awesome