D2 log 008 – More entity groundwork

After redoing the entity list I still wanted to improve handling game objects more.

Turned cameras into regular game Entities. They now use the safe handle-based referencing system to bind to other entities that they should “follow”. Also I can setup cameras easily in the editor without extra effort.

HUD overlays are now entities too. They link to the player via an Entity ID, get ammo & health info easily. No explicit wiring in main game code. Player dies – no problem.

The amount of code I was able to remove from the main game loop was quite substantial. I guess I should try to do more things like that. The original Devastro had a system of triggers also implemented as game entities. I used them for setting up conditions for victory – for example, there was a level where the player had to protect a herd of sheep.

This was setup completely in the editor by wiring the triggers for “alive” for each sheep into an “AND” node and wiring that into the “WIN” node. Pretty neat, now that I remember it… maybe I’ll use that approach again.

 

D2 log 007 – Cocoa

Working on the level editor I realized I’d really like my window size to match the phone format which means there won’t be enough space to fit the editing tools, such as entity list & properties, tile picker etc.

I could open a second window to render the editor stuff using the same renderer as the game and IMGUI is great but I already have some “imgui-style” widgets of my own and feel like mixing them together could lead to some hard to fix problems.

So I decided to use Cocoa, the native macOS UI framework. I’ll make a few floating panels independent of the main window. Clean separation, less trouble.

Starting with a simple entity list:

Grid view for selecting map tiles:

And a very early version of an entity property panel:

(Fields are generated dynamically for each entity type based on the property metadata).

Still a lot of work ahead to put it all together and wire it into the editor system, but already looking much better than my previous attempts.

Gogs

As I lean towards self-hosted software, I recently switched from Bitbucket to Gogs.

Gogs is one of the best web-apps I have used in a long time. It’s easy to install and maintain, the interface is great and the whole thing is fast.

The issue tracking and wiki are quite minimal but work well. I do miss an equivalent of Github’s “gists” to hold small snippets of code independent of any repository.

Running on a standalone $5 Digital Ocean VPS.

Rant: fman criticisms

Here’s my reaction to a reddit thread where a bunch of people were wrong about something. It was about the fman file manager and how the author had spent over 3000 hours making it.

Some people argued that making a general file manager application was “easy” and the author of fman had spent way too much time making such a simple app.

Wrong! Anyone who goes to make a tool as general and versatile as a file manager deserves huge respect.

Doing a UI prototype for two pane file list that lets you browse files is EASY. Making a file manager that actually helps you manage files is HARD.

Let’s see what needs to be considered when we try to COPY A FILE:

  • Cross-platform
  • All filesystems
  • All OS versions
  • Network volumes
  • Filename length limits
  • Case sensitivity
  • Special character encoding
  • Handle and report errors
  • Detailed progress indicator
  • Estimate remaining time
  • Pause/resume
  • Interactive options to overwrite/skip/ duplicates
  • Symlinks
  • Hard links
  • Correctly copy attributes, even when support varies between src/destination
  • Sparse files
  • Special files such as /dev/zero
  • Block size (20 byte file can use 4KB of disk space)
  • Quotas
  • Channels
  • Compression
  • Encryption
  • Optimize for SSD/HDD
  • Optimize for same-volume and cross-volume, cross-device copies
  • Sandboxing

All this must work 100% of the time, on 100% systems, otherwise someone is going to lose their data.

I don’t even know if fman actually takes care of all that, but my point is that I can imagine one could easily spend a good portion of development making JUST THIS and I would consider it a great achievement if it actually worked.

D2 log 006 – Rain

Let it rain! There was a rain effect in the first game, so why not bring it over? Good opportunity to see how easy it is to add a new entity type… turns out it it’s really smooth! Only one file to edit and it was up and running, including a custom “numParticles” property in the editor & XML serialization process.

I’m pretty sure I’ll revisit this later and improve it with sound, lighting & thunder effects and little splashes on the ground. For now though, it sets the mood of the level quite well already.

D2 log 005 – Xcode templates

While adding some new code to the project I got annoyed by the default C++ file templates that Xcode used. The header it creates contains old-style #ifdef guard and has a .hpp extension. Every time I use the template I obsessively delete all that stuff and start over with a simple #pragma once.

Why not make it the default? Turns out it’s not hard to create custom templates. They go into ~/Library/Developer/Xcode/Templates/File Templates/Source/ and have this kind of structure:

I’ve made the __FILEBASENAME__ files almost empty, because that’s what I want:

D2 log 004 – Tileset blueprint

Each level has a tilemap as the base layer. Tilemaps are easy to build using the built-in editor. I wanted to experiment with the scale of the tiles. The actual pixel size of tiles is now 512×512 but I can scale it up or down to make the result look nice.

To get a better idea about how much they take up on screen, I decided to make another tileset, with a blueprint-style grid on it. In Photoshop I drew a tile with thin lines around the border and across the center. Also added a label with the tile number.

I didn’t want to export each tile manually, so I used the “data sets” feature in Photoshop where you can feed it a text file with values and it applies them to the assigned layers. Then do an batch export for each row of the data set and voilā:

D2 log 003 – PAK files

Game assets are packed into a single “PAK” data file, with optional compression. The game’s virtual filesystem can access files in the PAK file and has a fallback to the native filesystem. For compression I used the LZ4 library – it’s fast and easy to integrate.

The format of the PAK file is quite simple. It contains data of all the files and at the end there’s a table of contents, with filenames, offsets and lengths.

I have a simple utility app that creates the data file. There’s an option to turn off compression for faster rebuilds during development; it also gets automatically disabled for  files smaller than 1KB and for all OGG audio tracks.

D2 log 002 – Entity system ready

Phew, new entity system is up and running.

First I wrote standalone code for basic entity management and a few unit tests to make sure it worked properly.

Next step was to integrate it into the existing codebase. Quite a transplant!

There were raw Entity pointers throughout the codebase and lifetime, ownership & validity checks were not exactly a solved problem – at least not systematically.

I also had to make sure the serialization still worked and preserve the same entity IDs across load/save cycles. Perhaps not 100% necessary but I want the diffs to look tidy.

Last major issue was the level editor, which needs to hold on to entities for a little longer than the rest of the game for undo/redo support. I added custom methods to remove them from the entity list without deallocating and to add them back with the same EntityID.

The level editor.

On to the next one. Time to clean up the filesystem layer.

D2 log 001 – Entity system

Getting back to D2 development after a year of focusing mostly on Tiny Player and Tiny Player for Mac.

Some of the C++ code is not so great. It’s been a while…

The property system is “awesome” but it seems like I was trying to be very clever and now it all looks pretty complicated for what it does. But at least it works, so let’s keep it for now.

First, I’m going to rework the entity system and make it simple and solid.

We won’t store entity pointers directly anywhere in the game. Instead, let’s store an index into the entity array and use a getter to retrieve the pointer when we need it. To make sure the array slot actually contains the object we expect, we’ll add a “generation” index, which gets increased each time a new entity is added. That way, we can query the entity list using a handle and get a valid pointer or NULL if it’s gone.

EntityID {
  index
  generation
}

EntityID AddEntity( Entity* e );
Entity *GetEntity( EntityID id );
void RemoveEntity( EntityID id );