Implementing moving platforms is one of the trickiest tasks with most of physics engines. The problem is that platforms are kinematic, not dynamic - in other words, they do not gain realistic motion due to collisions with other objects, yet they perform movement. However, other rigid bodies obviously should react to collisions with the platform (its purpose is to transport something, after all).
How could one achieve this kind of interaction with dmech? Pretty easy! To represent a platform, you should use static body. It won't be actually static, of course, but it is necessary to fully avoid unwanted collision behaviour. We will move this body by changing its velocity and then manually integrating position. It would be convenient to define a special KinematicObject class for this:
class KinematicObject
{
PhysicsWorld world;
RigidBody rbody;
this(PhysicsWorld world, Vector3f pos, Geometry geom)
{
this.world = world;
rbody = world.addStaticBody(pos);
world.addShapeComponent(rbody, geom, Vector3f(0, 0, 0), 1.0f);
}
void moveToPosition(Vector3f pos, double dt)
{
rbody.linearVelocity = (pos - rbody.position) / dt;
rbody.position += rbody.linearVelocity * dt;
rbody.updateShapeComponents();
}
}
moveToPosition function should be called every timer step to update the body's position. Remember to call it before world.update(). To determine new position, you can, for example, use linear interpolation between two points. Or you can implement curve-based trajectories (Bezier, NURBS, etc.) to achieve complex and visually appealing motions - you have all the freedom.
The awesome thing is, all interactions of dynamic bodies with the platform will work automatically - you don't need to adjust anything! You can even put a stack of boxes onto the platform, and dmech's solver will be able to transmit motion to all of them.
Moreover, you can use Character object from previous tutorial together with KinematicObject - the character will behave as expected, standing on the platform and moving with it. If done correctly, the character can also walk on it - add the following code to character's update method before resetting the speed:
if (floorBody && speed == 0.0f)
rbody.linearVelocity = floorBody.linearVelocity;
This requires floorBody, a reference to body on which the character is standing on. You can detect it with the use of raycasting:
bool checkOnGround()
{
floorBody = null;
CastResult cr;
bool hit = world.raycast(rbody.position, Vector3f(0, -1, 0), 10, cr, true, true);
if (hit)
{
if (distance(cr.point, rbody.position) <= boundingSphereRadius)
{
floorBody = cr.rbody;
return true;
}
}
return false;
}
boundingSphereRadius depends on geometry that you use for your character's rbody. Calling checkOnGround also detects if the character is standing on the ground. Remember onGround from previous tutorial? Now the full update will look as follows:
void update()
{
Vector3f targetVelocity = direction * speed;
Vector3f velocityChange = targetVelocity - rbody.linearVelocity;
rbody.linearVelocity += velocityChange;
onGround = checkOnGround();
if (floorBody && speed == 0.0f)
rbody.linearVelocity = floorBody.linearVelocity;
speed = 0;
}