I have recently needed to learn Swift at work, so I decided to come up with a new game project to help inspire me. Since I am not using Unity, I wanted something light on art and I decided to look at old text based adventure games, like Zork, and found that it actually held quite a few interesting challenges. Simultaneously I have been intrigued by an architecture pattern called Entity Component System (ECS) which I have really wanted to dabble with for awhile now. Although I am new to both Swift and the ECS pattern, I feel that the results are worth sharing!
About this Project
This project is a tutorial which explains the implementation of a text-based game using the Swift programming language and an Entity Component System architecture. Familiarity with Xcode and Swift are required as I wont be going in-depth on the use of the IDE for topics such as how to create projects, or how to create a screen with storyboards and its various features like auto-layout, and I also wont discuss the syntax and rules of the Swift language.
This project was created using Xcode Version 7.3.1. You can obtain a copy of this software for free using the “App Store” on a mac or by clicking here. If anyone wants to follow along with Xcode 8, then you can use Xcode’s ability to automatically convert the code to Swift 2.3 or Swift 3. Alternatively you can still download the older versions of Xcode if you have a developer account by clicking here.
If you don’t already know Xcode and Swift, you should check out some of the free resources available such as from the iOS Developer Library: Start Developing iOS Apps (Swift)
I primarily looked at Zork 1: The Great Underground Empire for inspiration, as well as to copy its content because it is available under the MIT license. You can play the full version of Zork at a number of websites, such as this one: PLAY ZORK. They also conveniently included manuals, maps and even complete walkthroughs of the game!
This project will be cloning a small subset of the features and content of Zork- really just enough to prove the validity (or at least my understanding of) the ECS architecture style. Below is a cropped and slightly edited map from Zork which shows the subset of the game’s rooms that I have implemented:
Entity Component Systems (ECS)
Everything I have learned so far about this architecture pattern should be credited to Adam Martin. He has written a lot about this topic, and for a full understanding, I would recommend you read over his 5 part series: Entity Systems are the future of MMOG development. For those of you who want to dive right in, I will also provide a brief overview.
An entity represents any “thing” defined by a game. In this project, my entities will include things like a “small mailbox”, “leaflet”, and “window”. Entities can also be pure logic objects like triggers. In this project, I use just such an object to enable and disable a pathway between the back of the house and the kitchen based on whether or not the window is open.
The actual implementation of an entity is nothing more than a unique identifier. In this project it was implemented as the primary key of a row in a sqlite database table. As you might imagine, by itself an entity is entirely useless. Conceptually, I think of it like a container which will hold behaviors that make up what an object really is.
A component describes a single aspect of what something is and how it interacts with the world. For this small project I ended up creating the following components (in no particular order):
- Room – A room is like a scene of conent. It has a title and a description which appear when navigating between rooms.
- Portal – A portal is like an exit pathway which takes a player from one room to another.
- Presence – This component indicates that the location of an entity is best described by its presence in a room(s). Some entities will appear in only one room like the player or mailbox. Other entities like a window or door exist on a room border and will appear in two rooms. Some generic entities like the ground can appear in almost every room.
- Notable – Marks an item which should be described upon entering a room.
- Noun – A sort of handle by which a user entered command could target a game item.
- Adjective – An extra handle to further specify a game item with a word. For example there might be a rusty dagger and a jeweled dagger and the extra label can help identify the intent of the player.
- Openable – An openable could be anything that has a concept of opening, such as opening a window, door, or mailbox. Opening and Closing an openable can have triggers such as enabling or disabling another component or changing another component’s values.
- Takeable – Indicates that an item can be taken and put into inventory, and or moved into another entity’s possession such as dropping it in a room or putting it into a container.
- Container – An entity which can conceptually hold another entity. For example, a mailbox holds a letter, or a plate of food is held by a table. Containers take into account their own capacity as well as the size of the items they contain.
- Containable – This component indicates that the location of an entity is best described in relation to another entity. For example, the leaflet is held inside the mailbox, or a dagger might be held by the player (in his inventory). The containing entity can be a room, but will usually be an entity with a container component.
- Readable – A readable could be anyhing with content to read such as a book or leaflet.
- Eatable – Something which can be eaten.
- Drinkable – Something which one can drink.
- ValueChanger – A value changer holds the data necessary to change one field of a component data struct. These will be used as components on Trigger entities that are targeted by some action.
The implementation for each component is a new table in a database. Each table will have a column for each relevant bit of data that the component might need to know about. For example, a container component includes a “capacity” column so that I know how much that entry is allowed to hold. I also create a model (programming term here like a ‘struct’ or ‘class’) for each component which conveniently wraps the database values for reading and writing when necessary. All game data should be held in a component, but none of the functionality – that comes next.
A system provides the functionality for your components. Any method you might have wanted to put on a model object would belong here. As a general rule you might expect to see one system per component, although some systems may manage a few components and how they work together. You might also see systems which don’t directly have a component. For example, I created a “GameSystem” which could handle saving, loading and restarting the game.
Each system is implemented in its own class, largely using class methods. Instances of the class are not necessary. In fact the class wasn’t strictly necessary either since Swift allows for global methods. I kept the class for organization sake.
An ideal implementation for an ECS is via a database. In this project I used an sqlite database both because it’s easy, and because it works with mobile devices. Like many game developers, I knew very little about working with databases, or SQL etc, so I relied on a few free things to get me started. To create and edit content for the game I used sqlite browser, which provided a nice visual way to create a database. This also saved me a lot of time in having to create my own game editor, although honestly it would be worth it in the long run to go ahead and make my own edtior tools. With a custom game editor I could create screens that are very specifically designed to help me create consistently formed data all while using named labels instead of row ID’s etc.
Below I have included the basic template for our database which I took from Adam Martins blog. Note that his version includes a few additional tables (assemblages) which weren’t necessary for this project so I didn’t include them here.
|entity_id||human-readable label FOR DEBUGGING ONLY|
It is pretty easy to guess that the database includes a table for the entities. Each primary key “id” represents a different game object. For easier editing and debugging I also made use of a label column to the entity table.
|component_data_id||[1..M columns, one for each piece of data in your component]|
It is also pretty easy to guess that the database will include a new table for each type of component, where there are columns for each property that an entry of that component would need to know about.
The part that was less obvious to me is how to relate an entity to its collection of components. The Object Oriented Programmer in me instantly goes to thinking about polymorphism and how all components should be able to be treated as the same kind of object. This would allow me to “fetch” all of an entity’s components regardless of what structure of data it actually held. To achieve this, there are actually two more tables:
|component_id||official name||human-readable description||table-name|
This table describes the component itself – giving it its own unique ID, a name, and a table name where instances of its data can be found.
For every component of every entity, a new row will be added to this table. With a combination of this table and the Components Table, it is possible to create a fetch request which can grab whatever data you need.
You can grab my demo sqlite database called “Zork.db” for this project here, and it will also be included in the demo project. Once you have downloaded a copy, poke around inside with your favorite tool or give sqlite browser a try – its free! All of the tables are already created and populated with data so you can browse around and get an idea of how it all works together.
In this week’s post I introduced the next project I’ll be writing about – a text based adventure game. The content comes from Zork, although I am not cloning it completely. The architecture for the project is based on an Entity Component System (ECS) for which I provided a very brief overview. I also pointed out a few specifics of how my project was implemented using that pattern.
Don’t forget that this project is accompanied by a repository HERE. There will be one commit per lesson so you can see exactly how the project would have been cofigured and how the code would look at each step.