vrijdag 4 februari 2011

Shoot the crate

Let's talk about something different than the platform game I'm working on. Last week one of our interns had a problem with the physics engine. The scenario was as follows: We're playing a character in first person style and we're holding a gun. We can walk around in the world and there are some non-static crates: if you bump into these they will move. This is all handled by the physics engine itself, there is no effort required by us to set forces or anything. Nice. But now we want that when we aim our gun at the crate and shoot, that the crate receives an impact and moves and spins realistically.

One solution here is to actually create a bullet and make it follow the path along our crosshair. As to whether this is a good solution I'm not too sure: bullets have to travel at high velocities which is something that could 'bug' the physics engine and unrealistic events might occur. I'm not sure this would occur as we haven't tried this solution. Additionally you'll need to create and delete the bullet (on impact) as well as keeping track of it: what happens when we fire the bullet into the sky (i.e. there is no impact, the bullet flies)? The bullet wouldn't get removed. Although you'd probably have to shoot a lot of bullets into the air before you'll notice a performance drop, it's sloppy. So you'd either have to give each bullet a lifetime as well and update it and remove it when its lifetime runs out or seal the world so bullets will always impact. This could all be done, but it's a bit of a hassle and as I said you might get unexpected results. Let's explore another solution.

Solution two is to immediately apply an impulse ourselves. So we cast a ray along our crosshair and get the first intersection. If this intersection is with a crate then we'll apply a force on it. But to apply a force on the crate the physics engine requires us to give a point on the crate and an impulse. Our impulse is whatever we like: for a small caliber gun we could give small impulse and the crate will move just a little. The problem here is that the physics engine requires us to give a point on the crate in it's local coordinates, whereas our intersection point is a point of world coordinates (see image below).

Here W is the center of world (0,0,0), while C is the center of crate (0,0,0) in its local coordinates. Ray r is the ray along our crosshair and I is the point of intersection.

As said, intersection I is returned to us in world coordinates, while we need it in the crate's local coordinates. You might think "Ok, so just take I and substract C (in world coordinates) from it", but this is only correct when the crate isn't rotated. Rotation needs to be taken into account as well because if we take the same crate and rotate it 90 degrees, you'll still have the same intersection point, but a different local coordinate. To get the correct local coordinate of the intersection we need to look at the transform of the crate. The transform is a matrix M which determines the model's position, rotation and scale with respect to its parent (in this case the world). When the model is drawn to the screen all of its vertices are multiplied by the matrix M to get to the world position. So what we need is the opposite: the inverse of matrix M. The inverse of matrix M is a matrix M' that satisfies: M * M' = ID where ID is the identity matrix (that matrix with 0's everywhere except on its diagonal it has 1's). Multiplying the identity matrix by a vector will result in the same vector: there's no change. Multiplying the inverse matrix M' of the crate by C (in world coordinates) will give us the local coordinates of the crate. Hence if we multiply the inverse matrix M' by the intersection I given to us in world coordinates we obtain the intersection in the crate's local coordinates, which is exactly what we want. We pass it to the physics engine and it works perfectly :)

Multiplying M by the Ilocal position brings us to the Iworld position. Multiplying M' by the Iworld position brings us back to the Ilocal position.

-- Stijn