Logic World Wednesdays: Return of the Jimmy

by @JimmyDeveloper1 week ago (edited1 week ago)

I’m back, baby!

Acknowledging the Absence

It’s true: I’ve been away for a while. I feel like I should talk about that.

A few months ago, I took a break from Logic World because I was forced to by carpal tunnel syndrome. Because of the break, I started doing all sorts of fun things with my life: I was playing new sports, making new friends, learning new skills, and working on new projects. Without really planning to, my quick break to let my wrists heal turned into an extended sabbatical, in which enjoying my life was my only priority.

I hadn’t taken a real break from working on this project since I started it in 2017. In hindsight, I think I really needed this one. But I’m back, baby, and I’m so freaking excited to be working on my dream game again!

Going forward, I’m gonna strive to have a better work-life balance. One lesson I’ve learned this year is that I’m actually way more productive if I work a little less and play a little more. I’m also investing in an ergonomic workspace, so I don’t injure my wrists again :)

Let’s take a look at what I’ve been working on recently!

Poggers Performance

As I’ve written much about on this blog, a big priority for update 0.91 is to make game go fast, especially for larger worlds. I have pretty much completed the optimization work for 0.91, so here’s a quick comparison!

World: FML-8 by Stenodyon (~70k components, ~78k wires)
Settings: 3840x2160, default graphics but with post-processing and shadows disabled
Hardware: Ryzen 7 2700X, RX Vega 64

0.90.3 0.91 preview 201
Loading time (cold) 78s 19s
Framerate 115-120fps 150-170fps

A major improvement indeed!

The game isn’t quite as fast as I wanted to get it for this update, but I’ve decided to not let perfect be the enemy of good here, and just move on to other features. Read below for confusing technical info about the few remaining performance issues I wasn’t able to solve.

Depth texture slowness

Something about instanced rendering makes the depth textures significantly slower compared to the old combined meshes approach. I’m not entirely sure why – I’m still very new to hardcore graphics programming – but I think it has to do with how the game needs to do a depth pass over every single instance, whereas with combined meshes, the engine could automatically cull faces and even entire meshes.

The end result of this is that 0.91 is slightly slower in the following situation:

  • The world is medium sized (~50k-150k components)
  • All post-processing is on
  • Shadow cascades are set to 4

If the world is small, the depth pass doesn’t have to run as much. If the world is large, the advantages of instanced rendering overtake the depth disadvantages. And if post-processing or shadow cascades are reduced, the depth texture doesn’t need to be used so much.

I think this small performance hit is acceptable for now, since it only applies to a situation where most folks aren’t struggling with framerate anyway, and the gains in other situations are so significant.

In the future, we’ll implement custom culling for instanced rendering, meaning that on a given frame the game will not try to draw any instances that are hidden (i.e. behind other instances). This will make everything faster across the board, but also it should fix the issues with slow depth textures.

Furthermore, I’m optimistic that the engine upgrade in 0.92 will do a lot to help with this issue. The newer versions of Unity and its Universal Render Pipeline have a lot of optimizations to shadows, post-processing, and depth textures themselves.

Colliders being little bastards

Colliders are the system that allow for collision with components. They prevent the player from walking straight through walls, and they allow us to detect which object the player is looking at. As I’ve talked about on this blog before, colliders are a big reason for performance problems in Logic World, especially for the slow loading times. I’ve spent a lot of time trying to implement a “virtual colliders” system, where colliders only exist for objects nearby the player. Unfortunately, I just have to give up on this idea. Logic World sandboxes are too collider-dense, and Unity colliders just have too much overhead; they can’t be moved around quickly enough for this application.

For now, I’ve done a lot of research and testing, and I’m dealing with collider setup about as efficiently as it’s possible to. Note the loading time improvement in the table above; the gains were mostly due to improved collider efficiency. But still, about 8 seconds of loading time are taken up on JUST colliders, and that number gets ever worse with bigger worlds. Not to mention the appalling RAM usage…

In the future, we’re going to totally replace the Unity collider system with an all-custom collider system hyper-optimized for Logic World’s use case. This system should take approximately zero seconds of loading time, no matter how big your world is. Unfortunately, that is really hard to code, so it’ll be a while before it gets implemented.

Glorious Glyphs

I’ve done a pass on Logic World’s text rendering capabilities! Most notably, text can now display mathematics symbols (like 𝞉𝞩ϵϕ𝚤), music notation symbols (like 𝄞𝄿𝅘𝅥𝄇), and EMOJIS!!!!!!!

I freakin love emojis and I’m so happy that Logic World can now display them. Eventually, I want to display colored emojis with Twemoji. However, I need to wait for TextMeshPro (Unity’s advanced text rendering system) to properly support colored glyphs. In the meantime, Google’s Noto project has released a full set of uncolored emojis. TMP has no problem rendering them, since they’re just regular uncolored glyphs, and in my opinion they look pretty good, so we’re now using them in Logic World.

In addition:

  • 46 new written scripts are supported! These are mostly historical writing systems like Mayan numerals or Old Hungarian.
  • Miscellaneous fixes for how the game displays Arabic and Adlam scripts (these and other RTL scripts are still not properly displayed as right-to-left, but will be soon!)
  • Added an option to display CJK characters in the Hong Kong style (in addition to the existing options of Traditional Chinese, Simplified Chinese, Japanese, and Korean)
  • Updated Font Awesome to 6.1.2, with over 7,000 new icons!

Most video games will give you tofu (□) if you try to type esoteric characters. I’m proud that, to an ever-increasing extent, if it’s supported by Unicode, it’s supported by Logic World.

Fixing Bugs by Fixing Points

If you’ve built a large complex structure in Logic World, you’ve probably run into this issue: past a certain depth of parent-child component relationships, positions and rotations start to get screwy. Even before things entirely break down into nonsense, the components aren’t perfectly lined up, which causes all sorts of issues while building.

This issue has plagued many a Logic Worlder, and this week I FIXED IT!!!

Here’s how I did it. Turns out it actually takes a lot of work to make a video game work in the way you would intuitively expect it to.

Switching to fixed points for positions

The imprecision issues are caused by the inherent imprecision of [floating point numbers], also known as “floats”. As you continue to manipulate floats, the tiny imprecisions compound on each other until they’ve built up to noticeable levels.

I’ve switched component positions to using fixed points, which have a specific and constant level of precision (in this case, one millimeter). You can manipulate fixed point numbers as much as you like, and they will never lose precision – unless you have to round them…

Smart and consistent rounding for rotations

Positions in 3D space are pretty straightforward, and it was relatively simple to switch them to fixed points. However, rotations in 3D space are really bloody complicated. I am not nearly smart enough to use fixed points in a rotation matrix.

Thankfully, it doesn’t seem to be necessary here. The game now rounds component rotations (to the nearest 0.1 degrees along each 3D axis) at two points: when assigning a new local rotation relative to the parent component, and after calculating the cumulative rotation from the whole stack. As you can see in the video, this rounding keeps the rotations nicely aligned.

Root reference frames

With the previous two changes implemented, I was still noticing a very very slight imprecision that built over time and nesting depth. However, this only occurred when the root component of a stack was not aligned with the world axes (i.e. placed on a Grasslands ground).

The disadvantage of fixed point positions is that you have to round them to their precision level (in this case, one millimeter). You can’t have a component at 420.69mm along the X axis; that number must be rounded to 421mm. This usually isn’t noticeable, since every component in Logic World has dimensions in a whole number of millimeters, and almost every connection between components is at a right angle, so all components stay precisely aligned with the millimeter-scale grid. However, when components are rotated at non-right angles, they get pushed off of the millimeter grid. Their fixed-point positions, then, needed to be rounded, and these rounding imprecisions very slowly accumulated.

I’ve fixed this issue by making components track their position and rotation not relative to the objective reference frame, but within the reference frame of their root component. Since component connections are almost always right angles, a component’s position within the root reference frame will stay aligned with the millimeter grid and not accumulate imprecisions due to rounding errors. After calculating its position/rotation within the root reference frame, the rotation of that frame can then be applied to find a position/rotation within the objective reference frame.

Shout out to PseudoGoose, who suggested this solution to me when we were discussing the problem some months ago :)

Thanks for being here. Thanks for caring about my crazy little video game that I love so much. It’s good to be back. I’ll see you next Wednesday.

More Logic World Wednesdays

@Vykori1 week ago


yayyyyyyyyyy .



ok with that out of my system, I’m also quite excited for those future 3 optimizations you mentioned. Culling of instanced rendering, unity update, and custom colliders <3

this post has kinda rejuvenated that feeling I had.. that “This is the game I would make if I made games” feeling.

@JimmyDeveloper1 week ago

Haha, that’s very sweet!

Indeed, we’ve got lots of very ambitious optimizations planned for beyond 0.91. The game will get ever faster and faster!