Data-driven game objects for use in simulated ecosystems
Abstract
During placement year, Arkode, an indie game development group founded by students created the IsoEngine; a voxel-based map editor with featuring terrain sculpting and the placement of 3D game objects. Namely items such as modular building pieces, foliage, and AI. There are currently three separate systems in the IsoEngine for handling non-voxel objects. Broader gameplay ambitions of the IsoEngine, require ecosystems to be designed through the map editor. To lay the groundwork for this feature, the three object handling systems need to be combined (collectively referred to as the prop system) to improve project maintainability and interactivity between AI, foliage, and the wider environment.
A core aspect of this merge is streamlining the integration and behavioural design process for new props, as existing systems are slow and hard to maintain. Data-driven design principles, and emergent behaviours will in future be employed to make ecosystems feel more intelligent to an observer or player. This data-driven design necessitates a new format for prop data storage which must be designed alongside the prop integration interface and future ecosystem behaviours.
The project’s success will be measured on how easy it is to add new content to the game, and how accessible this data is for future use. Accessing configured prop data and implementing ecosystem behaviours is not the focus of this project, but a minor secondary success metric will be how easy it is to use the new data format. Therefore a few select behaviours may be implemented to test the new prop system.
Introduction
Overview of Arkode
Arkode is an indie games development business, set-up as a partnership by Joshua Matthews and Joshua Ellis. Matthews has stepped away to focus on his studies, and Ellis is continuing the development of Arkode’s lead project: IsoEngine. The goal of the business was to make a success of building games with simplified design aims, but robust gameplay systems and mechanics. The brand has a distinct simplified colourful style, in both its branding and chosen game art.

Overview of IsoEngine
The founding aim of Arkode was to develop a customisable RPG. To that end, IsoEngine was developed as a player usable map editor. The RPG focus has changed, but the toolset remains.
Voxel engine
The IsoEngine uses a custom voxel system for terrain and structures. Instead of cubes, terrain is composed of primitive low-poly shapes.

Different materials are supported in the engine (sand, dirt, stone brick etc.), and each material can have a unique set of models and textures.

Voxel terrain can be sculpted using “brushes” of different types. Voxels placed in this manor automatically chose the most appropriate surface primitives to “smooth out” overall appearance.

Prop placement
3D models can be placed using the prop system. This could be anything from plants, decorations, humanoids, or structures. To unify the voxel and prop system, each prop must be grid aligned and have a defined voxel volume. This allows systems such as navigation to treat props and voxels as one and the same.

Project aims breakdown
Pathfinding
A working pathfinding system for organisms isn’t core to project goals but is useful for both AGCP and is a technical hurdle that if not crossed is a barrier to later ecosystem work. Implementing and testing pathfinding work early on ensures other work does not go to waste, and that the direction of the project can pivot if necessary.
AI agents must be able to navigate across all traversable terrain. This includes static props of various shapes and sizes. Existing voxels can be altered in real time, so dynamic navigation meshes must be considered by agents, and updates triggered upon alteration.
Agents can have various heights, but for simplicity will not have a front profile wider than 1 voxel unit (1 meter). This should fulfil requirements for most creature types while maintaining code simplicity. Future updates could improve this.
Merge old game object systems
The IsoEngine currently has three controllers for handling different types of 3D asset. Stationary 3D models placeable in a voxel world are handled by the current version of the prop controller. A separate controller handles objects with physics capabilities. Lastly, an AI controller can observe and effect changes on physics objects.
Each of these controllers achieve the same result, which is the manipulation of 3D objects in the voxel environment. This has led to a lot of code duplication and tightly coupled classes that are hard to make alterations to. To solve these issues all three will be combined into a single controller, that on a function-by-function basis can make special considerations for the physics state of an object, if necessary, as well as driving prop behaviours specified in the prop design interface. This new controller will be called the (new) prop system.
Ecosystem considerations
Long term, a major feature of the IsoEngine will be a simulated ecosystem of AI. Therefore, any systems, tools or processes need to be tailored towards this goal. The implementation of the ecosystem itself is not the aim of this development effort, but a basic design and exploration of potential features needs to be explored so that prop data storage and prop design interfaces can be designed and implemented accordingly.
Designer features
The interface for adding a new prop to the editor needs to replace an existing pipeline. At present, prop data is configured through UE4s blueprint system, which is a misuse of the toolset. During previous development cycles, many props were configured through blueprints, which has led to monolithic node connections, and a hard to maintain layout as seen below.

The new interface needs to be approachable by someone without programming knowledge or experience with the broader IsoEngine technology stack. It also needs to offer the right balance of the fewest fields to achieve maximum behavioural expression with said values.
Visual design goals
Throughout development, many assets have been bought for the IsoEngine featuring a low-poly aesthetic. Any opportunity to test the new prop design interface will make use of these assets.

Additional assets were purchased specifically in preparation to add animal ecosystems to the game. A few of these assets will also be implemented if the opportunity arises.

Sustainability/modularity
To sustain the project long-term and to support future additions, code and data storage methods will be modular where appropriate. Data-driven design is employed to support this.
Data-driven design
Many modern games, such as For Honor (Data-Driven Dynamic Gameplay Effects on For Honor, 2019) implement a data first approach when it comes to developing systems and gameplay mechanics. What is meant by this, is that behaviours are decoupled from values relating to specific entities and can be fed into behavioural systems without the need for a recompile of source-code, as described in a GDC talk by Gas Powered Games (Bilas, 2002). This technique will be strived towards during the development of prop data storage and configuration tools.
Development pipeline
Toolset
Platform
A comparison of potential IsoEngine development tools was carried out at the start of the development process, and general tools are finalized. IsoEngine is built within Unreal Engine 4, which was chosen over Unity due to its support for C++. Voxel engines and game optimisations enabling high entity counts at higher framerates are common academic pursuits for the C++ language (Wilder, 2015), broadening my pool of reference.
The target export platform for this project is Windows x64 for PC, due to it being my primary OS and therefore most convenient for testing and deployment purposes. Other target exports may be considered for final releases of the IsoEngine as a product.
Prop data storage
Choosing an appropriate replacement tool for prop configuration is key to an efficient prop design pipeline. Real-time prop data access must be efficient and the new system iteratively upgradable. Something that is easy to integrate with existing systems is a massive bonus.
Techniques for data storage and manipulation have been selected for comparison based on their commonality in the development of data-driven systems, game systems or UE4 content management processes.
| XML | SQL | UE4 data-tables | |
|---|---|---|---|
| Overview | XML is an old, well established concept, designed to provide human and machine-readable structure to groupings of data fields. | SQL was developed as a common standard for the creation and querying of databases which are systems built for the sole purpose of storing data with shared formats. | Data-tables are a UE4 asset type used for database-like storage of data driven design elements using C++ and UE4 datatypes. (Data Driven Gameplay Elements, n.d.) |
| Interface usability | Text can be challenging to make sense of. Not designer friendly. | If paired with an SQL interface and storage mechanism, interfaces can be easy to use, but require familiarity with database usage and the format of target data. | The interface for updating data-tables is minimalist, with each unique datatype offering a unique input medium. Data fields are arranged horizontally with each row corresponding to a related collection of information. |
| UE4 compatibility | Models and other UE4 UAssets would only be plain-text modifiable without additional plugins. | Models and other UE4 UAssets would only be plain-text modifiable without additional plugins. | Compatibility is as good as it gets, being an inbuilt asset type. |
| C++ compatibility | XML files could be interpreted as plain text if written to a file but writing to it would be error prone and require a custom error handling and feedback system. | SQL has a long history of pairings with C++ code, particularly in games development. As such there is an abundance of toolsets supporting interactions between the two languages. | By extension of being a UAsset, C++ support is guaranteed, although may not be documented. |
With tight integrations and support for UE4 and C++, data-tables appear to be the obvious choice for the prop system’s development. XML lacks the approachability required by content designers, and databases can lack flexibility in the storage of dynamically sized fields, both of which are key to feasibility.
Project management
A development pipeline has already been established for IsoEngine. Version control is managed by Git and BitBucket is used to host the online repository. SourceTree is used to manage local repositories. To keep general track of feature implementation, Jira and its Kanban template are used. Use of these tools can be relaxed now IsoEngine is worked on by a single developer.
BitBucket over the course of development has been the cause of a few issues that have required a complete reset of repositories. It’s also incapable of tracking feature level changes on binary files, which is a major drawback considering most asset types in UE4 are binary files. Perforce is a prevalent contender, built with specific UE4 functionality that can handle binary files.
Weighing benefits of implementing Perforce based on online discussion (Arthur, 2021) against the time requirement of setup and learning Perforce it is decided that the project will continue using BitBucket. There’s currently only one developer on the project so smart binary file source tracking isn’t going to be an issue, but Perforce will be picked up at a later date once opened back up to a larger team.
Preparation
Risk assessment
Self-plagiarism
AGCP will be worked on alongside this project. To avoid self-plagiarism, some clear rules have been established to follow during development:
The AGCP assignment must have a separate set of development goals.
A* Pathfinding is specifically excluded from AGCP marking consideration.
Inversely, movement following generated paths is specifically excluded from this project’s marking consideration.
If a feature from this project is essential for AGCP or vice versa, they will be labelled as external efforts in design documents. The AGCP project has achieved this in a development graph using colour-coding.
Major changes will be made to AGCP AI behaviour, but these changes will be clearly documented throughout this report.
Data-table concerns
Technical limitations of UE4 data-tables have not been fully explored, and some limitations may be present. To avoid project altering discoveries the following will be done:
A data-table prototype will be designed in parallel to behavioural designs so that the planned architecture is feasible.
Behaviours will be built in C++ but rely heavily on fundamental datatypes and UE4 specific datatypes to ensure compatibility with the data-table.
Resource constraints
This project has to be worked on during final year studies and so has a strict deadline. Lab access is restricted due to lockdown and working from home has been a challenge due to personal circumstances. This will likely inflate planned timeframes significantly, but there are ways to mitigate such issues:
This project will be worked on alongside other assignments to lay out initial systems and prototype early concepts, saving time.
AGCP prototyping will help flesh out basic physics interactions and help explore basic asset loading techniques, which can be reused and altered for this projects behavioural testing.
Behaviours developed for the play mode will be an optional bonus used for testing purposes, limiting the scope of the initial implementation to something more feasible; an interface for designers to use that offers parameters for ecosystem and physics behaviours.
Monetary investments
There is no formal financial backing or investment for Arkode or IsoEngine requiring the following:
The specific goals of this project exclude polish and artistic direction. This limits scope to behavioural functionality, with art being an optional bonus.
Free or open-source development tools have been specifically selected to curb expenses. Epic games and Atlassian tools have free tiers at the time of development.
System design
Pathfinding
Pathfinding in games is extremely common, with popular techniques being well documented and usually solving routes over a collection of “nodes”. Nodes are points in 3D space, that can be connected to form a navigation “mesh”. We can reimagine the voxel grid (including empty space) as a node grid, which simplifies the solution no matter the chosen technique. To select a technique, several need to be analysed for suitability and efficiency:
Dijkstra's Algorithm:
This pathfinding technique places nodes into two lists incrementally. Namely TO SEARCH and SEARCHED. All nodes around the agent are added to TO SEARCH. On each search step all TO SEARCH nodes are iterated across, and their neighbours are added to TO SEARCH, maintaining a reference to the node that added them. Once all neighbours are searched the calling nodes are added to SEARCHED. This continues until the target node is located. At which point you build a chain of nodes by backtracking through adding nodes from the goal. This creates a route.
A* search:
A* builds on Dijkstra’s by implementing a weighted heuristic at the search step. Nodes in TO SEARCH are ordered by distance to target and closer nodes are always searched first. In most simple traversal cases this narrows the search space, lowering memory overhead and improving search time. Heuristics can be increased or decreased by factors such as node traversal time and node-to-node distance.
Jump Point Search (JPS):
JPS improves further on A* by pre-analysing routes of uniform distance and culling them from the search space. Due to pre-analysis, any changes to the environment would require a complete recompute of the search space. This process is costly.
| Technique | Pros | Cons |
|---|---|---|
| Dijkstra's Algorithm |
|
|
| A* Search |
|
|
| Jump Point Search (JPS) |
|
|
JPS can be eliminated from consideration due to costly map analysis stages. This leaves the simple Dijkstra’s algorithm and the improved A* search to choose between. Performance comparisons in several studies (Noori & Moradi, 2015) indicate that for traditional search spaces A* performs better than Dijkstra’s algorithm.
Due to CPU performance constraints and the need for many agents pathfinding at once, A* has been selected for implementation. For performance reasons we can trim the search space down to what voxel spaces are walkable when the map is first loaded. This navigation mesh can then be altered locally whenever a voxel or prop is added/removed.
Ecosystem data
For each potential target behaviour that a prop may use within its ecosystem, a corresponding set of configuration values will need to be defined.
Organism growth
Almost all organism types in the real world undergo growth. Stages of aging (Otin et al., 2013) can be generally simplified into some common patterns (although many organisms do not fit this simplification):
Infancy/childhood – Once born organisms are focused on growth and mental development for a significant period. This can be simplified to limited exploration and following a parent.
Adulthood/reproductive age – Adults of a species typically focus on having children or raising them. This is true for both animals and plants.
Old age/death – Once past reproductive age animals tend to simply survive, continue to raise young or reach the end of their lifecycle.
As definable stages are common in organisms, but each stage is typically denoted by capabilities and characteristics, we can use a completely different prop for each growth stage. A designer could select one or more props that an individual could “grow” into, as well as a time taken to grow. These props could be chained together to form a complete lifecycle.
To reduce duplication, behavioural groups should be split up, including growth patterns, so they can be selected by several props and reused. More on this later.
Reproduction
Creatures in any ecosystem are at risk of death, so individuals need to be able to reproduce to perpetuate their species. Mechanisms for this also need to be simple. Organisms are not all the same in their reproduction patterns, even among plants (Bengtsson & Ceplitis, 2000), but as with growth stages, different categories can be identified:
Asexual reproduction – plants tend to follow a model of spawning offspring near their location once at maturity through seed dispersion.
Sexual reproduction – an animal of reproductive age tends to locate another animal of the same species but of the opposite sex.
Fields will be required to within the interface to define sexual preference and animal sex (male/female). A target range for a litter of offspring should also be defined. Reproduction is typically a resource intense process, so reproduction could force the parent to expend resources on their children. The reproductive cost for an organism could be equal to the internal spawn values of the organisms they are creating. This also necessitates internal resources.
Resources
To replicate real-world conditions for reproductive cost, and to drive organisms to interact with the environment around them, props must have an internal resources system or inventory. Ecosystems naturally evolve food-chains and resources persist, albeit in a constant state of transformation (Perry, 2021), which for IsoEngine means needing to categorise props by what materials they are composed of and what materials in the world they need to consume to survive. Living animals could drop consumable resources on death, whereas plants could be consumable as is.
Certain “soft” needs or “drives” will also be evaluated for integration with the prop interface, as social behaviours or bigger picture behaviours like stockpiling could be considered during implementation of complex ecosystem behaviours. Each prop may have a unique list of dietary requirements and needs.
Survival planning
With animals having to plan and carry out potentially complex actions to survive, GOAP (goal-oriented action planning) seems like a potential AI design paradigm according to the book AI for Games (Millington, 2021), which would allow organisms to chain together actions to achieve a given goal. For a designer to be able to implement a complex chain of actions however, data-tables would have to support function pointers, which unfortunately after much research, they do not.
Instead, state-machines will be used to manage simple, compartmentalized behaviours that a designer only has limited configuration access to.
Data storage principles
Due to the database like functionality, and to establish data storage best practice, IsoEngine’s data-tables will be designed using “normal form”. Normal form is a set of best practice rules for designing database layouts, cutting down on disk-space and memory usage.
IsoEngine’s table rows represents a prop or singular behavioural setup, and a column represents a single atomised piece of data such as health. Each column is limited to a single configurable datatype. This satisfies the database design rule of 1NF (first normal form).
2NF requires that rows have primary key (a unique identifier like prop name) and external data links make use of a foreign key. Foreign keys can be implemented as data table row handles which are just links to a tables row.
To move to 3NF, data within a table must have a unique link to the primary key. For example, relationship data could be shared across props and therefore should be moved to a new table and referenced by props that want it. Moving to further normal forms leads to diminishing returns on improvements, with greater levels of indirection within tables potentially leading to an increased data access penalty at run-time.
Summary
Features of the existing prop system, and newer considerations for ecosystem behaviours can now be consolidated into a single actionable list that implementation success can be measured against. It also provides a development roadmap.
Essential data-table features:
The following are going to be columns or tables configurable on a prop-by-prop basis.
Categories of prop for use by the editor.
A list of selectable movement types [static, dynamic, walk].
A list of selectable reproduction preferences [none, asexual, sexual].
A dietary list of other props.
A configurable inventory to be dropped on death or consumed by others.
Something a prop can grow into, and timer denoting how long growth takes.
A size, weight or strength value.
A list of potential offspring and min and max quantities for a litter.
Essential behaviours
- Configured props being placeable in editor.
Optional behaviours (can be cherry picked and implemented for testing purposes)
Props swapping into the next growth stage after a timer has elapsed.
Props dying when no next available growth stage, but timer defined.
An internal resource list that decays over time.
The ability to move toward and consume dietary resources.
A prop dying when inventory resources are depleted.
The ability to for animals to kill smaller props for food.
The ability to for animals to avoid bigger animals trying to eat them.
Animals being born and consuming resources from their parent in the process.
Animals of sexual maturity will search for a male if they can afford the reproductive cost.
Pathfinding
Pathfinding is being considered separate from other objectives as it will be essential for the completion of AGCP. It’s implementation and use in this project however is optional, but setup for its eventual use will need to be considered as the architecture of the three object systems is consolidated.
A node generation step at world spawn.
Node updates on voxel and prop spawns/deletions.
A* pathfinding for multiple agents.
Development
Pathfinding
Node network population
Nodes on the navigation mesh need to be voxel aligned, and so are initialized through a 3-dimentional vector that can be resized as the world size changes. Each node contains a list of pointers to other nodes that it is connected to, i.e. it’s adjacent nodes that are traversable. These nodes are populated with links when the world is generated.
Dynamic node manipulation
Whenever a change is made to a prop or voxel, the position of the change is sent to the navigation mesh, so that the area effected can be rebuilt (if necessary).
A* search
A tutorial for A* pathfinding is available on YouTube (javidx9, 2017) and has been used as the basis for IsoEngine’s own A* implementation. In essence, as explained more thoroughly in the comparison of methods for pathfinding, the algorithm searches through nodes adjacent to the source object, slowly expanding it’s search space in the direction of its target. Once the target is found, the algorithm can backtrack though connected nodes until the calling agent is reached, and this path is retuned to the agent for navigation purposes.
2D to 3D enhancements
One key limitation of “javidx9’s” implementation is that it’s 2D navigation. Converting this to 3D was a trivial process, as the list of connected adjacent nodes that are searched can just be expanded to include vertical differences and fed to the search algorithm. A* is a graph search algorithm and built for no specific dimensionality. The heuristic has also been modified to be a 3D distance rather than a flat 2D one.
Performance enhancements
Simple algorithmic improvements were made in the process such as ending the search as soon as the destination is found, rather than also try and find a shorter route.
A more complex aspect of agent performance, however, is that a frame can “hang” while waiting for the search to complete. To mitigate frame rate hitches, each step of the search has been broken up, and can now be can now be called on a frame-by-frame basis. The amount of search “cycles” per frame can be controlled based on whatever circumstance chosen by the developer and has resulted in much smoother framerates.
Multi-agent adaptations
To allow multiple searches to occur simultaneously by any number of given agents, the pathfinder itself has been packaged up as an instantiable class, which an agent can spawn and possess.
Real-time mesh updating
In spreading out the search of a mesh over multiple frames, there is the possibility that a route could change mid-search. There is also the possibility that a route could change once possessed by an AI. Both scenarios require a recompute of the path. To search for every route using a node in real time would be costly, so the solution implemented, is to pass each node a pointer to every route using it at any given time. If a node is therefore deleted, it can force a recalculation of all the routes relying upon it.
Data tables
To make a data table we need to first define its structure. This can be done in C++, and if done in the scope of the prop systems C++ code, both internal systems and data-tables can share and use the structs accordingly.
Structure
C++ structs that inherit from “FTableRowBase” can be used to define layouts for data tables. “USTRUCT” macros expose C++ structures to UE4 and UAssets.
USTRUCT(BlueprintType)
struct FExampleStruct : public FTableRowBase {
GENERATED_USTRUCT_BODY()
public:
//Insert data here:
};Data field
A piece of data for the end user to fill in can be implemented by simply adding a variable to the struct specified and providing it with the “UPROPERTY” macro on the line above. It’s through omitting this mechanism that some data can actually be made hidden from the UI if it’s for internal purposes only.
One to one relationships
To achieve a one-to-one relationship between data groups, an enumeration or plain struct can be used, removing the necessitation for another data table to be created. This option is only available for data with limited variability.
//Example of creating a one to one option
UENUM(BlueprintType)
enum MatingStrategy
{
None UMETA(DisplayName = "None"),
Asexual UMETA(DisplayName = "Asexual"),
Sexual UMETA(DisplayName = "Sexual")
};
//Example of using a one to one option
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Attributes)
TEnumAsByte<MatingStrategy> matingStrategy;One to many relationship
Due to the grouping of data that has the potential for re-use, some categories of data can be placed in their own data table. These pre-configured containers can then be used by multiple props, saving time and storage costs down the road. This also allows behaviours to be centrally managed if certain data groups are shared.
“FDataTableRowHandle” is used to point to a row within another data-table. By housing this in a dynamic array, multiple of these handles can be stored together and a one-to-many relationship can be established (one row to many properties). The “UPROPERTY” macro can be modified to specify the structure type that our target data table is based on.
![]()
The above specifies that a prop can have different offspring defined, but that the row type must be another prop.
Result
After combining the above principles implementation principles, and breaking up data into tables based on design principles like normal form, a starting point for our data framework has been laid out:

One-to-one relationships are included in the diagram as fields (as they are simply enumerators stored in each data table). This diagram has been refined alongside the creation of each struct. If a new piece of data has been required as per the specification, I have added it to the first logical struct (or a new struct if required) and then applied database normal form principles to achieve a suitable layout.
Editing note: One small addition missing from this diagram is the categories table, which can contain many props. This is corrected in later diagrams.
Architecture consolidation
With a new internal data layout to act on and plan around, the existing object systems can be consolidated into a single controller. This requires potential changes to 2000+ lines of code. To make the process easier, every section of code specific to the AGCP project, such as combat has been completely removed. Other functions have been worked through one by one until the majority of core behaviours have been combined, which has led to the following benefits:
Lower memory overhead
Rather than storing data locally in C++ structs, the new data table system can be queried directly, almost halving memory overhead. Additionally, global variables that were duplicated across classes, such as world size are now also consolidated, reducing global information by a third.
Lower cognitive load
Previously it was mentally taxing to juggle the needs and interactions between props, objects, and AI. If a new feature needed to be added, its interactions with 3 systems would have to be considered. Now only one system needs to be considered, and more time can be spent making it easy to use and adding additional functionality.
Reduced duplication
Previously, unique functions regarding physics, world information and states had to be written for each object type. Now however each object can be queried using a single function. While special considerations still must be made depending on the 3D model and physics type of certain props, these differences can be accounted for by each prop containing enumerators defining said states, and a function can have a switch statement for each state type.
Editor flexibility
Previously the editor could only support prop placement but now it is possible to spawn physics objects directly too. Assets can just be loaded in without running behaviours on them until required by the play mode.
Performance improvements
With a central controller, props can be split up pointer lists based on features. These lists can be queried selectively preventing searching through every item in the game for specific searches. The controller can now also apply sweeping changes to all props of a certain type if necessary.
Code cleanliness improvements
Certain helper functions such as converting between voxel and world space have been consolidated from the three older controllers into a separate “VoxelHelperFunctions” namespace. Code quality has generally improved across the board.
Prop data access
To begin the process of spawning props through the map editor, old data access methods for prop data need updating to be compatible with the new data-table system.
To enable efficient access to each data-table prop, the category data-table is loaded into the C++ prop system, and each prop row is interpreted as a pointer to the underlying prop struct. This pointer is saved to an “std::vector”, so that each member of the prop struct can be accessed directly based on the ID of the category it belongs to and the ID of the prop within the category.
//Load prop information from the saved datatable.
//For each row in our prop categories.
for (auto& categoryMap : propCategoriesDataTable->GetRowMap())
{
//Add a new handle to each category for direct access.
propCategories.Push(reinterpret_cast<FPropCategory*>(categoryMap.Value));
propModels.push_back(vector<UInstancedStaticMeshComponent*>());
//For each prop within a prop category row
for (int modelID = 0; modelID < propCategories.Last()->props.Num(); modelID++)
{
FPropInformation* prop = propCategories.Last()->props[modelID].GetRow<FPropInformation>(FString(""));
//Add a new instanced static model type to the list
propModels.back().push_back(NewObject<UInstancedStaticMeshComponent>(this, UInstancedStaticMeshComponent::StaticClass(), *FString(propCategories.Last()->name.ToString() + prop->inGameName.ToString())));
//set instanced static model info such as model and possition
propModels.back().back()->RegisterComponent();
propModels.back().back()->SetWorldLocation(FVector(0, 0, 0));
propModels.back().back()->SetStaticMesh(prop->staticModel);
}
}To access data within a related table through the “FDataTableRowHandle” type, the built in “GetRow” function can be used, which accepts a template parameter for the underlying data layout. This allows the row handles contents to be accessed like a struct.
int GetStarterHealth(int category, int type) {
return propCategories[category]->props[type].GetRow<FPropInformation>(FString(""))->health;
}This is the basis for every getter within the prop system, and allows quick and easy access to any data table property providing you know the prop category and type ID. These calls can be chained together to access further levels of indirection.
Play mode improvements
Merging systems has been no small task, with a large effort being made to account for three different model types and physics setups. The approach taken to this area of development has been to function by function go through old systems, and if a function can be generalised or merged, I have added it to the new prop system. Many functions require no specialisations, but those that do simply use a switch statement to determine the correct behaviour. Props of all types are now interchangeable and compatible. Here is an example from the GetWorldPosition function:

With the merge, such improvements have made it easier to do refactoring. A quality-of-life improvement made in preparation for testing, is the ability to toggle the play mode directly through the map editor. Previously, to test a feature a developer would have to build a level, save it, back out of the map editor and load up the play mode. An added benefit is that props and AI can be spawned and tested in the middle of simulation.
Testing and further development
Testing overview
To test the designer interface, props of different types will be implemented, and this process will be timed. Alterations may be made to table layouts or connectivity if glaring issues present themselves. These changes will not be timed but will be recorded and documented as with other development features. To achieve the broadest range of testing with the fewest assets, a plant, animal, and inventory contents will be set up.
Timing tests will be carried out on a total of seven props. Namely a hare and pumpkin with two growth stages, and an inventory item each. The hare will have two sexes at the adult stage to allow future reproduction testing. Timings for each data-table within the system will be recorded on a per field basis, rounded to the nearest significant value, and once complete for all data-tables results will be totalled as a metric for project success and feasibility.
Prop data-table
The old blueprints system took anywhere from 3 to 30 minutes to configure a new prop for use. Editor categories, and the convoluted prop volume interface could balloon configuration times.
The new data-table has streamlined the interface into a single cohesive layout. The decoupling of props and categories under correct database design principles ensures that props and categories are no longer tightly coupled, and data duplication is reduced significantly.
Interface view

Usability testing
| Prop | Setting | Value | Setup time (approx.) |
|---|---|---|---|
| Baby Hare (leveret) | In game name: Icon: Static Model: Animated Model: Voxel Positions: Model Offset: Health: Behaviours: Inventory: |
Leveret (baby hare) Temp icon Hare_Cub_1 None (0,0,0) (0,0,0) 3 To be determined in later testing Meat |
10s 5s 10s 0s 10s 10s 5s 0s 10s |
| Hare Adult Male | Differences are insignificant. A different name, model, and health value set to 6. | Approx. the same timings. | |
| Hare Adult Female | Differences are insignificant. A different name, model, and health value set to 6. | Approx. the same timings. | |
| Pumpkin sprout | Differences are insignificant. A different name, model, and health value set to 2. | Approx. the same timings. | |
| Pumpkin plant | Differences are insignificant. A different name, model, and health value set to 3. | Approx. the same timings. | |
| Meat | Differences are insignificant. A different name, model, and health value set to 1. | Approx. the same timings. | |
| Greens | Differences are insignificant. A different name, model, and health value set to 1. | Approx. the same timings. |
Being the core prop table, implementation time variability is small. One key use-case missing from this test is a prop with a complex voxel volume, but no such suitable assets are available currently. With improved times for simple voxel volumes however, it can be assumed that timings are also improved across the board, although further testing may be required.
Category data-table
An essential component of the old blueprint system and loading assets into the editor are prop categories. To add a new category through data-tables, a designer creates a row, gives it a name, and populates its prop list using rows from the prop table. To make things easier you can search for props by name, whereas before you used to have to know the exact location of the prop node in blueprints and connect it manually:
Interface view

Usability testing
| Category | Setting | Value | Setup time (approx.) |
|---|---|---|---|
| Hares | Name: Props: |
Hares Leveret (BabyHare), Hare Adult Male, Hare Adult Female |
5s 30s |
| Plants | Name: Props: |
Plants Pumpkin sprout, Pumpkin small |
5s 20s |
| Consumables | Name: Props: |
Consumables Meat, Greens |
5s 20s |
Time to implement a new category or add a prop to an existing one is almost insignificant. Again, a great result. This is in addition to improvements in not needing to physically manage prop and category locations in a blueprint, and not needing to manage a tangle of connections. This will save in hidden time costs.
Growth data-table
With props defined testing needs to be carried out to determine ease of use when chaining together props as part of growth stages. Props can grow into a random selection of one or more props, with a field denoting growth time in seconds. If no prop is specified within a prop’s growth behaviours, this indicates an end of lifecycle after the specified time.
Example interface view

This interface supports non-linear growth patterns and complex branching lifecycles. Growth loops are also possible, although no use-case for such a pattern has become apparent yet. As seen above, Leverets are genderless, but if developed as a behaviour, can choose between male and female props when growing.
Ease of use test
| Growth profile | Setting | Value | Setup time (approx.) |
|---|---|---|---|
| Leveret | Next growth: Growth time: |
Hare Adult Male, Hare Adult Female 60s |
30s 5s |
| Hare | Next growth: Growth time: |
None 100s |
0s 5s |
| Pumpkin sprout | Next growth: Growth time: |
Pumpkin 60s |
15s 5s |
| Pumpkin | Next growth: Growth time: |
None 120s |
15s 5s |
While growth settings are new to IsoEngine props as a concept, configuring said settings is easy and fast.
Resources and new materials
An oversight in the original design for resource consumption, which occurred to me during testing, is that some materials must be transmuted when consumed by other props. For example, herbivores must convert plants to muscle and bone for survival and reproduction.
To accommodate this a conversion table between resources, or a table of consumption ratios could be implemented. Both solutions would lead to increasingly complex relationships and longer maintenance times as the quantity of resources grow. Alternatively, new “fundamental” materials which can be contained by props and transferred to others have been implemented as a table.
To make adding these fundamental types to props streamlined, a general grouping of common resources and their ratios to one another has been performed by another new table called composition groups. For consistency, each group aims to be comprised of 64 units of resources, but this can be lower or higher if certain groups require different resource densities. In a props core settings, you can now select what group/s it belongs to:

Through implementing this new material representation, it highlights how easy it is to modify the layout and design of the data-driven prop system, which supports the initial design aims of this project. The new architecture with resources incorporated is as follows:

Every other area of the design can now rely on the new composition system, which is a more fleshed out version of earlier inventory prototypes. For example, reproduction could theoretically use material quantities when deciding reproductive costs of offspring using their consumption groups as reference.
Editing note: To further simplify this architecture the old drives system is being shelved for a single list within behaviours called “diet”, which contains a selection of composition groups instead of prop categories.
Decay and death
As discussed in the design of this system, resources should decay over time. This has been implemented by pairing materials with decay rates. Decay rates are a percentage rate at which the resource should decay per second when instantiated on an animal during the play mode. This will later drive foraging behaviours as an organism races to replace internal resources.
Example interface

Ease of use test
| Material | Setting | Value | Setup time (approx.) |
|---|---|---|---|
| Protein | Decay rate: | 1 | 5s |
| Fat | Decay rate: | 1 | 5s |
| Water | Decay rate: | 1 | 5s |
| Calcium | Decay rate: | 0.1 | 5s |
| Fibre | Decay rate: | 1 | 5s |
| Wood | Decay rate: | 0 | 5s |
| Stone | Decay rate: | 0 | 5s |
Materials are easy to define through the data-table but may require lots of testing by the designer to achieve some sense of balance in the decay of the ecosystem.
Material composition groups
Composition groups have also been altered to include minimum and maximum material quantities for a specific resource. Props could suffer adverse effects once decayed to minimum resource quantities. This forces the designer to answer the question “how little of a material can this type of object contain before it ceases to be that item?”. If an animal for example loses x percentage of muscle mass, it could take damage, and eventually die.
Example interface

Despite being new data-tables, they can still be tested in relation to ease of use.
Ease of use test
| Composition group | Setting | Value | Setup time (approx.) |
|---|---|---|---|
| Meat | Composition: | (Protein, Starter quantity: 64, Min quantity: 0, Max quantity: 64) | 30s |
| Wild animal | Composition: | (Protein, Starter quantity: 56, Min quantity: 24, Max quantity: 64), (Calcium, Starter quantity: 16, Min quantity: 8, Max quantity: 32) |
1m 10s |
| Vegetable | Composition: | (Protein, Starter quantity: 32, Min quantity: 16, Max quantity: 64), (Calcium, Starter quantity: 32, Min quantity: 16, Max quantity: 64) |
1m |
| Fruit | Composition: | (Protein, Starter quantity: 56, Min quantity: 20, Max quantity: 128), (Calcium, Starter quantity: 8, Min quantity: 4, Max quantity: 16) |
1m 5s |
| Plant matter | Composition: | (Fibre, Starter quantity: 64, Min quantity: 20, Max quantity: 128), | 30s |
Despite compositions being significantly slower to set up when designing prop behaviours, they are a category of data that generally will be shared across many props, and so relatively speaking will be a small portion of the work required for prop configuration. Adding an existing composition to a prop or its diet takes 10 seconds approx. per reference.
Reproduction
While explored in the design section of this project, reproduction is a complex subject that requires a further research and the full design of behaviours required by the engine. Hares have been set up within the table as a placeholder, but plants tend to have more complex interactions with other species during fertilisation and seed dispersion. This will require greater scrutiny of a design not yet finalised for behavioural expression in plants before settling on a data model. As such, this table is subject to significant changes, and testing for this data-table is being skipped.
Example interface

Relationships
Relationships are not a core ecosystem design pillar but will eventually be required for player driven gameplay. As with reproduction, further design work is required before finalizing its data layout. Therefore, testing and evaluation of the use of this table is being skipped.
Example interface

Data-table usability summary
Per table implementation time breakdown
| Data-table | Min row time | Max row time | Per row average | Per prop average |
|---|---|---|---|---|
| Props | 60s | 60s | 60s | 60s |
| Categories | 25s | 35s | 28.33s | 12.14s |
| Growth | 0s | 35s | 16s | 11.43s |
| Materials | 5s | 5s | 5s | 2.14s* |
| Consumption groups | 30s | 1m 10s | 42.5s | 27.14s* |
*Note 1: only 3 of the 7 materials were used by props within the system, so 3 is used for the average calculation.
*Note 2: only 5 of the 6 groups were used by props and so 5 is used for the average calculation.
Result analysis
Implementation times for props could be anywhere from 3 minutes to 30 minutes under the old system. Using the above values, the new minimum implementation time for a prop with new behaviours, is 2 minutes total. Not too impressive, but an improvement. If we assume categories and consumption groups are already established however, and that the prop in question has no behaviours, the theoretical minimum implementation time is 1 minute and 10 seconds. A nearly 3x improvement.
The new maximum implementation time including new behavioural tables and fields (excluding complex voxel volumes) is 3 minutes and 25 seconds. This assumes that new external categories must be created. Given that this is so close to the original minimum I would call this a massive success. This of course needs to be re-evaluated once an asset is implemented with a complex voxel volume to see if it significantly changes the result.
The most reliable metric for the success of the conversion is looking at the average time to implement individual props, categories and behaviours included. This new average is 1 minute and 57 seconds. One important note about this metric, is that in theory average timings will improve as assets are added to the system. This is due to prop and material categories generally being shared, so additional time due to category creation will become increasingly diminished, driving prop average times closer to the minimum recorded time.
Usability success summary
All three analysed metrics are incredibly promising and affirms that this project has been a success when measuring against content implementation times alone. Of course, this cannot speed up the process of external design efforts such as gameplay design, modelling, animation etc, but it does make the life of a designer much easier, as they only have to refine their own disciplinary skills and learn how to use this simpler refined interface.
Behavioural implementation test 1
To test suitability for the use of the system when implementing ecosystem behaviours, some related ideas have been cherry picked from the pool of suggested testing features and implemented. For the first test, which is looking at general usability, internal composition groups have been implemented for the play mode, along with a visualisation of internal values. In addition, resource decay has been correctly applied to the composition groups, and once a prop hits a materials minimum composition value death has also been implemented to complete that particular gameplay loop, causing it to drop any inventory contents on death.

Testing such a process is more subjective and less measurable than usability times, but the experience of pulling data from the data-tables was straight forward. The prop data retrieved was always correct (unless a programming mistake had been made) and was never noticeably became the source of any bugs. Accessing more indirect tables within the system could be confusing at times, but to compensate, getters and their parameter names have been closely matched to their data-table counterparts, which means if stuck a developer can use the prop configuration interface is a direct source of reference when trying to find how to access something.
Overall, despite a few development hiccups that can’t be blamed on the data-system itself, the process was easy and data access for new behaviours seems to be a complete success. The simplicity of using the system to retrieve data is promising for future work, as features down the road may be significantly more complex.
Behavioural implementation test 2
To fully test recursive access of props through other props, (a common pattern in reproduction or growth tables), growth stages and age-related death has been implemented. Amazingly, taking a table’s “FDataTableRowHandle” to another prop works as intended and works well.
A hurdle encountered during this process, however, was the realisation that you need to know a props category ID and type ID to be able to use most getters internally, despite having a direct link to all of the data you would need for that props creation in editor. Another issue highlighted is that as props are loaded internally through the editor categories table, it isn’t possible to load a prop that is configured but not assigned a category. This behaviour should be possible, as a designer may encounter a use case where they want an prop to be present in game, such as a weapon projectile spawned by other props, but may not want that prop to be placeable through the level editor.
A temporary workaround to this issue, that does not satisfy all concerns was to implement a function that searches through the category/type indexing system and locates the handle that matches the one provided, returning a new category and type ID.
void APropController::GetCategoryAndType(FDataTableRowHandle prop, int& category, int& type)
{
for (int i = 0; i < propCategories.Num(); i++)
{
for (int j = 0; j < propCategories[i]->props.Num(); j++)
{
if (propCategories[i]->props[j] == prop)
{
category = i;
type = j;
return;
}
}
}
category = -1;
type = -1;
}This is an inefficient and potentially unsustainable workaround, so getters need to be eventually updated to take prop handles, and not ID pairs, but this also requires alterations to the way Instanced Static Mesh models are implemented in engine to support this new change. Not a small undertaking.
With the workaround in place however, data access was again straight forward, but unfortunately a last-minute bug has broken the aging behaviour preventing it from being shown. It has been narrowed down to a memory management error in relation to UI, and is not caused by accessing prop data, so cannot really be held against the system itself. This second round of testing clearly highlights that despite a new and improved data system, it is not a magic bullet, and typical development issues will still arise.
One aspect of this test that has been successful is that aging behaviours are the first to use a design pattern discussed previously, where the prop is added to an special prop pointer list called aging. The controller can iterate over this list of props every frame to achieve the desired timer checks and eventual prop swaps that drive the feature. This proves once again, that early assumptions about potential new features were valid and that the ecosystem behaviours within IsoEngine are viable in the long-term, which is reassuring.
Critical evaluation
Project evaluation
Completion of project aims
| Goal | Reflection | Status |
|---|---|---|
| Merge old systems | Configured props of different asset types have been successfully loaded into the editor, and both supporting getters and setters have been integrated. Code duplication has been reduced to an absolute minimum. Most essential functions, such as physics and state related ones have been successfully integrated and specialized, although many AI behaviour functions have not been implemented. This is in part due to lack of necessity at this stage, as well as these functions potentially changing or being redundant once actual behaviours have been design. More work could be done, although it is not essential at this point in the project. | Partially achieved |
| Create a more accessible and efficient prop creation interface | In preliminary testing, the new prop integration interface is very quick to use, especially when considering a per prop average. Hopefully, this means that with enough familiarity with the system, new content can be added to the game at an unprecedented pace when compared to older tools. There are two areas of critique I have for this testing process however. Firstly, the props configured during tests have been very simple, and so times may not reflect day to day usage of the system. Secondly, a neutral third party such as a content designer or artist has not tested yet used the interface, so assumptions I may make as the developer might not be obvious or clear to other people with whom new props might take a lot longer to implement correctly. An outside pair of eyes may also see glaring issues with the data-table design that I have glossed over. |
Achieved |
| Use data-driven approaches | A complete decoupling of prop and AI settings from their underlying behavioural code has been achieved, meaning both can be freely iterated on independently. This is a full realisation of the data driven workflow. | Achieved |
| Design for future ecosystem behaviours | As demonstrated during behavioural testing, adding ecosystem designed behaviours is more than possible, and typical development challenges aside, is a robust process regarding data access. As proven throughout wider testing it is also possible to make continued alterations to the layout and markup of data-tables in response to changing behavioural designs. One issue that presented during the second implementation test was that internal systems need to in future take a more data-table row handle centric design for data access, without relying on the prop categories table for asset loading. This critique however technically falls outside of the scope of this project and can be explored further in later behavioural and ecosystem development. |
Achieved |
| Implement a suitable pathfinding algorithm | A successful and efficient enough algorithm has been built for use by the IsoEngine and the old AI system. Time limitations and a focus on other behaviours have prevented successful integration into the new prop system. An unexpected set of architectural changes relating to how paths are exposed to the prop system set integration back significantly. Had this been planned for correctly, integration may have been successful and was an oversight of the planning stage. | Partially achieved |
| Uses purchased low-poly assets | All assets used are prior purchased for use by the IsoEngine. No issues doing so has presented so far. | Achieved |
Feature completion checklist
| Essential features | |
|---|---|
| Feature | Done? |
| Categories of prop | |
| Selection of movement types | |
| Reproductive preferences | |
| Dietary preferences | |
| Configurable inventory | |
| Growth stages | |
| A weight value | |
| Offspring settings | |
| Props placeable in editor | |
| Navigation mesh generation on world spawn | |
| Navigation alterations upon map changes | |
| Navigation supports multiple agents | |
| Optional behaviours | |
| Growth implementation | |
| Old age death | |
| Decaying resource compositions | |
| Props can follow A* paths | |
| Prop starvation death | |
| Inter species animal aggression | |
| Predator avoidance | |
| Reproduction and reproductive costs | |
| Sexual maturity and sexual mating behaviours | |
A result I’m extremely happy with is that every primary goal of the essential implementation plan was met during development. It was deliberately kept simple to focus on the experience of using the interface. Many optional secondary aims were not explored, but this will be left for when the ecosystem is fully developed.
Summary of project success
In conclusion, this project has been a massive success in terms of meeting target aims, and as a bonus, several test behaviours have been implemented to ensure suitability for long-term IsoEngine plans. The groundwork is now laid ready for ecosystems to be developed fully, and for new content to be added at an increased pace and quality.
As for areas of improvement, a better plan for how to develop the chosen systems should have been explored, as key areas like navigation meshes failed to make it into the final work simply due to oversights in planning. A major failure of testing also occurred in that it likely failed to capture realistic conditions for who would use the interface and the complexity of props being built. If repeated, this test should have been done with a small sample of users across a broader range of more complicated props.
References
Research papers
Wilder, M. W. W. (2015, August). An Investigation in Implementing a C++ Voxel Game Engine with Destructible Terrain. University of Akron. https://ideaexchange.uakron.edu/honors_research_projects/217/
Otin, C. L. O., Blasco, M. A. B., Partridge, L. P., Serrano, M. S., & Kroemer, G. K. (2013, November). The Hallmarks of Aging. Europe PubMed Central. https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3836174/
Bengtsson, B. O. B., & Ceplitis, A. C. (2000). The balance between sexual and asexual reproduction in plants living in variable environments. Department of Genetics, SoÈlvegatan. https://www.researchgate.net/profile/Alf-Ceplitis/publication/227904311_The_balance_between_sexual_and_asexual_reproduction_in_plants_living_in_variable_environments/links/5b46893245851519b4b079dc/The-balance-between-sexual-and-asexual-reproduction-in-plants-living-in-variable-environments.pdf
Noori, A. N., & Moradi, F. M. (2015). SIMULATION AND COMPARISON OF EFFICENCY IN PATHFINDING ALGORITHMS IN GAMES. https://periodicos.ufsm.br/index.php/cienciaenatura/article/view/20778
Books
Millington, I. M. (2021). Artificial Intelligence for Games by Ian Millington (18-Sep-2009) Hardcover. CRC Press; 2 edition (18 Sept. 2009).
https://periodicos.ufsm.br/index.php/cienciaenatura/article/view/20778
Perry, D. A. (2021). Forest Ecosystems 2nd (second) edition Text Only. https://books.google.co.uk/books?hl=en&lr=&id=ZWNtHLz3fXYC&oi=fnd&pg=PR13&dq=forest+ecosystems&ots=msPip2Yjjj&sig=bNZSKTdRZGGnZqDDgsZ9GnB-mTw&redir_esc=y#v=onepage&q&f=false
Articles
Arthur, N. (2021, March 2). State of UE4 SCM: Git vs. Perforce vs. Plastic - Nick Arthur. Medium. https://narthur157.medium.com/state-of-ue4-scm-git-vs-perforce-vs-plastic-dddcc1bb7669
Online video
javidx9. (2017, October 9). Path Planning - A* (A-Star) [Video]. YouTube. https://www.youtube.com/watch?v=icZj67PTFhc&t=1547s&ab_channel=javidx9
Data-Driven Dynamic Gameplay Effects on For Honor. (2019, August 2). [Video]. YouTube. https://www.youtube.com/watch?v=JgSvuSaXs3E&t=425s&ab_channel=GDC
Presentations
Bilas, S. B. (2002, January 1). A Data-Driven Game Object System [Slides]. Gitee.Io. https://neil3d.gitee.io/reading/assets/slides/data-driven-game-object-system.pdf
Technical reference
Data Driven Gameplay Elements. (n.d.). Unreal Engine Documentation. Retrieved 8 July 2021, from https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/DataDriven/

