Saturday, October 2, 2010

Preventing Unnecessary Loss of Limbs in Games

‹prev | My Chain | next›

The players in my (fab) game are suffering some serious injuries:

Loss of limbs is not occurring immediately or even after a long time roaming about the game room. No, the player's legs are becoming detached after bouncing into each other repeatedly:


Crazy. Where does one even start to track down a problem like that?

Well, since I have made changes to it recently and since it is controlling the player walking animation, I start with animate-frames, my animation plugin for raphaël.js.

In there, I add an offsets() method to the individual frames making up the animation. The offsets calculate the x-y differences between the various strokes of the pen that comprise the frame (e.g. a pen stroke for a foot, leg, body, etc). The x-y differences are between each of the elements and the last element.
  Frame.prototype.offsets = function() {
var diffs = []
,main = this.getBBox();

for (var i=0; i<this.elements.length-1; i++) {
var box = this.elements[i].getBBox();
diffs.push([main.x - box.x, main.y - box.y]);

return diffs;
The individual offsets should always remain the same—a player's foot should stay 16 pixels away from the body if it was first drawn 16 pixels away. Looking at the first offset when the player first enters the room, I find:
0: -6.787272029958575
1: -16.76693459682566
After moving the player about the room for a bit, I find:
0: -6.787412453957089
1: -16.766913780265497
Almost exactly the same. I am not sure about the differences (in the 10,000ths decimal place). Is that caused by simple rounding errors? Maybe timing differences between stop() calls of individual elements is sufficient to cause that problem? Is that small a change even a problem?

Before investigating further, I try colliding my player with another player in the room. Then, I check the offsets again:
0: -6.039369453957079
1: -16.56494217026568
That is a much more dramatic change. Banging players together some more furthers the offset change:
0: -5.144486633957058
1: -16.511516330265806
Whatever the problem, it seems that the bouncing of players is the best place to continue my investigation.

But wait...

If I know the expected offset of the individual elements in a frame, can I use them to sync the frame back up to where it is supposed to be?

I think that I can run through each offset, calculate the difference between the current and original offset, then translate the affected frame element by the tiny offset it needs to get back to the right place:
  Frame.prototype.tighten = function() {
var offsets = this.offsets();
for (var i=0; i<offsets.length; i++) {
var x_diff = offsets[i][0] - this.original_offsets[i][0]
,y_diff = offsets[i][1] - this.original_offsets[i][1];

this.elements[i].translate(x_diff, y_diff);
Yay! That works just as expected. A player falling to pieces gets put back together (tightened up) with a single call.

To put that to practical use, I call it at the end of the translate() method of each Frame object. And, just like that, my players keep all of their limbs.

That is not an entirely satisfactory solution—I never did solve why limbs were flying away in the first place. Rounding errors seems a reasonable explanation, but reasonable explanations do not elicit learning the way that digging deep does. Something to investigate another day.

Day #244

No comments:

Post a Comment