Crafting Skills, and Ranting About UI Programming

Published October 30, 2013
Advertisement
a57K21u.jpg

Did some more coding on the spellbooks and crafting units UI today. I can see a significant refactor coming pretty soon, now that I have gotten both a better handle on how the UI system works, and have gotten some refinements done on the code I am using to instance UI elements.

Made a few tweaks to the spellcasting system to incorporate crafting skills. The spellcasting system defaulted to a target selection mode when any spell was cast, since most combat skills require a target or target location. However, crafting skills don't take a target and should happen immediately, so I had to make that change.

A crafting skill is essentially just an instant-cast spell. It consumes certain specified resources, and triggers an effect that adds other resources to the caster. In the current tests, both the Basic Workbench and Basic Spell Tome share the same crafting list, with but two elements: Create Wood Planks (consumes 2 Wood, creates 1 Plank) and Create Tinder (Consumes 2 Leaves, creates 1 Tinder). Tinder is now the resource of choice for TestFireball. In order to cast any fireballs, you have to chop down a "tree" (in the caves, mushrooms are trees) in order to obtain a few leaves, then convert those leaves to tinder at a workbench. This is a basic template for most of the gameplay I have in mind: you scavenge around, obtaining materials with which you can cast spells or do other things.

As part of the impending refactor of some of the spellbook UI stuff, I'm going to need to incorporate hover text for skills. Given my small set of icons, it's getting hard to remember which buttons do what since they are all drawn from the same 4 icons.

I've always found UI systems to be awkward. It's just the nature of the beast. You have a hierarchy of elements, all of which require a fairly decent chunk of data to describe, requiring a deeply nested structure of descriptions to be instanced as elements. Either you write a bunch of boilerplate to do it, feeding it a table of data and obtaining an instance hierarchy at the other end, or you build them all by hand, laboriously and with much proneness to error. However, the act of instancing it through boilerplate includes its own gotchas.

For example, UI elements in Urho3D UI generate events that pass through the core event management system, which can be received by any Object-derived class that subscribes to them. You can subscribe to events globally, such as the main-loop Update event. Any object signing up for Update gets the game tick. Or you can subscribe to events originating only from a given object. This is how the nuts and bolts of inter-component communication works. A TakeDamage event sent by a specific Node is listened for in some of the various internal components, to do things like modify life, generate floating combat text, log the hit to the combat log, etc... If these components couldn't sign up to hear Takedamage only from their owning node, then they would receive all TakeDamage events generated anywhere in the game, and the result would be madness.

So in order to get events from a particular UI element, you need to subscribe to events from that specific element. In order to do so, you need a reference to the element. The problem with this approach is that UI hierarchies can get deep. For example, I have a top-level player UI element that contains all of the child elements for a particular player unit, including a container for any spellbooks opened by workbenches, the quick bar, the quick bar spell library, the action bar, and so forth. A spellbook itself is a hierarchy consisting of a background, a scroll view, a close button, a UI element panel holding the buttons/icons representing the spells, etc... It can get deep, and unfortunately these icons/buttons inside the panel are the ones we need to obtain in order to sign up for their events for Pressed, Released, etc...

The problem with instancing a UI through a chunk of boilerplate that consumes a hierarchical description and returns the instanced element, is that all of the child elements are hidden away in a UIElement's children list; and all of their children are hidden away in their own lists. And so on. So to get to the action buttons of a spellbook, I need to descend down through the hierarchy, from the player root window, through the spellbook container, into a particular specified spellbook, into the spellbook's background element, into it's scroll view, into it's button panel and container, and finally to the buttons. Given the clumsiness of the interfaces for delving down through hierarchies, this quickly becomes quite a large pain in the ass.

A solution, of course, is to build all the elements by hand, stashing away references to elements we are interested in using later to sign up for events. This, of course, leads to the even bigger pain in the ass of instancing elements by hand, with all the wordiness and crushing tedium that entails. A compromise is to instance hierarchies for some bits using the boilerplate, and instance the interesting bits by hand, using hierarchical descriptions for those elements in turn and stashing away the references. This is the method I am currently using.

The problem with this method is that it leads to spaghetti code, functions or methods that have to do a lot of by-hand wiring up and munging around. I've already gotten some leaks doing it this way, and I am currently using code that I find to be highly suspicious, and not trustworthy at all. I haven't done any kind of long-duration stress testing on it, and I'm afraid that if/when I do it'll all kind of unravel on me, slowly and messily.

I've always hated UI code, specifically for this hairy mess of getting all the doohickeys wired up to the thingamabobs in anything even approaching an elegant fashion. This tedium is why I find UI programming to be the most onerous part of game development.
5 likes 4 comments

Comments

Servant of the Lord

The problem I run into alot with my GUI code is that GUIs, by their very nature, use multiple fundamentally different paradigms than the rest of the game logic, and where the two paradigms meet I feel like I always resort to sloppy duck-taping the code together to get them to play nice.

If you're hard-coding connections anyway, how about some kind of "reference directory", which your event subscriber objects can query for GUI elements by name to get the reference they need to subscribe to events.


On UIElement creation: ReferenceDirectory["Custom name"] = this_handle;
GameObjectThatNeedsHandle: SubscribeToEventsFrom( ReferenceDirectory["Custom name"] );

Random thought 4 minutes to midnight, so take it with a grain of salt.

October 30, 2013 04:56 AM
AgentC

Note that if your Urho3D UI elements are uniquely named, you can do a recursive search for arbitrarily deep children from the root of your UI element. GetChild("ElementName", true)

October 30, 2013 10:01 AM
FLeBlanc

Man, I hear you. I hate, hate, hate UI programming. It's always ugly. Unfortunately, I've never seen any good solutions to the complexity. If you have a complex UI, you're going to have to write a lot of ugly code to get things hooked up. Like Servant said, duck-taping two paradigms together gets messy.

Looking pretty awesome, though. If you need any volunteers for your stress-testing, let me know. I'm not a huge fan of turn-based, but maybe a complete turn-based noob is what you need to find the weak spots. :D

October 30, 2013 03:07 PM
JTippetts

Huh, somehow I missed GetChild(). That does make it easier. I did do a little refactoring, and the new result is much nicer.

October 30, 2013 09:54 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement