Game.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  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. Color4 color;
  10. float friction;
  11. Line(const Vector2& a, const Vector2& b, Color4 color, float friction)
  12. : a(a), b(b), color(color), friction(friction) {
  13. }
  14. Line(const Vector2& a, const Vector2& b)
  15. : a(a), b(b), color(Color4(0xFF, 0xFF, 0xFF, 0xFF)), friction(0.0f) {
  16. }
  17. };
  18. static List<Line> lines;
  19. static constexpr float SPEED = 2.5f;
  20. static constexpr float GRAVITY = 0.5f;
  21. static constexpr float COLLISION_STEP = 0.005f;
  22. static Vector2 windowSize;
  23. static Vector2 lastPosition;
  24. static Vector2 position{150.0f, 50.0f};
  25. static Vector2 size{50.0f, 50.0f};
  26. static Vector2 velocity;
  27. static Vector2 acceleration;
  28. static Vector2 drag;
  29. static float mass = 5.0f;
  30. static bool onGround = false;
  31. static float jumpPower = 0.0f;
  32. static float steepness = 0.0f;
  33. static float friction = 0.0f;
  34. static bool physicsToggle = true;
  35. static Vector2 waterPosition;
  36. static Vector2 waterSize;
  37. static Vector2 projectileLastPosition;
  38. static Vector2 projectilePosition;
  39. static Vector2 projectileVelocity;
  40. static Vector2 projectileSize{20.0f, 20.0f};
  41. static float lastAngle;
  42. static float angle;
  43. static float angleVelocity = 5.0f;
  44. static void addForce(const Vector2& force) {
  45. acceleration += force / mass;
  46. }
  47. static void addMovement() {
  48. physicsToggle = physicsToggle ^ Controller::start.wasReleased();
  49. float movement = Controller::right.isDown() - Controller::left.isDown();
  50. float actualSpeed = SPEED * (1.0f - std::abs(steepness) * onGround);
  51. if(physicsToggle) {
  52. addForce(Vector2(actualSpeed, 0.0f) * movement);
  53. } else {
  54. // movement by velocity should have the same terminal velocity as
  55. // movement by force
  56. float terminalFactor = 1.0f / ((1.0 - drag[0]) * drag[0]);
  57. velocity[0] = (actualSpeed / mass) * terminalFactor * movement;
  58. }
  59. }
  60. static bool isIn(const Vector2& posA, const Vector2& sizeA, const Vector2& posB,
  61. const Vector2& sizeB) {
  62. Vector2 maxA = posA + sizeA;
  63. Vector2 maxB = posB + sizeB;
  64. return posA[0] < maxB[0] && maxA[0] > posB[0] && posA[1] < maxB[1] &&
  65. maxA[1] > posB[1];
  66. }
  67. float ERROR = 1.0f / 512.0f;
  68. static bool isBetween(float y, float y1, float y2) {
  69. return y >= std::min(y1, y2) && y <= std::max(y1, y2);
  70. }
  71. static bool compareFloats(float a, float b) {
  72. return std::abs(a - b) < ERROR;
  73. }
  74. static bool intersect(float x11, float y11, float x12, float y12, float x21,
  75. float y21, float x22, float y22) {
  76. if(compareFloats(x11, x12)) {
  77. if(compareFloats(x21, x22)) {
  78. return false;
  79. } else {
  80. if(!isBetween(x11, x21, x22)) {
  81. return false;
  82. }
  83. float k = (y21 - y22) / (x21 - x22);
  84. float d = y22 - k * x22;
  85. float y = d + x11 * k;
  86. return isBetween(y, y11, y12) && isBetween(y, y21, y22);
  87. }
  88. } else {
  89. if(compareFloats(x21, x22)) {
  90. if(!isBetween(x21, x11, x12)) {
  91. return false;
  92. }
  93. float k = (y11 - y12) / (x11 - x12);
  94. float d = y12 - k * x12;
  95. float y = d + x21 * k;
  96. return isBetween(y, y11, y12) && isBetween(y, y21, y22);
  97. } else {
  98. float k1 = (y11 - y12) / (x11 - x12);
  99. float k2 = (y21 - y22) / (x21 - x22);
  100. if(compareFloats(k1, k2)) {
  101. return false;
  102. }
  103. float d1 = y12 - k1 * x12;
  104. float d2 = y22 - k2 * x22;
  105. float x = (d1 - d2) / (k2 - k1);
  106. if(!isBetween(x, x11, x12) || !isBetween(x, x21, x22)) {
  107. return false;
  108. }
  109. float y = k1 * x + d1;
  110. return isBetween(y, y11, y12) && isBetween(y, y21, y22);
  111. }
  112. }
  113. }
  114. static bool areColliding(const Line& a, const Line& b) {
  115. return intersect(a.a[0], a.a[1], a.b[0], a.b[1], b.a[0], b.a[1], b.b[0],
  116. b.b[1]);
  117. }
  118. static bool doesPlayerCollide() {
  119. Vector2 posX = position + Vector2(size[0], 0.0f);
  120. Vector2 posY = position + Vector2(0.0f, size[1]);
  121. Vector2 posXY = position + size;
  122. for(const Line& line : lines) {
  123. if(areColliding(line, {position, posX}) ||
  124. areColliding(line, {position, posY}) ||
  125. areColliding(line, {posX, posXY}) ||
  126. areColliding(line, {posY, posXY})) {
  127. return true;
  128. }
  129. }
  130. return false;
  131. }
  132. static void slopeSteepnessAndFriction() {
  133. Line left{position - Vector2(0.0f, 10.0f * COLLISION_STEP),
  134. position + Vector2(0.0f, size[1])};
  135. Line right{position + Vector2(size[0], -10.0f * COLLISION_STEP),
  136. position + size};
  137. float min = 100.0f;
  138. float value = 100.0f;
  139. for(const Line& line : lines) {
  140. if(areColliding(line, left) || areColliding(line, right)) {
  141. float f = (line.a[1] - line.b[1]) / (line.a[0] - line.b[0]);
  142. if(std::abs(f) < min) {
  143. min = std::abs(f);
  144. value = f;
  145. friction = line.friction;
  146. }
  147. }
  148. }
  149. steepness = value;
  150. }
  151. static void applyDrag() {
  152. waterPosition = windowSize * Vector2(0.5f, 0.0f);
  153. waterSize = windowSize * Vector2(0.5f, 0.125f);
  154. drag = Vector2(0.1f, 0.1f);
  155. if(isIn(position, size, waterPosition, waterSize)) {
  156. drag *= 4.0f;
  157. }
  158. acceleration -= velocity * drag;
  159. }
  160. static void applyGravity() {
  161. acceleration -= Vector2(0.0f, GRAVITY);
  162. }
  163. static void handleJump() {
  164. bool justJumped = false;
  165. if(Controller::a.isDown() && onGround) {
  166. jumpPower = 15.0f;
  167. onGround = false;
  168. justJumped = true;
  169. }
  170. addForce(Vector2(0.0f, jumpPower));
  171. jumpPower *= 0.9f;
  172. jumpPower *= Controller::a.isDown() && (velocity[1] > 0.0f || justJumped);
  173. }
  174. static void relaunchProjectile() {
  175. if(projectilePosition[1] >= 0.0f &&
  176. !isIn(position, size, projectilePosition, projectileSize)) {
  177. return;
  178. }
  179. projectilePosition = windowSize * Vector2(0.0f, 0.5f);
  180. projectileLastPosition = projectilePosition;
  181. Vector2 start = projectilePosition;
  182. Vector2 end = position + size * 0.5f;
  183. constexpr float strengh = 25.0f;
  184. float best = 100000000.0f;
  185. float bestRad = 0.0f;
  186. for(float angle = -30.0f; angle < 90.0f; angle += 0.25f) {
  187. float rad = angle * 0.017453293f;
  188. Vector2 v(strengh * cosf(rad), strengh * sinf(rad));
  189. float t = (end[0] - start[0]) / v[0];
  190. float y = v[1] * t - GRAVITY * t * t * 0.5f + start[1];
  191. float diff = std::abs(y - end[1]);
  192. if(diff < best) {
  193. bestRad = rad;
  194. best = diff;
  195. }
  196. }
  197. projectileVelocity =
  198. Vector2(strengh * cosf(bestRad), strengh * sinf(bestRad));
  199. }
  200. static float sign(float f) {
  201. return (f > 0.0f) - (f < 0.0f);
  202. }
  203. static void applyFriction() {
  204. if(!onGround) {
  205. return;
  206. }
  207. float rad = atanf(steepness);
  208. float c = cosf(rad);
  209. float fnLength = mass * GRAVITY * c;
  210. Vector2 fn(fnLength * c, fnLength * sinf(rad)); // already swapt
  211. Vector2 fullFriction = fn * friction;
  212. fullFriction =
  213. Vector2(std::abs(fullFriction[0]), std::abs(fullFriction[1]));
  214. float signX = sign(acceleration[0]);
  215. float signY = sign(acceleration[1]);
  216. fullFriction *= -Vector2(signX, signY);
  217. addForce(fullFriction);
  218. if(sign(acceleration[0]) != signX) {
  219. acceleration[0] = 0.0f;
  220. }
  221. if(sign(acceleration[1]) != signY) {
  222. acceleration[1] = 0.0f;
  223. }
  224. }
  225. static void addLines() {
  226. lines.clear();
  227. lines.add({{0.0f, 0.0f}, windowSize * Vector2(1.0f, 0.0f)});
  228. lines.add({{0.0f, 0.0f}, windowSize * Vector2(0.0f, 1.0f)});
  229. lines.add({windowSize, windowSize * Vector2(1.0f, 0.0f)});
  230. lines.add(
  231. {windowSize * Vector2(0.75f, 0.0f), windowSize * Vector2(1.0f, 0.5f)});
  232. lines.add({{0.0f, 30.0f}, {400.0f, 0.0f}});
  233. lines.add({{0.0f, 60.0f}, {300.0f, 0.0f}});
  234. lines.add({{0.0f, 90.0f}, {200.0f, 0.0f}});
  235. lines.add({{0.0f, 120.0f}, {100.0f, 0.0f}});
  236. lines.add({{100.0f, 180.0f}, {200.0f, 180.0f}});
  237. lines.add({{200.0f, 180.0f},
  238. {300.0f, 230.0f},
  239. Color4(0xF0, 0x00, 0x00, 0xFF),
  240. 0.2f});
  241. lines.add({{300.0f, 230.0f},
  242. {400.0f, 280.0f},
  243. Color4(0xC0, 0x00, 0x00, 0xFF),
  244. 0.4f});
  245. lines.add({{400.0f, 280.0f},
  246. {500.0f, 330.0f},
  247. Color4(0x90, 0x00, 0x00, 0xFF),
  248. 0.6f});
  249. lines.add({{500.0f, 330.0f},
  250. {600.0f, 380.0f},
  251. Color4(0x60, 0x00, 0x00, 0xFF),
  252. 0.8f});
  253. lines.add({{300.0f, 140.0f},
  254. {400.0f, 200.0f},
  255. Color4(0xF0, 0x00, 0x00, 0xFF),
  256. 0.2f});
  257. lines.add({{400.0f, 200.0f},
  258. {500.0f, 220.0f},
  259. Color4(0xC0, 0x00, 0x00, 0xFF),
  260. 0.4f});
  261. lines.add({{500.0f, 220.0f},
  262. {600.0f, 240.0f},
  263. Color4(0x90, 0x00, 0x00, 0xFF),
  264. 0.6f});
  265. lines.add({{600.0f, 240.0f},
  266. {700.0f, 260.0f},
  267. Color4(0x60, 0x00, 0x00, 0xFF),
  268. 0.8f});
  269. lines.add({{600.0f, 380.0f}, {1000.0f, 580.0f}});
  270. }
  271. void Game::tick() {
  272. windowSize = Vector2(static_cast<float>(Engine::getSize().width),
  273. static_cast<float>(Engine::getSize().height));
  274. lastPosition = position;
  275. acceleration = Vector2();
  276. applyGravity();
  277. addMovement();
  278. handleJump();
  279. applyFriction();
  280. applyDrag();
  281. if(steepness != 100.0f) {
  282. if(steepness < -1.0f) {
  283. acceleration[0] += 0.1f;
  284. } else if(steepness > 1.0f) {
  285. acceleration[0] -= 0.1f;
  286. }
  287. }
  288. addLines();
  289. velocity += acceleration;
  290. onGround = false;
  291. Vector2 energy = velocity;
  292. while(energy[0] != 0.0f || energy[1] != 0.0f) {
  293. for(int i = 0; i < 2; i++) {
  294. if(energy[i] != 0.0f) {
  295. float old = position[i];
  296. if(energy[i] > COLLISION_STEP) {
  297. position[i] += COLLISION_STEP;
  298. energy[i] -= COLLISION_STEP;
  299. } else if(energy[i] < -COLLISION_STEP) {
  300. position[i] -= COLLISION_STEP;
  301. energy[i] += COLLISION_STEP;
  302. } else {
  303. position[i] += energy[i];
  304. energy[i] = 0.0f;
  305. }
  306. if(doesPlayerCollide()) {
  307. if(i == 0) {
  308. float oldY = position[1];
  309. position[1] += COLLISION_STEP;
  310. if(!doesPlayerCollide()) {
  311. continue;
  312. }
  313. position[1] = oldY;
  314. }
  315. energy[i] = 0.0f;
  316. velocity[i] = 0.0f;
  317. position[i] = old;
  318. }
  319. }
  320. }
  321. }
  322. float oldSteepness = steepness;
  323. slopeSteepnessAndFriction();
  324. onGround = std::abs(steepness) < 1.0f;
  325. if(onGround) {
  326. velocity[1] = 0.0f;
  327. }
  328. if(steepness == 100.0f) {
  329. steepness = oldSteepness;
  330. }
  331. projectileLastPosition = projectilePosition;
  332. projectileVelocity -= Vector2(0.0f, GRAVITY);
  333. projectilePosition += projectileVelocity;
  334. relaunchProjectile();
  335. lastAngle = angle;
  336. angle += angleVelocity;
  337. }
  338. void Game::render(float lag, Renderer& r) {
  339. r.translateTo(0.0f, 0.0f)
  340. .scale(1.0f, -1.0f)
  341. .translateY(Engine::getSize().height)
  342. .update();
  343. r.drawRectangle(waterPosition, waterSize, Color4(0x00, 0x00, 0xFF, 0xFF));
  344. Vector2 pos = Utils::interpolate(lastPosition, position, lag);
  345. r.push();
  346. r.translateTo(0.0f, 0.0f)
  347. .translate(-size * 0.5f)
  348. .rotate(atanf(steepness) * 180.0f / M_PI)
  349. .translate(pos)
  350. .translate(size * 0.5f)
  351. .scale(1.0f, -1.0f)
  352. .translateY(Engine::getSize().height)
  353. .update();
  354. r.drawRectangle(Vector2(), size, Color4(0xFF, 0x00, 0x00, 0xFF));
  355. r.pop();
  356. r.update();
  357. for(int i = 0; i < 360; i += 120) {
  358. r.push();
  359. r.translateTo(0.0f, 0.0f)
  360. .translate(size * 0.75f)
  361. .rotate(Utils::interpolate(lastAngle, angle, lag) + i)
  362. .translate(pos + size * 0.5f)
  363. .scale(1.0f, -1.0f)
  364. .translateY(Engine::getSize().height)
  365. .update();
  366. r.drawRectangle(Vector2(), size * 0.25f,
  367. Color4(0xFF, 0x00, 0xFF, 0xFF));
  368. r.pop();
  369. r.update();
  370. }
  371. r.drawRectangle(
  372. Utils::interpolate(projectileLastPosition, projectilePosition, lag),
  373. projectileSize, Color4(0xFF, 0xFF, 0x00, 0xFF));
  374. for(const Line& line : lines) {
  375. Vector2 dir = line.b - line.a;
  376. Vector2 normal(dir[1], -dir[0]);
  377. normal.normalize();
  378. normal *= 2.0f;
  379. Vector2 ap = line.a + normal;
  380. Vector2 bp = line.b + normal;
  381. Vector2 an = line.a - normal;
  382. Vector2 bn = line.b - normal;
  383. r.drawTriangle(ap, bp, an, line.color);
  384. r.drawTriangle(bp, an, bn, line.color);
  385. }
  386. r.translateTo(0.0f, 0.0f).update();
  387. r.setStringSize(2);
  388. float y = 10.0f;
  389. y = r.drawString(10.0f, y,
  390. physicsToggle ? "Force Physics" : "Velocity Physics");
  391. StringBuffer<100> s("a = ");
  392. s.append(acceleration).append(" before collision");
  393. y = r.drawString(10.0f, y, s);
  394. s.clear().append("v = ").append(velocity).append(" after collision");
  395. y = r.drawString(10.0f, y, s);
  396. s.clear().append("Ground = ").append(onGround);
  397. y = r.drawString(10.0f, y, s);
  398. s.clear().append("Angle = ").append(atanf(steepness) * 57.295779513f);
  399. y = r.drawString(10.0f, y, s);
  400. s.clear().append("Friction = ").append(friction);
  401. y = r.drawString(10.0f, y, s);
  402. }
  403. bool Game::isRunning() {
  404. return !Controller::select.isDown();
  405. }