Deep Sea Adventure: Dev Diary
Posted: Mon Jul 14, 2008 6:41 am
I thought I would keep some notes on my progress with this project for fun and posterity. I plan to update "occasionally."
I have been working on this for about three evenings now. First a little bit about the concept and goals, and then onto the technical fun.
The basic idea of the Deep Sea series, which will now become a trilogy (with absolutely no plot relationship), is that you pilot a submarine with some cool and highly unrealistic abilities through undersea caves filled with various enemies. In the first game you used mini-subs with smaller size and special abilities to collect bombs planted by evil terrorists; when you had collected them all the path to a final boss fight opened up. In the second game had you do treasure-hunting to pay off a debt, with a "town" screen that gave you opportunities to buy equipment and engage in minimalistic dialogue trees.
For this game I'd like to bring in all the features of the previous games, with a tighter interface, and expand the content to have many levels and a real plotline. I also want to have a really, really nice level editor for it. It'll be an open source game once I have a good demo ready - everyone is welcome to contribute.
===============
On the first day I familiarized myself a little with Love and set down some ideas about how I was going to structure the game engine. With each project I try to learn a little something and try to take a fresh approach to the architecture. I have never really focused on low-level details, primarily interesting myself in ways to make gameplay easier to create and the logic more reliable. This time around my focus was on the entity model. I have concluded that notions of an Entity class and Entity objects as a first-class data structure are nonsensical. Not that they don't work, but naive uses of this approach lead to a "god class" where Entity is expected to do almost everything, and subclasses of Entity do a tiny bit extra.
So modern game architectures are heading towards a compositional approach. The gist of this is that the Entity is nothing more than a container for some component structures: for example, basic x/y coordinates and velocity vector can be encapsulated in a component called SimpleMovable. The sprite data and anims can be encapsulated in a Display2d component. Collision is contained in Collision components - perhaps you want to use a different type of collision for the scene background vs. individual entities.
Lua makes it a lengthy, but relatively straightforward process, to do this. "entities" is a table with an incrementing index, each entry containing a table with component names. Each component contains its own name and index number so that components can access each other's data.
Some bookkeeping for adds and deletes is done per-component; my Display2d components live in a table separate from entities, as well as on the entity table. That way is more efficient for blasting out draw updates than querying every entity for their display component. I don't like the bookkeeping, though. I wonder if there is a way I can eliminate the bulky code it produces in each component.
A "gotcha" with this strategy, which I spent this evening resolving, are that when you start adding and deleting entities you get gaps in your tables, and you can't eliminate gaps without reordering indexes and opening another can of worms. table.insert and ipairs(table) don't seem to deal with gaps properly, so I ended up not using those functions.
Along the way I did some helper functions for loading data. Resources are accessed through a table for each type of resource(gfx, sfx, bgm) and I recursively traverse directories with the same names to autogenerate resource names like "/bg/tile/tile0", for example. Each name is assigned an index so that table lookups for using resources are mostly integers, not strings. (faster) I would also like to improve this a little to condense multiple images into anims automatically.
Here you can see the test case for the engine as-is, using the tile tutorial graphics. The top number is frame rate, the other is the number of sprites. The things I wanted to test are:
-Basic sprite drawing
-Performance of entity system
-Adding and removing many entities
-Basic usage of components
So in the test, I create 1000 entities with a SimpleMovable and a Display2d, with random positions, sprites, and directions. The main update loop processes each one naively and tells them to bounce off the edges by reversing velocity. The display loop reads from the table of all Display2d components, gets the associated SimpleMovable from the Display2d id, and then combines the data to place the sprite in the right position. Last, I had the update loop add three and delete three entities each frame, to prove that I can dynamically add and remove entities safely.
To cover mass data-manipulation functionality well, there are four general cases to handle:
0 - no data available
1 - a single instance(usually your first test)
>1 - groups require more logic than singular instances
lots - how well it can scale
Failing to cover any of these means you get bugs later on when you use your data differently. Hence why I focused on adding and removing entities even though there's no gameplay. This is the time when it's easiest to do so. And -- working with thousands of entities here quickly revealed problems in my original add and delete functions.
In the commercial project I've been working on, the engine has to work on the Wii, with tight memory constraints - 88MB total RAM. Compare this with my laptop's 1GB, and the faster processor too, and it becomes plain that we operate in entirely different worlds. While the engine I'm setting up in Love has ways to be generally optimized -- in its current state it is not too difficult to understand and it is open to localized optimizations.
For example, if I wanted to reproduce the current test with better performance, it could be embedded entirely in a single component. I plan to do such things for the major abstractions of the game like maps.
I have been working on this for about three evenings now. First a little bit about the concept and goals, and then onto the technical fun.
The basic idea of the Deep Sea series, which will now become a trilogy (with absolutely no plot relationship), is that you pilot a submarine with some cool and highly unrealistic abilities through undersea caves filled with various enemies. In the first game you used mini-subs with smaller size and special abilities to collect bombs planted by evil terrorists; when you had collected them all the path to a final boss fight opened up. In the second game had you do treasure-hunting to pay off a debt, with a "town" screen that gave you opportunities to buy equipment and engage in minimalistic dialogue trees.
For this game I'd like to bring in all the features of the previous games, with a tighter interface, and expand the content to have many levels and a real plotline. I also want to have a really, really nice level editor for it. It'll be an open source game once I have a good demo ready - everyone is welcome to contribute.
===============
On the first day I familiarized myself a little with Love and set down some ideas about how I was going to structure the game engine. With each project I try to learn a little something and try to take a fresh approach to the architecture. I have never really focused on low-level details, primarily interesting myself in ways to make gameplay easier to create and the logic more reliable. This time around my focus was on the entity model. I have concluded that notions of an Entity class and Entity objects as a first-class data structure are nonsensical. Not that they don't work, but naive uses of this approach lead to a "god class" where Entity is expected to do almost everything, and subclasses of Entity do a tiny bit extra.
So modern game architectures are heading towards a compositional approach. The gist of this is that the Entity is nothing more than a container for some component structures: for example, basic x/y coordinates and velocity vector can be encapsulated in a component called SimpleMovable. The sprite data and anims can be encapsulated in a Display2d component. Collision is contained in Collision components - perhaps you want to use a different type of collision for the scene background vs. individual entities.
Lua makes it a lengthy, but relatively straightforward process, to do this. "entities" is a table with an incrementing index, each entry containing a table with component names. Each component contains its own name and index number so that components can access each other's data.
Some bookkeeping for adds and deletes is done per-component; my Display2d components live in a table separate from entities, as well as on the entity table. That way is more efficient for blasting out draw updates than querying every entity for their display component. I don't like the bookkeeping, though. I wonder if there is a way I can eliminate the bulky code it produces in each component.
A "gotcha" with this strategy, which I spent this evening resolving, are that when you start adding and deleting entities you get gaps in your tables, and you can't eliminate gaps without reordering indexes and opening another can of worms. table.insert and ipairs(table) don't seem to deal with gaps properly, so I ended up not using those functions.
Along the way I did some helper functions for loading data. Resources are accessed through a table for each type of resource(gfx, sfx, bgm) and I recursively traverse directories with the same names to autogenerate resource names like "/bg/tile/tile0", for example. Each name is assigned an index so that table lookups for using resources are mostly integers, not strings. (faster) I would also like to improve this a little to condense multiple images into anims automatically.
Here you can see the test case for the engine as-is, using the tile tutorial graphics. The top number is frame rate, the other is the number of sprites. The things I wanted to test are:
-Basic sprite drawing
-Performance of entity system
-Adding and removing many entities
-Basic usage of components
So in the test, I create 1000 entities with a SimpleMovable and a Display2d, with random positions, sprites, and directions. The main update loop processes each one naively and tells them to bounce off the edges by reversing velocity. The display loop reads from the table of all Display2d components, gets the associated SimpleMovable from the Display2d id, and then combines the data to place the sprite in the right position. Last, I had the update loop add three and delete three entities each frame, to prove that I can dynamically add and remove entities safely.
To cover mass data-manipulation functionality well, there are four general cases to handle:
0 - no data available
1 - a single instance(usually your first test)
>1 - groups require more logic than singular instances
lots - how well it can scale
Failing to cover any of these means you get bugs later on when you use your data differently. Hence why I focused on adding and removing entities even though there's no gameplay. This is the time when it's easiest to do so. And -- working with thousands of entities here quickly revealed problems in my original add and delete functions.
In the commercial project I've been working on, the engine has to work on the Wii, with tight memory constraints - 88MB total RAM. Compare this with my laptop's 1GB, and the faster processor too, and it becomes plain that we operate in entirely different worlds. While the engine I'm setting up in Love has ways to be generally optimized -- in its current state it is not too difficult to understand and it is open to localized optimizations.
For example, if I wanted to reproduce the current test with better performance, it could be embedded entirely in a single component. I plan to do such things for the major abstractions of the game like maps.