Growing Pains (Part 1 of N)

This is gonna get really technical really fast…

Every good engineer should know and love the KISS principle: Keep It Simple, Steve. No, I’m not trying to be clever with Valentine’s Day. Tonight, I was working on making my penetration depth (c’mon now grow up) calculations more robust, and once again, it was another exercise in keepin’ it simple. The old code:

function SignedDistAABB2D( bmin, bmax, p ) : float
var overallSdf = 0.0;

for( var d = 0; d < 2; d++ ) { var sdf = 0.0; if( bmin[d] > p[d] )
sdf = bmin[d]-p[d];
else if( p[d] > bmax[d] )
sdf = p[d]-bmax[d];
// choose the less severe of the two.. we don't really know which way was "in" just based on the point..
sdf = Mathf.Max( -(p[d]-bmin[d]), -(bmax[d]-p[d]) );

if( d == 0 )
overallSdf = sdf;
// we take the max, because this is a box. The shortest way out.
overallSdf = Mathf.Max( overallSdf, sdf );

return overallSdf;

function SignedDistBox2D( box : BoxCollider, worldpos : Vector3 ) : float
var localpos = box.transform.InverseTransformPoint( worldpos );

// we must apply scale still, because that affects which direction's SDF gets chosen
// we could also apply translation, but it doesn't affect the SDF value, so just don't
var scaledpos = Vector3.Scale( localpos, box.transform.localScale );
var scaledmins = Vector3.Scale( - box.size/2.0, box.transform.localScale );
var scaledmaxs = Vector3.Scale( + box.size/2.0, box.transform.localScale );
var minSdf = SignedDistAABB2D( scaledmins, scaledmaxs, scaledpos );

return minSdf;

And the new code..

// Returns the SDF of the contact relative to the given box
// 'normal' is assumed to be pointing outward from the box
function ContactBoxSDF( point : Vector3, normal : Vector3, box : BoxCollider ) : float
// put everything in local space
localNorm = box.transform.InverseTransformDirection( normal );
localPos = box.transform.InverseTransformPoint( point );

// compute SDF, using localScale, since that stretches the box
// note that the normal is unaffected by scale (using xformDirection)
scaledPos = Vector3.Scale( localPos, box.transform.localScale );
scaledRadii = Vector3.Scale( box.size/2.0, box.transform.localScale );

// We compute the SDF by considering 2 planes with normal localNorm, one containing the given point and the other
// at the box's max-corner. We then simply compare their absolute distances, since we know the box contains the origin.
cornerPlaneToOrigin = Mathf.Abs( Vector3.Dot( localNorm, scaledRadii ) );
pointPlaneToOrigin = Mathf.Abs( Vector3.Dot( localNorm, scaledPos ) );
sdf = pointPlaneToOrigin - cornerPlaneToOrigin;

return sdf;

About half as long and with virtually no if-else logic. Cleaner, easier to explain, and more robust! Sometimes, it feels like I enjoy coding the most when I get to delete a buncha’ old crap. As the Smashing Pumpkins said, cleanliness is Godliness.

Realistic, robust growing physics has been the biggest technical challenge of this game (designing good puzzles is probably the biggest overall). Modern physics engines typically don’t expect that their rigid bodies will ever change scale (they are, after all, supposed to be rigid). I’m using Unity’s built-in PhysX package, and it sure as hell does not expect them. Now for the most part, things behave OK, and shrinking actually “just works.” But growing? Oohhhh boy…

Half a year ago, I started prototyping Pupil by making my character out of a bunch of rigid bodies stuck together with joints. Then when it had to grow, I pushed those bodies out and periodically rescaled them to “fill in the gaps”. This was embarrassingly complicated, extremely difficult to tune and debug, and ultimately not very robust at all. Some time last year, I came upon a much simpler method: Just use a single rigid body and let it grow while overlapping others, but just not too much. The physics engine should just “fix” the overlaps and everything would work as expected! That’s what I’m doing right now, but as you will probably experience, it’s not very robust. You still go through walls and fall through geometry. But, I think it is the way to go, and I just need to be more careful about how much I grow. I’ve got a fix that I’m currently implementing, and tonight’s work was the first step in that direction.

Sleep time, and happy Valentine’s Day to those lucky enough to have one!


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s