1 module cc; 2 3 import std.math; 4 5 import dlib.math.vector; 6 import dlib.math.matrix; 7 import dlib.math.affine; 8 import dlib.math.utils; 9 10 import dmech.world; 11 import dmech.rigidbody; 12 import dmech.geometry; 13 import dmech.raycast; 14 import dmech.contact; 15 16 /* 17 * CharacterController implements kinematic body on top of dmech dynamics: 18 * it allows direct velocity changes for a RigidBody. 19 * CharacterController is intended for generic action game character movement. 20 */ 21 class CharacterController 22 { 23 PhysicsWorld world; 24 RigidBody rbody; 25 bool onGround = false; 26 Vector3f direction = Vector3f(0, 0, 1); 27 float speed = 0.0f; 28 float maxVelocityChange = 0.75f; 29 float artificalGravity = 50.0f; 30 Matrix4x4f localMatrix; 31 Vector3f rotation; 32 33 this(PhysicsWorld world, Vector3f pos, float mass, Geometry geom = null) 34 { 35 this.world = world; 36 rbody = world.addDynamicBody(pos); 37 rbody.bounce = 0.0f; 38 rbody.enableRotation = false; 39 rbody.useOwnGravity = true; 40 rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f); 41 rbody.raycastable = false; 42 if (geom is null) 43 geom = new GeomSphere(1.0f); 44 world.addShapeComponent(rbody, geom, Vector3f(0, 0, 0), mass); 45 localMatrix = Matrix4x4f.identity; 46 rotation = Vector3f(0, 0, 0); 47 48 rbody.onNewContact ~= (RigidBody b, Contact c) 49 { 50 if (dot((c.point - b.position).normalized, rbody.gravity.normalized) > 0.5f) 51 { 52 onGround = true; 53 } 54 }; 55 } 56 57 void updateMatrix() 58 { 59 localMatrix = Matrix4x4f.identity; 60 localMatrix *= rotationMatrix(Axis.x, degtorad(rotation.x)); 61 localMatrix *= rotationMatrix(Axis.y, degtorad(rotation.y)); 62 localMatrix *= rotationMatrix(Axis.z, degtorad(rotation.z)); 63 64 direction = localMatrix.forward; 65 } 66 67 void update(bool clampY = true) 68 { 69 Vector3f targetVelocity = direction * speed; 70 71 Vector3f velocityChange = targetVelocity - rbody.linearVelocity; 72 velocityChange.x = clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange); 73 velocityChange.z = clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange); 74 if (clampY) 75 velocityChange.y = 0; 76 else 77 velocityChange.y = clamp(velocityChange.y, -maxVelocityChange, maxVelocityChange); 78 rbody.linearVelocity += velocityChange; 79 80 speed = 0.0f; 81 } 82 83 bool checkOnGround() 84 { 85 CastResult cr; 86 bool hit = world.raycast(rbody.position, Vector3f(0, -1, 0), 10, cr, true, true); 87 if (hit) 88 { 89 if (distance(cr.point, rbody.position) <= 1.1f) 90 return true; 91 } 92 return false; 93 } 94 95 void turn(float angle) 96 { 97 rotation.y += angle; 98 } 99 100 void move(float spd) 101 { 102 speed = spd; 103 } 104 105 void jump(float height) 106 { 107 if (onGround || checkOnGround) 108 { 109 rbody.linearVelocity.y = jumpSpeed(height); 110 onGround = false; 111 } 112 } 113 114 float jumpSpeed(float jumpHeight) 115 { 116 return sqrt(2.0f * jumpHeight * artificalGravity); 117 } 118 }