Do you ever get into working on something, and you’re absorbed in it, and you keep putting off telling people about it? I apparently do…
So about a year ago I started work on trying to make AI characters more flexible compared to Banished, both in what they do visibly and in the way the code and data are setup. I wanted characters and objects that could be modified by me and by end users easily, because modding is cool. But things spun out of control. A bit.
I started with a text based behavior tree system, which once I got working looked a lot like a scripting language, so I started adding other features that made it a scripting language. (I had a post ready to go about how awesome it was.) It then got more complicated, needed a runtime debugger, and it started running other parts of the game, not just AI. Maintaining it was getting messy. Writing a game in a language that is being modified is not cool. Having script do slow things was not awesome. Not making progress on a game because you’re writing something to write the game is not the best.
The only great thing about it was that writing a programming language was personally satisfying. But it just doesn’t get other projects done. I could have told you that before all this, but I fell into it a trap I knew about.
One day, I just deleted all the code, and immediately felt better. But don’t worry, it’s in source control if I want any of it back. There were some cool tangential things in there that I might use one day. It was time to move on and work on something different for a bit.
I tend to turn to graphics related things when I need a change of pace. So…
What I’m working on is now open world-ish. And as I started moving between play areas, which are currently islands, they visibly deformed. Mountains flattened, detail disappeared jarringly, and area near the coast line changed unacceptably. I stopped using a system of regular grids that smoothly changed detail based on distance. I instead switched to a system that is based on binary triangle trees that subdivide detail levels based on an allowed error metric. I’m using simple geo-morphing to smooth out the cracks between detail levels. This allowed islands to keep more detail in rough parts, and at coastlines, and use less detail in flat parts. It also reduces triangle count under water since you never see it. It uses an order of magnitude less triangles for the same and even better perceived detail, so it maybe even renders faster.
And, yes, I know the newer hotness is using the tessellation units on the GPU to add detail in areas based on view distance and terrain roughness, and sampling the height on the GPU as well, but I want to keep my shader model on the low side so that the majority of older GPUs out there can run the game.
I also added pre-built low resolution versions of islands with baked textures, so distant views can load them fast and display them in a single draw call without the overhead of having an entire terrain and all textures loaded.
With that out of the way I started filling in some missing parts of the scene, basic sky, simple ocean, and a unified lighting model for everything. It doesn’t look the greatest, but for now gets the point across, and is setup to allow for some neat effects. Waves, clouds, time of day, sunsets, etc. All lovely things that will wait until later.
Going open world is tricky. I can’t load and generate and run everything at once. Some things need to be loaded in a low resolution, some full resolution, some not at all. When you leave one place, everything needs to be saved so that if you come back it’s still there.
To assist with this, I changed my game engine from having one scene to multiple scenes. Imagine a scene as a collection of the visual display, the collision data, pathfinding data, and a bunch of objects that do things grouped together. Having multiple of them allows me to load them in the background, perform any expensive setup or shutdown, and then add or remove them from gameplay in an instant as needed.
Making a change like this to an existing engine is painful. In theory it sounds simple, like, just create another one of what’s already there and be done! But all the separate scenes need to work together when running – they aren’t always isolated regions.
And each scene has its own transformation, which can make thinking about where objects actually are slightly more difficult. At a high level I treat everything as being in world space, but each scene has its own local transform that it works in.
I broke everything temporarily and had to modify almost every underlying system that runs the game. That’s graphics, hierarchy and animation, audio, pathing, collision, entities, background loaders, and the core engine loop. Phew.
To be fair, I did it piecemeal, and wrote a test app that didn’t use most of the engine at the same time so I could test the change to each system. Changing 100’s of files without regular testing is just disaster. Once put all together, debugging was a fun few days of mysterious problems and errors. All seems well now.
With that out of the way, it’s time to start implementing things that the AI does again. Anything that was behavior tree/script just doesn’t exist anymore. So a bit needs to be redone. It’s a bit like coming full circle and sort of starting over.
I hope you are all well, hugs to all this year, it’s been crazy, and thanks for reading!