Advertisement

Designing a data driven crafting system using tags

Started by October 13, 2023 10:04 PM
3 comments, last by frob 1 year, 2 months ago

I've been working out the design for a data driven system for a game economies using tags. This is largely inspired by Entity Component Systems (ECS) and the principle of “favor composition over inheritance”. The general idea is that for all entities in the game, they are defined by tags. Tags may have a corresponding component, but not necessarily. Tags are just descriptors which add attributes or behaviors to an object. Tags can also represent a collection of other tags (Tag sets). The tags an entity contains are all unique (stored in a dictionary, keyed by tag name), so if two tag sets are assigned to an entity and both tag sets contain a shared tag, that shared tag is included once due to how dictionaries data structures work.

I think the advantage is that by defining tag sets, you automatically get inheritance while also avoiding the diamond problem of double inheritance. It also gets around the general problem of classification / abstraction for ambiguous cases. For example, is a platypus classified as a bird or a mammal? If we have to fit it into a generalized class, it's hard. But if we just use tags to define it, we just don't know or care about what abstract class it fits into. Tags can also be added or removed from an object. If we have a lump of iron ore, and it includes the tags “Iron” and “Ore”, and then we process it in a smelter, then all the smelter needs to do is remove the “Ore” tag and it now becomes just “Iron”.

If you have a robustly defined tagging system for objects, you can also query an object to see if it contains a particular tag. Suppose you have a crafting recipe to a hammer which requires a stick and a hammer head. The recipe template might look like this: {"Stick", “Wood”} + {["Metal", “Wood”, "Stone]} => Hammer
So, the “Wood” tag is a tag assigned to all types of wood, and similarly with “Metal” and “Stone”. You could supply ash wood for the stick and copper for the hammer head, and the crafting result would be a copper hammer made of ash wood, and you could combine the properties of both ash and copper to define the final attributes of the crafted hammer. The materials used to craft an item affect the properties of the crafted item.

Someone might say, “Wait a minute, isn't this still just an entity component system? All you are doing is replacing the word ‘component’ with ‘tag’ and rebranding it as something new! It's not new and you aren't original!”
Sort of, but not quite. At the most primitive level, tags can be defined as simply a collection of strings. Some tags can just be collections of other tags. Not all tags have a 1:1 mapping between the tag and a component, though it would probably be common to pair tags with components. If all of your game objects are defined as collections of tags instead of a predefined collection of components or object inheritance hierarchy, then you can define all of your objects in a plain text file using JSON, CSV, XML, or some other data warehousing format. If you want to change the properties of an object, or create a new object, all you have to do is change the data file instead of changing classes or components. This would mean you can update your game economies, crafting systems, and add new items without necessarily recompiling, repackaging, and redistributing binaries to customers.

I'm sure I'm not the first person to come up with this, so I'm curious to know how common something like this is and how other people have designed similar systems. If I look at “Age of Wonders 4”, it appears that they have a tagging system of some sort on the backend, but I am not sure if its used for crafting economies. What are your thoughts? Are there any oversights on my part?

slayemin said:
stored in a dictionary, keyed by tag name

I'd suggest not using strings as keys. Strings will break as soon as you need to rename a tag, or have two tags with the same name. I'd suggest using a guaranteed unique identifier, such as a UUID. The bonus for this is that UUIDs only require 16 bytes, no memory allocation, and can be compared for equality with just a few SIMD instructions, whereas strings are much slower and can require more memory and allocations (assuming your names can be >15 characters and can't use short string optimization).

It sounds pretty similar to data-only components in ECS, I don't see much difference in the abstract. In my engine I have a system for tagging objects (and also components) with string tags, but it works a bit differently. Each tag is implemented as a string + list of references to the objects that have the tag. This way I only store the tag string once, and can rename the tag while still keeping all references to where it is assigned. Objects with tags assigned don't need to have a list of their tags (saves memory+allocations). It's also easier to get all objects with tag X, because I can just iterate over the list.

Advertisement

That's a great idea. I'll take your advice and run with it.
I was initially thinking that usability is king, performance is an after thought since it would be a negligible impact. But I can shield the user (designer) from that by creating a quick UI system which is mostly a CRUD implementation for a JSON file containing the item database. This would remove the potential for user error with such things as misspellings, wrong capitalization, renaming things, etc. The UI could programmatically generate the JSON file and use GUIDs on the backend for indexing tags and items.

I remember playing crafting games with similar mechanics in the 80s and early 90s, especially around construction or economy games. A few BBS games (IIRC Trade Wars?) had a mod-friendly mechanic where people could add resources to the data tables for the economy.

Use key / value pairs in data, like 3 wood, 7 iron, 1 small engine, or whatever. No strings needed, except for UI and localization.

You can have multiple sets of data, such as a minimum resource to create, the resources it provides if consumed, and the resources it provides if broken down. Customized items might provide 75% of their source ingredients, or whatever works for the game.

This topic is closed to new replies.

Advertisement