Extending the Unity Editor with custom tools using UI Toolkit | Unite 2022

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi my name is Damian I lead the UI Tech Team at Unity we're sort of the back end of UI Frameworks and today I'm going to be talking about extending the unity editor with custom tools using UI toolkit if you haven't tried UI toolkit yet it is a UI framework that we're building at Unity it's meant to replace I'm GUI on the editor side as well as yukui or Unity UI on the runtime side so game UI and it's also heavily influenced by webtech like CSS and HTML but it's otherwise all built specifically for unity's needs and with that introduction out of the way let's get started with our demos so today I'm going to spend most of my time in unity 2022.2 it's a beta it's going to come out soon and of course this will be the 22 LTS because I want to show some of the new things that are coming to UI toolkit I will also briefly very briefly show at the end 2023 and what we're coming what's coming later down the line um but uh yeah let's get started so this project is very simple game uh we've actually used this project before in previous UI toolkit demos and as a result all of this UI is using UI toolkit but we're not going to spend that much time on runtime UI today we're going to focus on the editor side so let's start with this play manager game object and play manager monobehavior this Model Behavior actually implements this entire game uh it's all done in one component and it kind of shows there's a lot going on here and what we're going to do today is try to simplify this so something that's new specifically in 22.2 and obviously future is that the default inspector which this is is now using UI toolkit to generate itself and draw itself and of course I'm just a random person so why would you believe me here's the UI took a debugger very useful for UI toolkit if I pick element you can see all of these are individual visual elements in UI toolkit and that includes the reordable list itself which was ported recently now that's all great but what's really great about it is that you can Implement custom property drawers directly in UI toolkit you don't have to if you've done this in the past you would know you don't have to create a fallback for I'm GUI in the case where it's used in a default inspector you can just go straight to UI toolkit so let's start with that um down here we have this reordable list of Tanks it's just called tanks if we look in the background here we can actually see what this field is it is an array of tank managers these tank managers are not the actual tanks that you see in the when the game is playing they are really just proxies that are used to actually spawn and initialize those tanks when you uh start the round so coming back to the editor you know we have starting Health spawn point stuff like that and we're going to create a custom property drawer for this tank manager object so basically cleaning up this section here so this is what this looks like to create a property drawer you just inherit from property drawer and you have an attribute where you specified the type the field type that you want to define the drawer for for you I took it specifically you want to override create property GUI where you get the serialized property that is being bound to and you can use that in the UI to to bind your UI and in this case generated uh in this case we're we're just recreating the default uh implementation of this drawer which is basically create a single property field which will do the recursive generation of the UI based on this property by the way for context reminder slash introduction serialized property and its equivalent parent serialized object they're just wrappers around the actual objects that we're inspecting so in this case tank manager and they provide a kind of pre-reflected list of fields for the UI to bind to and generate from in case you weren't familiar so with that implemented let's see what that does nothing because we just re-implemented the default that's fine uh now we have something to start with so let's uh let's go further the sorry going back here the goals I have are one get rid of this fold out I don't really need it it's a bit more more complexity than I need and compensate that lack of complexity with uh adding a an embedded inspector for this transform which serves as our spawn point right now I have to select the transform individually change the values change the location and then come back here what I want to do is just change the transform position values directly here embedded in place so two goals and we're going to achieve those all at once copy paste off screen all right so to start with we no longer just return a single property field as before we instead create a new route that is just a simple visual element visual element is the base class of all elements in UI toolkit everything derives from it and it doesn't really do anything by itself it it serves as a nice container and parent for the rest of our UI then we create three property Fields individually for exactly the fields we want to expose um then what I do is I create a box element which is again it doesn't do anything it's just decorative so it has a darker background but it serves nicely as a container for the mini embedded inspector we're going to spawn for the spawn point transform then I register a callback of these change events type object on the spawn point field these are going to be sent every time the user changes the value to another spawn point another transform and it's also going to be called the first time this is initialized by The Binding system so it's perfect this is where we can do all of our work to to bind and create the that mean inspector okay I'm going to stop teasing you with that compiler error thank you and actually create the function here's our callback we start by cleaning up the spawn inspector the Box because again this is going to be called every time the user changes the uh the object we're going to look at the new value in the event itself and we're going to use that to initialize this inspector element this inspector element is a visual element which is why we can add it as a child of Spawn inspector and what it does is it takes whatever object you're binding to and it first tries to find any any custom inspectors that you define for it and if it doesn't find that it generates a default inspector for you which basically means going through the the object fields and generating a property field for each so doing this kind of automatically and it's also the element that's used in the actual inspector window itself so it's we're literally uh nesting inspector elements inside of each other okay let's see if that worked out and it did so I got rid of the foldout because the root is now just a simple visual element and I embedded this mini mini inspector inside of each of these rows I can now make changes directly here to the spawn transform location and I can also if I change the spawn point itself you can see it's being rebound to the new new values okay that's looking better now we can move on to this upper section over here and start simplifying it making it more useful so we can actually start by making some changes directly in the monobehavior script itself so let's do that you can use these attributes like hide inspector there's also header shells and maybe shell Force I want to turn into a slider so I'm going to do a range attribute from 10 to 40. to two there we go slight cleanup kind of slider got a header some stuff hidden it's some progress now this is all really ancient history these these attributes existed for a long time I'm not introducing something new but I do want to point out that what is somewhat new is that these are now implemented entirely in UI toolkit and it also means that you can Implement your own versions of these in UI toolkit but uh obviously we want to go a lot further than this so it's time to take the next step and commit and create a whole new custom inspector for the play manager Model Behavior same principle well same pattern as uh custom property drawers so we have an attribute where we specify the type for which we're defining the custom inspector and we inherit from editor it's just the name for custom inspectors and for you I took it we override create inspector GUI same idea we create a new visual element as our root we fill it with some UI some children and we return the root now in this case I'm not going I'm not creating a new inspector element and binding it to in this case ourselves I am simply using this utility to create a default inspector for ourselves we're literally binding to ourselves I'm recreating the default inspector basically uh but it is kind of important to not use new inspector element bound to yourself here because like I said what it's going to do first it's going to try to find a custom inspector defined for well this which is the play manager it's going to find this custom inspector it's going to call it which is going to create a new inspector element which is going to repeat the process uh onwards and onwards um so you don't want that but this this works just fine and it's a good starting place to to start modifying the inspector with so if I just confirm uh don't worry about that error you can see it didn't see anything um yeah we're back to the default inspector okay but we want to customize this so obviously we don't just want the default inspector but I know that I will eliminate a lot of the fields that are currently in the default inspector I'm going to focus on very few fields and expose very little so I still want to leave myself the option to access the default inspector in case I want to change something I don't know more advanced so let's do something quick here to do so what I'm going to do is I'm going to create a fold out element just classic fold out expanded collapse State and I'm going to put all of the default inspector inside this foldout and then add the folder to the root um before I dive into the result I do want to point out quickly this view data key attribute um if you set this to a unique value inside of this inspector or this whatever editor window you're in uh it's going to let the fold out remember its expanded State between domain reloads closing and opening the window or even editor restarts whereas if you don't do this it will always be recreated with the default state which is expended I believe um so if you want to retain that state it's also useful for scroll view to retain scroll position um it's good to give it a view data key so there we go so now the whole inspector is inside well fold out and I can get rid of it okay now I could start again um customizing like I did with the property drawer in C sharp but you I took it really starts shining when you use the assets that it comes with so like I mentioned in the beginning um UI toolkit is heavily inspired by webtech so it's HTML equivalent is an XML based asset text asset called uxml where you can Define the layout and for the equivalent of a style sheet or css is an asset called USS which is really just CSS style sheets and we could create this we could create both of these Assets in text in Visual Studio but we also have a dedicated tool inside the UI inside the unity editor called UI Builder where you can visually author these assets so this is that tool UI Builder and to start off with let's actually create something button and you can see the uxml being generated down here that's what it looks like button now right away you'll notice that the button doesn't really look like any other button in the editor and that's because the uh the UI Builder is defaulting to uh for for the purpose of creating runtime UI so it has a very basic runtime theme that's that's super basic intentionally so that you can you know put your own styles on top and we don't interfere that much so it's just barely functional but we're creating editor extensions here so what you want to do if you're going to do that is select the canvas and turn on editor extension authoring and now there's two things that are going to be changed the library is going to gain additional controls like I am going container uh you know toolbars and property field for example so editor only controls and also you'll have access now to the editor theme so you can switch to the activator theme and now the button looks like what you'd expect so let's save that okay now let's uh bring these two together so the easiest way to do that is to expose a public visual public tree asset like that and then because this is a scriptable object it's an actual asset if we click on it here this is the play manager editor that I'm defining here we can see our uxml field that we just exposed and if we take our play manager editor uxml that we're building in the in the UI Builder let's drag that over here and now they're a match made in heaven so what I can do now is I can just assume that it's never going to be null and directly clone the tree into the root this function basically just goes through all of the XML tags in this document and it you know actually instantiates visual elements from them save let's go back play manager and there's my button okay uh let's I don't know let's add another button we can never have too many buttons now you'll notice that the updates are not reflected immediately in the inspector um we actually have a feature for this it's called UI toolkit live reload it's not turned on by default because it's a little bit expensive to monitor all of the assets all the time in case something changes but if you're going to work in a single window for a long time it's very useful and there we go now we have updates and if I add another button we'll have three buttons amazing and you'll notice that it even does this even though I did not save this uh document which is it's so incredibly convenient that it's very easy to forget to save and you should not forget to save always save um right so now we are free to start authoring we have everything hooked up you know we can create property Fields directly in the Builder just like we did in c-sharp we can bind them to I don't know this sorry um shell random range and you can see in the inspector use case it's it's already bound it comes even with the decorations that we added originally so all the attributes are applied the property field takes care of all of that we can even go even further and bind to something crazier like the tanks array and then that property field which is recursive recursively generates its UI will generate this reordable list so these property fields are quite they're like mini inspectors basically but I want to go even more custom uh even outside of property field I want to be precise in what UI elements I create and I bind and that's because I have well three goals for this uh this upper section so goal number one is I want a very obvious UI to represent the game State and game mode just while I'm in game I want to quickly look at the inspector and see this information second goal is I want to expose just shell Force make it very easy to edit and also very easy to tell at a glance what the value is between this range because I'm going to do some balancing work uh specifically around this and then I want the tanks array but not an editable reordable list with mini inspectors and all that I just want to read only flat view compact view just to get a glance of how everything is happening in the game so let's start with that and this is where the UI Builder starts to shine because it's very easy to just mess around and create uis pain one get to watch me create UI live let's delete that two panes let's create a game section for the game State and game mode I'm gonna create a shells section for the shell Force and then for now in the second pane I'm just going to have this message section we'll we'll come back to the tanks array later on so for the game section uh we want game State copy that and I'm going to use a enum field call it game state and paste The Binding path in here I want to get rid of the label and for the type we want of course game state find in the plane manager this is relatively new feature we now have autocomplete for the type attributes really convenient for the game mode I'm going to use a relatively new control as well called radio button group and again we remove the label and we bind it to game mode and the choices are normal and God mode obviously now for the shells I'm going to use a simple slider for the easy modification part of the goal so again get rid of the label binding path is M shell Force and we have a low value of 10 high value 40. and we want the the input field actually it's kind of nice to see what the value is and you can see this is fully bound it's same same binding as the one in the default inspector it all just you know works there okay now I also had the sub goal of making the shells shell Force very visible in the inspector and I have just the control for that this time I'm going to use a default a custom control that I wrote specifically for this which is this pie chart which has similar controls so we have low of 10 high 40 . I'm just going to give it a mock value here just so it's not broken but ultimately we're going to bind it to Shell Force just like the slider and voila we now have nice visual representation of the shell Force now this is a good opportunity to dive in to this pie chart control a little bit see how it's made so this pie chart is a custom visual element this is something that you can make yourself um all you have to do is really inherit from a visual element in this case I'm inheriting from a sub class of visual element which adds a little bit more functionality which is bindable element because I want to be able to bind it to uh to data to a serialized object you do need to if you want this element to be exposed in uxml as a tag or by extension in the UI Builder you have to Define this uxml Factory where you basically say what the name of the tag will be and then if you want attributes as well in uxml and again by extension in the Builder you have to you use the uxml trades class for that and you can see here I have low value and high value the other thing I want to point out and that's important specifically for the UI Builder is if you want the the inspector here to work properly you have to also expose these public properties that are one to one with the uxml traits that you define up here so and they also have to be named the same except camel case so here I have low value and here I have low value we're going to improve this API in the future but for now something to keep in mind okay for the drawing part itself uh the fun starts in the Constructor so we create a simple visual element again and all visual elements have this callback called generate visual content this callback we get the mesh generation context which you we can use to draw any mesh we want so we can just give it vertices and polygons and draw a mesh but newer unities have this new API Which is higher level it's called painter 2D and it lets you draw shapes so here you can see how I'm drawing you know part of the pizza um I have a fill color set begin path move two an arc command with the radius and then fill so you know five commands and I draw this pie chart instead of having to do all of the manual tessellation and mesh generation myself and doing this also gives you all of the advantages we have on the Shader side to create really really smooth edges like that and that's pie chart so that's looking good next up is this message which is just a label um let's change some inline Styles here let's increase the label size get fancy here with letter spacing and I have a message already written double click to edit in place and there we go okay I need to turn on text wrap which we can do here and yeah this is just I just wanted some way to show that we have Rich Text Now on UI toolkit um it's been a long time coming it's great to see back um and it's really nice and that includes hyperlinks it's been a long time requested feature so this is not just you know fake blue I can actually click on it and we go to I know very risky Choice here for URL but uh yeah unity.com and that's rich text so let's move on to Styles now I have the layout that I want and now we can start playing with Styles so I've already shown how to change Styles in line This these styles are going to be applied only to this element but what's really neat is when you start creating style sheets and start sharing Styles between many elements so sure create a style sheet and I'm going to create a simple selector here called section this selector is basically looking for all elements that have this class or tag if you will we can see what current class assignments are and we can assign it really easily like this drag and drop so my sections now are sections and now with this selected we can Define the styles for all of these elements at the same time and we're going to start with the background color and instead of setting a hard-coded value I'm going to actually set a variable to a default editor variable for the color default background there we go now the reason I want to do this is well I want to be a good citizen of the editor I want my UI to look consistent with the rest of the editor it's always looks better that way but also I get this functionality for free where if the user uses light theme I will get the new value for the light theme on this variable I don't have to manage different style sheets myself in c-sharp for the themes so pretty useful next up I'm going to just give everything a bit more breeding space the margin and of course I realized that I forgot to assign this class to the message just totally not scripted there we go now they all have margin and I want to give them all some padding as well and there it is okay I just realized that this doesn't look that great there's a lot of empty space we can fix that really fast so I'm selecting the pane I'm just going to change the flex direction to row there we go and let's make it even nicer give each section A width of 50 percent and uh 50 percent and now everything's nicely divided okay cool now we're ready to take on the tanks array or rather tank manager array so we have a few options for these there's lots of different containers that UI toolkit comes with the oldest one is of course the list View and actually what am I just talking without showing uh I can show some samples for this using this relatively unknown window that's been in the unity editor for a very long time so on the UI toolkit you have the samples window and this is just a very uh brief collection of controls with example uses for those controls actually it's a single sample for each one and because this is all implemented emui toolkit of course the controls themselves are live like they're functional um you know you can see them in action see what they look like and you also see how they kind of behave in each of these uh assets so you can see what the c-sharp looks like what example your assess would look like for this control and uxml gives you a quick idea of the control now this is these are very simple examples and obviously if you want real samples and detailed documentation I highly recommend going to the online manual uh if you've been there before and you found it not so filled out I recommend checking it out because uh there's been a lot of progress in the last few years in the last few months specifically so uh give it another chance but the reason I'm here is I want to show list view entry View uh this is a big one trivia is now public it's been kind of an internal tool for a long time but now it's been finally promoted to public so you have a tree View and the list view has been around for a long time but I just want to use it as a kind of an example of how these these uh collection views if you will work so these are all visualized containers and what that means is that when you first create this list View even though the data is actually quite large it only creates elements for what can be visible at a time so in this case it probably creates 14 rows worth of elements and then as you're scrolling it's just rebinding those same elements to new data so it's it's really really good for for performance when it comes to large data sets and the way this works in code is there's uh two callbacks we have make item where you define right here how what the UI looks like for each row and then you have bind item where you define how the data will be bound to that that UI so we're creating a label here and for The Binding Parts we're taking that label and we're assigning the text to whatever is at this point in the data and then the last thing we need to do is attach the items array to the item Source on the list View so this is the general pattern for all of these containers you're making the UI you're binding it and you're attaching the source but we're not going to use the list view we're actually going to use an even more fancy new control which is the multi-column list View and it is so fancy that it's not yet available in the UI Builder library but it is available in uxml so we can just add it manually always make sure if you're going to start messing with the uxml in code outside of the unity Editor to save in UI Builder first otherwise you will lose those changes so let's copy paste some uxml here 's the list view okay we can even Define The Columns directly in uxml and that's all we really need to do in Visual Studio once we come back to Unity we have the multi-column list here and now we can continue to make edits and author in the UI Builder with the multi-columnist view we just needed to instantiate it this way for now and that's exactly what I'm going to do I'm going to give it its own section I don't call tanks and drag it inside and assign the style that we already have there we go it has its own section okay now Comes The Binding part for this control it's a little bit more tricky than just giving a binding path because we have to specify exactly how each column is bound so for that we need to return to C sharp play manager editor and now I'm going to paste a whole bunch of code all at once try to keep up I'll go through it don't worry Don't Panic um so we start here this is the familiar territory we start here with the initialization of the list of the multi-column list View by well same thing as the list view attaching the item source which is our tanks array then we move on to the equivalent of make item which is make sell because we actually have to call it on each for each column so each column can have different UI for each uh row so we have to do that for each one so pretty simple for name and starting Health we just have a label and then for current health we have a progress bar now the equivalent of bind item is of course buy and sell again per column we get our visual element that we created we got an index and we decide how we want to bind that element so for name it's pretty simple for starting Health we actually want to literally bind it different kind of bind this is data bind um so we are signing at The Binding path which is inside of the tanks array itself inside the tank manager on the starting health field and we have to explicitly call bind because this is this can be called later after the inspector has been originally bound you know as the user is scrolling so we have to explicitly call bind here but it's still to ourselves then on the current health side it's a little bit more complicated because we have two states either the tank instance itself like the actual playable tank prefab instance exists um or it doesn't so if it doesn't exist it means that we are not in play mode or we haven't entered a game and in that case we're going to assign the current health to the starting health so same as above here same bind but if the tank does exist so we're in play mode we're actually going to bind it to the current health field on the tank health component on the tank instance so very deep and very remote but it still works so we're binding to the actual instance of the tank and checking its current health same bind but this time with the different serialized object that's created from the tank instance from the component on that Tech instance there's also an unbind which is just a mirror so it gets called when your element when your row goes out of scope and then we just need a little bit of extra logic here to detect when the game state has changed and we literally use the UI enum field that we created here to detect that and we just call refresh items on the list View this essentially just gives us the opportunity to decide this question again so once we enter play mode we're going to probably have an instance where we didn't before and we're going to rebind to the to the tank instance so simple well I wanted something more juicy to to show you how it's made um so yeah that's it that's what it looks like fully bound and of course if we go into play mode and we start causing Havoc you can see the values updating okay back to the Builder I noticed there's something missing here um so the multi-conomist through comes with a lot of features and actually I want to point out this one which actually applies to all of the views all the collections we now Support also Dynamic Heights originally it was just fixed it's a little bit more expensive to run but you know if you need Dynamic Heights you need Dynamic Heights and that's supported now um but the feature I wanted to expose is this one alternate colored Rose best feature ever also this comes with full column manipulation resizing turning it on and off um I didn't Implement that in this example but there's also sorting and drag and drop reordering that's all supported in this control all right now there is a fourth goal that I uh sneakily didn't mention and that is I want a big button here to restart the round the game uh button button okay so there's this button here let's call it restart which looks super boring and um I uh I kind of want to make it look like the in-game button so I did say that the game UI is using UI toolkit the editor UI is obviously using our toolkit so why not just combine the two so we're going to add an existing USS this time I'm going to go to demo UI menu open and here's my menu button class just going to drag that over and voila we have the same Styles as the the UI in the game and since we're here we might as well improve the game UI why not um so here's another fairly new feature which is transition animations in USS lets you add a transition animation in this case we're just going to give it a little delay so now if I hover you can see it's a little bit smoother and I can preview this in the your Builder too and I also want to change what the hover style actually is so the style we're actually transitioning to when you're hovering that's this one here you can see the the color actually I want to change that color yes much much better and for this I'm going to use yet another new feature which is transforms this used to be available only in C sharp but now it's also available in Styles which means UI Builder which means I can make it part of my transition give it a bit of a extra scale yes cool save and now if we go to play mode we get those Styles in play mode as well because we were literally changing the style sheet that's used in the game okay so we're done with this UI um now let's see where else we can put this UI so the first thought I have is right now I have to make sure I select the play manager every time I want to actually make changes to it or see it and I don't want to use the lock feature I just I want this to be available at all times no matter what my selection is so the answer to that usually is to create a custom editor window and that's pretty easy now that we have the UI especially since I already wrote this all you have to do is just change the false to true and you're good um here's the basic editor window inherit from editor window this is a typical way to spawn the window so we insert a menu entry and top menu get the window by type set up title then in the unenable I find my unique play manager game object by tag and I also find the play manager component and then the UI is all created in this create GUI function so in here I create a scroll view to act as my container again view data key to remember scope position and I use the Google inspector element to bind to my play manager uh if you've used you I took it for a while now and you've created Windows before uh it does work to create the UI in the unenable as well but it doesn't work so well once you start using that um UI toolkit live reload feature that I showed for the inspector so this this thing here it's better to to create your UI in the create GUI function so yeah that compiled if we go to play manager here's the editor window and of course it all works the same same UI very easy to duplicate somewhere else and speaking of somewhere else uh why don't we just push this a little bit so again runtime UI is UI toolkit editor UI is UI toolkit why don't we try to put this in the runtime I mean we're all the data that we're binding to here is runtime data it's monobehavior data so can hard to try here's the document for the runtime UI and here's the thing we just created I'm just gonna drag this over here okay no explosions looks good the only thing that's broken is the missing background colors and we actually know why they're missing um remember that I use that variable the editor theme variable which obviously does not exist in the runtime theme but we can just redefine just that variable for this runtime style sheet and fix that really quickly without touching the editor side now we can't Define variables new variables in the UI Builder again that's going to be fixed but for now you have to do this in a text editor here's how a variable looks like in USS and I'm just I have the same selectors before now I'm actually going to just copy this and paste it here this works by the way if you didn't know it's a pretty useful tool you can just you know copy anything else here and paste it in a text editor and vice versa same for you XML and there we go it's fixed and now if I dive into this so I actually have a uxml inside this uxml right I can actually dive in while keeping the context and now I'm inside and if I click on this section and I go to background color view variable I can clearly see that this color's default background is now coming from our style sheet not the default style sheet just something to to know um and also yeah this feature is pretty neat too you can dive into a child uxml and make changes and then go back to the parent so now if I press play go in here we go we have our UI in the game now the problem is it's not bound to any data uh I could be I could bind it to data of course I did that for this UI for for uh previous demo but it involves a lot of manual c-sharp code and we really wanted to improve that experience so with that let's take a quick peek at 2023 really quick um so this is Unity 2023 don't pay too much attention to this exact version that's not when this feature will come out just happens to be my current Branch but it should be out in 2023 the the cycle so that feature is the new binding system for UI which works in runtime there we go and I can change it on the editor side runtime side it's all there now very quick sneak peek at the API so here we're talking about runtime UI so we're actually we're actually back in the player play manager Model Behavior we're in the on enable function all UI toolkit is driven in the runtime through this UI document component so we get a reference to that this component has the equivalent root visual element that we had in you know the uh the the editor window so we get access to that and then the two new apis that I want to point out there's more but a sneak peek sneak peek is the data source attribute on any visual element where you can specify where your data is coming from in this case we're just sending it to ourselves and uh the the new binding system actually uses continues to use the binding path attributes which is great for this case because all of The Binding paths that I set in the Builder throughout this demo will continue to work in runtime and then the other new API that I want to point out is set binding which is how you actually create a binding which is an object it has some settings that you don't see here like you know is it bi-directional or not and it also you also have to specify the actual attribute on the element that you want to set so for the editor side you know if you bind an object field we're always going to bind to the value attribute of that element but the new binding system lets you pick which attribute you want to bind to so you can bind to not just the value you can bind to many other attributes you can even bind to style properties and uh and that's about it so that's that's the new binding system uh sneak peek at any way and with that that's a thank you uh thanks for sticking around and uh of course we're very active on the UI toolkit Forum so if you have any questions after this event you can also go there and have a good one
Info
Channel: Unity
Views: 58,060
Rating: undefined out of 5
Keywords: Unity3d, Unity, Unity Technologies, Games, Game Development, Game Dev, Game Engine
Id: J2KNj3bw0Bw
Channel Id: undefined
Length: 48min 38sec (2918 seconds)
Published: Thu Nov 17 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.