New build up!

Play in your browser right here! I’m pretty happy with the growth physics right now, even though there are some issues remaining. It works well 95% of the time, and most players probably won’t notice the 5% unless they’re trying to break it. There are issues with growing when you’re on top of another growing block, so I took out puzzle 10 (which needs tweaking anyway), but I’ll deal with that later. I’m fairly proud of the Simplex solver, so I’ll put that up somewhere as open source. You never know when you’ll need linear programming!

For now, it’s time to refocus time and energy on puzzle design and implementing some new mechanics. Next up? Magnetic fields! Nothing fancy – I just want a force field that effectively ties your Y position to your size – and that’s roughly what a bar magnet does, where F=1/r.

Growing Pains (Part 2 of N), and a screen shot

Just a quick technical update. I am this close to getting near-perfect growing physics. Over the weekend, I finished prototyping yet another approach, and this one is fairly complex although much more principled. I bypass the physics engine’s contact solver almost completely by doing my own collision solving using linear programming. Everything that grows in the game is an axis-aligned 2D square, and everything it may collide with is made of planes (possibly non-axis-aligned). This can be formulated as a linear program to maximize the size of the square, where the variables are simply the square’s radius and center, and each potentially contacting plane contributes constraints to keep the square on one of its sides. There are tons of further details, but that’s the basic idea.

Implementing a Simplex solver in JavaScript was no trivial matter, and there may still be a few bugs in there causing some instability (even with double precision). Performance is a concern, and I’ll need to make some serious optimizations to maintain a decent framerate (there are plenty of opportunities for it though).

I gotta say, I’m getting a little burnt out on perfecting the growth physics, so I think after a day or two more of this I’ll switch gears to another part of the game. Such as, working on the graphics! The talented artist I’m working with sent over some early, non-final work, but I like how it looks in the game already:

That is one determined box guy, ain’t it? Determined to solve some Monday to Friday puzzles. We want to keep the art in vector/polygon form so it’ll scale well, so I need to go directly from SVG to 3D without any rasterizing in between.

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];
else
// 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;
else
// 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.center - box.size/2.0, box.transform.localScale );
var scaledmaxs = Vector3.Scale( box.center + 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!

On Game Design and Taste

I like to think of games like food. Everyone has different tastes, and there are rarely simple guidelines that will satisfy everyone. The guidelines that do work most of the time tend to be fairly trivial (e.g. “don’t eat moldy things”). Otherwise, it’s a complex art. So instead of looking for “guidelines” and rules for “what makes a good game”, I think it’s more helpful and productive to try and understand the “big picture” of the game design space. This means learning about all sorts of game design philosophies, some of which you will like and some of which you won’t. This also means trying to get an idea of how many people in a given market like what kinds of designs. If you’re going for mass market, you should probably design with the pieces that seem to appeal to the mass market (WoW, FarmVille). But if you’re OK with a smaller market and want to focus on a genre that you’re personally interested in, then it helps to understand that space as well and how it fits in with everything else. These are all things you can discuss and learn about, and I think as designers, we absolutely should so we can explore the design space with agility.

And the most exciting thing about video games right now? There is so much of the design space that we haven’t explored. Just look at how many awesomely creative indie games come out every year!

Results from my first day of public pre-alpha testing

Overall lesson: I have a ton of work to do 🙂 I got about 50 plays and 12 replies (from reddit and TIGSource forums). People dug the idea and the puzzles, but I really need to fix the collision issues. Buggy physics really killed the experience for some people and made them stop playing (I really, really appreciate the honest feedback). That’s gonna be priority #1 for the near future, and I’ll probably post some tech-heavy notes about it.

I used Playtomic to track some play stats, and here’s a plot of # of times people entered a level:

As expected, I lose about half the players half way through. But what’s with the first level getting so little love? Well, it’s pretty clear when you look at the level select layout:

This is how you start in the level select hub each time. Players probably figured that the introductory level was actually level 1 because you start to the right of it, and thus didn’t bother going “back” to it. Fascinating 🙂