Following not-so-hot on the heels of my not-so-hot request for UI design feedback (thanks to none of you who responded!), I wanted to post what I’ve gotten so far and talk a little about the standards that I’m going to try to apply to the UI work going forward.
As stated in the previous post, there’s not so much a “right way” to handle the organization of code and objects, but there are most certainly “wrong ways”. The quickest way to identify when you’ve turned down the wrong alley is to try and get your UI elements to work together, to access each other’s information, and to take actions that the UI is meant to enact. That’s why the hierarchy of UI elements is important.
This fine diagram to my left is what I came up with for my main menu hierarchy. I need to preface this by talking a bit about the data controller though. The data controller is an empty game object — which means you can’t see it in the game — that has one component: Database. Database is a C# script that handles tracking the state data (stuff that changes when you play, like your character and your progress) and main data (stuff that is always the same, like item stats). The empty game object that holds it is set to not be recycled when we switch scenes, so it will persist throughout the game. In addition, the code is set as a singleton, which means it is self-policing so that it ensures that there’s only ever one copy of the data. The data controller isn’t represented in this screencap, but know that it sits above and outside of the Main Menu Controller.
The Main Menu Controller is another empty game object. It is an umbrella beneath which we will find all of the UI elements that make up the main menu, the loading and saving screen, options, and confirmation dialogs like overwrite confirm and quit confirm. The benefit of this is that the empty game object can hold scripts that we can refer to from anywhere in the hierarchy, but more importantly, it allows us to make a prefab of the menu. A prefab is a “master copy” of an object that we can deploy anywhere in the project. When we make a change to the master object, it cascades to all deployed instances. If we make a change to deployed instance, we can either let that change stand just for that instance or have the instance update the master object. I’ve already created a prefab of the main menu hierarchy, which is why the text is in blue.
So what about the organization itself? There are five main levels: MainMenu, GameList, Options, QuitConfirm, and ConfirmOverwrite (yes, OCD folks, I will change the Quit and Overwrite to match naming patterns).
Main Menu is what you’d expect: the main menu. This is what you’d get when you start the game, or when you hit the ESC key while playing the game. It allows you to start a new game, load an existing game, set options, or quit.
This is where the difficulty comes in. Both NEW and LOAD share the same concept: choose a slot from three options. If it’s filled with details, we need to know what option we chose from the main menu: are we starting a new game? If so, we need to make sure the user knows she is going to overwrite an existing file. Are we loading an existing game? Then we need to get the save game data into Database from disk.
And here’s our load/new UI. The first slot is filled in with an existing game. We simply call it “Save game 1” and display the last save date. The other two slots are empty. If the user is starting a new game, she can choose the last two slots and just start the game. If she chooses “Save game 1” then we need to warn her.
If she chooses YES, then we’ll set up a new game and overwrite the slot that’s currently occupied.
What handles all of this? There are two scripts: Main Menu and Game List.
Main menu is attached to the MainMenuController game object because this handles the opening and closing of panels other than the main menu itself. One thing that UI systems need is a way to handle which windows are open and which are not. One way to do this in Unity is through GameObject.SetActive([boolean]). When boolean is false, the window is inactive. Unfortunately, when a game object is inactive, it is unaddressable, meaning we can’t tell it to show itself because it’s just not listening. To get around this, we put a script at a higher level than the object, create a property, and drag that object into the property. Now, when we want to show the windows, we do so from above the window itself…at the controller level. Currently, the main menu itself isn’t represented, which will get fixed later.
What we do not see are methods called event handlers. These are code bits which the buttons use. When someone clicks NEW GAME, the event handler “hears” the activity and takes an action. We point the button’s click event to that method in order to join the two. Here, we have three event handlers: One to display the game list panel, one to display the options panel, and one to display the quit confirmation panel.
The real tricky part was the game list panel.
In this case, we have the script attached to the UI element itself. Why not attach this to the Main Menu Controller? Well, for one reason: we need to take an action when the UI is activated — when the button on the main menu is clicked. If we put this on the Main Menu Controller then as soon as that object is created (at scene load) then those actions would fire. Since the game list panel isn’t active at that point, running that code is pointless.
The activation action runs the code within Database that loads the list of saved games. We use this to update the text of each button on the panel. If there’s a save game in slot one, then we need to change the text of slot one from EMPTY SLOT to the name and last saved date. We need to have references to these buttons inside the script in order to do this, so we create properties to hold those references. We could technically use hierarchy and search methods to find SaveGame1,2,3 buttons, but searching like that is expensive and doesn’t make the assignment visible through the inspector during design time. We also have a reference for Confirm Overwrite Panel because its visibility is controlled based on the state of the button the user presses, and the action she intends to enact (new or load).
If the user is starting a new game, then Database handles the initialization by loading the master data, creating a new Game State Object, merging data where necessary, and then making all of that available through the Database instance. If the user is loading a game, we load the master data and read the appropriate state file from disk. In the case of a new game, we will then send the user to another scene where she can make some limited customization of her character (since the character has no visible presence, it’s mostly just custom name, gender, and paper doll selection). Then, she’d have to decide whether or not to tackle the tutorial. If she’s loading a game, then we would send her to the scene where she left off when the game was saved.
So that’s it! 1300 words to describe a menu system (be thankful I didn’t include all the code!). Needless to say even something as “simple” as a menu can be more complicated than you know, so remember that the next time you’re ripping into a game about animations or physics — there’s a lot of moving parts, and what you see is only the end result of mountains of effort whose structures are really only as good as the organization of its parts.
Read More »
One of the cornerstones of the development world is the idea of “best practice”. Since coding is as much about style and flexibility as it is about solving concrete problems and modeling desired outcomes there’s really no one single way to approach a solution…nor should there be! What we have instead is a kind of trend towards certain efficiencies such as least amount of code to get the job done, making it reusable, making sure it doesn’t trip up or over other code, etc. Best practice can be abused when people insist that their way is the only way because anything less is just going to cause your app to sputter and die a wheezing death. But best practices are really the distillation of a whole lot of people’s trial and error, hopefully with a dash of advice from those who created the language.
Finding the “best practice” is often difficult for a few reasons, first and foremost being that as a rule, there is no best practice. Everyone swears by what works best for them. Some people like cars, some like motorcycles, but both will get you to where you need to be so who really cares? The second is that often times developers can be real cagey with their knowledge. There’s some undercurrent that believes divulging experience in development is like a magician telling people how the trick is done, not because it ruins the illusion, but because it suddenly makes the original magician a lot less valuable if everyone can do the same trick.
That’s why I’m throwing this out there in the hopes that someone can at least share some insight into their own experience since it seems like more of what we call a pattern than a practice. A pattern is more of a mindset than an actual flow. It’s how things are arranged so you can get yourself into a practice. In this case, I’m looking for advice on structuring and controlling a user interface in Unity.
And before I get to the question at hand, just a bit of a preamble. The way I’ve been working with Unity UI, I see two methods of wiring up the interaction. The first is to have the UI itself handle interaction with the code that puts and pulls data to and from the game’s data store. The second is to centralize the code that interfaces with the data store, and then have another script on the UI that deals with the UI itself. Then, these scripts communicate among themselves to pass data along.
Option 1: Direct Script Interface
The first option is probably the most direct and easiest to conceptualize. The Unity UI system allows us to draw a box, say, which contains fields for text input (in our example). In order to get this data into the game data store so we can apply it elsewhere, we need to recognize that the textboxes exist.
After we draw our UI, we attach a script to it. This script has properties that are intended to hold references to the UI elements like the textbox (and the button used to submit the data to the data store). When the script receives the command to read the data from the form, it refers to those properties so it can read the contents. It would then take the data from the textboxes and “do something” with it — send it to another part of the game, or put it into the database or something.
The benefit to this is that the UI is completely sorta self-contained. We could copy this and paste it elsewhere and it should work exactly the same, which is one of Unity’s greatest strengths. It’s also easier to manage because we just need to go to the source — the UI elements — and we have access to the visuals and the code.
The downside to this is that we end up with a whole lot of repeated code. Anytime we want similar but slightly different behavior we would need to modify the underlying code and then add a reference to other UI. It also means that we can’t easily take advantage of global patterns such as singletons which allow for persistence of data without having to go “back to the well” of a back-end system. Finally, if we had one script to handle several UI elements, we’d have a whole lot of properties tied to UI elements to deal with, which might make management a nightmare.
Option 2: MVC-esque Interface
This option is more complex, so please bear with me as I try and explain the diagram.
First, MVC is a pattern called “model-view-controller” which puts the display in a “view”, the logic in a “controller” and uses a “model” to represent the data passed between the two. Here, the UI has a controller. This is a game object which centralizes a whole lot of behavior for the express purpose of bundling it together for easy access. Here, a UI controller would be the base of either this or all scene UI elements. We’d have scripts for managing windows in the scene, but also for handling the UI elements (textboxes, buttons, etc).
The difference here is that this script would need to communicate with another script on a more global scene controller. The scene controller is like the MCP from the original TRON: it’s got access to everything you might need, just hanging out in space, easily accessed from whatever and where ever you need it.
The benefit of this method is that the scene controller is all seeing and all knowing. It works for the UI, but it’s also available for other elements of the game. In addition, it can implement the singleton pattern which when combined with telling Unity not to destroy the object on scene transition, offers us a persistent data conduit.
The downside? It’s messy. And complex. Technically, this diagram can be warped a bit. The UI doesn’t need its own controller, for example, and the interface script can live on the top-level element (“canvas”) of each UI we need to communicate with the scene controller. We could also eschew the scene controller itself and make the UI controller the top level way of data store interaction, although we’d lose our data persistence unless we did have some singleton somewhere that wasn’t destroyed when we move between scenes.
So there we have my conundrum. As a hobbyist developer in this realm, I’m not sure what the best option is. I had been using the more complex method previously, but now that I see things in writing I’m thinking of trying the less complex method, although I’m not sure what pitfalls are unaccounted for along that trail.
Are there other methods? This is where I’d love to hear from other developers who have worked with Unity UI and who are willing to offer advice!
Read More »
This project I’m working on, this cyberpunk corporate financial warfare thing, is pretty simple on the surface. I don’t mean in terms of gameplay; I’m not sure it’ll even qualify as a “game”, per se. Rather, I mean in how it looks when you experience it.
One big disconnect between the consumer and the producer is that the consumer has no idea what actually goes into producing a product. The best that the consumer can do is make logical assumptions, filling in the gaps with knowledge explained or researched, and just glossing over what remains or what’s deemed “unimportant”. One element that I think doesn’t get anywhere near enough interest is user interface design and execution. It’s so critical to the overall operation of the game, but the most attention is receives is when people bitch about how ugly it is or how much room it takes up.
This cyberpunk project is mostly UI. I mean, there’s no little CEO running from map to map collecting resources and turning them into products or anything. You click buttons and slide sliders and maybe enter numbers, and then review your financial empire while you wait for your research or production or distribution or marketing to complete. In order to be as efficient as possible, you’ll need to jump from UI element to UI element, from a broad overview to a micromanagement perspective and back again to make sure you’re not spending money on idle facilities. All of this means I’ve had to really come to grips with one element that I’ve never been really good at: user interfaces. I’m mostly a back-end developer, although most of my jobs have required me to wear the hat of a designer as well. Being a hobbyist developer means that I don’t get to hire anyone to do things like UI for me. I have to do it all myself, which is both a blessing — I get to learn stuff and hopefully get better over time — and a curse — mostly in the form of actual cursing because my UI looks like shit, or doesn’t behave as expected.
Take, for example, the act of opening a simple window. In order to accomplish this you need — a window. This window is normally invisible, or off-screen somewhere so that the user doesn’t see it until it’s needed. Aside from considering whether or not to animate this window (Fade in? Fly in? Zoom in?), the second hurdle beyond design is how to get the window to show up in the first place. In this project, the player will be asked to pick an item to research based on market research that randomly selects a base item type that the population says they’ll buy if certain attributes about that item meet their (intentionally vague) demands. Once the player has decided on which demographic to appease by building their dream product, she’ll need to pick an unused research facility from the map by clicking on it. Currently, these facilities are just small, colored tokens. When the player clicks on one of these, they’ll get a UI panel popup that shows them how much it’s going to cost them to rent the facility each month.
What the user sees is “click the icon, get the window”. What I see is “holy crap where do I attach this UI? How do I get the clicking on the icon to work? How does the act of clicking on the icon tell the window to show itself? And how do I get rid of the window when they decide they don’t want this facility, or when they do choose to rent it?” The biggest hurdles have been where to put the UI, and how to get the action to trigger the UI. In Unity, the UI is now represented by actual game objects, as opposed to the “in pure code” from Unity versions gone by. This means that we can design the UI in the Scene (working) view and see it during edit-time, but it also means that all UI elements are objects in the Scene, like models and lights and audio sources. There’s a certain hierarchy that needs to exist in order for the UI to render, so there’s also a tiered structure that needs to be considered when dealing with different UI elements.
I put the rental fee UI on the facility icon itself, which makes talking between the two easier. What I found very quickly was that with more UI elements comes more complexity when trying to figure out where to put the UI and how to talk to it. Putting the rental fee UI on the icon might work now, but what if there’s a situation where I need to open that UI from somewhere not associated with the icon? I envision a master control panel that will list all research facilities currently rented and operated by the player, so she could click on an item in that list which moves the camera to that point on the map and opens the panel that explains what’s going on with that facility. None of that would be sourced from the item itself; it would need to be called from outside the item, like from a main menu or toolbar. I could add component references between the buttons — wherever they are — and their UI targets, but that results in a lot of spaghetti connections that are difficult to trace when debugging.
The answer was “abstraction” and “oversimplification”. I created a “window manager” which is nothing more than a list of windows in the system. When a window starts up, it registers itself in the master window manager list, and then either stays put, or is hidden (or moved to a point off-screen). With the window manager system attached to the Game Data Object — the global, non-destroyed, singleton object that is accessible from everywhere in the game — I can call methods like ActivateGameWindow() and pass in a name as an argument, like “ResearchRentalUI”. If that UI has self-registered and is in the management stack, then the method will find it and turn it on, activate the animation, or do whatever it needs to do in order to activate that element. This messenger systems ensures that I don’t need to have one specific element know about the existence of another specific element, so long as I don’t misspell the name of the UI element I want to send the message to.
I’m not writing this because this is some kind of revolutionary design technique, but rather because what we see on the surface isn’t always what we think we’re looking at. Sometimes things are more complex, and sometimes they’re more simplistic, and sometimes they’re way out in left field.
Read More »
I’ll admit that I’ve been violating Rule #7 of Software Development by not storing my project in a version control system. Actually, I’m storing it in my OneDrive folder, which means it synchronizes across multiple computers so, you know, I can work on it in multiple locations.
The other night I booted up the project at home, but I didn’t see my most recent changes. In fact, I did, but they were duplicate copies which had been renamed to refer to the other platform on which I’d last been using them. “Oh well,” I thought. “They’re just dupes.” So I deleted them. Hard deleted them.
Turns out those files were the most recent updates, and what I had left over were the older copies. I freaked out, to the point where I downloaded an app that supposedly allowed me to undelete files (which is scary when you see how many of your files you can undelete). But when I said I hard deleted them, I mean it, so good news is that Windows does a decent job of scrubbing things that your average off-the-shelf recovery software can’t find, if that matters to you, Mr Snowden.
Needless to say, I was pissed because one of the files was my core data management scripts that contained all of my loading and saving functions. Those functions comprised the one part of the game that I felt was “complete” in that it was generic enough that I wouldn’t need to touch it again even when adding elements that needed to be saved to the overall game itself. Nice, efficient, powerful. And gone.
I spent the next session re-writing the whole thing mostly from scratch. I still had a much older copy to serve as a reference, but was missing several key methods, like the one that managed the save game manifest file (the manifest holds info on all of the actual save game files, and is what is read when you click “Load Game” to allow you to select a saved game to load). The good news is that I had just enough of a memory (and some notes) to allow me to recreate the system, but better. I feel that this new iteration is more robust even than the original, which I was a big fan of. And once I’d completed the work, I zipped up the code and stashed it somewhere safe.
So I need to get this stuff into Github, which I’ll hopefully end up doing this weekend. That way, I can avoid future costly mistakes. Learn from my sorrow, kids: Always use source control.
Read More »
Listen, I’m not a gifted hobbyist when it comes to working with Unity. My approach has been to logically suss out what I want to do using my knowledge of application development, build some test cases in code, and hopefully rely on the script (that I know) far more than I would rely on whatever automation Unity offers (which is what I’m generally fuzzy on). Of course, using Unity means using Unity, so I can’t simply script my way to Nirvana; at some point I’m going to need to use the Unity Editor to Get Stuff Done ™.
Exhibit A: Animating a panel to fly in on a button press, and to fly out when I click Cancel. The goal seems relatively simple enough, and I figured I was halfway there because many moons ago I had done some work in Flash which introduced me to the concept of key frames, which are points along a timeline where properties of an object to be animated can be changed. Unity’s animation system, at it’s most basic, uses keyframes as well, so I figured that I could create an animation clip, set some keyframes and alter properties, call the Play() method from script (either SlideIn or SlideOut) when the appropriate button was pressed, and everything would work as I assumed it would.
Check out this monstrosity. Click to embiggen and feel your soul being drained as I explain how this simple task is hampered by the Power of Unity.
The top portion of the screen is my design surface. You can see the frame of the UI boundary on the left, and the panel to fly in is on the right. When we run this, that panel is outside the bounds of the camera that draws the UI, so we can call it “exit, stage right” (or left depending on whether you’re on stage or in the audi…you know what, I don’t care about this metaphor). The properties of the panel are just a whole lot of technobabble, but the important part is the bottom of the screen. That’s the “flow” of animation that I was forced to deal with. What isn’t shown is the panel that allowed me to define the animation through the use of keyframes. In that, there are two “clips”: one that moves the panel into view from right to left, and one that moves the panel out of view from left to right.
At some point, I had actually had a simple version working. I had just defined the two clips, assigned them to the panel, and was able to call them from script, by name, and it worked flawlessly. Then Unity decided to cough up a techno-hairball and screwed up a whole bunch of UI elements Just Because(tm), forcing me to rebuild a few. That messed up my animation setup, and when I attempted to recreate it, Unity decided that now it wasn’t good enough, and started throwing errors. This sent me in search of a way to animate a UI panel.
Let me tell you: there’s fuck-all for info on how to do something as simple as UI animation in Unity. Doing a search for “animation unity3d” with any additional criteria thrown in only brings back information on how to animate character models or sprites. Yeah, I get that the character is really the heart and soul of a game and it’s also pretty complicated, but does everyone understand UI animation? Am I just Extra Clueless? That’s rhetorical, by the way.
What I had to do was to create the Idle state for the panel, which means it’s not doing anything when you start, or after the panel has moved. I also created two boolean flags: IsOpen and IsClosed, and are using them to brute-force the situation. When the user clicks the OPEN button, the IsOpen is set to TRUE and IsClosed is set to FALSE, which routes the state machine to the SlideIn clip, and then back to Idle. Reversing the booleans when I click CANCEL routes to SlideOut, and then back to Idle.
I can accept this, but I really question why I have to, since I’m not using script and not Unity-proper to handle my buttons. I feel that it would be a lot easier just to build the animation clips for a GameObject, put those clips in a reference bucket on that object, and then call those clips from code to have them play. In fact, I know this can happen, because the Play() method exists, but I can’t find a straightforward tutorial on how to set up the environment in a way that allows me to refer to those clips by name, and since I did have it set up once, but was rebuffed when I tried to replicate that situation, I’m more confused than I was before.
Read More »
I’ve been working on that “mystery game idea” since I mentioned it in the last post in this column, and now I want to talk a little about it.
The idea is relatively simple on paper: you are the head of a corporation in a cyberpunk universe. You have to make your company financially viable by researching, manufacturing, distributing, and marketing products. Being cyberpunk, however, it’s not just about financial warfare; it’s also about actual warfare, so you can steal and sabotage your competitor’s resources which will strike a blow to their pipelines.
And that’s the pitch.
Right now, I’ve got the very initial framework down that allows me to create a CEO and a corporation, choosing a portrait and a corporate logo. All of that saves to disk. You can also choose to load a saved game from disk. Hey, that’s been the Achilles Heel mechanic of most of my previous project attempts, so I’m super pleased that this aspect of the game is already on the books (more or less) so I can concentrate on other things.
The game moves in phases. First, you start with nothing, and need to choose your first item to work on. You’ll get a “market research” panel that will give you three random options, and what it is about current products on the market that they don’t like. Maybe one option will be an anti-cybernetic-rejection drug that should be “more potent” but also “less addictive”. Choice of item really has no bearing on anything except as a way to goad the user into picking something, and giving them a vague target to aim for: better than the attributes the panel say they dislike.
Next, you’ll need to pick a location on the map to set up an R&D facility. It costs money to rent, and you get a level 1 lab which installs the selected item to research. Research is a passive affair, but you’ll need to decide which attributes to boost and which are OK to sacrifice. Each item will have X attributes, but only a few will be relevant when trying to appease the market research subjects. In order to increase your chance of making something better than what they have now, you’ll use sliders to spend points on attributes. But in order to get those points to spend, you’ll need to decrease the sliders on other, less important attributes. If you’ve got the money, you can hire a researcher who adds permanent points to your pool that don’t require you to sacrifice anything, but you can’t hire enough to max out an attribute, only to supplement it so your other attributes don’t suck as bad as they would otherwise.
You can get a market research panel to research new products, keep manufacturing the same stable of products, concentrate on a market or diversify, hire and fire, improve the level of your facilities, and so on.
That’s about as far as I have conceptually. Beyond this, there’s ideas for manufacturing and setting a price, then getting into a market which may or may not have been represented by the initial study that you worked hard to live up to in the first place. You can improve your chances of selling even the crappiest items by crafting a killer marketing strategy by hiring celebrities or creating controversies.
The corporate warfare is something I want to include because it’s a cyberpunk staple. If you’d rather not hire a researcher, say, you can hire a street merc squad to “extract” a researcher from a rival corporation, scoring you not just a researcher but other boosts in the form of bad PR for your target opponent. Of course, they can do the same thing to your off-duty researchers, so you’ll want to hire security for your facilities which could resort in some collateral damage along the way.
There’s a lot to flesh out, including the all important “what’s the damn point” in a game where your actions all support increasing your profit, but currently for no good purpose…and no end. Maybe you’ll have to run your rivals out of business, but how? Maybe you have to reach some kind of domination scenario, but how? I know it’s not really “a game” without a win or lose condition, but there’s always a bunch of opportunities for both when we’re talking about corporate warfare.
Read More »