Jump to content

  • Log In with Google      Sign In   
  • Create Account





Goblinson Crusoe, Urho3D, and Other Stuffs

Posted by JTippetts, 02 April 2013 · 1,517 views

Goblinson Crusoe Urho3D hex-based turn-based rpg
I made an executive decision this last week about GC. To this point, I've been implementing it as prototype code on top of Urho3D, but I've been using my original Lua-based framework and ignoring the fact that Urho3D implements a quite robust framework of its own.

Like so many others, I've jumped on the component/entity bandwagon in recent years. When I first started GC, I started experimenting with an object composition framework, and over the months I've refined it and iterated on it. It works pretty good. However, Urho3D implements its own object system under the hood, and when I migrated to this new 3D library I hacked together an engine kernel that did the perfunctory tasks it needed to using Urho3D's built-in framework, then tacked my Lua framework on top. It works, but it seems a little bit counter-productive, and the Lua bindings to make it work have been ugly and hackish, with a lot of intermediary and proxy objects that have been kind of a pain to code (and are not complete yet for all renderable types).

When implementing an object composition framework, one of the thorniest problems to solve is the problem of inter-entity communication. Good design will reduce coupling between entities as much as possible. Now, some frameworks implement entity-to-entity communication by providing the interface to directly query an entity for a given component or class of components. Something like entity->GetComponent<Transform>().SetPosition(x,y,z). Other systems opt to use a message passing system, where instead of querying for a particular component, you instead just "send a note" to the target entity. entity->SendMessage("SetPosition", positiondata). Each of these particular approaches has its advantages and disadvantages.

An advantage of the first scheme is directness. You set the entity's position directly, through a known interface, with relatively terse code that doesn't include a lot of baggage or introduce many opportunities for error; most errors will be caught at compile time. A disadvantage, of course, is tight coupling; the calling entity has to know that the target entity has a Transform component, or at least is likely to have one. (GetComponent() can always return nullptr). This knowledge of the component make-up of another entity runs somewhat counter to the philosophy of composition design, but I have found that in actual usage it isn't too much of a problem. Games are orderly enough that you can usually make some assumptions about what kind of object you are acting on at any given time.

The message passing scheme is just the opposite. Handing someone a note does not require that you know anything about that someone, so passing a "SetPosition" message to an entity that has no Transform component would be silently ignored, and it is not required that the sender know whether the receiver implements that particular component. The compilation unit doing the sending doesn't even need to include headers for the targeted component, so compile-time coupling is reduced as well. However, the act of communicating via message passing is indirect and somewhat clunky, especially in a statically typed language such as C++.

C++ does not handle the idea of "free form data" very elegantly out of the box. When implementing a message-passing scheme, you often need to pass different data depending on the message being passed. A "SetPosition" message, of course, would need to pass "x", "y" and "z" data. A SendRawDamage message, on the other hand, would need to send different data: a damage value, a damage type/class, the ID of the originating object, etc...

Implementing this kind of free-form data structure in C++ involves some tricky and/or tedious programming, which is why I favor dynamically-typed languages such as Lua for this type of thing. In Lua, passing a message of this sort is trivial, as you can construct the free-form data packet in-place: entity:SendMessage("SetPosition", {x=5, y=0, z=10}) Lua's tables are the very essence of free-form data structures.

In C++, you end up with mildly hackish schemes such as Win32's LPARAM, WPARAM, etc... parameters which are re-interpreted based on the context of the event being sent. More robust solutions (such as what Urho3D implements) operate on the idea of a Variant data object, or an object that can hold any of a number of data types depending on the usage. This solution is still a little bit hackish, but it is an idea that has a long and distinguished history of implementation in computer science. The basic variant structure in C/C++ is union, but it can be implemented in other ways as well, ways that are a bit safer than union. But the idea is the same general idea as union: a chunk of data that can represent any of a number of types.

Urho3D implements a VariantMap, which is a map (Key->Value) of Variants. Message payload data is sent via a VariantMap to any registered message handlers. Of course, language limitations make it difficult to implement a VariantMap class that can be constructed in-place with the elegance of a Lua table, so you typically end up instead with laborious constructions such as:
VariantMap data;
data[ShortStringHash("MessageType")]=MSG_SetPosition;
data[ShortStringHash("x")]=5;
data[ShortStringHash("y")]=0;
data[ShortStringHash("z")]=10;
node_->SendEvent(E_UPDATETRANSFORM, data);
That sort of thing is fine for a sparse message passing scheme, but when message passing forms the very backbone of your paradigm it gets icky in a big hurry.

The thing about Urho3D, however, is that it actually implements both schemes. You can query a node directly for a given component, or you can send an event through the event system. Events can be used to handle the bulk of entity-to-entity or kernel-to-entity communication, while certain components can query their owner node directly for other components. For example, a GC combat object will always have a CommandQueueComponent, which acts as the strings of the puppet. It implements the low-level functionality (wait, move here, cast this spell) of combat. They will also always have a higher level Controller. Controller comes in 2 main flavors: Player and AI. The Controller makes the high-level decisions, and passes the results of that thinking down to the CommandQueue for execution. In this case, it makes sense to be able to query the node directly for a given component, and call methods on that component rather than jumping through the laborious event-sending hoops. If an object has a Controller, it will also always have a CommandQueue as well (unless there is a bug in the object spawning code.)

Another advantage that Urho offers out of the box is robust serialization of scene/node structures including custom components and attributes. All nodes and all components implement a Serializable interface, and inside the interface you can designate specific members as attributes, to be automatically serialized. Serialization is another aspect of developing in C++ that can be sort of tricky, requiring some tedious coding (another advantage of Lua; a quick and dirty recursive table traversal can easily and quickly dump a Lua table to a file).

In Urho3D, serialization is well-supported; by extension, so is network replication, providing a strong framework for multiplayer development. It's actually a pretty decent framework, a sort of undiscovered gem, and I'm pretty glad I have finally taken the time to get to know it better. I'm in the process of converting my prototype Lua code over to use the core Urho3D framework right now, and it's going pretty smoothly. There have been wrinkles and snags, but they've been relatively minor.

A final note about Urho3D: it implements a number of other useful systems, including a strong ScriptObject component functionality allowing you to write component behavior in AngelScript for run-time modification of object behaviors. This is built-in to the system, robust and clean. As well, Physics is well supported through a set of physics components built atop Bullet. Audio is in there as well, and Networking. The only big thing lacking (at the moment) is pathfinding, but Recast/Detour can fill that hole with a little bit of effort, and the Urho web page indicates that pahtfinding is being looked at for future releases. Additionally, Urho3D can be built for either DirectX (currently D3D9, shader-only) or OpenGL (2+, shader-only) with build paths for Windows, Mac, Android, etc.... If anybody is looking for a good, solid, fairly powerful 3D framework and is tired of mucking about with the aging and creaky existing open-source frameworks, Urho3D might be a good candidate. The developer (AgentC on these forums) has been friendly and willing to help with my occasional issues as well. The community around Urho3D is still rather small, but I hope to see it grow as the word gets out.




I've experimented with both types in the past.

 

To my mind there's a clear need for both types of calling convention. In many situations to do know who the object is, what components it has, etc and you simply do want to call player.Transform.Move(x, y, z). In other situations, you don't know who will respond to a given message - for example an explosion event which causes splash damage. You have the option of doing a local search for all objects in a radius and calling object.Damage(d), or simply tossing the event out there with some data like "position, radius" and letting the objects themselves decide if a) they even care about these types of events and b) if they're going to be affected.

 

The messaging approach works really well in the fire and forget scenarios. I toss something out there, but don't care about the results. I'm just telling you about something. Up to you what you do with it. However I've found that it can become a bit of a pain setting up such a system and you have to make decisions about how you hook and who responds to the message - do the individual entities hook the event, which means lots of little hooks everywhere to be managed, or do you have a "manager" which hooks the events and then passes down to the entities it looks after? I never quite settled on the approach I felt happy with, to be honest.

 

This approach also sucks if you need to handle return values from a message. You end up having to hook the sender for response messages and then have to track something to correlate the result with the original message.

 

All these are solvable problems and have been solved, but it can be a bit of a pain. You definitely have to think "async" in this style of system. A message won't be processed in the same game tick, but perhaps the next - and you might wait another tick or so for your response. Unless you end up allowing a "pass this message directly to X" system, which I've seen and used myself. But then you move onto RPC style systems. This approach works nicely for high latency or remote systems though, where everything is async.

 

The RPC approach is easy to read and allows you to move away from that horrible stuff you mentioned about packaging up the message for sending. However as you said, it implies more knowledge of the components an object has and promotes that you know directly which object you're talking to.

 

Unity 3d is very heavily in favour of the RPC approach. You literally call entity.GetComponent<Transform>.Position = XXX to change the position. You have to know which object you want to set the position for and have to know that it indeed has a position (which Unity actually does by default). Unity does, however, promote several well defined components. They're so common that are actually shotcutted on the GameObject class. There's a strong chance they're there, if not the property is null. This forces you to code defensively, if (object.rigidBody != null) { ... do stuff...  }.

 

Unity does also provide a "sendMessage" call, which is sent to every component which derives from the MonoBehaviour base. This is a varg style system which reflects off the message name as the object method (eg: "SetPosition") and then passes the specified args to it.

 

MonoBehaviour is very much about "this is your stuff, I have no idea what it is". Imagine you had 2 custom components; MyBehaviour and AnotherBehaviour. Both had a "DoSomething" method with the same args.

 

It obviously lets you pull back "MyBehaviour" as a strongly typed component and call DoSomething directly. But sendMessage gives you the option to route a call for DoSomething to both components, without actually caring which are attached. There's also a broadcastMessage option which sends the message down to all children attached to the object. I'm unsure if it cascades down the tree, or just stays at the first level of objects. This system doesn't work on the in-built common components, however.

 

Quite an interesting system in my mind. It acknowledges that there's several strongly typed components that a lot of things are likely to have, and provides shotcuts to them, but leaves you the responsibility for checking that they do. But here, you can do direct calls to the components. It also then shrugs and says, have a simple message system too - but only for you stuff.

 

So back to the best approach - I'm honestly not sure. I've spent a long time implementing various methods and have never decided anything other than there is no best approach. I am, however, starting to favour Unity's approach - whilst it still has a few issues, it does seem like a fair compromise, as does the Urho3D approach.

My original Lua framework was implemented as message-passing only, mostly for things like:

But sendMessage gives you the option to route a call for DoSomething to both components, without actually caring which are attached. There's also a broadcastMessage option which sends the message down to all children attached to the object.

I would implement things like FloatingCombatTextQueue, which would listen for things such as healing or damage messages, as well as other components like CombatLogging (which also would grab those same messages) and Vitals. All three might need to hear the same messages, so the message passing works well.

One of the weirdest parts of my framework was:

This approach also sucks if you need to handle return values from a message. You end up having to hook the sender for response messages and then have to track something to correlate the result with the original message.

I ended up implementing return values from the SendMessage procedure. (Actually, I implemented SendRequest, which worked identically but provided return values). Anything listening for a particular request event could package up its answer into a table and return it; the framework method would take this return value and insert it as an entry in another table. The event sending method, then, could iterate the returned table and check all returned entries. You could, for example, send a request to all object of the group Players, requesting their status, and all components implementing that particular event handler would return their little bit of data. Vitals might return current health, Status might return sleeped/paralyzed/rooted, and so forth.

This kind of request behavior(or lack of), actually, is the biggest sticking point for me right now in Urho3D. At first glance, you might think that you could just use the VariantMap passed as data to the SendEvent method to store return values for requests, and it seems like it might work well for single-returns, but for a general broadcast query, not so much, since the Variant class is structured such that setting values is easy, but reading them back is done by returning a const reference, so you can't modify it. It would be nice to set an entry in the VariantMap to be a VariantVector, then just populate that vector with request answers in each component event handler, but const-ness gets in the way.

Callbacks are, of course, an answer (if an icky one). Fire off an event, and responding components can fire off a reply event. In fact, the new iteration of my turn-based combat scheduler built on Urho3D currently uses this approach. It sends a RequestStatus event, and all interested parties call back with their status, calling into event handlers in the scheduler code which will populate a vector. It's a hack, just as it sounds.

The more I think about it, though, the more I think that a hybrid system of events plus direct component access, while icky in an abstraction sense, is the most practical way to go here.

If you're sure that the Variant in question holds for example a VariantMap or a VariantVector value, you could const-cast the const away. But in case the variant had some other type, it returns you a system-wide empty object, which you'd then be modifying..

That is certainly possible, and an answer I might choose to go with at some point.

However, for the moment I'm using this as an opportunity to re-evaluate some aspects of my design. General broadcast queries to all combat objects are actually an exceptional case (used for only one or two tasks) so before I go hacking around const, I might be better off just sticking with the callback scheme or figuring out if there is a better way to handle these cases than a general broadcast. In the end, I'll probably end up thinking that sticking with the callback+queue is better than wasting a whole lot more time trying to figure something else.

December 2014 »

S M T W T F S
 123456
78910111213
141516171819 20
21222324252627
28293031   
PARTNERS