1 module testbed.character;
2 
3 import std.math;
4 
5 import dlib.core.memory;
6 import dlib.math.vector;
7 import dlib.math.matrix;
8 import dlib.math.transformation;
9 import dlib.math.utils;
10 
11 import dmech.world;
12 import dmech.rigidbody;
13 import dmech.geometry;
14 import dmech.shape;
15 import dmech.contact;
16 import dmech.raycast;
17 
18 /*
19  * CharacterController implements kinematic body
20  * on top of dmech dynamics: it allows direct
21  * velocity changes for a RigidBody.
22  * CharacterController is intended for
23  * generic action game character movement.
24  */
25 class CharacterController
26 {
27     PhysicsWorld world;
28     RigidBody rbody;
29     bool onGround = false;
30     Vector3f direction = Vector3f(0, 0, 1);
31     float speed = 0.0f;
32     float jumpSpeed = 0.0f;
33     float artificalGravity = 50.0f;
34     float boundingSphereRadius;
35     RigidBody floorBody;
36 
37     this(PhysicsWorld world, Vector3f pos, float mass, Geometry geom)
38     {
39         this.world = world;
40         rbody = world.addDynamicBody(pos);
41         rbody.bounce = 0.0f;
42         rbody.friction = 1.0f;
43         rbody.enableRotation = false;
44         rbody.useOwnGravity = true;
45         rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f);
46         rbody.raycastable = false;
47         world.addShapeComponent(rbody, geom, Vector3f(0, 0, 0), mass);
48         boundingSphereRadius = 1.0f;
49     }
50 
51     void update(bool clampY = true)
52     {       
53         Vector3f targetVelocity = direction * speed;
54         Vector3f velocityChange = targetVelocity - rbody.linearVelocity;
55         velocityChange.y = jumpSpeed;
56         rbody.linearVelocity += velocityChange;
57 
58         onGround = checkOnGround();
59 
60         if (onGround && floorBody && speed == 0.0f && jumpSpeed == 0.0f)
61             rbody.linearVelocity = floorBody.linearVelocity;
62             
63         speed = 0.0f;
64         jumpSpeed = 0.0f;
65     }
66     
67     bool checkOnGround()
68     {
69         floorBody = null;
70         CastResult cr;
71         bool hit = world.raycast(rbody.position, Vector3f(0, -1, 0), 10, cr, true, true);
72         if (hit)
73         {
74             if (distance(cr.point, rbody.position) <= boundingSphereRadius)
75             {
76                 floorBody = cr.rbody;
77                 return true;
78             }
79         }
80         return false;
81     }
82 
83     void move(Vector3f direction, float spd)
84     {
85         this.direction = direction;
86         this.speed = spd;
87     }
88 
89     void jump(float height)
90     {
91         if (onGround)
92         {
93             //rbody.linearVelocity.y = jumpSpeed(height);
94             jumpSpeed = calcJumpSpeed(height);
95             onGround = false;
96         }
97     }
98     
99     float calcJumpSpeed(float jumpHeight)
100     {
101         return sqrt(2.0f * jumpHeight * artificalGravity);
102     }
103 
104     void free()
105     {
106         Delete(this);
107     }
108 }