Advertisement
alien_fx_fiend

Asteroids Game GDI-Based (Preliminary Requires 0-Flicker)

Nov 1st, 2024
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.52 KB | Source Code | 0 0
  1. #include <windows.h>
  2. #include <vector>
  3. #include <cmath>
  4. #include <ctime>
  5. #include <algorithm>  // Include this header for std::min and std::max
  6.  
  7. #define WIN_WIDTH 800
  8. #define WIN_HEIGHT 600
  9. // Add with other global variables
  10. bool keyStates[256] = { false };  // Track key states
  11.  
  12. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  13. void DrawTriangle(HDC hdc, POINT pts[], int x, int y, double angle);
  14. void DrawAsteroid(HDC hdc, POINT pts[], int x, int y, double angle);
  15. bool CheckCollision(POINT player[], POINT asteroid[], int px, int py, int ax, int ay);
  16. void MoveAsteroids();
  17. void ShootBullet();
  18. void DrawBullet(HDC hdc, int x, int y);
  19. bool CheckBulletCollision(int bx, int by);
  20. bool DoLinesIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
  21. int Direction(int x1, int y1, int x2, int y2, int x3, int y3);
  22. bool OnSegment(int x1, int y1, int x2, int y2, int x3, int y3);
  23. bool PointInPolygon(int px, int py, POINT polygon[], int ox, int oy);
  24. void GenerateAsteroids();
  25. void SpawnNewAsteroid();
  26. void UpdatePlayerMovement();
  27.  
  28. struct Asteroid {
  29.     int x, y;
  30.     double angle;
  31.     POINT shape[8];
  32. };
  33.  
  34. struct Bullet {
  35.     int x, y;
  36.     double angle;
  37. };
  38.  
  39. std::vector<Asteroid> asteroids;
  40. std::vector<Bullet> bullets;
  41. POINT player[3] = { {0, -30}, {20, 20}, {-20, 20} };
  42. int playerX = WIN_WIDTH / 2, playerY = WIN_HEIGHT / 2;
  43. double playerAngle = 0;
  44. int score = 0;
  45. int lives = 3;
  46.  
  47. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  48.     WNDCLASSEX wcex;
  49.     HWND hwnd;
  50.     MSG msg;
  51.  
  52.     wcex.cbSize = sizeof(WNDCLASSEX);
  53.     wcex.style = CS_HREDRAW | CS_VREDRAW;
  54.     wcex.lpfnWndProc = WndProc;
  55.     wcex.cbClsExtra = 0;
  56.     wcex.cbWndExtra = 0;
  57.     wcex.hInstance = hInstance;
  58.     wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  59.     wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  60.     wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  61.     wcex.lpszMenuName = NULL;
  62.     wcex.lpszClassName = TEXT("Asteroids");
  63.     wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
  64.  
  65.     RegisterClassEx(&wcex);
  66.  
  67.     hwnd = CreateWindow(TEXT("Asteroids"), TEXT("Asteroids"), WS_OVERLAPPEDWINDOW,
  68.         CW_USEDEFAULT, 0, WIN_WIDTH, WIN_HEIGHT, NULL, NULL, hInstance, NULL);
  69.  
  70.     ShowWindow(hwnd, nCmdShow);
  71.     UpdateWindow(hwnd);
  72.  
  73.     srand(static_cast<unsigned int>(time(0)));
  74.  
  75.     GenerateAsteroids();
  76.  
  77.     // Main game loop
  78.     // Then modify the main game loop in WinMain:
  79.     while (true) {
  80.         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  81.             if (msg.message == WM_QUIT) break;
  82.             TranslateMessage(&msg);
  83.             DispatchMessage(&msg);
  84.         }
  85.         else {
  86.             UpdatePlayerMovement();  // Add this line
  87.             // Add this collision check right after
  88.             for (auto& ast : asteroids) {
  89.                 if (CheckCollision(player, ast.shape, playerX, playerY, ast.x, ast.y)) {
  90.                     lives--;
  91.                     if (lives <= 0) {
  92.                         MessageBox(hwnd, TEXT("Game Over!"), TEXT("Asteroids"), MB_OK);
  93.                         PostQuitMessage(0);
  94.                     }
  95.                     playerX = WIN_WIDTH / 2;
  96.                     playerY = WIN_HEIGHT / 2;
  97.                     playerAngle = 0;
  98.                     // Reset movement
  99.                     memset(keyStates, 0, sizeof(keyStates));
  100.                 }
  101.             }
  102.             // Add random asteroid spawning
  103.             if (rand() % 100 < 2) { // 2% chance each frame to spawn a new asteroid
  104.                 SpawnNewAsteroid();
  105.             }
  106.  
  107.             MoveAsteroids();
  108.             for (auto it = bullets.begin(); it != bullets.end();) {
  109.                 it->x += 10 * cos(it->angle * 3.14159 / 180);
  110.                 it->y += 10 * sin(it->angle * 3.14159 / 180);
  111.  
  112.                 if (CheckBulletCollision(it->x, it->y)) {
  113.                     it = bullets.erase(it);
  114.                 }
  115.                 else {
  116.                     ++it;
  117.                 }
  118.             }
  119.             InvalidateRect(hwnd, NULL, TRUE);
  120.             Sleep(16); // ~60 FPS
  121.         }
  122.     }
  123.  
  124.     return (int)msg.wParam;
  125. }
  126.  
  127. /* LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  128.     HDC hdc;
  129.     PAINTSTRUCT ps;
  130.     RECT rect;
  131.     static TCHAR scoreText[50];
  132.  
  133.     switch (msg) {
  134.     case WM_PAINT:
  135.     {
  136.         hdc = BeginPaint(hwnd, &ps);
  137.         GetClientRect(hwnd, &rect);
  138.  
  139.         // Clear screen
  140.         //FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));
  141.         // Replace the FillRect line in WM_PAINT
  142.         HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
  143.         FillRect(hdc, &rect, blackBrush);
  144.         DeleteObject(blackBrush);
  145.  
  146.         // Draw player
  147.         DrawTriangle(hdc, player, playerX, playerY, playerAngle);
  148.  
  149.         // Draw asteroids
  150.         for (auto& ast : asteroids) {
  151.             DrawAsteroid(hdc, ast.shape, ast.x, ast.y, ast.angle);
  152.         }
  153.  
  154.         // Draw bullets
  155.         for (auto& bullet : bullets) {
  156.             DrawBullet(hdc, bullet.x, bullet.y);
  157.         }
  158.  
  159.         // Draw score and lives
  160.         wsprintf(scoreText, TEXT("Score: %d  Lives: %d"), score, lives);
  161.         // Before TextOut call
  162.         SetTextColor(hdc, RGB(255, 255, 255));
  163.         SetBkMode(hdc, TRANSPARENT);
  164.         TextOut(hdc, 10, 10, scoreText, lstrlen(scoreText));
  165.  
  166.         EndPaint(hwnd, &ps);
  167.     }
  168.         break;
  169.  
  170.     case WM_KEYDOWN:
  171.         keyStates[wParam] = true;
  172.         break; */ //USEFUL FUNCTION
  173.         /*        switch (wParam) { //deprecated code block
  174.                 case VK_LEFT:
  175.                     playerAngle -= 5;
  176.                     break;
  177.                 case VK_RIGHT:
  178.                     playerAngle += 5;
  179.                     break;
  180.                 case VK_UP:
  181.                     // Calculate movement from the tip of the triangle
  182.                     playerX += 5 * cos((playerAngle - 90) * 3.14159 / 180);
  183.                     playerY += 5 * sin((playerAngle - 90) * 3.14159 / 180);
  184.                     break;
  185.                 case VK_SPACE:
  186.                     ShootBullet();
  187.                     break;
  188.                 }
  189.         */
  190.         //USEFUL UNCOMMENT
  191.     /*case WM_KEYUP:
  192.         keyStates[wParam] = false;
  193.         break;
  194.  
  195.         // Wrap around screen
  196.         if (playerX < 0) playerX = WIN_WIDTH;
  197.         if (playerX > WIN_WIDTH) playerX = 0;
  198.         if (playerY < 0) playerY = WIN_HEIGHT;
  199.         if (playerY > WIN_HEIGHT) playerY = 0;
  200.  
  201.         // Check for collisions
  202.         for (auto& ast : asteroids) {
  203.             if (CheckCollision(player, ast.shape, playerX, playerY, ast.x, ast.y)) {
  204.                 lives--;
  205.                 if (lives <= 0) {
  206.                     MessageBox(hwnd, TEXT("Game Over!"), TEXT("Asteroids"), MB_OK);
  207.                     PostQuitMessage(0);
  208.                 }
  209.                 playerX = WIN_WIDTH / 2;
  210.                 playerY = WIN_HEIGHT / 2;
  211.                 playerAngle = 0;
  212.             }
  213.         }
  214.  
  215.         InvalidateRect(hwnd, NULL, TRUE);
  216.         break;
  217.  
  218.     case WM_DESTROY:
  219.         PostQuitMessage(0);
  220.         break;
  221.  
  222.     default:
  223.         return DefWindowProc(hwnd, msg, wParam, lParam);
  224.     }
  225.  
  226.     return 0;
  227. }*/
  228.  
  229. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  230.     HDC hdc;
  231.     PAINTSTRUCT ps;
  232.     RECT rect;
  233.     static TCHAR scoreText[50];
  234.  
  235.     switch (msg) {
  236.     case WM_PAINT:
  237.     {
  238.         hdc = BeginPaint(hwnd, &ps);
  239.         GetClientRect(hwnd, &rect);
  240.  
  241.         HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
  242.         FillRect(hdc, &rect, blackBrush);
  243.         DeleteObject(blackBrush);
  244.  
  245.         // Draw player
  246.         DrawTriangle(hdc, player, playerX, playerY, playerAngle);
  247.  
  248.         // Draw asteroids
  249.         for (auto& ast : asteroids) {
  250.             DrawAsteroid(hdc, ast.shape, ast.x, ast.y, ast.angle);
  251.         }
  252.  
  253.         // Draw bullets
  254.         for (auto& bullet : bullets) {
  255.             DrawBullet(hdc, bullet.x, bullet.y);
  256.         }
  257.  
  258.         // Draw score and lives
  259.         SetTextColor(hdc, RGB(255, 255, 255));
  260.         SetBkMode(hdc, TRANSPARENT);
  261.         wsprintf(scoreText, TEXT("Score: %d  Lives: %d"), score, lives);
  262.         TextOut(hdc, 10, 10, scoreText, lstrlen(scoreText));
  263.  
  264.         EndPaint(hwnd, &ps);
  265.     }
  266.     break;
  267.  
  268.     case WM_KEYDOWN:
  269.         keyStates[wParam] = true;
  270.         break;
  271.  
  272.     case WM_KEYUP:
  273.         keyStates[wParam] = false;
  274.         break;
  275.  
  276.     case WM_DESTROY:
  277.         PostQuitMessage(0);
  278.         break;
  279.  
  280.     default:
  281.         return DefWindowProc(hwnd, msg, wParam, lParam);
  282.     }
  283.     return 0;
  284. }
  285.  
  286. void DrawTriangle(HDC hdc, POINT pts[], int x, int y, double angle) {
  287.     POINT rotated[3];
  288.     for (int i = 0; i < 3; ++i) {
  289.         rotated[i].x = pts[i].x * cos(angle * 3.14159 / 180) - pts[i].y * sin(angle * 3.14159 / 180);
  290.         rotated[i].y = pts[i].x * sin(angle * 3.14159 / 180) + pts[i].y * cos(angle * 3.14159 / 180);
  291.         rotated[i].x += x;
  292.         rotated[i].y += y;
  293.     }
  294.     // In DrawTriangle function, before Polygon call
  295.     HPEN hotPinkPen = CreatePen(PS_SOLID, 1, RGB(255, 20, 147));
  296.     HBRUSH hotPinkBrush = CreateSolidBrush(RGB(255, 20, 147));
  297.     SelectObject(hdc, hotPinkPen);
  298.     SelectObject(hdc, hotPinkBrush);
  299.     Polygon(hdc, rotated, 3);
  300.     DeleteObject(hotPinkPen);
  301.     DeleteObject(hotPinkBrush);
  302. }
  303.  
  304. void DrawAsteroid(HDC hdc, POINT pts[], int x, int y, double angle) {
  305.     POINT rotated[8];
  306.     for (int i = 0; i < 8; ++i) {
  307.         rotated[i].x = pts[i].x * cos(angle * 3.14159 / 180) - pts[i].y * sin(angle * 3.14159 / 180);
  308.         rotated[i].y = pts[i].x * sin(angle * 3.14159 / 180) + pts[i].y * cos(angle * 3.14159 / 180);
  309.         rotated[i].x += x;
  310.         rotated[i].y += y;
  311.     }
  312.  
  313.     // In DrawAsteroid function, before Polygon call
  314.     HPEN goldPen = CreatePen(PS_SOLID, 1, RGB(255, 215, 0));
  315.     HBRUSH goldBrush = CreateSolidBrush(RGB(255, 215, 0));
  316.     SelectObject(hdc, goldPen);
  317.     SelectObject(hdc, goldBrush);
  318.     Polygon(hdc, rotated, 8);
  319.     DeleteObject(goldPen);
  320.     DeleteObject(goldBrush);
  321. }
  322.  
  323. void UpdatePlayerMovement() {
  324.     if (keyStates[VK_LEFT]) {
  325.         playerAngle -= 5;
  326.     }
  327.     if (keyStates[VK_RIGHT]) {
  328.         playerAngle += 5;
  329.     }
  330.     if (keyStates[VK_UP]) {
  331.         playerX += 5 * cos((playerAngle - 90) * 3.14159 / 180);
  332.         playerY += 5 * sin((playerAngle - 90) * 3.14159 / 180);
  333.     }
  334.     // Add to UpdatePlayerMovement() function
  335.     if (keyStates[VK_DOWN]) {
  336.         playerX -= 5 * cos((playerAngle - 90) * 3.14159 / 180);
  337.         playerY -= 5 * sin((playerAngle - 90) * 3.14159 / 180);
  338.     }
  339.     // Add to UpdatePlayerMovement() function
  340.     static int shootCooldown = 0;
  341.     if (keyStates[VK_SPACE] && shootCooldown == 0) {
  342.         ShootBullet();
  343.         shootCooldown = 10; // Adjust this value to control firing rate
  344.     }
  345.     if (shootCooldown > 0) {
  346.         shootCooldown--;
  347.     }
  348.  
  349.     // Wrap around screen
  350.     if (playerX < 0) playerX = WIN_WIDTH;
  351.     if (playerX > WIN_WIDTH) playerX = 0;
  352.     if (playerY < 0) playerY = WIN_HEIGHT;
  353.     if (playerY > WIN_HEIGHT) playerY = 0;
  354. }
  355.  
  356. bool CheckCollision(POINT player[], POINT asteroid[], int px, int py, int ax, int ay) {
  357.     for (int i = 0; i < 3; ++i) {
  358.         int px1 = player[i].x + px;
  359.         int py1 = player[i].y + py;
  360.         int px2 = player[(i + 1) % 3].x + px;
  361.         int py2 = player[(i + 1) % 3].y + py;
  362.  
  363.         for (int j = 0; j < 8; ++j) {
  364.             int ax1 = asteroid[j].x + ax;
  365.             int ay1 = asteroid[j].y + ay;
  366.             int ax2 = asteroid[(j + 1) % 8].x + ax;
  367.             int ay2 = asteroid[(j + 1) % 8].y + ay;
  368.  
  369.             // Check if the line segments intersect
  370.             if (DoLinesIntersect(px1, py1, px2, py2, ax1, ay1, ax2, ay2)) {
  371.                 return true;
  372.             }
  373.         }
  374.     }
  375.     return false;
  376. }
  377.  
  378. bool DoLinesIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
  379.     // Calculate the direction of the lines
  380.     int d1 = Direction(x3, y3, x4, y4, x1, y1);
  381.     int d2 = Direction(x3, y3, x4, y4, x2, y2);
  382.     int d3 = Direction(x1, y1, x2, y2, x3, y3);
  383.     int d4 = Direction(x1, y1, x2, y2, x4, y4);
  384.  
  385.     // Check if the lines intersect
  386.     if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) {
  387.         return true;
  388.     }
  389.  
  390.     // Check for collinear points
  391.     if (d1 == 0 && OnSegment(x3, y3, x4, y4, x1, y1)) return true;
  392.     if (d2 == 0 && OnSegment(x3, y3, x4, y4, x2, y2)) return true;
  393.     if (d3 == 0 && OnSegment(x1, y1, x2, y2, x3, y3)) return true;
  394.     if (d4 == 0 && OnSegment(x1, y1, x2, y2, x4, y4)) return true;
  395.  
  396.     return false;
  397. }
  398.  
  399. int Direction(int x1, int y1, int x2, int y2, int x3, int y3) {
  400.     return (x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1);
  401. }
  402.  
  403. bool OnSegment(int x1, int y1, int x2, int y2, int x3, int y3) {
  404.     int minX = x1 < x2 ? x1 : x2;
  405.     int maxX = x1 > x2 ? x1 : x2;
  406.     int minY = y1 < y2 ? y1 : y2;
  407.     int maxY = y1 > y2 ? y1 : y2;
  408.  
  409.     if (minX <= x3 && x3 <= maxX && minY <= y3 && y3 <= maxY) {
  410.         return true;
  411.     }
  412.     return false;
  413. }
  414.  
  415. void MoveAsteroids() {
  416.     for (auto& ast : asteroids) {
  417.         ast.x += 2 * cos(ast.angle * 3.14159 / 180);
  418.         ast.y += 2 * sin(ast.angle * 3.14159 / 180);
  419.  
  420.         // Wrap around screen
  421.         if (ast.x < 0) ast.x = WIN_WIDTH;
  422.         if (ast.x > WIN_WIDTH) ast.x = 0;
  423.         if (ast.y < 0) ast.y = WIN_HEIGHT;
  424.         if (ast.y > WIN_HEIGHT) ast.y = 0;
  425.     }
  426. }
  427.  
  428. void ShootBullet() {
  429.     Bullet bullet;
  430.     // Calculate the tip of the triangle (point[0] is the top vertex)
  431.     bullet.x = playerX + player[0].x * cos(playerAngle * 3.14159 / 180)
  432.         - player[0].y * sin(playerAngle * 3.14159 / 180);
  433.     bullet.y = playerY + player[0].x * sin(playerAngle * 3.14159 / 180)
  434.         + player[0].y * cos(playerAngle * 3.14159 / 180);
  435.     // Adjust the bullet angle to match the ship's orientation
  436.     bullet.angle = playerAngle - 90;  // Subtract 90 degrees to align with the ship's nose
  437.     bullets.push_back(bullet);
  438. }
  439.  
  440. // Modified DrawBullet function for green bullets
  441. void DrawBullet(HDC hdc, int x, int y) {
  442.     HPEN greenPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));    // Bright green
  443.     HBRUSH greenBrush = CreateSolidBrush(RGB(0, 255, 0));      // Bright green
  444.     SelectObject(hdc, greenPen);
  445.     SelectObject(hdc, greenBrush);
  446.     Ellipse(hdc, x - 4, y - 4, x + 4, y + 4);
  447.     DeleteObject(greenPen);
  448.     DeleteObject(greenBrush);
  449. }
  450.  
  451. bool CheckBulletCollision(int bx, int by) {
  452.     for (auto it = asteroids.begin(); it != asteroids.end();) {
  453.         if (PointInPolygon(bx, by, it->shape, it->x, it->y)) {
  454.             it = asteroids.erase(it);
  455.             score += 10;
  456.             return true;
  457.         }
  458.         else {
  459.             ++it;
  460.         }
  461.     }
  462.     return false;
  463. }
  464.  
  465. bool PointInPolygon(int px, int py, POINT polygon[], int ox, int oy) {
  466.     int i, j, nvert = 8;
  467.     bool c = false;
  468.  
  469.     for (i = 0, j = nvert - 1; i < nvert; j = i++) {
  470.         int polyX1 = polygon[i].x + ox;
  471.         int polyY1 = polygon[i].y + oy;
  472.         int polyX2 = polygon[j].x + ox;
  473.         int polyY2 = polygon[j].y + oy;
  474.  
  475.         if (((polyY1 > py) != (polyY2 > py)) &&
  476.             (px < (polyX2 - polyX1) * (py - polyY1) / (polyY2 - polyY1) + polyX1)) {
  477.             c = !c;
  478.         }
  479.     }
  480.     return c;
  481. }
  482.  
  483. void GenerateAsteroids() {
  484.     for (int i = 0; i < 5; ++i) {
  485.         Asteroid ast;
  486.         ast.x = rand() % WIN_WIDTH;
  487.         ast.y = rand() % WIN_HEIGHT;
  488.         ast.angle = rand() % 360;
  489.         for (int j = 0; j < 8; ++j) {
  490.             double angle = j * 45 * 3.14159 / 180;
  491.             ast.shape[j].x = (rand() % 20 + 10) * cos(angle);
  492.             ast.shape[j].y = (rand() % 20 + 10) * sin(angle);
  493.         }
  494.         asteroids.push_back(ast);
  495.     }
  496. }
  497.  
  498. void SpawnNewAsteroid() {
  499.     Asteroid ast;
  500.     // Randomly choose a side of the screen to spawn from
  501.     int side = rand() % 4;
  502.     switch (side) {
  503.     case 0: // Top
  504.         ast.x = rand() % WIN_WIDTH;
  505.         ast.y = -30;
  506.         break;
  507.     case 1: // Right
  508.         ast.x = WIN_WIDTH + 30;
  509.         ast.y = rand() % WIN_HEIGHT;
  510.         break;
  511.     case 2: // Bottom
  512.         ast.x = rand() % WIN_WIDTH;
  513.         ast.y = WIN_HEIGHT + 30;
  514.         break;
  515.     case 3: // Left
  516.         ast.x = -30;
  517.         ast.y = rand() % WIN_HEIGHT;
  518.         break;
  519.     }
  520.  
  521.     ast.angle = rand() % 360;
  522.     for (int j = 0; j < 8; ++j) {
  523.         double angle = j * 45 * 3.14159 / 180;
  524.         ast.shape[j].x = (rand() % 20 + 10) * cos(angle);
  525.         ast.shape[j].y = (rand() % 20 + 10) * sin(angle);
  526.     }
  527.     asteroids.push_back(ast);
  528. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement