Question on Utilizing Data Driven Gameplay/Design in Games

Hi everyone!

I’ve got a design question that I’ve been wondering about. How this questions pertains to me is outlined below (under the Spoiler to keep this nice and tidy), but if you aren’t feeling like reading through it all, which is completely understandable, my question is essentially this: H****ow much can/should I rely on, or use, data to fill in a class’s data and configure behavior (via construction/initialization) versus creating sub-classes of a base class and changing things there (aka, traditional OOP)?

[SPOILER]
I haven’t used a lot of data driven design/gameplay in past projects so take this all with a grain of salt as it’s my first “stab” at it.
I’m currently working on a “portfolio” project (little third-person shooter game) to test out this new design method, and my goal with it is to see how easy I can make it to create many variations of certain game objects (in this case weapons) without adding tons of boilerplate classes that really only change some variables, not explicitly changing functionality (the variables alter final behavior, such as a weapon attack). The idea being that I can put the power into the designers hands and better utilize my time on implementing other systems too. As such, I’m experimenting with the concepts of data driven gameplay and its implications on design. In this project, I have about 20 “weapons” that the player can use while playing (either starting with them already equipped, or picking them up in the world at run-time), which vary from hit scan guns, to explosive grenades, to melee weapons. For the weapon system in the game, I have a “WeaponManager” actor component on the character that handles all the player-to-weapon interactions, such as attacking with the equipped weapon, equipping/unequipping weapons, reloading (if applicable, and this is actually a part of the question too), spawning and attaching them, etc. The designer can optionally specify tags of weapons to spawn in the weapon manager when play begins.

Next I have an actor class, Weapon, that serves as the base weapon class and is the class that the weapon manager operates on. Now as I said, I’m experimenting with data driven gameplay, so I’ve set up a struct containing data relevant to all weapons, FWeaponData, which inherits from FTableRowBase so that I can use a data table to retrieve the relevant data for a certain weapon and drive behavior in the game such as damage dealt, attack range, attack speed, etc. This would give any designers the ability to easily edit and test new/existing weapons without any new lines of code needing to be written. The Weapon class then has an FGameplayTag property, whose tag name I use to retrieve a specific row from the FWeaponData data table that corresponds to the weapon. This allows me to get all the data I need in code, while also making it easy for designers to add new weapons (by adding new gameplay tags and a corresponding row in the data table), while also preventing errors in spawning the correct weapons in the game due to misspelled row names (since the gameplay tags are in a little drop down box like an enum).
In theory, this means I can just have one weapon class and simply “create” all the different weapons in the game by using its relevant data from a data table. In fact, this *is *what I’ve been doing and for all intents and purposes it’s working just fine.

However, it just doesn’t *feel *right if that makes any sense (maybe I’m just too used to OOP design). I kind of want to make a subclass of Weapon for each unique attack type, such as HitScanWeapon, ProjectileWeapon, ExplosivesWeapon, MeleeWeapon, etc so that I can implement the weapon’s Attack function differently in each class without having to perform a switch on the weapon’s type (using gameplay tags, not an enum) in the Weapon class’s Attack function that then performs different attack behavior (which is what I’m currently doing).
Additionally I could remove the “Reload” function from Weapon so that Melee and Explosive don’t inherit it since they don’t need it, and just implement Reload by way of a UInterface on weapon classes that can be reloaded.

But then comes the question of having specific data structures that relate only to the weapon type in question. At the moment, my FWeaponData contains data that doesn’t necessarily apply to everything, such as MaxAmmo, but in code I only use data relevant to the specified weapon’s tag so the unused data doesn’t really muddy the water in practice. It’s really only a design consideration at that point (in my eyes anyway). The idea behind that decision was that I could A) keep all data in one data table so designers don’t have to swap between different data tables that were specific to a certain subclass of weapons (like MeleeWeaponTable, HitScanWeaponTable, etc), which could potentially cause mistakes that are difficult to debug later, and B) to make it so I could just use one weapon class to spawn the weapons as I mentioned earlier.

But I guess at the end of the day, when making a game and shipping a product, if it works it works right? Maybe? I don’t know. shrug I know some share that opinion, albeit with some concessions, but I personally try to design my code in a manageable and extensible manner. This has just been a journey into something I’ve never tried before and ultimately I just hope to get some insight/opinions on everything above; whether I’m on the right track, should make some changes, or whatever.

So yeah, long story short my question again boils down to: How much can/should I rely on data to initialize a weapon versus creating sub-classes of my base weapon class and editing functionality there?
[/SPOILER]

Sorry for the long-winded post but thanks for listening! Any insights are much appreciated!

P.S. I’ve already read many of the UE4 docs/blogs on the subject, read other blogs and forums, and have watched multiple talks on data driven design. However, I have yet to see a physical example beyond a few lines of code, which is in part why I’m asking this question too.

1 Like

Sadly I think the answer to your primary question is… it depends… I personally leverage DDD AND OOP quite equally depending on the situation. In Games such as a straight forward FPS an inventory system where each item is it’s own specialized blueprint may be ideal, but in another where items can be crafted and improved repeatedly a more Data Driven approach may be prudent. If your objects have only visual or “Data” differences (such as a new mesh or better damage) than DDD is probably perfect, but if items have some specialized behavior than subclasses are idea. My own Inventory system leverages both, a set of core OOP classes for each different item type and functionality with a whole lot of Data Tables/ Data Assets to fill in variation. The difference between a sword and a knife is only cosmetic with maybe a smaller reach, thats data differences. The difference between a sword, a gun, or a Healing Potion is a behavior difference. However All these things inherit from one master Item class that has the shared functionality of all of these items, that being they can all be put into your inventory.

1 Like