Physics in GameMaker
GameMakerPublished April 27, 2011 at 9:46 pm No CommentsAfter developing a mediocre rigid body physics engine in C++, I thought I’d see just how impossible such a task was in GameMaker. In reality, the math behind a physics engine isn’t incredibly complex. Wrapping your head around it all isn’t simple, but once you’ve got something going, it’s not difficult to follow.
That in mind… I didn’t get very far with GameMaker. I settled on a simple ball-on-a-bungee program before getting too frustrated to continue.
It’s not that GameMaker can’t handle the math… you could get very far before noticing a major slow down. That bungee on ball was about 30 lines of code with no loops and only one if statement. Very simple.
The thing that is bothersome about writing physics in Game Maker is simple: Game Maker Language. I think GML is a great way to learn programming, but its simplicity can be a major downfall to anyone wanting something more serious… and powerful. Let’s take a look at that code.
In my simple engine, I have various two-part variables. For example, for velocity:
velocityx velocityy
This is unnecessary. In a physics engine, you deal a lot with values that have an X and a Y. We use vectors to represent these values in more powerful languages. So in GameMaker, you’re not dealing with a lot of vectors, you’re dealing with even more scalar variables.
Not only that, but this makes things like normalizing a vector a two part deal as well. In reality, it’s always a two part deal, but with powerful languages we can simplify the process. Here’s how it has to be done in GameMaker:
magnitude = point_distance(0, 0, forcex, forcey); forcex /= magnitude; forcey /= magnitude;
Whereas in C++ we could do this:
force.normalize()
Now, just to compare, here’s the bungee code in that gm81, and the one I used in my C++ engine. Note that my C++ engine is 3D, not 2D. Which adds a whole nother dimension that would just boggle down GML even more.
GML (2D):
var rotpointx, rotpointy, forcex, forcey, magnitude, stretch; // Calculate local rotation point rotpointx = lengthdir_x(16, direction); rotpointy = lengthdir_y(16, direction); // Get world rotation point forcex = x + rotpointx - anchorx; forcey = y + rotpointy - anchory; // Calculate bungee stretch magnitude = point_distance(0, 0, forcex, forcey); stretch = restlength - magnitude; // Only pull, do not push if (stretch > 0) exit; // Normalize force forcex /= magnitude forcey /= magnitude; // Add constant to stretch (spring force) stretch *= constant; // Multiply direction vector by spring force forcex *= stretch; forcey *= stretch; // Add the linear and angular forces to the rigid body. velocityx += forcex; velocityy += forcey; rotation += (rotpointx * forcey) - (rotpointy * forcex);
C++ (3D):
// Calculate local rotation point jVector3 _TorquePos = Point; jMatrix3 _Rot = jMatrix3::Rotate(pPart->GetOrientation()); _TorquePos = jVector3::MatrixMultiply(_Rot, _TorquePos); // Get world rotation point jVector3 _Pos = _TorquePos+pPart->GetPosition(); jVector3 _Force = (_Pos)-Position; // Calculate bungee stretch double _Stretch = Rest-_Force.Magnitude(); // Only pull, do not push if (_Stretch > 0) return; // Normalize force _Force.Normalize(); // Add constant to stretch (spring force) _Stretch *= Constant; // Multiply direction vector by spring force _Force *= _Stretch; // Add the linear and angular forces to the rigid body. pBody->AddForce(_Force); pBody->AddTorque(_TorquePos%_Force);
If you’ve never used C++, the method used there may look more confusing. In reality, it makes things much easier because all your methods are stored elsewhere and you can use them whenever you want. Take, for example, my matrix multiply (matrices being an important part of many physics engines… there are no matrices in GML!). Here’s what that looks like:
jVector3 jVector3::MatrixMultiply(jMatrix3& pA,jVector3& pB)
{
jVector3 _Ret;
_Ret.X = jMath::DotProduct3D(pA.M11, pA.M21, pA.M31, pB.X, pB.Y, pB.Z);
_Ret.Y = jMath::DotProduct3D(pA.M12, pA.M22, pA.M32, pB.X, pB.Y, pB.Z);
_Ret.Z = jMath::DotProduct3D(pA.M13, pA.M23, pA.M33, pB.X, pB.Y, pB.Z);
return _Ret;
}
Imagine typing that out everywhere you needed to multiply a vector and a matrix. Even worse, imagine using quaternions in GameMaker! Have fun converting that quaternion to a matrix every time:
jMatrix3 jMatrix3::Rotate(jQuaternion& pQuat)
{
jMatrix3 _Ret;
_Ret.M11 = 1-(2*(pQuat.Y*pQuat.Y)+2*(pQuat.Z*pQuat.Z));
_Ret.M12 = 2*pQuat.X*pQuat.Y+2*pQuat.Z*pQuat.W;
_Ret.M13 = 2*pQuat.X*pQuat.Z-2*pQuat.Y*pQuat.W;
_Ret.M21 = 2*pQuat.X*pQuat.Y-2*pQuat.Z*pQuat.W;
_Ret.M22 = 1-(2*(pQuat.X*pQuat.X)+2*(pQuat.Z*pQuat.Z));
_Ret.M23 = 2*pQuat.Y*pQuat.Z+2*pQuat.X*pQuat.W;
_Ret.M31 = 2*pQuat.X*pQuat.Z+2*pQuat.Y*pQuat.W;
_Ret.M32 = 2*pQuat.Y*pQuat.Z-2*pQuat.X*pQuat.W;
_Ret.M33 = 1-(2*(pQuat.X*pQuat.X)+2*(pQuat.Y*pQuat.Y));
return _Ret;
}
One Last Thing
Despite what it looks like, I’m not trying to put down anyone using GML. As I mentioned, it’s a good way to learn programming basics. If you think you’re a pro using GML, you’re wrong. There’s much more you can do, and if you plan on making something as complex as a physics engine, I highly advise you to check into C++, C# or something as powerful.
Also, with the recent additions from Mike Dailly on Game Maker, the idea of classes and structures might not be too far off and a physics engine in GameMaker may be a reality. As it stands, I’m writing all my bungees in C++.
jalb


