Here it is! A rough-cut of what it’s like to dock at a space station to buy trading goods. Contain your excitement, folks, there’s enough here for everyone.
I wish I had the patience to get the jump gates working before I’d recorded this, but that’s a system I just haven’t worked out quite yet and requires some thinkin’.
Menus and Messaging
I had updated the menu system a bit. The original system had each menu parented to an empty game object which I could latch on to in order to show or hide the visual elements. In order to keep things more organized, the three different menu controller objects are now parented to a “purpose game object”. What the video shows is the “station purpose” menu controller. It’s an empty (invisible) game object which contains all of the game objects which contain the menus needed to drive the station experience. The reason behind this is that I can turn the top level controller into a prefab and have the station menu framework “portable” and self contained for each and every scene.
I also started to switch over to a messaging based system for inter-object communication. Unlike traditional business app development, Unity allows you to write scripts and attach them to anything. In some cases, what you write determines where it needs to be attached, like a movement script to the player object. In other cases, it can be open to interpretation. For example, the DOCKING process that shows the “Press ‘F’ To Dock” button was originally in a script attached to the station trigger, because the station trigger is what detects the player when he crosses the trigger boundary. Responding to that “F” key, then, needed to display the station menu, which required communication from the trigger to the menu controller — which is a totally different object in the scene. And then when the player wants to leave, he has to click the UNDOCK button. It doesn’t make sense to have the menu control the player’s undocking event, so the menu button needed to communicate with a script on an object that makes sense to handle undocking process, which happened to be the same object which handled docking. Not super optimal, but it’s logically organized to some extent. What also complicates things is that each object that raises an event needs to know specifically who to send it to, so an event-raising object needs to Find() or have an Inspector-added reference to the object and script which will handle the event. That’s called “tightly coupled” and removes a lot of the flexibility that Unity normally provides.
Messaging is a kind of lazy developer’s way of communicating between objects. It’s less about sending a message than it is shouting someone’s name in a crowded plaza. In a messaging system, a script subscribes to one or more “named events”, like “onPlayerDocking” or “onCommodityListSelection”. That subscription is called the listener and also contains a callback, a reference to a block of code that is called when the listener hears it’s named event being “shouted” by the other side of the equation, the broadcaster. These names are broadcast in response to an event, like a button press or a collision. The benefit of a messaging system is that objects don’t need to know about one another…they don’t even have to exist, technically, because when someone shouts “DOUG!” in a crowd, if there’s no one named Doug, then no one responds…end of story. While this is way more flexible than tying objects together in a very specific way, it also has it’s potential downsides. I’m not sure what the processing cost of messaging is compared to tying objects together. There’s also the fact that one shout can be answered multiple times. That’s pretty good if you want to announce the presence of a player in a pirate-infested asteroid belt, but you also don’t want every pirate in the scene to swarm the player; that would be disastrous. And as I said when I started this paragraph, messaging can be looked at as kind of lazy. Shouting an event with the expectation that it’s going to get picked up by someone somewhere is fine if you obsessively catalog your broadcaster-listener pairs, but forget one listener and you’re in for a potential world of hurt trying to debug a system with no real connection between the two end-points.
For now, I returned to the tight coupling of the menu buttons by putting the “big event handlers” at the top of the UI hierarchy. I know that the station-purpose tree is always going to be there in that form, so having events handled that far from the object that raises them is OK by me. I’m keeping the messenger system on stand-by for other situations where I’m sure I’ll need to set up some kind of “fire and forget” system where tight coupling isn’t going to be a wise decision.