From the Burrow

TaleSpire Dev Log 219

2020-08-27 02:06:31 +0000

Today has been spent working on the behavior of uncommitted action.

Whenever you add or delete tiles, we need to perform the action on the other players’ boards. To get the right result, the actions need to be applied in the correct order. This is done by sending all of these actions to a player, which was automatically designated as the ‘host’. They then relay the action to everyone, which gives us an order. We call an action which has been given an order a ‘committed action’.

Given that we can only apply the actions once they have been committed, we are in this annoying position where we need to wait for that roundtrip before we see a change from our keyboard/mouse click. That feels bad[0], so we needed to do something about it. By the time of the beta, we had made a system that could update the board’s visuals for our uncommitted changes so that the change looked immediate. It was complicated, but it was made much more so by the fact that we had to spread some changes over many frames due to Unity not being able to keep up with spawning without impacting frame-rate.

Now that we have taken control of batching, we can happily spawn thousands of tiles a frame with no issues, and this lets us drop the performance workarounds. I’m now rewriting the code that handles applying visual changes for uncommitted actions.

It’s going pretty well. I have uncommitted actions working for adding tiles and undo/redo of the same. I have made good progress on delete and, although it’s significantly more complicated, I’m pretty confident I’ll have it working tomorrow. If I can get this done in two days, that’ll be pretty great as the last version took months to get right. [1]

As soon as this is behaving, I will be switching back to physics. The first task is to make the ground a physics object again, and then I will be looking at dice. I hope to have some familiar dice rolling gifs by Monday :)

Hope this finds you well folks.

Peace

[0] It might seem like not a big deal, but we had complaints about the responsiveness of undo/redo in the alpha, and back then, that delay was mostly due to performing the action on key-up rather than key-down. Input latency really matters.

[1] Of course, it’s not an apples-to-apples comparison as the codebase is already structured in a way where this would be possible. However, it definitely feels much simpler.

TaleSpire Dev Log 218

2020-08-25 17:53:43 +0000

Heya folks,

Today I started out looking at the slab code again. The slab is the way we refer to a section fo tiles that have been copied. When you paste a slab string, that slab appears in your ‘hand’ in-game, and you can place it like a tile.

Recently we have been writing batching code, something that used to be handled by Unity. It got to the point where I needed something similar for the slabs, which I started yesterday. There are a couple of differences from the standard code batching code:

  • We don’t want physics to be set up for the tiles/props
  • We don’t want scripts to start running
  • We need to update the position of every object in the slab every frame (as the player moves it)

This mainly involved taking the existing code, taking a chainsaw to the parts we didn’t need, and stitching together the remains. It’s still very rough, but I was able to try pasting in the slabs from the community… which of course crashed the game :P It was obviously not the fault of the community content, it was that it was a much more complete test that I had run up until then and it found a few bugs. One was a case where we had assets in TaleWeaver with a MeshFilter with no mesh specified. Another was that I hadn’t written the code to handle missing assets.

I got the code to the point that I could paste a slab. However, it feels pretty rough as the ground plane in the game doesn’t exist in the new physics system yet. I’ll be back working on the physics code soon, and then I can take another crack at this.

The rest of today has been spent looking into building code. I need to re-implement the stuff that handles things that have been affected by changes that have not yet been committed (confirmed by the host). It’s pretty hard going, but I’m hopeful I can make some decent progress this week.

I’ll leave ya with a view from the cabin one of our friends were kind enough to invite us along to over the weekend,

Ciao

the hills

TaleSpire Dev Log 217

2020-08-25 03:40:31 +0000

After a lovely couple of days out of town, I’ve got back to business, and it’s been a busy one.

I spent the first half the day on dynamic colliders. For now, these are the colliders that are attached to tiles (and props) and are moved by scripts. This was a somewhat painful process as I ended up bouncing back and forth between TaleWeaver and TaleSpire as I needed to make some tweaks to the code that dealt with the object hierarchies.

dynamic collider 0

dynamic collider 1

With that behaving, I stubbed out the code for computing the motion data required by the physics engine. Finally, I tweaked the per-frame code so that the physics engine knows when it doesn’t need to be rebuilding all the internal data for static objects. This should, in normal cases, give decent performance.

For the rest of the day, I switched to slabs. I really wanted to be able to paste large objects from the community and see them appear immediately. Annoyingly they have managed to avoid working yet. The majority of the work is done. I’m just chasing down smaller bugs and mistakes, but it’s late, and I need to sleep.

I expect to write up a bit more on this once it’s working.

Seeya tomorrow.

p.s. I’ll show a pic from the weekend to those curious when I get them from my partner. I’m using a dumbphone currently, and photos are not it’s strong suit :D

TaleSpire Dev Log 216

2020-08-21 17:10:11 +0000

chest

This looks like nothing. If you used TaleSpire, you’ve seen this a thousand times. For me however, this is great. The fact it looks the same means the following:

  • Spaghet’s realtime scripts are being controlled correctly by the state-machine scripts
  • The custom animation system is working
  • The new batching system is working
  • All the work from the last week to replace the old physics engine has paid off. If you can pick a tile (which we are), then the raycasts are working.

This has been a slog. Any time you have multiple days without being able to start the game, it’s mentally draining, and these last weeks have been tough. There are piles of issues still[0], but I think I might be through the worst part.

Have a great weekend everyone

[0] To name just one, mesh colliders aren’t properly oriented

p.s. I’ll be offline for the next few days as I’ll be off recharging in the country somewhere :)

TaleSpire Dev Log 215

2020-08-17 09:19:38 +0000

Progress feels slow but is steady.

The batcher now produces the data the physics engine needs for static colliders. Thanks to that, I’ve started writing some helper methods that mirror the parts of the older physics API we use. This should make the porting of the existing code noticeably easier. I’ll find out how that goes today as I’m planning to start porting that now.

You’ll notice I specifically mentioned static colliders above. I haven’t worried about the colliders attached to objects moved by scripts yet. The reason is that they are, for the most part, just a slightly more complicated version of the thing I’ve already done, whereas the board tools and dice are still unknown quantities.

I’m hoping the tool side goes smoothly today as I really want to start looking into things like dice and creature, which will have a hybrid setup in this system. By hybrid, in this case, we mean that we will be using Unity’s GameObjects, but we will write custom components to hook them into our physics setup [0].

Speaking of the physics setup, we’ve always had two sets of colliders we keep in different layers in Unity. We have the ‘explorer’ layer, whos colliders we use when placing tiles, and the ‘tile’ layer[1], which is the version used during play by dice/creatures etc. The split allows us (and modders) to provide a better building experience by tuning the explorer colliders. As I’ve been working with the new physics engine, I’ve started to think that it might be worth using two totally separate physics-worlds, rather than layers.

The reason is one of performance. When querying the colliders, we usually care about either the explorer layer, or the tile one and the physics engine has to skip the ones we don’t care about. It does this via a layer mask, so it’s a quick check, but not doing something is always faster than doing something. I’m also musing about if this allows us to do less work per frame by having different update frequencies for the two worlds. We will be trying to keep things fast by, per-frame, only giving the physics engine data for the zones[2], which contain the player’s cursor or dynamic objects like dice. However, there is a tradeoff between changing data (which requires more frequent rebuilds of internal data-structures[3]), and the number of things being tracked by the physics engine (which affects query times).

This is something that can be tuned over time, so I may just set up the code so that swapping and changing will be less painful in the future.

Alright, that’s all that I need to ramble about for now. Hope to see you in the next one.

Ciao.

[0] In contrast, all tiles/props are managed/batched by us. It’s a tradeoff between having Unity help us with lots of things and performance.

[1] Yeah, not great naming :P

[2] 16x16x16 regions of world space

[3] BVH https://en.wikipedia.org/wiki/Bounding_volume_hierarchy

p.s. oh yeah I’ve also started on tools to visualize colliders in our new system

raycast

TaleSpire Dev Log 213

2020-08-14 08:45:48 +0000

Morning all,

Yesterday I got the new collider data loaded into TaleSpire and started writing the code that would lay out all the static colliders.

I originally planned to store the colliders per asset[0], but after writing a significant amount of code, I remembered that Unity.Physics’ colliders don’t support scaling. This means I need to store scald versions of the asset’s collider for each Tile/Prop[1] that uses it.

I realized this fairly late in the day, and it was a bit of a bummer to have to start rewriting then, so I stopped for the evening and relaxed instead.

Today I’ve got to detour into some other code, but I’ll get back to this asap.

We do learn from this however, that we won’t be able to animate the scale of the parts of assets that have colliders. I’m not sure if this means we’ll just disallow animating scale or only give warnings if you do it to something with scale. I’ll chat with Ree about this in our next daily.

Alright folks, hope you are doing well.

Seeya

[0] In TaleSpire, Tiles, Props, and Creatures are made of assets arranged in different ways. Assets themselves are made of objects, but you don’t address those sub-assets directly.

[1] Each kind of tile, not one for every instance of the tile.

TaleSpire Dev Log 212

2020-08-12 00:47:53 +0000

Today was pretty simple. I spent the day reading into the Unity.Physics package and trying to work out more of the nitty-gritty of how we will work with it. Tomorrow I’ll start implementing things.

The first thing to do will be to work out the serialization for colliders on assets. I’ll be slightly changing the data stored per object inside assets to avoid additional processing on the TaleSpire side. I’ll then be looking at the colliders that live on tiles and are used by the building tool.

It’ll then be over to Unity to look at loading this stuff and how to asynchronously create the mesh-colliders. I’ll need to ensure that the other systems aren’t notified that the asset is loaded until this processing is complete.

I’m sure I’ll run into some other little complications on the way, but that’s fine.

I expect this will take most of tomorrow, and so on Thursday, I’ll start updating the batcher to handle the physics data. Colliders attached to animated objects will likely require some attention to get right.

With all that in place, it will hopefully be time to write a little helper API and to start updating all the existing code to use this new system.

We’ll keep you posted on how this goes.

Seeya

TaleSpire Dev Log 211

2020-08-10 15:05:16 +0000

Finally, something decent to show again!

I won’t lie, the last couple of weeks of coding have been hard. I’ve been working on the new animation system, and, as you’ll see, this dragged in a LOT of other systems. While the final solutions are not overly complicated, finding out what they needed took a lot of tries, and the number of systems involved made it hard to keep in my head.

First, though, videos time!

The following clip shows 10k animated objects being spawned, all with their own scripts running independently, without dropping below 60fps. They are all in sync as I wanted to see lots of things happening, so I made time loop every 2 seconds, causing the animations to replay.

This might be a bit underwhelming to look at, but I’m so glad to see this. Let’s dive into why.

Scripting

For a long time, we knew we needed to be able to add behaviors to tiles and props. Without hackery, Unity doesn’t let you add more code at runtime, so we can’t just load c# code from asset-packs along with tiles and creatures. This got us looking into scripting languages.

We started looking at LUA. The C VMs are fast, but there are also c# VMs that have excellent integration with other c# datatypes.

Side note:

A lot of the time, my job is looking at something simple and saying the words, “And then the user spawns 10000 of these” (let’s call this the ‘10k-case’). Making 10k of something in TS is very easy, just drag out a 100x100 region. This is why everything in user-generated-content games is more complicated than it seems.

My role is to look at this scaling problem try and limit the worst-case complexity. The players will always be able to make enough things that the game performs poorly, but we usually can find things to improve the situation.

Looking at various scripting languages, my big concern is what happens when 10k are spawned as:

  • Each needs an unknown and potentially unbounded amount of memory.
  • Each might be used in ways that aren’t thread-safe.
  • Everything object created needs managing by the GC.

The last one is especially concerning. If you’ve ever shipped a game in a managed language (like c#), you’ve probably spent time minimizing memory allocations to stop the GC from impacting your frame rate. Without understanding the impact of a script, I was unsure we could make something that we knew would run well.

The next experiment was to use LUA like a scriptable composition system. We would provide performant ‘operators’ and modders would connect them up using a LUA script, which would be invoked less frequently (for state machines it would be on state change).

This was promising, but we are in this weird case where we have made LUA not like LUA anymore. So the people who like LUA and want to use it cant. And the people who hate LAU still have to use something they won’t like. And we still have the threading questions.

Spaghet

This made me take a day to hack together a simple compiler. It produced a very simple byte-code that we would run on a little VM built on top of Unity’s Job System. By using the job system, the VM code is optimized by LLVM and we can run the scripts on many threads in a way that fits in well with our other systems.

Even simple tests showed promise. We split the world into two kinds of scripts:

  • State-machine scripts
  • Realtime script

State-machine scripts (currently) run only in response to user input, and they are fully synchronized across the network.

Realtime scripts can run every frame and are not synchronized (as there would be far too many messages). Their programming model is similar to shaders in that the tile’s visual state is recreated from scratch every frame from the input.

The trick now is that each state of a state-machine script can have an associated realtime script. This means that when in that state, the realtime script is run automatically.

For example, for a door, the state only changes (and is synchronized) when the user opens or closes it. However, by animating the door in the realtime scripts, you get the expected animation on all player’s machines at the right times.

The fall of the GameObject

Unity’s traditional approach to making this is all object-oriented. It’s compelling and, in the right hands, is an incredible canvas for creativity. Alas, when performance is critical object-oriented programming (OOP) is problematic. OOP makes it very hard to control where things stored are in memory, and this is essential when trying to batch jobs together to run quickly. CPUs can do unfathomable amounts of work per frame if they aren’t spending their whole time waiting on things to arrive from RAM.

relative speedasnimation

Memory access latency from cpu caches v ram – Andreas Fredriksson

On top of this, Unity’s GameObject’s are (relatively) slow to create, fine for most games, but terrible for our 10k-case.

We worked very hard trying to keep using GameObjects as they have such significant advantages for content creation and experimentation. Arguably, this contributed to the delay of the Beta as working around the speed limitations had some very non-obvious interactions with our building systems. However, it was not to be, and even though the Beta shipped, we continuously see performance issues that we don’t have clear solutions too if we stick with the current approach.

Of course, we’ve known about this issue a long time, and the good news was that an answer was already in production. Unity’s ‘Entities’ system is their new approach to handling things in the game and promised impressive performance boosts. We were very much banking on that solving our issues.

This was a mistake on my part. Naturally, the Entities system is a complicated beast with its own tradeoffs, and Unity is taking its time to get it right. When we finally tested the Beta, it still had some concerning performance characteristics for our use case. That sucked.

The good news was that amongst all this new code, there were many high-performance tools that we could use to make our own system. The problem is that, with GameObjects, we got so many things for free and now we need to handle them ourselves. Here are a few concerns:

  • The new physics engine is excellent, but we use collision casts everywhere so most systems in the game are gonna need some modification
  • There is no culling or batching written for us. We need those.
  • There is no animation system at all
  • There is no way to specify lights expect using the classic approach
  • feel: This is a terrifying one. Game feel takes ages of tiny tweaks based on the creators’ intuition. If we lose that it’ll take a long time to recreate

We knew we had to just go for it. As tiles and props are where the perf issues lie, if we can make a new system for them, we can afford to keep the other stuff as GameObjects.

Too late now >:)

Anyway, there was nothing to do but to get started. I jumped over to TaleWeaver (our modding tool) and started work on the new data formats for everything we would need.

It was critical that we didn’t mess without our artist’s ability to work. Making games is hard, and everyone needs to be able to work as effectively as possible. Your smart little ideas and solutions must not tread on someone else’s ability to work. To me, this meant analyzing the GameObject representation in TaleWeaver, and writing out our own, TaleSpire specific, version of this data.

Luckily once again, new tools exist in Unity to help. The Entities package has a Blob data system that promised fast and job-safe data encoding un unmanaged memory. I made some tools that let it hook into Unity’s classic asset serialization system and got to work.

I extract animations, compile scripts, flatten transform hierarchies, and other simple stuff and pack the result into out blobs. Most of the slowdown here is split between learning new things in Unity, and working out what we need. We also made atlases from the tile icons.

I replaced Spaghet’s graph UI and iterated on the VM. Again, most of the time is just from working with new tools and trying to find the best ways to do the silly things we need.

On the TaleSpire side, I wrote a new asset database to accommodate the new formats we have made and hooked up enough of that to keep working[0]. I then started work on the batcher.

The batcher is a beast. Each frame we need to tell Unity what to draw and Unity, like any engine, needs that information in some format it can use. Luckily for us, most of our objects don’t move each frame, so if we can get it in the correct format when the board changes, we can keep reusing that data. From now on, we will use the term ‘statically-batchable’ to talk about all the things where we can use this technique. All of the objects where we do need to update the position/rotation/etc per frame we will dub ‘dynamically-batched’.

So I spent a day or so writing the static batcher. Each zone (16x16x16 unit region of space) in the game runs this independently and concurrently. We do not combine batches across zones. This means we make more draw-calls than we could, but this lets us enable and disable zones without recomputing the batches for any other zone.

Dynamically-batched content is harder. I had already decided that the animation system would be directly tied to the scripting system, that is, Spaghet will be animating the objects. That lets us simply say that any part of the tile that can be modified by a script during runtime is dynamically-batched, and the rest are static[1].

I spent some days getting the Spaghet wired up enough to continue. I then made jobs to run the realtime scripts, compute the transforms for the objects and write them into the batches. A fun thing to note is that the number of dynamically batched things is not changing frame to frame. This means that you can do the following:

  • Size a batch with enough room for every dynamic & static instance you need
  • Write the static batch info leaving enough room at the end for the dynamic ones
  • Every frame write the updated dynamically-batched transform into the reserved space

This is nice as you don’t need any extra allocations or draw calls. The downside is the complexity it adds to the system, but hey, we are here for the performance.

During all this, I kept finding small issues that meant I needed to change the data format to help the engine. This is the right thing to do, but switching back and forth between the projects was tough on the brain.

I’m gonna skip a pile of details for all of our sakes, but the result is that the thing we are now making is capable of getting us where we need to be. It’s not fast enough, or stable, but the tests are finally showing something promising.

  • Wrong thing to conclude: Unity is slow
  • Right thing to conclude: Unity is fast. But, for what we need, the default tools they have to drive are not.

Warning about performance numbers. We can’t make decent measurements of the game yet, I still need to write up a lot of new stuff before we can start getting metrics we can rely on. But what we can guarantee is that TaleSpire is getting faster.

I hope you enjoyed this. I also hope I managed to convey the reason I’ve been less mentally available recently. Things are going to stay ugly for me for most of this month, but I know we will have something cool in the end.

Have a great day.

Peace.

[0] Funnily enough, the old one is still in there now being used by the UI. I’ll drop it when I get to that part of the rewrite.

[1] there are obvious cases where we don’t have to recompute a tile’s transform every frame, for example, when an animation has finished playing. This is true and will be handled by Spaghet scripts knowing when they can go to ‘sleep’. I’ll cover this in another post.

TaleSpire Dev Log 210

2020-08-05 01:47:05 +0000

Hi folks, I skipped yesterday’s log as I was far too deep in code, and to be honest today has been much the same.

The TLDR is that I’m working on TaleWeaver and the modding tools. I’m doing this as we want to move to more directly managing the batching of what is drawn rather than leaving it all to Unity. Changing the way that works requires different data, a lot of which can be packed into the asset when it is exported from TaleWeaver. Changing this batching approach also means we need our own animation system, which is fine but related to the scripting system (Spaghet). Before you know it, we have major changes to scripting, animation, rendering, and more.

It’s been a heck of a lot to keep in my head at once. Every decision impacts how another system works. Complicating this, of course, is this should be at least reasonably performant, so you cant just use <insert your favorite design-pattern/paradigm/etc> and trust that it will come out rosy :P

All in all, I’ve just not had enough bandwidth to tackle anything else.

Ree has also been busy in the experiments around props. I’m already very excited to see what comes out of that.

Tomorrow I need to take some hours to look into some networking issues some users are having. Sorry for not getting to you sooner, you know who you are.

Warm regards from code-land.

TaleSpire Dev Log 209

2020-08-01 00:21:51 +0000

Phew, what a week. I’ve almost lost track of all the things that have been going on. Amongst it all, more code is getting written.

Ree has been able to get through enough other tasks that he’s got some time to start experiments with props behaviors. Props are one of the most significant missing pieces from the game right now, and whatever behavior we settle on, we are going to have to live with for a while, so it’s good to take our time with this and find something we like.

I’ve been back in TaleWeaver, looking at how board-assets work. Yesterday I wrote a tiny compiler for the state-machine scripts and added code for showing errors in the state-machine script graph.

Today I’ve written code to export the tile information in a new binary format. TaleWeaver now also makes atlases for the tile icons. This should allow TaleSpire to batch more draw calls in the UI, hopefully speeding that up a little bit.

The next stop for me is to take this new format, get it loading into TaleSpire, and rewrite the code that manages looking up tile information. I’ve also had great progress in the design of system that will run the realtime scripts, so everything is lining up to work well. Just got to keep hammering at it!

Hope this found you well.

Seeya