1 /** 2d kinematic body */ 2 3 module re.phys.kin2d; 4 5 import re.ecs.component; 6 import re.ecs.updatable; 7 import re.math; 8 import std.math; 9 import re.time; 10 11 class KinBody2D : Component, Updatable { 12 mixin Reflect; 13 public float mass = 1.0f; 14 public Vector2 velocity = Vector2Zero; 15 public Vector2 max_velocity = Vector2Zero; 16 public Vector2 accel = Vector2Zero; 17 public Vector2 drag = Vector2Zero; 18 public float max_angular = 0; 19 public float angular_velocity = 0; 20 public float angular_accel = 0; 21 public float angular_drag = 0; 22 23 // - transform 24 25 @property public Vector2 pos() { 26 return transform.position2; 27 } 28 29 @property public Vector2 pos(Vector2 value) { 30 return transform.position2 = value; 31 } 32 33 @property public float angle() { 34 return transform.rotation_z; 35 } 36 37 @property public float angle(float value) { 38 return transform.rotation_z = value; 39 } 40 41 @property public float stdAngle() { 42 return -angle + std.math.PI / 2; 43 } 44 45 @property public float stdAngle(float value) { 46 return angle = -(value - std.math.PI / 2); 47 } 48 49 // misc. getters 50 @property public Vector2 momentum() { 51 return mass * velocity; 52 } 53 54 override void setup() { 55 } 56 57 void update() { 58 auto dt = Time.delta_time; 59 60 // apply max velocity 61 auto vls = velocity.LengthSquared(); 62 auto mvls = max_velocity.LengthSquared(); 63 if (mvls > double.epsilon && vls > mvls) { 64 // convert to unit and rescale 65 auto unit_vel = velocity.Normalize(); 66 auto ratio = mvls / vls; 67 auto reduction_fac = pow(ratio, (1 / 12f)); 68 // smoothly reduce to max velocity 69 velocity = velocity * reduction_fac; 70 } 71 72 // new velocity components 73 auto nvel_x = velocity.x; 74 auto nvel_y = velocity.y; 75 76 // apply acceleration 77 nvel_x += accel.x * dt; 78 nvel_y += accel.y * dt; 79 80 if (nvel_x > drag.x * dt) { 81 nvel_x = nvel_x - drag.x * dt; 82 } 83 84 if (nvel_x < -drag.x * dt) { 85 nvel_x = nvel_x + drag.x * dt; 86 } 87 88 if (abs(nvel_x) < drag.x * dt) { 89 nvel_x = 0; 90 } 91 92 if (nvel_y > drag.y * dt) { 93 nvel_y = nvel_y - drag.y * dt; 94 } 95 96 if (nvel_y < -drag.y * dt) { 97 nvel_y = nvel_y + drag.y * dt; 98 } 99 100 if (abs(nvel_y) < drag.y * dt) { 101 nvel_y = 0; 102 } 103 104 velocity = Vector2(nvel_x, nvel_y); 105 106 apply_motion(velocity * dt); 107 108 // apply max angular 109 if (max_angular > 0) { 110 if (angular_velocity > max_angular) { 111 angular_velocity = max_angular; 112 } 113 114 if (angular_velocity < -max_angular) { 115 angular_velocity = -max_angular; 116 } 117 } 118 119 // apply angular accel 120 angular_velocity += angular_accel * dt; 121 122 // apply angular drag 123 if (angular_velocity > angular_drag * dt) { 124 angular_velocity -= angular_drag * dt; 125 } 126 127 if (angular_velocity < -angular_drag * dt) { 128 angular_velocity += angular_drag * dt; 129 } 130 131 transform.rotation_z = transform.rotation_z + (angular_velocity * dt); 132 } 133 134 protected void apply_motion(Vector2 pos_delta) { 135 // apply motion to this object. typically this is passed onto a physics body. 136 transform.position2 = transform.position2 + pos_delta; 137 } 138 } 139 140 @("phys-kin2d") 141 unittest { 142 import re.ng.scene : Scene2D; 143 import re.util.test : test_scene; 144 145 class TestScene : Scene2D { 146 override void on_start() { 147 auto nt = create_entity("block", Vector2(0, 0)); 148 // add kin body 149 auto bod = new KinBody2D(); 150 bod.accel = Vector2(0, 9.8); 151 bod.angular_accel = C_PI_2; 152 nt.add_component(bod); 153 } 154 } 155 156 auto test = test_scene(new TestScene()); 157 test.game.run(); 158 159 // check conditions 160 assert(test.scene.get_entity("block").get_component!KinBody2D().pos.y > 0, 161 "KinBody2D did not move"); 162 assert(test.scene.get_entity("block").get_component!KinBody2D() 163 .angular_velocity > 0, "KinBody2D did not rotate"); 164 165 test.game.destroy(); 166 }