“Unfinished – An Artist’s Lament” was released about two months ago. I said I planned to release a small update, not to add content, but to improve some of the problems with it. One of the worst bugs is properly game-breaking: if you use an ability to move a box to be able to jump on, there’s a good chance it will wall through the floor or glide through the walls as you move it. This bug is fixed now, and this article is to help explain what I did before, what I did now to fix it, and will hopefully help other developers doing something similar.
Picking up and moving things isn’t uncommon in games. Minecraft has it. The Elder Scrolls games have it. In theory its easy: have the object follow the movement of the player or the player’s input, but keep track of collisions to avoid the object going through things. The characters do the same thing, why not do it for objects?
Unity3D has different methods to handle physics. Generally, you would use either a collider, a rigidbody, or a character controller. In theory, collider is great for recognizing collisions, but must be moved directly via object translation which typically ignores physics. Rigidbody is supposed to be more realistic and use “forces” to implement gravity or movement, and looks great, albeit difficult to properly control. Character controller is named to be used for characters, and is a simplified physics option to allow greater control while still allowing for reasonable physics as you program them. I’ve spent several months in the past proving that you could use any one of these methods and recreate physics with similar behaviour. In some cases you can combine them too.
So I needed to use a “magnet” ability to automatically move the closest applicable box, to follow the player’s movement, but without going through walls, and allowing the character to jump on top of it. I remember first trying rigidbody and finding it unpredictable as it fell through the floor. In hindsight, a ‘kinematic’ rigidbody might have worked, I don’t remember if there was a reason I didn’t use that. Instead, I thought it would be simpler to write a custom physics option using just colliders.
And that’s what the game had when released. Basically I would have the box move in the direction the player moved, and do a manual check in the direction of movement to make sure no colliders were in the way before moving. In theory this makes sense: you could write a proof where, if no other objects were moving, the physics engine should only check the direction of the focus’ object movement. Simple. Easy. Perfect.
Except it didn’t work. If you moved erratically, it was easy to have a box go through a wall, and potentially not be able to come back. I tried adding extra checks to find collisions and move in the opposite direction to float out of it (a behaviour similar to rigidbody), but this still didn’t solve the problem. It didn’t help that there was a bug where the player fell faster than the box did, such that by combining abilities you could infinitely jump on the box and fly to the top of the level. Again, in hindsight some people suggest that using “FixedUpdate()” instead of “Update()” could have helped fix this, but due to the rest of the code in the game this wasn’t possible without rewriting much of the game.
So my new solution was to try using character controller. This has a disadvantage in that you could only have a capsule-shaped collider (not great for a box), but this was identical to the player’s physics, which up to that point had no such bugs. Perhaps this was a more reliable way of getting rid of this game-breaking bug.
What I ultimately did was use both a box collider and a character controller: the character controller for movement, and the box collider for other objects coming near it when not in motion. This helped a bit with the shape of the colliders. But a new error appeared!
It turns out character controller’s “move()” function (which must be used to properly detect physics) is built to “slide along colliders.” This means if you pass in a vector to tell the move() function how to move the controller, the function will modify that direction if there is another collider blocking the way. That sounds fine, but what if two colliders clamp down on the character controller? Instead of moving sideways to avoid both, the function seems to give in to one of the colliders and a collision occurs. That’s what happened when I used it. It worked fine for not going through walls in the environment, but if the player walked into it, the box would at points seem to be pushed, and ultimately go through walls. If the player jumped on it, the box would move down slightly, like hammering a nail into the ground. And because this function is protected as a part of Unity’s framework and is the only way to move a character controller, I seemed stuck again.
After hours of thought, I thought of a silly solution, tried it, and it worked. Basically, here’s what it looked like:
//disable player’s collider / character controller
//enable player’s collider / character controller
I disabled the player’s colliders, the source of the problem, ran the move function on the box to let it calculate movement ignoring the player’s presence, and enabled the player’s colliders again. It’s stupid, and probably inefficient. But it worked. And you will now be able to play the level without worrying about the game breaking and requiring you to restart the level. I think.
The point of this article is that physics calculations like most things are possible in Unity, but not trivial. I should have spent more time debugging this from the start, but the fact is it was implemented maybe a month or two before the game’s release. Anyway, I’m finding trouble having the time to fix other things in the game, but I do intend to release an update for “Unfinished – An Artist’s Lament” in the next week. And maybe a sale to celebrate.