Data Driven Game Architecture - examples?

Started by
9 comments, last by Bluefirehawk 11 years, 6 months ago
I have been programming for a few years now, self taught for the most part, and have been able to understand quite a bit on my own, but I am having a hard time wrapping my brain around a 'proper' data driven architecture for game development.

For a while now, I have been sort of moving in a data driven direction, subconsciously, but have only now recognized it for what it is, and would like to learn more about it, and take full advantage of using a data driven approach.

For example, say I am doing a tower defense game... From what I understand about the data driven approach, I would essentially have an array of entity ID's, which other tables would reference for the individual 'data' pertaining to each entity. I would then have methods which I would use to manipulate this data (separate from any 'Object')...

What I don't understand... is what about the finer details? Like say I have a Poison Tower, which does AoE damage per tic. Then I have a basic Pea Shooter, which shoots a strait bullet at the 'oldest' creep x times per second. Finally lets say there is a Snare Tower, which will slow down creeps within a set radius around the tower.

So obviously there is common 'data' between these towers, such as x/y position, active/inactive, radius of influence, price to purchase, price for upgrades, etc. This 'common' data could all be managed using the same methods, but what about the more uncommon information? Do I create a separate method for each individual tower type, or do I use a switch to determine the type, and handle the 'special conditions' within the main method?

From an OOP approach, I would create a base Tower Object, and derive each new tower type from the parent, using overloaded functions, or adding additional functions if needed.

To be more specific... When every tower is created, the user needs to decide on the X, Y position, probably with a mouse or finger. Every tower needs this, so it would go in the generic 'tower creation system'. When the user buys an upgrade, the data can be taken from the table, and it is the same for every tower. On the other hand, when it comes time to scan the oldest enemies in the towers radius of influence, and then deciding on if it should shoot a bullet, apply a poison, or reduce speed... is this handled by the same system, or should I create a separate system for each TYPE?

What about in games where there are SEVERAL different types of enemies... Does each enemy type need its own 'system', or do I just make one big system, and use a switch to manipulate the data appropriately?

I really like the data driven approach, and like I said, every time I would write individual onHoverBegin, onHoverEnd, onClick, etc for each button in my UI, I would always tell myself... "There has to be a better way for this". Now I realize a data driven system is exactly what I was looking for, now I just need a bit of help understanding it on a 'personal' level.

Thank you in advance. Yes, I have used google, as well as the search field on this website, but if you can find something I missed, I am more than happy to check it out!
Advertisement
I struggled with this at first but found the data driven architecture to be quite nice. I have the basic Object class which you can attach any number of different types of attributes too. These include mesh/materials, variables, lights, particle systems, rigid bodies, etc. Attributes are user create-able too so you can attach any number of your own attribute types to the object. These can be used and manipulated in the specific game code, outside of the engine. One other thing you can do is attach a lua script to an object which has the ability to manipulate the object directly. Obviously some things need coded specific to the game itself, but most things can be accomplished with a flexible attribute system. I know this still sounds like a tangle web, but I invite you to just try coding a simple prototype... I think you'll find it easy to get through once you start. My code is on github, feel free to take a look. Here's a starting point: https://github.com/palodequeso/Element-Games-Engine/blob/master/Engine/Game/ObjectAttribute.h
Douglas Eugene Reisinger II
Projects/Profile Site
From an OOP approach, I would create a base Tower Object, and derive each new tower type from the parent, using overloaded functions, or adding additional functions if needed.
OOP != inheritance. Composition is a much bigger part of OOP than inheritance is, and use of inheritance should actually be quite rare in OOP.

For something as small as Tower Defense, you could keep it pretty simple, with structures that can be loaded from data files fairly easilly:struct ProjectileType
{
int speed;
int splashArea;
enum SplashType
{
Damage,
Slow,
Poison,
} type;
int amount;
};
struct WeaponType
{
ProjectileType projectile;
int shotsPerSecond;
};
struct TowerType
{
vector<WeaponType> weapons;
};


struct WeaponInstance
{
int timeOfNextShot;
void Update(WeaponType&, int time, int x, int y);
}
struct TowerInstance
{
TowerType* type;
vector<WeaponInfo> weapons;
int x, y;
void Update( int time )
{
for(int i=0, end=weapons.size(); i!=end; ++i)
weapons.Update( type->weapons, time, x, y );
}
}
If you're working on a larger game, or a Tower Defense type game where you're adding new weapons and projectiles with new behaviours every second day, then the enum+switch will become a bit bloated, so you could turn [font=courier new,courier,monospace]ProjectileType[/font]into an interface, with an [font=courier new,courier,monospace]OnHit[/font] function/etc... You'd then likely use the factory pattern, so that in your data files you can write "PoisonProjectile" as a string, and the factory will turn that string into [font=courier new,courier,monospace]new PoisonProjectile();[/font] when loading the data.
If I were doing this, I would have a Towers XML file which defines the towers with image/animations, cost, upgrade costs, and weapon type(s). I would then have a separate weapons xml file which defines all the weapons a tower can use, what it's upgrades do, the image/animation, etc.. In code, I would have Tower be an component of a Tower entity,and also contain a Weapon Component, which defines the weapon it uses. When it's time for the tower to fire, it generates a new entity which is defined by the weapon.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

The core of data driven system is that objects are initialized by a data descriptor object be they data from XML, scripting, internally, etc.. This initialization chain follows all the way down to all the child objects. Objects themselves are not usually created on demand but rather by a factory due to the complexity of creation in data driven designs.

A data descriptor is just a unified data accessor class which makes it simple for data access and accounting. Imagine if u will an object definition is a tree and iteratiring down the tree to create all the components and child components, you'll need to keep track of that iteration as well as have data accessors to make sure data conforms to both read / write access.

Data driven design can be as simple or complex as you like but the drawbacks with such a system is that u have to have runtime checks for validity and data and class integrity which can increase the burden of testing and maintenance. So people usually write tools to manage this for them, when the data set becomes too large. Another common design pattern for data driven system is to use object composition vs inheritance to create complex objects but that's really just user preference and both can co-exist.

Good Luck!

-ddn

So obviously there is common 'data' between these towers, such as x/y position, active/inactive, radius of influence, price to purchase, price for upgrades, etc. This 'common' data could all be managed using the same methods, but what about the more uncommon information? Do I create a separate method for each individual tower type, or do I use a switch to determine the type, and handle the 'special conditions' within the main method?

You add that information via composition. Composing common data is trivial, as you have observed, but then you also need to compose the differing functionality under a common interface.


drawbacks with such a system is that u have to have runtime checks for validity and data and class integrity which can increase the burden of testing and maintenance

Otherwise known as comprehensive unit tests and functional tests, both of which you should have already. I hate to harp on it, but code without tests is broken code.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]


If I were doing this, I would have a Towers XML file which defines the towers with image/animations, cost, upgrade costs, and weapon type(s). I would then have a separate weapons xml file which defines all the weapons a tower can use, what it's upgrades do, the image/animation, etc.. In code, I would have Tower be an component of a Tower entity,and also contain a Weapon Component, which defines the weapon it uses. When it's time for the tower to fire, it generates a new entity which is defined by the weapon.


Brilliant, I hadn't thought of doing it that way. :D

Thank you all for responding, by reading through these responses, I am starting to see the 'bigger picture'. ddn, I like that way of thinking: instance vs composition. I really do feel like the data driven way of doing things fits in well with my own mentality, and like PaloDeQueso said, I'll just have to stop thinking about it, and start working with it for it to truly become natural.

Any other advice or insight is welcome!

Otherwise known as comprehensive unit tests and functional tests, both of which you should have already. I hate to harp on it, but code without tests is broken code.


I think TDD has it's place, but for smaller development houses where it's just 2-3 programmers you have to make do with what time you have. Sure for larger teams where it's critical that components function to specification and teams are separated by timezones u don't want to mess around with email tag trying to get core critical systems working. For individual developers its up 2 u if you want to do TDD and to what extent. I find for individual developers code maintenance becomes a much larger problem, try looking at code you wrote 3 years ago for example..

-ddn

I find for individual developers code maintenance becomes a much larger problem, try looking at code you wrote 3 years ago for example.

And I say again: you should have tests. Tests are the best defence against code-rot.

Tests not only validate that your code from 3 years ago works, it also validates that it does what it says on the tin. And given that your code is either documented, or self-documenting (it is one of the above, right?), it should be a breeze to pickup your 3 year-old code...

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Personally I think its personal choice for individual developers. It depends on why you code. For me i code for experimentation and play, when I develop code when on my own time to experiment with things, there really is no need for TDD since by the thesis of the code itself, i am pushing the bounds and i want it to break, I know it will break. For work yes I've used TDD when working with larger teams but for small teams the payoff isn't as great as the limited manhours become an overriding issue. But I'm willing to give it a go and see if TDD does have any advantages for the individual developer, maybe i'll use it on my next library.

This topic is closed to new replies.

Advertisement