Movement is very important in Exon. Beyond merely getting around you’ll need to keep moving to avoid attacks and get the drop on your enemies, and you’ll want to explore to find alternative routes and secret bonuses.
Although it is operated top-down, it is a game in full 3D — height is important for exploration and all the mild platforming that will entail. That, however, made the logic that controls movement very very very complicated.
Can you guess what’s coming? I think you can guess what’s coming. I just rewrote my core character movement system!
Character movement is a fundamental thing, the action which you will spend the vast majority of your time using (probably even more time than you’ll spend in combat), so it has to feel right. My general preference is for absolute responsiveness; no obvious acceleration or deceleration as characters start and stop, but movement that does precisely what the button you just pressed told it to do.
For this purpose, Unity provides the Character Controller. This exists outside the physics engine, which neatly absolves it of all the vagaries of force and torque; it does exactly as you tell it, only touching the physics world to stop itself from going through walls.
On paper, that’s great; I get the precision and responsiveness that I want. The problem is that you then have to… reimplement everything it touches.
Very much knowing my own mind, I took on that challenge.
I managed to implement walking and running fairly easily; they’re just a matter of pointing the object and moving it forwards. Falling, too; it’s just applying downward motion when you’re not on the ground (except you also have to apply downward motion even when you are on the ground because Reasons).
Sliding was… a bit harder; having to counteract all of the above with an alternative motion that damped itself and… eugh. (My sliding was functional but not pretty, and that’s probably where the rot truly began.)
So things were bumbling along just fine… until I put steep slopes into my current working scenario.
The movement system just didn’t bother with them at all — if you jumped onto a steep slope, you just stopped there, as if standing on flat ground. Since I’m making a lot of stylish non-vertical sci-fi walls to stop you from getting everywhere too easily, that was a problem, as you could repeatedly jump to get over anything.
My solution was not pretty. It worked… barely.
Even so, for quite a while I’d been worrying over the broader issue that my game actually had two physics systems in it. One, the underlying PhysX simulation that’s really really good at what it does, the other my ham-fisted jammed-in Character Controller movement system. As I kept running into problems when the two butted up against each other, I began to think, this can’t be right. (Also physics is a wheel I have absolutely no desire to reinvent, let alone the skill to do so.)
But, oh, I was so afraid to move to a fully PhysX-owned rigidbody-based system, afraid of slippery controls and unpredictable forces.
Eventually I stumbled upon an official Unity sample project with basic implementations of both approaches side by side (which I now can’t find to re-link, so you’ll have to trust me that it did exist). Sure enough, the Character Controller mode was precise but didn’t really respond to steep slopes. Sure enough, the rigidbody mode was a little bit slippery… but it wasn’t that slippery, and I noticed a few tricks that reduced the slipperiness to something much more manageable than I’d expected.
So I took the plunge, and the basic code for rigidbody movement turned out to be a fraction of the size of my jumbled Character Controller mess — and, crucially, it makes sense. Out is a tangle of competing vectors desperately adding and subtracting and multiplying each other; in is a straightforward application of force based on the the user’s input.
I applied the new system to a single character and left the rest of the units in my scenario alone. I opened up the profiler to see how it compared, because the old system had always been something of a bottleneck. For an old-style unit, it took 0.2ms to update (used to be 0.4ms before I turned off transform auto-synching — eek). The new controller took just over 0.06ms.
Half the code? More than double the execution speed? Even I can’t pit a little bit of slipperiness against that. (And that’s just in the editor, I’m expecting it to be even more brutal in a final build.)
It also turned out that relying on the built-in physics system allows me to ditch a load of buffer code. My moving platforms, for example, previously had to talk to a component that knew if it was a rigidbody or a unit and could shift the object accordingly; now everything is a rigidbody and platforms can just talk to those directly. Hell, units can now jostle each other out of the way which will vastly improve the behaviour of clusters of attackers, for literally zero code on my part.
So while there are still a few niggles around the edges I need to clean up, the new system is unarguably superior to the old one.
Well, there’s one thing that’s perhaps a little more than a niggle: you know that problem that happens in some games where you do something and get spontaneously rocketed straight up into the sky? Well…