I’ve been working on some physics based modeling using Verlet integration (example in the BIT-101 labs). I started a very simple system of just two connected particles with a “stick” between them and initialized them with opposite y velocities. (Note, if you don’t know what I’m talking about in the first few paragraphs here, jump down to paragraph six or so. That’s where the important stuff is!)
Anyway, the result of this system should have been a spinning stick. But no matter what I did, the thing kept drifting off to the left, and even had a tendancy to drift upwards if no gravity was applied.
It didn’t make sense. There was NO initial x velocity in the system, and the y velocities were equal and opposite. The system shouldn’t move at all, just spin.
I checked over my code a million times and even tried reversing several operations, figuring that I’d find something that would make it start drifing to the right or down, and then I’d have it isolated. No luck. Always tended to the top left corner.
Finally found the problem using the Flash IDE debugger (maybe the 2nd or 3rd time I’ve ever opened it on purpose!)
As the particles spun around each other, I was calculating how much to move each one on the x and y axis to keep them the correct distance apart. In the first iteration, I came up with a value something like 0.2478…. This was being added to one particle’s x position, and subtracted from the other’s. So assuming they started out with x positions of 100 and 200, the new positions would be 100.2478…. and 199.7521…
Now I was setting the _x and _y properties of movie clips directly with these numbers, and using _x and _y to hold the values for the next iteration. Makes perfect sense, right? NO!!!
Movie clip positions, like other pixel-based measurements in flash, are rounded down to the next lowest “twip”. A twip is a twentieth of a pixel. Thus, the most accurate measurement you can get is down to .05 pixels.
The problem became clear in the debugger. Instead of the calculated positions above, I was getting 100.2 and 99.75. See? It’s going to the next lowest twip. Doesn’t seem like a big deal, but that rounded-down value was being used in the next iteration, and the result of that was rounded down! Over many frames it started to add up. And in the Verlet integration scheme, you do multiple iterations of constraint handling, so I might end up losing a couple of pixels on each frame. Because it rounds down, It’s going to wind up going towards lower x’s and y’s, hence the motion to the top left.
The solution was to just create additional x and y variables to keep track of the actual numbers, and just pass these to _x and _y after all the calculation was done. That straightened it out and got the system behaving like it should have.
I think this was talked about in the Retro Gaming section (or at least used in that platform game source file) of “Flash MX 2004 Games Most Wanted.” Didn’t you write a couple of chapters in that book 😉
*Disclaimer- I’m not claiming to be smart just because I read a book. I’ve been relying on KP’s books, tutorials, and source files for all of my flashin’ fun.
I did do a couple chapters of that. Should have read the rest. 🙂
Actually, I already knew that MovieClip properties do that, just never considered the impact. The iterative process I was using made it really stand out.
Do you think rewriting your billiardball class using the verlet method would show a performance increase?
I’d really like to see a much simpler example of verlet integration (like a 2d circle that you could throw around in a box). I remember reading that same article back when you posted about the infamous ragdool swf, but I got lost trying to apply it to a working example.
Would you mind posting a basic verlet integration example? Thanks.
yeah this is pretty well documented. When ‘reading’ the value of a property of a MovieClip object you can never assume that what flash tells you will be *exactly* accurate. In most instances the difference is small enough not to matter, but as you’ve found in gaming it can make quite a big difference over quite a short space of time.
Definitely always create your own properties to store what the values are, then just apply those to the built-in properties e.g.
this._rotation = this.rotation;
this._y = this.ypos;
this._x = this.xpos;