Resources for building large-scale RPG systems (items, NPCs, etc)

Started by
9 comments, last by Kylotan 7 years, 2 months ago

I guess what I'm looking for are articles, books, or post-mortems that discuss how major RPG games tackled the issue of how they handled different types of items, characters, monsters... Right now I'm looking to expand my knowledge about this - studying things like inheriting from classes, or the entity-component system. Ideally I'd like to know about what different kinds of techniques are there, what is best for the RPG medium. It's a personal topic of interest for me, so any resources would be greatly appreciated.

Advertisement

For role playing games specifically, there was a book Programming Role Playing Games with DirectX but it is incredibly outdated. You can still use the code, but with the caveat that DX9 is 15 years old, the APIs have been deprecated for nearly 10 years, and there will be incompatibilities with modern compilers. However, the content of the book and code examples may still be useful as a guide if not verbatim.

For large software systems there's the classic Design Patterns book and various spinoffs, Patterns of Enterprise Application Architecture, Software Architecture in Practice, Refactoring to Patterns, Refactoring in Large Software Projects, Working Effectively with Legacy Code, Code Complete, Refactoring: Improving the Design Of Existing Code. There are many more, but that's plenty of reading material.

Then if you're using C++, there's all the standard recommendations: Effective C++, Effective Modern C++, Effective STL, Exceptional C++, More Exceptional C++, The C++ Standard Library. If you're using another language you'd better know it, whether that is Java or Python or JavaScript or whatever else.

Are you primarily interested in multiplayer or single player RPGs?

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

Areas like "procedural generation" (no idea what it does other than large-scale generation of stuff), or map/room generation techniques could be useful to study too.

Are you primarily interested in multiplayer or single player RPGs?

Single-player, mostly. Though multi-player would be just as interesting. Just curious how games like Skyrim or Dragon Age (or even sci-fi ones, because I can't imagine the systems behind the scenes change much) handle such large amounts of data, or how they're structured. Items, for example: are they separated into categories which each inherit from a base Item class, or is it something more along the lines of an entity-component system where they just add and remove components as needed?

Don't be misled by "such large amounts of data" - if you can store 2 things, you can store 200, or 200000. The key is simply to not duplicate things that don't need to be duplicated. One concept that comes in common with game data is the Type Object concept, sometimes known as the Definition/Instance division. Basically you have one lump of data that represents archetypical data representing a whole category of objects, and a different lump of data representing individual instances of that object, and no matter how many instances you have, you only ever need one of the first 'lumps'.

Example:

Short Sword 'type' data:

  • damage per hit: 1-6
  • durability: high
  • weight: medium
  • hands needed: 1
  • inventory image: short_sword.jpg
  • world model: short_sword.fbx

Short Sword 'instance' data:

  • location: world position (123, 456, 789)
  • wear-and-tear level: 95%
  • current enchantments: +1 to hit, +2 to damage

Notice how you never need more than 1 instance of the first block in your game, just as many instances of the second block as you have short swords in the world. The 2nd block also needs a reference to the 1st block, so that you can get the shared data like damage and durability.

As for defining the difference between different object types, it's quite common to just have a Type value (eg. 1=Weapon, 2=Armour, 3=Potion, 4=Key, 5=Edible, etc) and then a bunch of different fields which may or may not be relevant for each of those types. The reason this isn't massively wasteful is because most of this exists on the Type Objects, and there aren't all that many of those.

You could implement this via inheritance instead, deriving different subclasses for item types - but in practice they often share more fields and behaviour than not, so you don't gain much.

Similarly, you could implement this with components, if you really wanted to. Most people don't. You might have a generic Item component plus a component for whatever else is interesting about it - but since your Item probably needs to know about its type in order to display it in the inventory or in the world, it's probably more hassle than benefit.

A concrete example of what Kylotan is describing...

Caveman 3.0 is a single FPSRPG / person sim hybrid with a stone age setting and an emphasis on realism.

Caveman makes extensive use of "type objects" - also known as "relational databases".

There's a list (database) of animal (monster) types. stuff like base HP, turn rate, base movement, number appearing, etc. NPC humans are just another species of animal.

Then there's a list of all the animals that are currently active in the simulation, with info like damage, fatigue, location, current_target, etc.

These's a list of object types - armor - weapons - foodstuffs - etc. Data includes things like price and weight - IE stuff common to all instances of that object type.

And a bunch of lists of objects: inventory lists, a list of dropped objects, list of items in containers, lists of items a band has to trade, etc. They just track type, quantity, quality, location, etc. IE instance specific data. the TypeID is the simply the index of the object type in the object_types array.

so you get things like:

if (player_encumbrance() + object_type[stuff_list[a].type].wt > player_max_encumbrance()

message("You can't carry it!")

or in the case of the animal types database, you get things like:

if ( animal[a].dmg > animal_type[animal[a].type].hp )

kill_animal(a)

I personally don't care for the lack of readability of double de-referenced array addressing, so i usually do something more like this:

t = animal[a].type

if ( animal[a].dmg >= animal_type[t].hp

kill_animal(a)

There's a list of actions, with info like prerequisites, time required, base chance of success etc.

But there's no real list of action instances. a bandmember can only do one action at a time, but they do have an action stack, so they can push a "travel cross-country" action and do a "rest" action, then go back to traveling cross-country.

There's a list of skill types, with prerequisites for research, time required for a training session, base chance of success, etc. These are used when the player does some "learn skill" type action. So they are basically the same as the list of actions, but for learn actions on a per skill basis.

The game also has a list of active projectiles. the projectile types are found in the object types database. weapons and ammo are just another object.

The game has 50 kinds of monsters, 300 kinds of objects including 65 types of weapons or weapon and ammo combos, 30 kinds of armor, and about 50 different skills.

A similar pattern is used in the graphics engine,with a single master database for each type of asset (meshes, textures, materials, rigid body models, rigid body animations, and skinned meshes). skinned mesh animations are not stored in a single master database, as the Dx skinned mesh API does not have built-in support for skinned mesh animation sharing. So i append shared animations to blender files before import as .x. This results in each instance of a skinned mesh having its own copy of all animations for that mesh - including those shared with other skinned meshes. So i have animation sharing, but not animation pooling. asset pooling in graphics is analogous to type objects or object_type databases in game code (IE a single store of common data using by a number of "things").

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Thanks to both of you for all the information about that, I think it's precisely what I needed! I've been reading through the Game Programming Patterns book but hadn't got to that section yet.

The system you've detailed here is eerily similar (at least I think) to something that I was trying to create on paper yesterday during some spare time in class. I thought when I made it that it was basically just inheritance, but maybe it's more along the lines of what's being described here. Here's the basic gist of it translated into a UML-style diagram. Is that more or less correct? The instanced object would have all of the qualities of the types above it.

I left out other categories, like Consumables (like potions) or Resources (wood, ore, herbs) or other possibilities, but the gist is the same for each. Is this more or less correctly following the type object concept?

It's worth considering how you would integrate more complicated effects into your item system. For example, how would this system account for a sword which has +3 lifesteal on each attack, a helmet that added +1 damage to all critical hits, or a potion which temporarily imbued your attacks with poison?

An item system modelled on inheriting traits may not be flexible enough to represent the sort of diversity represented in a modern RPG. It may make more sense to model an item system as a set of 'effects', where effects act as modifiers to base attributes.

In this model all attributes belong to the character (not the gear), and each attribute works like a stack - equipping gear, casting spells, or drinking potions pushes attribute values onto the relevant attribute stacks, and unequipping gear or potion/spell expiry pops them off. The attribute value at any given time is obtained by summing the contents of the stack.

For example, under an effects-based system:

  • A character starts with base stat values (probably based on level). i.e. 1 physical damage, 100 health, 100 mana, 5 movement speed, and so on.
  • Equipping a basic sword adds +5 character's physical damage attribute. Equipping basic armour adds +10 to health and +1 to physical damage reduction.
  • Casting haste adds a +1 to movement speed of all nearby allied characters for 20 seconds.
  • Drinking a mana potion adds +100 to mana pool, and +0.1 to mana regen for 20 seconds.

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

Yea, I was trying to figure out how modifying existing values would work - I just figured I'd take something similar to what Kylotan illustrated above whereby the type would have a 'current_enhancement' field which pointed to a specific spell or something that would then modify the values. Not sure how potentially unnecessarily complicated that could get, though. Might just be a method in the 'Equipment' type that gets specifics from the associated 'Spell' type.

That said, I really like the system you've detailed - and now that I think about it, I'm curious to know what other stat-based games have used a similar setup. I imagine more complicated ones that rely on a higher amount of stats, like an MMO. Even still, seems like a perfectly good solution for an RPG that's smaller in scale.

This topic is closed to new replies.

Advertisement