Game.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #include "Game.h"
  2. #include "Engine.h"
  3. #include "input/Controller.h"
  4. #include "utils/List.h"
  5. #include "utils/Utils.h"
  6. struct Line final {
  7. Vector2 a;
  8. Vector2 b;
  9. };
  10. static List<Line> lines;
  11. static constexpr float SPEED = 2.5f;
  12. static constexpr float GRAVITY = 0.5f;
  13. static constexpr float COLLISION_STEP = 0.005f;
  14. static Vector2 windowSize;
  15. static Vector2 lastPosition;
  16. static Vector2 position{150.0f, 50.0f};
  17. static Vector2 size{50.0f, 50.0f};
  18. static Vector2 velocity;
  19. static Vector2 acceleration;
  20. static Vector2 drag;
  21. static float mass = 5.0f;
  22. static bool onGround = false;
  23. static float jumpPower = 0.0f;
  24. static float steepness = 0.0f;
  25. static bool physicsToggle = true;
  26. static Vector2 waterPosition;
  27. static Vector2 waterSize;
  28. static void addForce(const Vector2& force) {
  29. acceleration += force / mass;
  30. }
  31. static void addMovement() {
  32. physicsToggle = physicsToggle ^ Controller::start.wasReleased();
  33. float movement = Controller::right.isDown() - Controller::left.isDown();
  34. float actualSpeed = SPEED * (1.0f - steepness * onGround);
  35. if(physicsToggle) {
  36. addForce(Vector2(actualSpeed, 0.0f) * movement);
  37. } else {
  38. // movement by velocity should have the same terminal velocity as
  39. // movement by force
  40. float terminalFactor = 1.0f / ((1.0 - drag[0]) * drag[0]);
  41. velocity[0] = (actualSpeed / mass) * terminalFactor * movement;
  42. }
  43. }
  44. static bool isIn(const Vector2& posA, const Vector2& sizeA, const Vector2& posB,
  45. const Vector2& sizeB) {
  46. Vector2 maxA = posA + sizeA;
  47. Vector2 maxB = posB + sizeB;
  48. return posA[0] < maxB[0] && maxA[0] > posB[0] && posA[1] < maxB[1] &&
  49. maxA[1] > posB[1];
  50. }
  51. float ERROR = 1.0f / 512.0f;
  52. static bool isBetween(float y, float y1, float y2) {
  53. return y >= std::min(y1, y2) && y <= std::max(y1, y2);
  54. }
  55. static bool compareFloats(float a, float b) {
  56. return std::abs(a - b) < ERROR;
  57. }
  58. static bool intersect(float x11, float y11, float x12, float y12, float x21,
  59. float y21, float x22, float y22) {
  60. if(compareFloats(x11, x12)) {
  61. if(compareFloats(x21, x22)) {
  62. return false;
  63. } else {
  64. if(!isBetween(x11, x21, x22)) {
  65. return false;
  66. }
  67. float k = (y21 - y22) / (x21 - x22);
  68. float d = y22 - k * x22;
  69. float y = d + x11 * k;
  70. return isBetween(y, y11, y12) && isBetween(y, y21, y22);
  71. }
  72. } else {
  73. if(compareFloats(x21, x22)) {
  74. if(!isBetween(x21, x11, x12)) {
  75. return false;
  76. }
  77. float k = (y11 - y12) / (x11 - x12);
  78. float d = y12 - k * x12;
  79. float y = d + x21 * k;
  80. return isBetween(y, y11, y12) && isBetween(y, y21, y22);
  81. } else {
  82. float k1 = (y11 - y12) / (x11 - x12);
  83. float k2 = (y21 - y22) / (x21 - x22);
  84. if(compareFloats(k1, k2)) {
  85. return false;
  86. }
  87. float d1 = y12 - k1 * x12;
  88. float d2 = y22 - k2 * x22;
  89. float x = (d1 - d2) / (k2 - k1);
  90. if(!isBetween(x, x11, x12) || !isBetween(x, x21, x22)) {
  91. return false;
  92. }
  93. float y = k1 * x + d1;
  94. return isBetween(y, y11, y12) && isBetween(y, y21, y22);
  95. }
  96. }
  97. }
  98. static bool areColliding(const Line& a, const Line& b) {
  99. return intersect(a.a[0], a.a[1], a.b[0], a.b[1], b.a[0], b.a[1], b.b[0],
  100. b.b[1]);
  101. }
  102. static bool doesPlayerCollide() {
  103. Vector2 posX = position + Vector2(size[0], 0.0f);
  104. Vector2 posY = position + Vector2(0.0f, size[1]);
  105. Vector2 posXY = position + size;
  106. for(const Line& line : lines) {
  107. if(areColliding(line, {position, posX}) ||
  108. areColliding(line, {position, posY}) ||
  109. areColliding(line, {posX, posXY}) ||
  110. areColliding(line, {posY, posXY})) {
  111. return true;
  112. }
  113. }
  114. return false;
  115. }
  116. static float slopeSteepness() {
  117. Line left{position - Vector2(0.0f, 10.0f * COLLISION_STEP),
  118. position + Vector2(0.0f, size[1])};
  119. Line right{position + Vector2(size[0], -10.0f * COLLISION_STEP),
  120. position + size};
  121. float min = 100.0f;
  122. float value = 100.0f;
  123. for(const Line& line : lines) {
  124. if(areColliding(line, left) || areColliding(line, right)) {
  125. float f = (line.a[1] - line.b[1]) / (line.a[0] - line.b[0]);
  126. if(std::abs(f) < min) {
  127. min = std::abs(f);
  128. value = f;
  129. }
  130. }
  131. }
  132. return value;
  133. }
  134. static void applyDrag() {
  135. waterPosition = windowSize * Vector2(0.5f, 0.0f);
  136. waterSize = windowSize * Vector2(0.5f, 0.125f);
  137. drag = Vector2(0.1f, 0.1f);
  138. if(isIn(position, size, waterPosition, waterSize)) {
  139. drag *= 4.0f;
  140. }
  141. acceleration -= velocity * drag;
  142. }
  143. static void applyGravity() {
  144. acceleration -= Vector2(0.0f, GRAVITY);
  145. }
  146. static void handleJump() {
  147. bool justJumped = false;
  148. if(Controller::a.isDown() && onGround) {
  149. jumpPower = 15.0f;
  150. onGround = false;
  151. justJumped = true;
  152. }
  153. addForce(Vector2(0.0f, jumpPower));
  154. jumpPower *= 0.9f;
  155. jumpPower *= Controller::a.isDown() && (velocity[1] > 0.0f || justJumped);
  156. }
  157. void Game::tick() {
  158. windowSize = Vector2(static_cast<float>(Engine::getSize().width),
  159. static_cast<float>(Engine::getSize().height));
  160. lastPosition = position;
  161. acceleration = Vector2();
  162. applyGravity();
  163. addMovement();
  164. handleJump();
  165. applyDrag();
  166. if(steepness != 100.0f) {
  167. if(steepness < -1.0f) {
  168. acceleration[0] += 0.1f;
  169. } else if(steepness > 1.0f) {
  170. acceleration[0] -= 0.1f;
  171. }
  172. }
  173. lines.clear();
  174. lines.add({{0.0f, 0.0f}, windowSize * Vector2(1.0f, 0.0f)});
  175. lines.add({{0.0f, 0.0f}, windowSize * Vector2(0.0f, 1.0f)});
  176. lines.add({windowSize, windowSize * Vector2(1.0f, 0.0f)});
  177. lines.add(
  178. {windowSize * Vector2(0.75f, 0.0f), windowSize * Vector2(1.0f, 0.5f)});
  179. lines.add({{0.0f, 30.0f}, {400.0f, 0.0f}});
  180. lines.add({{0.0f, 60.0f}, {300.0f, 0.0f}});
  181. lines.add({{0.0f, 90.0f}, {200.0f, 0.0f}});
  182. lines.add({{0.0f, 120.0f}, {100.0f, 0.0f}});
  183. lines.add({{100.0f, 180.0f}, {200.0f, 180.0f}});
  184. lines.add({{200.0f, 180.0f}, {600.0f, 380.0f}});
  185. velocity += acceleration;
  186. onGround = false;
  187. Vector2 energy = velocity;
  188. while(energy[0] != 0.0f || energy[1] != 0.0f) {
  189. for(int i = 0; i < 2; i++) {
  190. if(energy[i] != 0.0f) {
  191. float old = position[i];
  192. if(energy[i] > COLLISION_STEP) {
  193. position[i] += COLLISION_STEP;
  194. energy[i] -= COLLISION_STEP;
  195. } else if(energy[i] < -COLLISION_STEP) {
  196. position[i] -= COLLISION_STEP;
  197. energy[i] += COLLISION_STEP;
  198. } else {
  199. position[i] += energy[i];
  200. energy[i] = 0.0f;
  201. }
  202. if(doesPlayerCollide()) {
  203. if(i == 0) {
  204. float oldY = position[1];
  205. position[1] += COLLISION_STEP;
  206. if(!doesPlayerCollide()) {
  207. continue;
  208. }
  209. position[1] = oldY;
  210. }
  211. energy[i] = 0.0f;
  212. velocity[i] = 0.0f;
  213. position[i] = old;
  214. }
  215. }
  216. }
  217. }
  218. steepness = slopeSteepness();
  219. onGround = std::abs(steepness) < 1.0f;
  220. if(onGround) {
  221. velocity[1] = 0.0f;
  222. }
  223. }
  224. void Game::render(float lag, Renderer& r) {
  225. r.translateTo(0.0f, 0.0f)
  226. .scale(1.0f, -1.0f)
  227. .translateY(Engine::getSize().height)
  228. .update();
  229. r.drawRectangle(waterPosition, waterSize, Color4(0x00, 0x00, 0xFF, 0xFF));
  230. Vector2 pos = Utils::interpolate(lastPosition, position, lag);
  231. r.drawRectangle(pos, size, Color4(0xFF, 0x00, 0x00, 0xFF));
  232. for(const Line& line : lines) {
  233. Vector2 dir = line.b - line.a;
  234. Vector2 normal(dir[1], -dir[0]);
  235. normal.normalize();
  236. normal *= 2.0f;
  237. Vector2 ap = line.a + normal;
  238. Vector2 bp = line.b + normal;
  239. Vector2 an = line.a - normal;
  240. Vector2 bn = line.b - normal;
  241. r.drawTriangle(ap, bp, an, Color4(0xFF, 0xFF, 0xFF, 0xFF));
  242. r.drawTriangle(bp, an, bn, Color4(0xFF, 0xFF, 0xFF, 0xFF));
  243. }
  244. r.translateTo(0.0f, 0.0f).update();
  245. r.setStringSize(2);
  246. float y = 10.0f;
  247. y = r.drawString(10.0f, y,
  248. physicsToggle ? "Force Physics" : "Velocity Physics");
  249. StringBuffer<100> s("a = ");
  250. s.append(acceleration).append(" before collision");
  251. y = r.drawString(10.0f, y, s);
  252. s.clear().append("v = ").append(velocity).append(" after collision");
  253. y = r.drawString(10.0f, y, s);
  254. s.clear().append("Ground = ").append(onGround);
  255. y = r.drawString(10.0f, y, s);
  256. }
  257. bool Game::isRunning() {
  258. return !Controller::select.isDown();
  259. }