I’m making an RPG in C++ and the items (loot/gear) will have immutable base versions, but then the player can get instantiations of them which can change with use and modification. These mutable instantiations will be saved in the DB. But I’m wondering if the base versions should be defined in JSON, or a separate DB (with the same schema), or maybe the same DB (seems dangerous), or if I should just hardcode them in C++ in the ItemFactory.
How have you approached this problem in your games? How do game engines do it? I’m using SDL2 so I’m doing most of these systems from scratch.
I like to use Unity, and I love using class inheritance where possible.
If I know a group of items will all share similar properties but might have one or two different methods, I create the base “Item” class with all the values that all items will share in the game, and then create a new class for each item that has a different property all on its own.
For example, a healing item has a healing method that a coin item would not have, but both of these inherit from the Item base class and thus can simply be detected as an item by the systems that only care about whether an object is an item or not. Such as when an object is picked up by the player and placed in the inventory. This keeps everything pretty simple for my purposes. Not sure if this gives you any ideas.
But where do you store the definition of an item? You hardcode each item type as a derived class?
Pretty much, yes. I generally have such few item types in my game that it doesn’t really matter if I hard code them.or not.
For example, Item might contain parameters like item-id, item name, carry weight, etc. Parameters that every item would need to have. Then HealthItem would inherit from Item, and thus would include all its base parameters and methods for being added to the inventory, etc. HealthItem would then also have new parameters like heal-amount and a method for healing its user.
Something like a health potion that heals 5 health and a medkit that heals 50 health would both use the same HealthItem class, and be stored in the game engine as separate prefabs. These prefabs would then be instantiated into the game for entities to interact with them.
In some of my apps (web apps, not games), I put a folder called “data” for files that just contain data that’s better suited for csv, xml, json, etc than it is for either hard coded object definitions or database.
Another term for this kind of thing, at least in the web dev world, at least in 2020 which was when I last was a developer, is “assets”.
A logo for a website is an example of an asset, as is a font file.
Assets is data that’s designed to be immutable.
One thing to keep in mind, however, is that balancing the game will likely involve changing the parameters of the base objects as well as the modifications they can get. After balancing is done, the data won’t change. But during balancing, the data might change quite a bit.
For this reason, it might be worth it to have a set of functions for manipulating these parameters, instead of just going into a big json file and manually altering numbers.
Balancing in this way might make storing this stuff in the database more useful.
Just some thoughts. Again, not a game developer. But I have written a lot of code, and fiddled with lots of “balancing” in systems.
It may seem like overkill to put such data in the DB, but there’s not as much downside as one might expect in my experience.
Unless I really know I need something more complicated, I would just make a struct for Item. Create an array of Items that serve as the templates for instantiation. Handwrite the first pass of a few Items in C++ (or maybe C99 for syntactical convenience). Switch to CSV later and work with bulk definition in a spreadsheet.
I wouldn’t use either a DB or JSON for this unless I had a really good reason. Like, I know I need to do a lot of complicated joins or I actually have hierarchical data – not just an array of objects, respectively. The additional complexity of dealing with them isn’t worth it otherwise.
I never considered CSV but that would be the simplest way to store basic definitions.
There will be complicated behavior. And a bit of hierarchical data (each item will belong to a specific map). Plus I want inheritance so they can perform different functions on different screens. But the basic data is fairly simple.
Is there a good CSV library you use to read the data into C++?
Honestly I’d just hand write a CSV parser, they aren’t that complicated ESPECIALLY if you don’t care about having commas in your data because then you don’t need to quote wrap the entries but that’s just me.
Same. I already have one I wrote myself that’s good enough for my needs – which means it doesn’t handle quoting or alternate line endings or other quirks. (I can just change the separator to
\t
if I really need commas in values, and since I control the data format, I can just say “it will always have Unix line endings” to keep it simple.)There’s probably thousands of open source CSV parsers out there though if you don’t want to roll your own, but I don’t have a specific recommendation.
Yeah, I had the same thought LOL. If you need commas in your data just use a TSV and call it good. I have yet to need the commas though
When your items have complicated behavior, I’d strongly suggest defining them in code.
If you already have a factory for items couldn’t it just access the base definitions stored in either a separate class or key them?
I’m actually leaning toward this. I might have a class that holds a large unordered_map with a string key, where the value is actually a function which calls a constructor loaded with the defining data (a function so that I can create the map in an object or function without creating ALL the objects every time).
Still, creating a huge unordered_map every time I just need one (or ten) items seems bulky. But there will be five maps, each with their own native items, so maybe I can break it up that way.
I’m also concerned about using magic strings to access values. I wonder if I should make another unordered_map just to hold string values (something like [“BASTARD_SWORD”] = “bastard_sword”) so that I never write the string, I just write… the string to access the string… maybe silly and bulky.
For now, all items in my game use Godot’s Node3D as the base, and I am moving towards having all their respected scenes contain component nodes and scripts for their functionality, to keep it clean.
There’s a lot more inheritance at play, but its been months since I touched the project, I should look at it and get more details. In reality, composition via nodes and types is going to be easier to maintain.