UI Toolkit Runtime Data Binding and Logic

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today I want to go a little further with UI toolkit and explore not just the inner workings of our inventory system but data binding as well you can see on the screen I've bound the coin display to the coins that are kept in my inventory and I'm giving myself another coin every frame we'll start where we left off last week by covering the model in the controller and then we'll take it another step further with some bindable properties it might be useful to watch last week's video before watching this one so I'll put a link for it but it's not strictly necessary let's get right into it last week we implemented a UI for our inventory system that publishes an event when one slot is dragged onto another let's have a look at these other seams in our diagram in our system the controller listens to events published from The View and from the model and then makes decisions about the inventory system let's dive into the part that the player doesn't see we'll start with the details which is static data that all items of a particular type share next an item will be the representation of an item or stack of items in the game the items will live in an observable array that will publish an event when the contents of the array change the model represents our inventory in the game and all of its properties finally we'll look at the controller and how everything is wired up together when we're done all that we'll have a look at binding properties in the model to the view let's take a look at one of these item details in unity these are common properties to all items of a particular type type any item that we instance in the game will have a reference to these static details this is going to let us use the flyweight pattern for all of the items since all squids share the same Sprite and they can only stack to one have the same description Etc let's have a look at this in code if I take away all my Odin tags you'll see there's not too much to this really it's exposing a few properties there that we can manipulate in the editor but the most important thing here is that it has its own create method this gives us a a little Factory method to be able to create an instance of an item that has a reference to these details at runtime I'm just going to restore my Odin tags for the rest of the demo but let's go and have a look at the item class next so an actual item is going to have its own ID to represent itself in our world in our game a reference to the details and it's going to have a quantity so in case we need to stack it in the future we'll add some methods so that an item knows how to serialize and deserialize itself for persistence now that we're familiar with those things we can start having a look at the model now the model is going to house all of the items inside of an observable array and if you've been watching this channel for a while you know about an observable list already this is very similar so we're not going to create it from scratch we'll just walk through it let's have a look at the interface first you can see that it's going to publish an event called any value changed will'll invoke this event anytime one of our public methods is called it also exposes a few properties like count and the ability just to get whatever's inside any particular index let's close that up and we can have a look at the actual implementation so there's an array of type T we assign an empty delegate to the action so that we never have to check it for null its Constructor accepts a size as well as an ey list which could be potential items to start the inventory with the private method invoke will invoke the event with a reference to the items we come down a little bit further we can see the swap method uses tupple deconstruction to just swap one item with another in the array next we have a clear method that will empty out our array try add will'll try to add an item to the inventory in the first available slot if there is one try remove we'll look for a specific item in the array and try to remove it next we're going to encapsulate those events that are published by the observable array with our own event we'll call that on model changed is just a wrapper for the event that's one level deeper down so when someone subscribed to on model changed they're really subscribing to the event published by the array and likewise when they unsubscribe in the models Constructor we can pass in some starting item details as well as the capacity and it'll use that information to create the new array iterate over all of the item details that we've passed in and try to create and add one new item into our array now we'll add a few public methods here that will be operations that we can use to manipulate our inventory array now these first few method me don't really have too much logic they're just passrs really even the swap method is essentially a pass through the combined method will have a little bit more logic basically it's going to Total the items in both slots put the total into the destination slot and clear out the source slot and that's it for the model we're going to come back to visit this again when we talk about binding at the end of the video now let's have a look at our controller that's going to do all the thinking and logic having to do with our inventory system first of all this is going to need a reference to both the view and the model and it's going to know the capacity of the entire system these values are going to get passed in through a private Constructor we're going to create a builder for this so we'll keep the Constructor private normally when assembling a service like this you want to make sure that none of the dependencies are actually null previously we've used techniques like a preconditions class that would actually throw exceptions debug. assert will do something similar except it's not going to Halt the operation of your program but it will give you a nice Orange message in your console log once we're pass that we can assign the references to their fields next I want to start a co- routine that's going to do all my initialization but this is not a mono Behavior The View on the other hand is a mono Behavior so I can run a co- routine based on that mono Behavior even though the initialized method is actually part of a pure C Clause so let's define that as I numerator initialize the view already has a method defined to initialize its own uxl document we just need to pass it in the capacity so we'll wait for that to finish and then continue on by subscribing to events we're going to fill this part in in a little bit we'll have to subscribe to events in the view the model and then we can just run a refresh to make sure that the view is up to date when we start let's quickly put together a builder for this we're going to pass in a reference to our view but the model is something that we'll create within the Builder we can pass in our starting items here we'll Define an initial capacity of 20 and then to enforce that we are all always passing in the view will make it a parameter of the Constructor we'll have some additional methods here so that we can pass in optional item details if we want starting items we could also Define a different capacity than the default if we wanted to finally in the build method we're going to create a new model we can start that off with either the optional starting items or just an empty array then we just run the private Constructor and return our new inventory controller so now we can build a controller let's connect up those events I'll just collapse the Builder section so it's out of the way so the view has an event called on drop that we want to subscribe to and handle the model has its own event on model changed we also want to be able to handle that and we'll need one additional method that just makes sure that the view is up to date with the model to refresh our slots what we need to do is just iterate over our capacity getting each item from the model if it's null we want to set that particular slot in our UI to empty slot otherwise we'll pass in the details the slot needs to know about itself which item it's representing its icon and quantity now just above that we'll have the handle model change now when that event fires it actually sends us a list of all the items right now I'm just going to do a blanket refresh view but you could optimize this a little bit and maybe just refresh the item slots that actually were changed our handle drop method I'll place right above that now handle drop has to handle various scenarios when one slot gets dropped onto another so we'll start with the simplest ones first and that would be what happens if you drop a slot onto an empty slot or you drop it onto the slot that you actually dragged from in both cases we can just do a swap you might be tempted to think let's just bail out of here if we're dropping a slot onto itself but actually I do want all the events to fire right now that's going to make sure that the view is refreshed but in the future we might have sound effects and so on now for more complex scen scarios where we're moving to a non-empty slot I first want to know what type of item this is and that is the ID that's stored in the item details so let's grab those IDs out of the details and we'll just store them locally here for comparison there's really two scenarios here one is that in the first case the IDS match and the item is allowed to be stacked if it's a stackable item we're going to use the models combined method and just pass those in otherwise we're just swapping positions we'll use the swap method we're pretty much done with the controller logic now if you had been looking in the repository before I released this video you probably up to speed on everything that we've covered so far really we just need to come over to the inventory class which is a monob behavior we're going to expose to Unity this will allow us to set up everything we want to construct and have a reference to the view and then inside of unity we can link these things up and use them to create our controller so in our awake method here we can just use our Builder to pass in those parameters run the build method and we're done the inventory should be running for us so why don't we jump back into Unity I'm going to hit controlr refresh my assets and hit play there we go we got our inventory on the screen let's just make sure that everything is working the way we think it should stacking is working great we can swap items around drag into empty slots yeah drag into itself perfect okay that's all the functional we've built so far looking good so with our inventory looking pretty good let's start talking about binding if I want to show a simple value in my UI that doesn't really have a lot of logic and just really needs to display the value of a property that we have inside the model one thing we can do is binding I'm going to use coins for this example so I'm going to add a few styles to my USS file here so we've got a parent container called coins it's very simple just going to display things in a row it has a child that's going to be a 40x40 container with a background image that has my little coin icon and beside that I'm going to place a label in our inventory view we're procedurally creating our UI so right above where we defined the ghost icon let's define a coins visual element that's going to be a child of the inventory that coins element is going to need two children as I just mentioned one is going to be for the label the label will show how many coins we actually have so I'll just create a new label here and I'll add it to the coins now we don't have any property to actually bind to this label yet so I'm just going to put a note in here and we'll come back to this after we've actually created something to bind to it is possible to pass a reference to the model directly into the view and then bind to a property on the model directly but I'm going to create a bit of a middle man here now often you'll hear people refer to this as a view model you see this most often in mvvm archit textures where a view model exposes data from the model in such a way that it's easy to consume by the view it hides all the details generally you use it to expose public properties and commands for binding to support this I'm going to create a new class bindable property which will be of any type t a bindable property will allow us to bind to Something in the model without actually exposing the model our view model class can contain a combination of bindable properties and just regular properties too let's start with the bindable property now I'm going to make this bindable property a one-way binding that means I only really need to have a getter now getter is just a function that's going to get whatever data it is that I want to supply now if you wanted to have a Setter it would kind of go the reverse so I'll just make a note here you could add an action of a type T that would act as a Setter it would just be the reverse of what we're about to do here I'm going to give this a private Constructor that accepts the getter now let's have a public property here that will allow us to execute the getter whenever we want something we're going to add the create property attribute now this comes from you can see up top there using unity. properties this attribute is going to allow us to connect a property to a visual element finally let's have a little static creation method this will give us a little shorthand for creating bindable properties so let's have a look at how we're going to use this in a view model type of scenario now the view model I want to have more information than just the coins we're already passing the capacity into the view we might as well add that to our view model as well then way we can just pass the model and we don't have to pass any extra parameters The View model can really encapsulate all that data now the coins can be a bindable property of type string because I want to be updating the label text to construct The View model we'll actually pass in a reference to the model so we can bind to it and we'll set the capacity and then create a new bindable property of type string and that'll be a bind to the model coins property then as part of the getter we'll call two string so that means our model needs to keep track of our coins now let's jump over to the model and add one more property here it can just be an integer for coins we're close to set up now but when we're creating our view previously we were just passing in the capacity let's change this so that we're actually passing in a new view model we can just construct it right here when we call our initialized view method instead of the capacity we'll say new view model and we'll pass in the modeling capacity here that'll create our new view model The initialized View signature is expecting an integer so we're going to have to make just a couple more adjustments for that if we jump over to the inventory view we can replace that with a view model now that we've changed that we need to change the reference to size everywhere that's going to be view model. capacity and one more spot down here then over in our storage view abstract class we also need to change the method signature down here I'll just scroll down and change it okay with those references all fixed up we can now come back over to the view and actually bind a property to a visual element to do that we can set the coins to have a data source of the view model. coins this means now that coins and any of its children have access to that property on The View model and so we can use the set binding method on the coins label like so what we're doing here is binding the label. text field to the actual bindable property string. value that property path is also a class of the unity. properties namespace now notice at the bottom here there's different binding modes we're using two target which means this is a getter but two Source would be a Setter you can have two-way and there's a couple other ones that two target once will only set it one time just about code complete but I forgot to put my icon in here so let's make that a child of the coins object as well I've already written all the styling for that so it should pop my icon into place how are we going to test this thing what if we come back over to the controller and we make a public method to add coins so we can just taking an amount and then we'll update our model with that many coins now that should be fine and out in the actual inventory class which is a mono Behavior what if we just add one more coin to our inventory every frame that way we'll know for sure if the binding is working let's jump back into Unity refresh and hit play there we go coins is just going up like crazy perfect that's exactly what we expect now you can use the same kind of technique for health or for anything else the bindings can be a little bit simpler if you want to go directly to the model or some other data structure but if you kind of want to have a middleman abstraction like this view model then the binding property can be very useful for you there's a few optimizations you can make to bindings for UI toolkit like versioning or you can also uh set events to fire that lets the view know that it's time to update whatever is referenced in The Binding so far we've really just scratched the surface of what's possible with bindings and some other new features of UI toolkit and they seem to be working on it a lot it's definitely the entire editor is now using UI toolkit I'm sure we'll be talking about this more in future videos as we keep working on various projects and things so uh hit the like button subscribe and hit the Bell if you don't want to miss any of those otherwise maybe click on one of these boxes on your screen and I'll see you there
Info
Channel: git-amend
Views: 2,962
Rating: undefined out of 5
Keywords: unity tutorial, unity arcade game, unity game tutorial, unity, game development, game dev, game development unity, programming, c#, software development, learn to code, learn programming, unity tutorials, unity devlog, indiedev, gamedev, unity3d
Id: g2a4ZK8cEso
Channel Id: undefined
Length: 17min 10sec (1030 seconds)
Published: Sun Feb 18 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.