Sign in to follow this  

Interesting design question

This topic is 4769 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have an interesting design question for people to mull over. In my masters thesis I proposed a software architecture for use in electronic games (I just submitted it for review, so I'll publish it soon for people to read - and flame :) It's 90 pages + 250 pages of appendix so it should make good kindling) . At its highest level it is a data-centered architecture where game data is in a central data store, and technology component attach and operate on that data. A Simple example: Game Object Contains: - Position - Orientation. AI tech component Requires: - Object Position Graphics tech component requires: - Object Position - Object Orientation Both tech components will attach to the data component and request position data from an object. Now for the design question: The tech components are designed and developed independently from each other and the data component. One may be expecting position as x,y "int", while the other wants x,y,z as "double". A tech component might even want a "location" and not "position" (i.e. wording is different). How can the tech component get the data it wants from the object? Sol'n 1: Objects implement an interface for each tech component. E.g. the object implements a "getLocation()" function that returns 2 integers so that tech component can call that function to get the data it needs. Pros: - Fast execution. Data access is a function call (as opposed to an SQL-like query). Cons: - Lots of duplication. For each component that requires position data, the object must implement a seperate interface specific to that component. - Not easy to use, everytime you add a new component, new interfaces must be implemented. I'm looking for other possible design solutions that people can think of for a follow-up paper. Any ideas would be appreciated. -Thanks, Jeff

Share this post


Link to post
Share on other sites
This problem creeps up on me all the time. My method of handling it is pretty cheesed-up and definitely a hack, but... why not implement the position/orientation data in its own helper-struct, with all public members. Then the object could implement a single method that passes out a copy of the data to the techs to be used as needed.

I'd love to hear about some ideas for this myself =)

Share this post


Link to post
Share on other sites
The game object must be implemented with only limited regard for the components that may depend on it. The reason is that there is no way for the implementer of a game object to anticipate its every possible use or every possible context in which it will be used.

One alternative to your solution is to put all the responsibility for compatibility in the component. That is, if the component needs a 2D integer position, then it must implement a way to convert from the game object's representation of its position. If the component needs a "location" rather than a "position", it is up to the component to convert a request for a "location" into a request for a "position". This design is especially appropriate if you anticipate creating new components.

What if the component is also fixed? In that case you would implement a mediator between the component and game object.

Share this post


Link to post
Share on other sites
I'm currently working on a design for discovery at component connection time. i.e. When the components are loaded, they handshake and figure out how to get the data.

Easier said than done though. Hard to keep things simple enough that a game developer can implement it easy enough so they won't say "I know what graphics engine I'm using, its easier to just tie the two together"

Fun stuff.

Share this post


Link to post
Share on other sites
You could consider using a form of dynamic typing, instead of directly using int or whatever, return a variant type which can automatically convert to whatever type the client wants. Performance wise, its only a couple of extra tests. Alternatively you could implement a pre-process that could automatically add type conversions where needed and do everything at compile time (this would be a lot of work though).

You could then implement a simple key or property system:

Variant getProperty(const string& property);

Again, you'll have to consider performance, it might be possible instead to get the property only once and use that,

(presuming C++, it's much easier in other languages):

class Client
{
Property& m_MyProp;

Client( Data& data ) : m_MyProp( data.getProperty("location" )
{
}

void foo()
{
Vector2 location = m_MyProp.Vector2();
}

};

Share this post


Link to post
Share on other sites
The way that I've solved this problem is as follows:

Each subsystem in the engine provides an interface which can be implemented to add its functionality to an object. For instance, we have IInteractable, IRenderable, IAudible, etc.

Each of these interfaces declares pure virtual functions which must be overridden to return the proper data needed by the subsystems. There is some overlap here, as IInteractable::GetPhysicsPosition() and IAudible::GetAudioPosition() most likely end up doing the same thing in the child object. However, this allows the child object to store its position and orientation data however it sees fit. It is reponsible for returning it in the formats necessary for each of the subsystems.

The one exception is IRenderable, which actually stores the position and orientation information in its associated scene graph node. This is a result of these interfaces being added to the system late in development, after our previous solutions were found to be inadequate. Refactoring the Graphics system to pull the data from the IRenderable objects would have been a nightmare, and was deemed unnecessary.

Also, I think the Variant return type may be a bit overkill. In all situations data such as position and orientation can be stored as floating point values. If it needs to be integral, it can be casted at the site of retrieval and then stored appropriately. If the casting to integral data is too expensive, then another interface could be required which returns the data in the correct format. Although, chances are, the data will be internally stored as floating point and just converted before it is returned... defeating the purpose.

I'd also like to point out that the mechanism which was presented by JuNC for retrieving properties moves variable name typo errors from compile-time to run-time. That is definitely a Bad Thing.

Share this post


Link to post
Share on other sites
Shaft, you've got the right idea. Typically, all major components/packages of an application should be logically seperated via an interface or adapter. The reason for this is that you may want to change a component later on in production. Since everything is accessing one interface, all you have to do is modify the way the interface interacts with the new component. Granted, you may need to create multiple interfaces but it's better than having to change every reference in the application if there's a major design change later on.

Share this post


Link to post
Share on other sites
Quote:

I'd also like to point out that the mechanism which was presented by JuNC for retrieving properties moves variable name typo errors from compile-time to run-time. That is definitely a Bad Thing.


True, but it could also move *correcting* the errors to runtime as well :)

And if you really wanted you could just

#define LOCATION "location"

and use that instead. The idea was to give each data object to present a dynamic interface which is resolved at runtime (so for instance the data object could be dynamically loaded, or distributed over a network).

Share this post


Link to post
Share on other sites
Just thought I'd post this to people reading this topic. My thesis is currently in draft form, and not ready to be publicly published till next week sometime. But for those interested, you can read the draft at:

http://www.jeffplummer.com/Writings/Writings.htm

The topic is:
"A flexible and expandable architecture for electronic games"

Share this post


Link to post
Share on other sites
I wrote two pages and accidentally touched the touchpad on my laptop, which was oddly hovering over the X in my browser tab. It's done that a few times before... *suspicious glare*

In any event, a protocol I think you might like to consider would be to have your basic object request, but instead of an interface, just pass the db a lambda function describing exactly how you would like your objects served up. e.g.

(let ((s (make-xy-coord)
(id 345))
(db-obj-request id s 'xy-coord 'not-strict ;not-strict will allow xyz coords too
(lambda (x y z-notused)
(set-xy-coord-x s x)
(set-xy-coord-y s y))))


Pros:
Flexible -each call can define how it wants its object.
Don't ever need to change db code except to reflect bugfixes.
Distributable over a network.
Cons:
C++ can't really do this.
Distribution of code makes compile time checking more difficult, though you can write unit tests to parse your lisp/scheme and verify calls to the db which is a.)not very difficult. and b.) just as good if not better (it can even run queries).
Issues:
Security is a factor since the db might run unsafe code unless you screen it somehow.
Social acceptance of S-Expressions may force you to use Water.

Share this post


Link to post
Share on other sites
The db is a good idea, I just worry about speed (And maybe that worry is unfounded). Do you have any details (papers, web pages, etc) on that approach.

Share this post


Link to post
Share on other sites
Quote:
Original post by shaft
The db is a good idea, I just worry about speed (And maybe that worry is unfounded). Do you have any details (papers, web pages, etc) on that approach.
I thought it was your idea:
Quote:
At its highest level it is a data-centered architecture where game data is in a central data store, and technology component attach and operate on that data.


By db, I merely meant your central data store. Otherwise, I did some thinking and I think such an architecture is poor since it is equivalent to a collection of singletons. I don't think that's a good idea at all. Each component should have it's own db that handles its own needs.

Why would the audio component need to access the same db as the graphics component?

Share this post


Link to post
Share on other sites
Why would an audio need acess to the same data as graphics?

An object usable by both would have position and orientation. Both components would benefit by some form of scene management (selective object processing). Why duplicated that for EVERY component.

Part of the architecture also allows for smaller level of components. No longer just one huge AI system. Instead you may have an AI component that attaches and performs pathfinding. Another attaches and performs general strategy. Attach both and you suddenly have objects that can plan and move. But each component is actually independent and could be developed and tested independently.


Read the thesis I posted. There is a method to my madness... Or atleast a madness to my method ;)

PS: I'm not saying that a component can't have component specific data hidden to all except the component. For example a graphics engine would require an object to have a "mesh" object that is meaningless to other attached components. In fact, "domain knowledge localization" is one of the requirements of the architecture.

I actually take that to the extreme and say the game developer doesn't even need to know about that data.

[Edited by - shaft on November 23, 2004 7:06:21 PM]

Share this post


Link to post
Share on other sites

This topic is 4769 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this