THE DOOM THAT CAME TO PETROV, PT. 1

THIS WHEEL’S ON FIRE

One of my new-year straighten-your-shirt-you-look-like-a-drunk type projects was an overhaul of my website and games portfolio, seeing as how I hadn’t touched Unity for at least a full year since landing my last job. I was tired of looking at the old site, and had heard and seen some interesting things about Unity 5, especially the news that the old WebPlayer tech was out, replaced with support for WebGL builds. What this means for me is that people can run my games on whatever device they have at hand, without the extra hassle and uncertainty of installing a plugin. That’s a potential game-changer in terms of prospective employers and collaborators being only one click away from checking out my content right there in the browser. I had to give it a try.

After making backups (I’m not an animal), I ran all my prototypes through the Unity 5 upgrade, and updated all the various Asset Store plugins and geegaws to compliance. Things went smoothly at first, a few functions had changed names or been moved to other classes, but the errors were easy to understand and fix. That is, until I got to Project Petrov, aka “Project Racer”.

I have previously detailed my sustained wresting match with the Unity WheelCollider, most of which can be attributed to my limited understanding of vehicle physics and a stubborn insistence on doing things the hard way. The book I used for reference to start the project moved the car around by applying forces to the car’s Rigidbody, and animated the wheels as an afterthought. As soon as I realized you could get a more realistic model by applying forces to the wheels themselves, I was determined to craft something cooler than the example I was working from. Despite my math deficiencies, it seemed reasonable that I should be able to at least trial-and-error up some passably car-like behavior, and indeed the wheel collider and I reached a truce, or close enough to where I was able to post the car demo on my site and people could drive it around. It’s no Cruisin’ USA, but it’s neat enough as a demo. Problem is, in Unity5 it just doesn’t work. Debug reports impossible variable values. The car jitters, then creeps forward, then does a 720 degree front flip.

So what happened here? Let me start with all the disclaimers: I’m not an engine programmer, or a physicist, or privy to any inside info. Everything I write here was learned by going online, asking questions about WTF happened to my wheels, and attempting to interpret the answers. This is not a rant, and I don’t have suggestions for improvement. This is just what happened to me. It’s well-understood that you don’t upgrade engine versions during a project, so all the pain described here is ultimately self-inflicted.

The long and short of it is, Unity’s upgrade to 5x includes an update of PhysX, the 3rd party physics engine (made by NVidia) that determines how objects in a game behave in response to gravity, inertia, outside forces, all that stuff. Many games don’t need physics solutions at all, but if you want an object to fall, or crash, or drive, somewhat like things in the real world do, you’re in PhysX territory. Unity 5 moves us from PhysX 2 to PhysX 3.  The NVidia physics coders, in their pursuit of ever-diminishing micro-seconds of increased processor speed, have decided to alter something fundamental that affects the way that Unity implements what it calls a “wheel”. Here’s Unity’s Jonas Echterhoff:

“The main point of updating to PhysX 3.3 in Unity 5 is performance. PhysX 3 is fully multi-threaded, and has some components (like the cloth solver) rewritten for optimal performance. Also, it solves some performance issues in previous versions (such as moving non-rigidbody colliders, or using uniformly scaled meshes for mesh colliders).

Don’t expect any significant feature changes in Unity 5.0 – we don’t plan any additional features for 5.0. However, expect changes in behavior for the wheel collider and cloth components, as the underlying implementations have been rewritten from scratch. (Behavior for other physics components is also likely to change, but in a less drastic fashion).”

So here’s where I admit that a lot of my information comes from a Unity community post with the unfortunately inflammatory name “Unity 5 WheelCollider component is unusable”. That’s a bit harsh, but they say misery loves company, and I’d be lying if I said I wasn’t relieved as all hell to see that other indie trench grunts like myself were equally mystified by the changes (as one user says, “definitely an exorcist is needed”). In threads like these, one must constantly calibrate for salt and bias, as everyone with a newly broken project chimes in to share their tale of woe. If you filter out the emotional responses, there’s a lot of fascinating stuff to learn here.

The general tone and tenor of the thread was that of a bunch of people looking around for a simple starting point, a standard basic setup one could use to create a plausible car. This turned out to be surprisingly elusive. Half a dozen users offered up various numbers they had tried for vehicle mass, suspension and springs, tire friction, etc. None of them were in agreement, none of them helped. Apparently a physics-enabled Unity game world has so many unique factors that the numbers only make sense in relation to one another, in the context they’re being used. There’s no template, no magic bullet, no easy shortcut.

It became clear to me how far out of my depth I had drifted when resident Unity physics genius Anthony Yakovlev (who goes by yant online) dropped into the thread to provide some explanations. I’m just going to reprint the image he shared here in this post, as no description can do it justice:

As shown, everything becomes quite simple once you understand the basic formula behind the new sprung mass system, to wit:

F = sprungMass * g + jounce * stiffness – damper * jounceSpeed

I know I’m being cheeky in response to a knowledgeable and generous engine dev trying sincerely to help, but I couldn’t do much with this info, and here’s where I got really interested. Is it the case now that Unity’s vehicle modelling is realistic enough that you need a deep understanding of physics to implement a vehicle? Cars are complicated machines, but should cars as game features be off-limits to all but the most math-savvy developers? Yes, I could still push the car’s rigidbody around arcade-style, but I wanted more, and I refused to accept that I couldn’t get cool behaviors without being able to parse the equations. It was time to do what I do best: break out the axe and start hacking.

SAMPLES AND EXAMPLES

There were some obvious places to start. No, not reading the manual, that’s a little too obvious. Unity provides, right out of the box, a series of packages called the Standard Assets, that contain example implementations of everything from smoke to grass to glass. One of these packages contains an example vehicle, and that’s where I started my investigation. Surely the car provided by the Unity team would contain all the clues necessary to set up my own. I extracted and started experimenting. Anyone who has read my previous shame-filled racer posts can imagine my dismay upon popping open the CarController script to find this:

// this is used to add more grip in relation to speed
private void AddDownForce()
{
m_WheelColliders[0].attachedRigidbody.AddForce(-transform.up*m_Downforce*
m_WheelColliders[0].attachedRigidbody.velocity.magnitude);
}

My previous version of the racer project used something very much like this, which I referred to as the “thumb of God”, and which I was deeply ashamed of, as it seemed an admission that that physics system had beaten me.  Now here it was in the Standard Assets! It was hard to avoid the conclusion that maybe something really was wrong, if the example implementers had felt obligated to control deviant wheel behavior through a heavy-handed application of downward force. In one sense this was discouraging, as it implied I would eventually be forced to fall back onto similar measures. In another sense, it felt great that what I had considered an unpleasant amateur hack was maybe not so far off from how a Real Developer might solve the problem. I made note of the various values used on the car and wheels, and moved on.

The next example I turned to ended up not being all that useful to me, but I want to call it out here as a satisfying and elegant piece of scripting. Unity community superstar JamesLeeNZ offered up a solution he calls the Automagic WheelCollider Configurator. There are some interesting things to note. First, the WheelColliders don’t actually exist in the Editor. The provided wheel script creates them at runtime, based on the size of the wheel mesh:

wheelCollider = WheelCollider.AddComponent<WheelCollider>();
wheelCollider.radius = wheel.GetComponent<MeshRenderer>().bounds.extents.y;

One bonus here is that the various values for the wheel parameters are set in script when the WheelCollider is created, so you never have to worry about a discrepancy between the script and however you juked the numbers in the Editor. The car itself has a script that declares an array of four wheel scripts at startup, so the whole thing takes care of itself quite nicely. The script also has some basic GUI elements and is well worth checking out, but ultimately I found I wanted to see the WheelColliders in Editor, whether the game was running or not.

A bit later in the notorious “unusable” thread, yant appeared again to drop more science, and this time provided some info that helped me to understand how things got so confusing. His post is worth reading in full, but the bit that interested me the most was about the parameters for tuning wheels. Apparently, when dealing with the PhysX updates, Unity decided to retain the WheelCollider parameters from Unity 4 like “stiffness” and “damper”, even though the new physics model offers other ways of describing wheel behavior that are both more intuitive and more accurate. This was a decision of expediency (as these things always are), and yant’s comments imply that Unity hopes to overhaul the user-facing interface for wheels, and that they are currently not as easy to manipulate as they potentially could be. This was a nice check mark for the “I’m not crazy” side of the ledger.

In his post, yant also linked a really interesting “Easy Suspension” project, which includes a wizard to help you create cars with up to 20 wheels (I guess there’s a big lunar lander constituency out there). I loaded this up and took more notes on the various objects, settings and values used, but by this time I had accidentally discovered this Offical Unity 5 WheelCollider Tutorial, which maybe would have been a good place to start rather than end up, but I got there eventually, and it guided many of my decisions going forward.

CIRCLING THE WAGONS

I made a new project called Wagon with a goal of mechanical purity: I would get kickass race car behavior out of a wheeled cube before proceeding further. I was dismayed by the tutorial’s instruction to scale my car cube along two axes for a car-like shape, as I had somehow internalized the axiom that You Don’t Scale Physics Objects, but as this was an official tutorial maybe it was not such a big deal? Nevertheless, I stuck with a pure cube and did most of my initial experiments with what I came to think of as “the pumpkin carriage” (and yes I know pumpkins are not generally purple).

As you can see I got my terrain and such in there, as I figured there was no use making a perfect car that wouldn’t work with my existing game world. I’ll have more to say about terrain troubles in part two of this already lengthy post. I had a cube with a BoxCollider on it, four WheelColliders, and a script to accept fwd/back and left/right input. The grand tuning had begun. The pumpkin was stable enough at low speeds, but once I started pushing the wheels with some serious torque, we were back to the flipping and tumbling behaviors I was familiar with from Unity 4. Some familiarity, in this context, was comforting, as I had confronted these problems before. And, at the risk of protesting too much, I’ve never been upset about the flips, as I assume they come from a place of physics realism. It’s easy in a game to supply a car, through accident or ignorance, with ten times, or ten thousand times, the amount of force a real-world engine would impart, and if you do so, terrible things should probably happen. Nevertheless, real car designers have to solve the problem of the car flipping over, and in my own little way I had to do the same.

During this process I gained a lot of practice with Unity’s various UI features, as I needed onscreen live-updated values for wheel slippage, engine rpm, and a dozen other things. I started out with the good old OnGUI function, but as that got unwieldy I graduated to a slightly nicer system involving a UI Canvas, a dedicated UI Camera, and a UI script to pluck values from various objects and update the onscreen text. I was doing a lot of online reading and I started at this point to grasp the outlines behind the idea of using messaging in Unity. Apparently U5 improves the handler system to be (in my limited estimation) somewhat closer to basic C#, which will save me a lot of headaches as I never bothered with the old SendMessage, preferring instead to just give these kinds of scripts public references to whatever objects they needed to track, and populate those either in the Inspector (so fragile!) or in Start, via a bunch of tedious calls to FindGameObjectWithTag, or transform.FindChild, or whatever. Now the car can just announce to the game world at large “my left rear tire has lost friction and is spinning freely” and the UI script can just listen for that event. This was probably possible in a roundabout way before, but after re-hacking in much of the old Petrov debug UI I’m a lot more interested in exploring saner options.

Some other interesting UI-type things I learned during the pumpkin phase of the project: the car would quickly move out of range of the Scene camera, and while for any given run I could fixate that camera on the car with Shift-F, I wanted to get this task out of my conscious awareness. Internet searching brought me once again the Unity Community Wiki, a motherlode of scripts that many generous souls have offered up to bridge the gap between what we want and what we’re gonna get. This Scene View Camera Follower did the trick. While I was there, I checked out some other camera scripts, and ended up adopting this Smooth Follow Advanced script to keep the main camera trained on the car. There’s a script in Standard Assets to make a camera follow a GameObject, which probably works great on something like a platformer or RTS, but when following a 3D object moving and turning at high speed it gets a little wonky. The new script wasn’t perfect out of the box, but once I started messing with the parameters I got the picture. There’s something about zooming out from the car as speed rises, and back in as it falls, that really felt right to me in the service of a racing game. It was one of those things that I had never considered, but once it became available I realized it was indispensable. One wonders how many such things I never discovered at all.

Once I could consistently see the vehicle in Scene view, I longed for some better visualizations on the WheelColliders. They are represented in the editor as green circles, and I wondered if I could somehow get them to cough up more debug information without actually bringing wheel meshes into the picture. I did a deep dive into the Gizmos system, which didn’t prove fruitful, but eventually some helpful posts led me to this:

UnityEditor.Handles.DrawWireDisc(wheelCollider.transform.position, transform.right, wheelRadius);

DrawWireDisc! It sounds like something out of Phantasm! The transform.right was arrived at after many hours of trying to figure out how to rotate a quaternion on one axis, futzing with Euler angles and the like. I feel like this is one of those problems I’ve solved half a dozen times, then promptly forgotten how to solve. Maybe writing it out like this will help? One can only live in hope. The relationship between WheelCollider behaviors and wheel mesh appearance brought me to a sort of epiphany, or maybe just a re-experience of a common mind-bending situation game devs have to deal with (I think an epiphany mean you won’t do it again, which I can’t promise).

void OnDrawGizmos()
{
	if (wheelCollider)
	{
		if (wheelCollider.GetGroundHit(out hit))
		{
			if (hit.forwardSlip > slipLimitFwd)
			{
				UnityEditor.Handles.color = Color.red;
			}
			else
			{
				UnityEditor.Handles.color = Color.white;
			}
			
			if (hit.sidewaysSlip > slipLimitSide)
			{
				UnityEditor.Handles.color = Color.yellow;
			}
			else
			{
				UnityEditor.Handles.color = Color.white;
			}
		}
		UnityEditor.Handles.DrawWireDisc(wheelCollider.transform.position, rootTransform.right, wheelRadius);
	}
}

This is, of course, filthy and depraved, UI functions should not be calling raycasts, we should be listening for messages from the wheel, which should be calling the raycasts, but let’s move quickly along because it worked and the WheelCollider in-editor visualization changed colors when the wheel was spinning fruitlessly without any friction against the ground. This was a hugely important advance! Because, you see, my wheels were slipping, and I didn’t know it.

THE AFFORDANCE TANGENT

In game design, and particularly in video game design, there’s this somewhat buzzwordy concept of affordance. The basic idea is this: if your game has doors that are, let’s say, circular magical gates, that have to be activated with a wand, that imposes a certain cognitive load on a player who is navigating your level, especially in a 3D environment. The problem is there’s only so much cognitive load to go around, and you want your player spending as much of it as possible actually engaging with the interesting parts of your game. If you instead put in a tall rectangle with hinges and a brass plate-and-knob fixture, suddenly you have freed up a certain amount of cognitive load from the player because they know what a door is and how it works, it’s not something they have to figure out in order to engage with the world of your game. Affordance means getting something for nothing in implementation terms, because you’re making a representation of something the player already knows how to interact with. Nobody needs a tutorial for a staircase or, for that matter, a crate.

The pitfalls are many (as are the crocodiles, cobras, and for some reason, endless rolling barrels). Woe to the implementer whose modelled car (for example) doesn’t do the dozen different things an actual car should be able to do. This is perhaps the root cause of “broken immersion”: dissonance between the understanding offered to the player by affordance, and the game’s actual version of whatever the thing is. Flat doors, motionless cars, toilets that don’t flush, “if they went to the trouble of making X, they should have at least let you Y”. Maybe you’re not really getting anything free after all?

Even worse, the developer or designer is far from immune to these bewitching effects. Case in point, the wagon car, which was first only showing me green wheel circles, and later tire meshes, led me to the impression that the wheels were behaving normally, doing what wheels do, and that the car’s weird, unpredictable movements were bugs or worse. Once I got that slip debug visualization on, I realized that the WheelColliders, what Unity considered the actual physics wheels, were almost always spinning frantically against the road. As one commenter in that notorious thread put it: “like a bar of soap on black ice”.

The point isn’t that I had my numbers wrong (I was torqueing the wheels by several orders of magnitude too many; if the wheels modeled rubber it would have disintegrated), the point is that I was led away from the solution to an error by the siren song assumption that a thing in this game would respond more or less like something in the real world, just because it looked more or less like something in the real world. Since I assumed that part was working properly, I went looking for the answer to the bad car behavior in half a dozen other spots, until I made the visualization happen, mostly out of frustration and boredom. OK, again, this is too long, I’ll link to this video about affordance for those who want to know more.

FINAL NUMBERS

Back in the project, I had the jeweler’s loupe out and was zeroing in on some  workable wheel friction curve values. I was also, at long last, reading the manual. One of the most vexing questions when building these kinds of simulated objects is, what is in the neighborhood of a good value for an obscure property like “spring dampener”? Ten, or ten thousand?  Here we can maybe see some of the fissures around this uneasy implementation. The official documentation puts Asymptote Value, to choose an example, at a default of 10000. If you import the Standard Assets car package, the WheelColliders on that vehicle have wheels where the Front Slip has an Asymptote value of 0.5. That’s a pretty significant difference! I can’t absolutely say that this sort of thing does or doesn’t point to some turmoil over wheels in the Unity org, but they have a million things to worry about, and to their credit have been honest and open through their public-facing devs that the current state of this stuff is not quite where they want it either.

Endlessly tuning the wheel friction curves was just too frustrating.They felt like a wilderness, where luck mattered more than planning. A game designer’s life consists largely of turning various knobs provided by Engineering (oh, the luxury of helpful on-site engineers!), and figuring out which knob positions provide the most satisfying results. This metaphor is really teetering, but bear with me. A box with one or two knobs is easily tuned. When you get into four and five simultaneous variable, things get more challenging. But there as at some point a limit, beyond which the work of tuning gameplay by adjusting variables become less and less useful, because you can’t tell what did what. You can isolate a variable and observe its different settings, but in many systems turning one knob actually turns all the other knobs too, an unknown amount in an unknown direction. I see the wheels and springs of the Unity 5 WheelCollider as just this sort of system, where even the creators have admitted the knobs don’t really do what they used to, but they haven’t had time to install new knobs. In these situations, I approach the game like I would approach a complex synthesizer: spend a certain amount of time messing around, just looking for something close to what you want, knowing most combinations will fail. When you finally get to something close to the desired result, make note of those settings, then slowly try moving each of the variables away from the “good-ish” setting, to see if things improve or get worse. This is a tedious, unsatisfying process, as there’s always that awareness that maybe you could have found a better starting point, or maybe your current best setting is only a few twists away from something really great, but there’s nothing in the loop of small changes and observations that will reliably point you in the right direction, because there are too many variables to be able to predict what any change will do.

Nevertheless, one has to stop somewhere, and along the way I had a bright idea that led to a useful technique. Since the friction curve is a curve, and since it was probably built in that shape to simulate what wheels actually do, then maybe I could get good results by changing every value in the curve by the same amount, so that I could observe a new result while keeping the general shape of the curve at least somewhat similar. Here’s an image showing, side-by-side, the default values of a Standard Assets wheel friction curve, and what I ended up “shipping” on my portfolio page:

Each of the points in the forward and sideways friction curve has a value amplified 3x from the baseline. 3 is the multiplier I arrived at after trying a bunch and weighing the grip, slip, and general “feel”. No doubt someone with different engine code, or a different car mass, or wheel size, would have to use different values, but I do think that trying to maintain the shape of the default curve as you go is not a bad rule of thumb, unless you’re someone with the knowledge required to make meaningful decisions around things like “more Asymptote and less Extremum”. Ultimately, I’ve come to the opinion that the relationship between numbers is much more important than the numbers themselves. As an example, every WheelCollider has a spring value and a damper value, which controls how much effort the spring will spend trying to get back to its starting value after being altered, and has a big effect on the feeling of “springiness”. As it turns out, that relationship is key: a wheel with spring 1000 and damper 500 will feel much more like a wheel with 10000/5000 in those values than it will feel like another wheel with sprint 1000 and a damper of only 50.

I found that Stiffness, the last value in the curve, could effectively be used as a multiplier by itself. Increasing the stiffness from 1 to, say, 4, without altering the rest of the curve, provides much more grip while retaining many of the same kinds of behavior. It’s one of the few “knobs” that can really be isolated usefully. Thankfully, there’s also a low ceiling: with a Stiffness of anything above 10 or so, the wheels just can’t turn at all for whatever reason. Presumably this is an accurate simulation of physics from some distant planet or alternate dimension, so devs in those locales should be pleased as well. I imagine those Stiffness 10 tires as the friction equivalent of Vantablack: can’t move, won’t move.

One final number that ended up being unexpectedly important was Force Point Application Distance. This is a float ranged 0 to 1 that indicates where, vertically, in the wheel the physics engine applies the forward torque to make the wheel move. I assumed (affordance blindness strikes again!) that you’d want the physics applied in the appropriate place, where the road meets the wheel, and most examples agreed. However, near the end of my tuning process I randomly jacked that number up to near the top of the wheel, and suddenly a bunch of handling problems evaporated. I was getting a much cleaner response all around, and this gave me the confidence to decide it was good enough to post online. As much as I’d like to encourage a high Force Point as a good rule of thumb, this whole process is proof that it’s probably not. Since every number is dependent on every other, in some other game it probably makes much more sense to use a lower number. Hell, maybe under the hood my simulation thinks the car is upside-down and backwards, but somehow that results in something tolerable on-screen. Who can say? It was finally time to move on, at least until the day I load up Unity 6 and this version of the car starts doing backflips and cartwheels.

COMING UP IN PT. 2

Hard part’s over, all I need to do is tidy up the game world, re-implement the race rules, the pick-ups, and some basic FX, whip the UI into shape, and deploy on this brand-new web system. Oh yeah, and maybe a transmission system too. No worries. I’m 90% there! Right?