How to best structure the connection between the UI and the inventory components

That’s the MVC/MVP approach @trizZzle suggested above. It’s common in web development. There’s a discussion about MVC here in this thread from last month: Awaiting Animations Triggered by Signals It is a good approach, and it will work, but I personally think it’s overkill for this application.

@Triky Based on your requirements, I think you would benefit from defining a relationship between VaultComponent and UI. In Object-Oriented Programming, we can break down everything into is-a and has-a. In other words, an object that needs functionality either is an object with that functionality, or has an object with that functionality. Godot does not support multiple inheritance, and encourages Composition over Inheritance with its Node system.

So let’s start with a Chest.


Chest
So Chest either has a VaultComponent, or it is a VaultComponent. Likewise, it either has a UI, or it is a UI Let’s assume that a Chest is an object that appears in-game either as a 2D or 3D object. I’m going to go a step further, and assume it can be either or both, because you might want to use this code again on another game. By assuming that, it will inform our decision on is-a vs has-a.

If we know that Chest is an object that appears in the game world, it makes sense that its appearance and interaction with the player (like an Area2D/3D to trigger interaction) is all a part of the object. Now, we could inherit from VaultComponent or UI. It seems to make less sense that it would inherit from UI, because it’s not UI - it’s an object on the screen. So let’s say it has UI. But now what about VaultComponent?

Chest could inherit from VaultComponent, but we will run into two issue there pretty quickly. First, Chest is in the world. It is either a Node2D or a Node3D because it needs positional data in the world. So to inherit from VaultComponent, VaultComponent would need to inherit from Node2D or Node3D. Not necessarily an issue, because you’re only doing one game and you don’t need to future-proof.

The second issue though, is that when we go to design how this looks with Player, if we decide Chest is-a VaultComponent, then does Player also become a VaultComponent or do we create an Inventory object that is-a VaultComponent, even though it doesn’t have positional data because it does not exist physically in the world.

Based on this, I would recommend that Chest is a Node3D, and has both a VaultComponent and a UI. (You may have an objection here that your UI system is its own thing and monolithic - and I would ask you to hold onto that thought.) We are not done though. The original question, was how do these two things interact?

  1. Signals?
  2. Direct connection?
  3. Profit???

Let’s say that signals is our default fall-back position for a second. If we are going to use a direct connection, what is the relationship between these two objects? We’ve got two possibilities we have discussed: is-a and has-a. There’s actually a third: None. And a fourth: Sibling.

Does it make sense for VaultComponent and UI to have no relationship? Not really. You can’t have one without the other. In fact, they are suspiciously like the Model and View parts of an MVC architecture.

Does it make sense for them to has a sibling relationship? Well, if we go the MVC route - sure. They don’t technically need to know about one another. But, what exactly does that get you? It gets you another object. VaultComponentWithUIContainer to wrap up everything, including the business logic. Or… something very decentralized (which then we would fall back on our signals.)

But really, if we look at the two pieces, they cannot function independently. One without the other is useless. So it makes sense that they know about one-another. So how can we combine them? Is it an is-a or a has-a relationship? Well, we’ve already determined that one cannot exist without the other, so an is-a relationship doesn’t make a lot of sense. That leaves us with has-a. So which has which? It probably makes more sense that the VaultComponent needs a UI, than the other way around.

So if UI is a node on the VaultComponent scene, the Chest would have VaultComponent as a node in its scene. And VaultComponent and the UI would just talk directly to one-another. When Chest gets an “interact” action from the Player, it calls the open() function on VaultComponent UI becomes visible, and either asks or is fed a list from VaultComponent of what’s in it. When the Player drags-and-drops, the UI calls the appropriate function on VaultComponent.

If you have an complete, integrated UI, this may feel like an issue. How do you make sure there’s enough room in the UI, etc? There are a couple of ways to handle this. First is floating windows = just make it a feature that the user can move chests, inventory, etc. around on their screen. Second, you can actually pass the VaultComponentUI as an argument signal to the MainUI and have it insert it in the right place.

Now that we’ve looked at a Chest, let’s look at Player.


Player
Really the player has an Inventory. In most single-player games, that’s usually single screen. In an MMORPG, it’s lots of mini-containers in your inventory that open up for more space. However you implement it, I would recommend that you have an Inventory node on your player, and inside that your VaultContainer In that way you can have your inventory open/close code inside your Inventory node and not clutter up your Player node code. But really, it’s up to you. Because at this point, you can drop that VaultContainer on anything you want and just interface with it.


Anyway, these are just my thoughts. The MVC approach will work, but as I said in the other thread, it’s really most useful in a distributed client-server architecture - which you don’t have here. Even if you had a multiplayer game, I wouldn’t recommend MVC for the VaultComponent itself. That said, I love MVC. When done well it’s very clean and easy to read. When done poorly - it becomes a mess of spaghetti code. So if you do use it, I recommend you just make sure you have your model, view and controller clearly defined.

2 Likes