Guest User

Untitled

a guest
Jan 27th, 2023
50
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 18.48 KB | Source Code | 0 0
  1. #include "precomp.h" // include (only) this in every .cpp file
  2. #include "merge_sort.h"
  3. #include "grid.h"
  4.  
  5. constexpr auto num_tanks_blue = 2048;
  6. constexpr auto num_tanks_red = 2048;
  7.  
  8. constexpr auto tank_max_health = 1000;
  9. constexpr auto rocket_hit_value = 60;
  10. constexpr auto particle_beam_hit_value = 50;
  11.  
  12. constexpr auto tank_max_speed = 1.0;
  13.  
  14. constexpr auto health_bar_width = 70;
  15.  
  16. constexpr auto max_frames = 2000;
  17.  
  18. // Global performance timer
  19. // thuis PC: 312818
  20. // laptop:  
  21. constexpr auto REF_PERFORMANCE = 312818; //UPDATE THIS WITH YOUR REFERENCE PERFORMANCE (see console after 2k frames)
  22. static timer perf_timer;
  23. static float duration;
  24.  
  25. //Load sprite files and initialize sprites
  26. static Surface* tank_red_img = new Surface("assets/Tank_Proj2.png");
  27. static Surface* tank_blue_img = new Surface("assets/Tank_Blue_Proj2.png");
  28. static Surface* rocket_red_img = new Surface("assets/Rocket_Proj2.png");
  29. static Surface* rocket_blue_img = new Surface("assets/Rocket_Blue_Proj2.png");
  30. static Surface* particle_beam_img = new Surface("assets/Particle_Beam.png");
  31. static Surface* smoke_img = new Surface("assets/Smoke.png");
  32. static Surface* explosion_img = new Surface("assets/Explosion.png");
  33.  
  34. static Sprite tank_red(tank_red_img, 12);
  35. static Sprite tank_blue(tank_blue_img, 12);
  36. static Sprite rocket_red(rocket_red_img, 12);
  37. static Sprite rocket_blue(rocket_blue_img, 12);
  38. static Sprite smoke(smoke_img, 4);
  39. static Sprite explosion(explosion_img, 9);
  40. static Sprite particle_beam_sprite(particle_beam_img, 3);
  41.  
  42. const static vec2 tank_size(7, 9);
  43. const static vec2 rocket_size(6, 6);
  44.  
  45. const static float tank_radius = 3.f;
  46. const static float rocket_radius = 5.f;
  47. const int CELL_SIZE = 32;
  48.  
  49. // -----------------------------------------------------------
  50. // Initialize the simulation state
  51. // This function does not count for the performance multiplier
  52. // (Feel free to optimize anyway though ;) )
  53. // -----------------------------------------------------------
  54. void Game::init()
  55. {
  56.     frame_count_font = new Font("assets/digital_small.png", "ABCDEFGHIJKLMNOPQRSTUVWXYZ:?!=-0123456789.");
  57.  
  58.     tanks.reserve(num_tanks_blue + num_tanks_red);
  59.  
  60.     uint max_rows = 24;
  61.  
  62.     float start_blue_x = tank_size.x + 40.0f;
  63.     float start_blue_y = tank_size.y + 30.0f;
  64.  
  65.     float start_red_x = 1088.0f;
  66.     float start_red_y = tank_size.y + 30.0f;
  67.  
  68.     float spacing = 7.5f;
  69.  
  70.     grid = std::make_unique<Grid>(SCRWIDTH, SCRHEIGHT, CELL_SIZE);
  71.  
  72.     // Get amount of threads available on the machine
  73.     unsigned int thread_amount = std::thread::hardware_concurrency();
  74.     cout << "Threads available: " << thread_amount << endl;
  75.     // Make the threadpool
  76.     thread_pool = std::make_unique<ThreadPool>(thread_amount);
  77.  
  78.     //Spawn blue tanks
  79.     for (int i = 0; i < num_tanks_blue; i++)
  80.     {
  81.         vec2 position{ start_blue_x + ((i % max_rows) * spacing), start_blue_y + ((i / max_rows) * spacing) };
  82.         tanks.push_back(Tank(position.x, position.y, BLUE, &tank_blue, &smoke, 1100.f, position.y + 16, tank_radius, tank_max_health, tank_max_speed));
  83.         grid->add_tank(&tanks.back());
  84.     }
  85.     //Spawn red tanks
  86.     for (int i = 0; i < num_tanks_red; i++)
  87.     {
  88.         vec2 position{ start_red_x + ((i % max_rows) * spacing), start_red_y + ((i / max_rows) * spacing) };
  89.         tanks.push_back(Tank(position.x, position.y, RED, &tank_red, &smoke, 100.f, position.y + 16, tank_radius, tank_max_health, tank_max_speed));
  90.         grid->add_tank(&tanks.back());
  91.     }
  92.  
  93.     particle_beams.push_back(Particle_beam(vec2(590, 327), vec2(100, 50), &particle_beam_sprite, particle_beam_hit_value));
  94.     particle_beams.push_back(Particle_beam(vec2(64, 64), vec2(100, 50), &particle_beam_sprite, particle_beam_hit_value));
  95.     particle_beams.push_back(Particle_beam(vec2(1200, 600), vec2(100, 50), &particle_beam_sprite, particle_beam_hit_value));
  96. }
  97.  
  98. // -----------------------------------------------------------
  99. // Close down application
  100. // -----------------------------------------------------------
  101. void Game::shutdown()
  102. {
  103. }
  104.  
  105. // -----------------------------------------------------------
  106. // Iterates through all tanks and returns the closest enemy tank for the given tank
  107. // -----------------------------------------------------------
  108. Tank& Game::find_closest_enemy(Tank& current_tank)
  109. {
  110.     float closest_distance = numeric_limits<float>::infinity();
  111.     int closest_index = 0;
  112.    
  113.     std::vector<Cell*> neighbouring_cells = grid->get_neighbour_cells(&current_tank);
  114.     std::vector<Tank*> neighbouring_enemy_tanks = grid->get_neighbouring_tanks(neighbouring_cells, current_tank.allignment);
  115.  
  116.     if (neighbouring_enemy_tanks.empty()) {
  117.         for (int i = 0; i < tanks.size(); i++)
  118.         {
  119.             if (tanks.at(i).allignment != current_tank.allignment && tanks.at(i).active)
  120.             {
  121.                 float sqr_dist = fabsf((tanks.at(i).get_position() - current_tank.get_position()).sqr_length());
  122.                 if (sqr_dist < closest_distance)
  123.                 {
  124.                     closest_distance = sqr_dist;
  125.                     closest_index = i;
  126.                 }
  127.             }
  128.         }
  129.  
  130.         return tanks.at(closest_index);
  131.     }
  132.     else {
  133.         for (int i = 0; i < neighbouring_enemy_tanks.size(); i++)
  134.         {
  135.             float sqr_dist = fabsf((neighbouring_enemy_tanks.at(i)->get_position() - current_tank.get_position()).sqr_length());
  136.             if (sqr_dist < closest_distance)
  137.             {
  138.                 closest_distance = sqr_dist;
  139.                 closest_index = i;
  140.             }
  141.         }
  142.  
  143.         return *neighbouring_enemy_tanks.at(closest_index);
  144.     }
  145. }
  146.  
  147. //Checks if a point lies on the left of an arbitrary angled line
  148. bool Tmpl8::Game::left_of_line(vec2 line_start, vec2 line_end, vec2 point)
  149. {
  150.     return ((line_end.x - line_start.x) * (point.y - line_start.y) - (line_end.y - line_start.y) * (point.x - line_start.x)) < 0;
  151. }
  152.  
  153.  
  154. void Game::set_routes() {
  155.     if (frame_count == 0)
  156.     {
  157.         for (Tank& t : tanks)
  158.         {
  159.             t.set_route(background_terrain.get_route(t, t.target));
  160.         }
  161.     }
  162. }
  163.  
  164. void Game::check_collision() {
  165.     for (Tank& tank : tanks)
  166.     {
  167.         if (tank.active)
  168.         {
  169.             std::vector<Tank*> nearby_tanks = tank.owner_cell->tanks;
  170.  
  171.             for (int i = 0; i < nearby_tanks.size(); i++) {
  172.                 if (nearby_tanks.at(i) != &tank && nearby_tanks.at(i)->active) {
  173.                     vec2 dir = tank.get_position() - nearby_tanks.at(i)->get_position();
  174.                     float dir_squared_len = dir.sqr_length();
  175.  
  176.                     float col_squared_len = (tank.get_collision_radius() + nearby_tanks.at(i)->get_collision_radius());
  177.                     col_squared_len *= col_squared_len;
  178.  
  179.                     if (dir_squared_len < col_squared_len)
  180.                     {
  181.                         tank.push(dir.normalized(), 1.f);
  182.                     }
  183.                 }
  184.             }
  185.         }
  186.     }
  187. }
  188.  
  189. void Game::update_tanks() {
  190.     for (Tank& tank : tanks)
  191.     {
  192.         if (tank.active)
  193.         {
  194.             //Move tanks according to speed and nudges (see above) also reload
  195.             tank.tick(background_terrain);
  196.  
  197.             //Shoot at closest target if reloaded
  198.             if (tank.rocket_reloaded())
  199.             {
  200.                 Tank& target = find_closest_enemy(tank);
  201.  
  202.                 rockets.push_back(Rocket(tank.position, (target.get_position() - tank.position).normalized() * 3, rocket_radius, tank.allignment, ((tank.allignment == RED) ? &rocket_red : &rocket_blue)));
  203.  
  204.                 tank.reload_rocket();
  205.             }
  206.  
  207.             // Kijken of de tank nog wel in dezelfde cell zit, of dat het geüpdate moet worden
  208.             Cell* new_cell = grid->get_cell(tank.get_position());
  209.             if (new_cell != tank.owner_cell) {
  210.                 // verander cell
  211.                 grid->remove_tank(&tank);
  212.                 grid->add_tank(&tank, new_cell);
  213.             }
  214.         }
  215.     }
  216. }
  217.  
  218. void Game::update_smoke_plumes() {
  219.     for (Smoke& smoke : smokes)
  220.     {
  221.         smoke.tick();
  222.     }
  223. }
  224.  
  225. void Game::calculate_convex_hull() {
  226.     //Calculate "forcefield" around active tanks
  227.     forcefield_hull.clear();
  228.  
  229.     //Find first active tank (this loop is a bit disgusting, fix?)
  230.     int first_active = 0;
  231.     for (Tank& tank : tanks)
  232.     {
  233.         if (tank.active)
  234.         {
  235.             break;
  236.         }
  237.         first_active++;
  238.     }
  239.     vec2 point_on_hull = tanks.at(first_active).position;
  240.     //Find left most tank position
  241.     for (Tank& tank : tanks)
  242.     {
  243.         if (tank.active)
  244.         {
  245.             if (tank.position.x <= point_on_hull.x)
  246.             {
  247.                 point_on_hull = tank.position;
  248.             }
  249.         }
  250.     }
  251.    
  252.     for (Tank& tank : tanks)
  253.     {
  254.         if (tank.active)
  255.         {
  256.             forcefield_hull.push_back(point_on_hull);
  257.             vec2 endpoint = tanks.at(first_active).position;
  258.  
  259.             for (Tank& tank : tanks)
  260.             {
  261.                 if (tank.active)
  262.                 {
  263.                     if ((endpoint == point_on_hull) || left_of_line(point_on_hull, endpoint, tank.position))
  264.                     {
  265.                         endpoint = tank.position;
  266.                     }
  267.                 }
  268.             }
  269.             point_on_hull = endpoint;
  270.  
  271.             if (endpoint == forcefield_hull.at(0))
  272.             {
  273.                 break;
  274.             }
  275.         }
  276.     }
  277. }
  278.  
  279. void Game::update_rockets() {
  280.     for (Rocket& rocket : rockets)
  281.     {
  282.         rocket.tick();
  283.  
  284.         //Check if rocket collides with enemy tank, spawn explosion, and if tank is destroyed spawn a smoke plume
  285.         for (Tank& tank : tanks)
  286.         {
  287.             if (tank.active && (tank.allignment != rocket.allignment) && rocket.intersects(tank.position, tank.collision_radius))
  288.             {
  289.                 explosions.push_back(Explosion(&explosion, tank.position));
  290.  
  291.                 if (tank.hit(rocket_hit_value))
  292.                 {
  293.                     smokes.push_back(Smoke(smoke, tank.position - vec2(7, 24)));
  294.                 }
  295.  
  296.                 rocket.active = false;
  297.                 break;
  298.             }
  299.         }
  300.     }
  301. }
  302.  
  303. void Game::disable_rockets_if_collided() {
  304.     for (Rocket& rocket : rockets)
  305.     {
  306.         if (rocket.active)
  307.         {
  308.             for (size_t i = 0; i < forcefield_hull.size(); i++)
  309.             {
  310.                 if (circle_segment_intersect(forcefield_hull.at(i), forcefield_hull.at((i + 1) % forcefield_hull.size()), rocket.position, rocket.collision_radius))
  311.                 {
  312.                     explosions.push_back(Explosion(&explosion, rocket.position));
  313.                     rocket.active = false;
  314.                 }
  315.             }
  316.         }
  317.     }
  318. }
  319.  
  320. void Game::update_particle_beams() {
  321.     for (Particle_beam& particle_beam : particle_beams)
  322.     {
  323.         particle_beam.tick(tanks);
  324.  
  325.         //Damage all tanks within the damage window of the beam (the window is an axis-aligned bounding box)
  326.         for (Tank& tank : tanks)
  327.         {
  328.             if (tank.active && particle_beam.rectangle.intersects_circle(tank.get_position(), tank.get_collision_radius()))
  329.             {
  330.                 if (tank.hit(particle_beam.damage))
  331.                 {
  332.                     smokes.push_back(Smoke(smoke, tank.position - vec2(0, 48)));
  333.                 }
  334.             }
  335.         }
  336.     }
  337. }
  338.  
  339. void Game::update_explosion_sprites() {
  340.     for (Explosion& explosion : explosions)
  341.     {
  342.         explosion.tick();
  343.     }
  344.  
  345.     explosions.erase(std::remove_if(explosions.begin(), explosions.end(), [](const Explosion& explosion) { return explosion.done(); }), explosions.end());
  346. }
  347.  
  348. // -----------------------------------------------------------
  349. // Update the game state:
  350. // Move all objects
  351. // Update sprite frames
  352. // Collision detection
  353. // Update tanks cell
  354. // Targeting etc..
  355. // -----------------------------------------------------------
  356. void Game::update(float deltaTime)
  357. {
  358.     //Calculate the route to the destination for each tank using BFS
  359.     //Initializing routes here so it gets counted for performance..
  360.     thread_pool->enqueue([this] {set_routes(); });
  361.  
  362.     //Check tank collision and nudge tanks away from each other
  363.     check_collision();
  364.  
  365.     //Update tanks
  366.     update_tanks();
  367.  
  368.     //Update smoke plumes
  369.     update_smoke_plumes();
  370.     //thread_pool->enqueue([this] {update_smoke_plumes(); });
  371.  
  372.     //Calculate convex hull for 'rocket barrier'
  373.     // calculate_convex_hull();
  374.     //thread_pool->enqueue([this] {calculate_convex_hull(); });
  375.  
  376.     //Update rockets
  377.     update_rockets();
  378.  
  379.     //Disable rockets if they collide with the "forcefield"
  380.     //Hint: A point to convex hull intersection test might be better here? :) (Disable if outside)
  381.     disable_rockets_if_collided();
  382.    
  383.     //Remove exploded rockets with remove erase idiom
  384.     rockets.erase(std::remove_if(rockets.begin(), rockets.end(), [](const Rocket& rocket) { return !rocket.active; }), rockets.end());
  385.  
  386.     //Update particle beams
  387.     update_particle_beams();
  388.  
  389.     //Update explosion sprites and remove when done with remove erase idiom
  390.     update_explosion_sprites();
  391. }
  392.  
  393. // -----------------------------------------------------------
  394. // Draw all sprites to the screen
  395. // (It is not recommended to multi-thread this function)
  396. // -----------------------------------------------------------
  397. void Game::draw()
  398. {
  399.     // clear the graphics window
  400.     screen->clear(0);
  401.  
  402.     //Draw background
  403.     background_terrain.draw(screen);
  404.  
  405.     //Draw sprites
  406.     for (int i = 0; i < num_tanks_blue + num_tanks_red; i++)
  407.     {
  408.         tanks.at(i).draw(screen);
  409.  
  410.         vec2 tank_pos = tanks.at(i).get_position();
  411.     }
  412.  
  413.     for (Rocket& rocket : rockets)
  414.     {
  415.         rocket.draw(screen);
  416.     }
  417.  
  418.     for (Smoke& smoke : smokes)
  419.     {
  420.         smoke.draw(screen);
  421.     }
  422.  
  423.     for (Particle_beam& particle_beam : particle_beams)
  424.     {
  425.         particle_beam.draw(screen);
  426.     }
  427.  
  428.     for (Explosion& explosion : explosions)
  429.     {
  430.         explosion.draw(screen);
  431.     }
  432.  
  433.     //Draw forcefield (mostly for debugging, its kinda ugly..)
  434.     for (size_t i = 0; i < forcefield_hull.size(); i++)
  435.     {
  436.         vec2 line_start = forcefield_hull.at(i);
  437.         vec2 line_end = forcefield_hull.at((i + 1) % forcefield_hull.size());
  438.         line_start.x += HEALTHBAR_OFFSET;
  439.         line_end.x += HEALTHBAR_OFFSET;
  440.         screen->line(line_start, line_end, 0x0000ff);
  441.     }
  442.  
  443.     MergeSort sorter = MergeSort();
  444.     //Draw sorted health bars (INSERTION SORT)
  445.     for (int t = 0; t < 2; t++)
  446.     {
  447.         const int NUM_TANKS = ((t < 1) ? num_tanks_blue : num_tanks_red);
  448.  
  449.         const int begin = ((t < 1) ? 0 : num_tanks_blue);
  450.         std::vector<const Tank*> sorted_tanks;
  451.         //insertion_sort_tanks_health(tanks, sorted_tanks, begin, begin + NUM_TANKS);
  452.         sorter.merge_sort_tanks_health(tanks, sorted_tanks, begin, begin + NUM_TANKS);
  453.         sorted_tanks.erase(std::remove_if(sorted_tanks.begin(), sorted_tanks.end(), [](const Tank* tank) { return !tank->active; }), sorted_tanks.end());
  454.  
  455.         draw_health_bars(sorted_tanks, t);
  456.     }
  457. }
  458.  
  459.  
  460. // -----------------------------------------------------------
  461. // Draw the health bars based on the given tanks health values
  462. // -----------------------------------------------------------
  463. void Tmpl8::Game::draw_health_bars(const std::vector<const Tank*>& sorted_tanks, const int team)
  464. {
  465.     int health_bar_start_x = (team < 1) ? 0 : (SCRWIDTH - HEALTHBAR_OFFSET) - 1;
  466.     int health_bar_end_x = (team < 1) ? health_bar_width : health_bar_start_x + health_bar_width - 1;
  467.  
  468.     for (int i = 0; i < SCRHEIGHT - 1; i++)
  469.     {
  470.         //Health bars are 1 pixel each
  471.         int health_bar_start_y = i * 1;
  472.         int health_bar_end_y = health_bar_start_y + 1;
  473.  
  474.         screen->bar(health_bar_start_x, health_bar_start_y, health_bar_end_x, health_bar_end_y, REDMASK);
  475.     }
  476.  
  477.     //Draw the <SCRHEIGHT> least healthy tank health bars
  478.     int draw_count = std::min(SCRHEIGHT, (int)sorted_tanks.size());
  479.     for (int i = 0; i < draw_count - 1; i++)
  480.     {
  481.         //Health bars are 1 pixel each
  482.         int health_bar_start_y = i * 1;
  483.         int health_bar_end_y = health_bar_start_y + 1;
  484.  
  485.         float health_fraction = (1 - ((float)sorted_tanks.at(i)->health / (float)tank_max_health));
  486.  
  487.         if (team == 0) { screen->bar(health_bar_start_x + (int)((double)health_bar_width * health_fraction), health_bar_start_y, health_bar_end_x, health_bar_end_y, GREENMASK); }
  488.         else { screen->bar(health_bar_start_x, health_bar_start_y, health_bar_end_x - (int)((double)health_bar_width * health_fraction), health_bar_end_y, GREENMASK); }
  489.     }
  490. }
  491.  
  492. // -----------------------------------------------------------
  493. // When we reach max_frames print the duration and speedup multiplier
  494. // Updating REF_PERFORMANCE at the top of this file with the value
  495. // on your machine gives you an idea of the speedup your optimizations give
  496. // -----------------------------------------------------------
  497. void Tmpl8::Game::measure_performance()
  498. {
  499.     char buffer[128];
  500.     if (frame_count >= max_frames)
  501.     {
  502.         if (!lock_update)
  503.         {
  504.             duration = perf_timer.elapsed();
  505.             cout << "Duration was: " << duration << " (Replace REF_PERFORMANCE with this value)" << endl;
  506.             lock_update = true;
  507.         }
  508.  
  509.         frame_count--;
  510.     }
  511.  
  512.     if (lock_update)
  513.     {
  514.         screen->bar(420 + HEALTHBAR_OFFSET, 170, 870 + HEALTHBAR_OFFSET, 430, 0x030000);
  515.         int ms = (int)duration % 1000, sec = ((int)duration / 1000) % 60, min = ((int)duration / 60000);
  516.         sprintf(buffer, "%02i:%02i:%03i", min, sec, ms);
  517.         frame_count_font->centre(screen, buffer, 200);
  518.         sprintf(buffer, "SPEEDUP: %4.1f", REF_PERFORMANCE / duration);
  519.         frame_count_font->centre(screen, buffer, 340);
  520.     }
  521. }
  522.  
  523. // -----------------------------------------------------------
  524. // Main application tick function
  525. // -----------------------------------------------------------
  526. void Game::tick(float deltaTime)
  527. {
  528.     if (!lock_update)
  529.     {
  530.         update(deltaTime);
  531.     }
  532.     draw();
  533.  
  534.     measure_performance();
  535.  
  536.     // print something in the graphics window
  537.     //screen->Print("hello world", 2, 2, 0xffffff);
  538.  
  539.     // print something to the text window
  540.     //cout << "This goes to the console window." << std::endl;
  541.  
  542.     //Print frame count
  543.     frame_count++;
  544.     string frame_count_string = "FRAME: " + std::to_string(frame_count);
  545.     frame_count_font->print(screen, frame_count_string.c_str(), 350, 580);
  546. }
  547.  
Advertisement
Add Comment
Please, Sign In to add comment