C++ Needle Insertion Game: From Basic Drawing to Collision Detection

“Needle Insertion” is an interesting physics collision game where players need to press the space bar to launch needles onto a rotating disk. All launched needles rotate counterclockwise, and if a newly launched needle collides with an existing needle, the game ends. Below, I will explain step by step how to implement this game using C++ and the EasyX graphics library.

C++ Needle Insertion Game: From Basic Drawing to Collision Detection

1. Drawing Basic Shapes: Disk and Needle

First, we need to draw a disk and a needle in the center of the screen. The coordinates of the center and the starting point of the line segment are both (width/2, height/2). The function setlinestyle(PS_SOLID, 3) is used to set the current line style, where PS_SOLID indicates a solid line with a width of 3 (the default line width is 1).

#include <graphics.h>
#include <conio.h>
int main() {
    int width = 800;   // Screen width
    int height = 600;  // Screen height
    initgraph(width, height);          // Open a new screen
    setbkcolor(RGB(255, 255, 255));    // Background color is white
    cleardevice();                     // Clear the background with the background color
    setlinestyle(PS_SOLID, 3);         // Set line width to 3, solid line
    setlinecolor(RGB(0, 0, 0));        // Set needle color to black
    line(width/2, height/2, width/2 + 160, height/2);  // Draw a needle
    setlinecolor(HSVtoRGB(0, 0.9, 0.8)); // Set disk line color to red
    circle(width/2, height/2, 60);     // Draw the central disk
    _getch();    closegraph();    return 0;
}

C++ Needle Insertion Game: From Basic Drawing to Collision Detection

2. Implementing Needle Rotation Effect

To make the needle rotate, we need to use trigonometric functions to calculate the coordinates of the needle’s tip. The starting coordinates of the needle are the center of the screen (width/2, height/2). Assuming the length of the needle is lineLength and the rotation angle is angle, the formula for calculating the tip coordinates is:

xEnd = lineLength * cos(-angle) + width/2;
yEnd = lineLength * sin(-angle) + height/2;

Since the y-axis direction of the EasyX drawing coordinate system is opposite to that of the general mathematical coordinate system, we need to calculate the trigonometric functions of -angle.

#include<graphics.h>
#include<conio.h>
#include<stdio.h>
#include<math.h>
int main() {
    float PI = 3.1415926;
    int width = 800;   // Screen width
    int height = 600;  // Screen height
    initgraph(width, height);  // Open a new screen
    setbkcolor(RGB(255, 255, 255));  // Background color is white
    cleardevice();  // Clear the background with the background color
    float lineLength = 160;  // Length of the needle
    float xEnd, yEnd;  // Tip coordinates of the needle (starting position is the center)
    float angle = PI/3;  // Rotation angle of the needle
    setlinestyle(PS_SOLID, 3);  // Line width is 3, making the needle more visible
    setlinecolor(RGB(0, 0, 0));  // Set needle color to black
    xEnd = lineLength * cos(-angle) + width/2;  // Calculate the tip coordinates of the needle
    yEnd = lineLength * sin(-angle) + height/2;    line(width/2, height/2, xEnd, yEnd);  // Draw a needle
    setlinecolor(HSVtoRGB(0, 0.9, 0.8));  // Set disk line color to red
    circle(width/2, height/2, 60);  // Draw the central disk
    _getch();    closegraph();    return 0;
}

C++ Needle Insertion Game: From Basic Drawing to Collision Detection

To achieve continuous rotation animation, we gradually increase the angle in a while loop. In C++, both integers and floating-point numbers have value ranges, so to prevent the angle variable from increasing indefinitely, we set it to angle = angle – 2PI when angle > 2PI.

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <math.h>
int main() {
    const float PI = 3.1415926;  // PI constant
    int width = 800;  // Screen width
    int height = 600;  // Screen height
    initgraph(width, height);  // Open a new screen
    setbkcolor(RGB(255, 255, 255));  // Background color is white
    cleardevice();  // Clear the background with the background color
    float lineLength = 160;  // Length of the needle
    float xEnd, yEnd;  // Tip coordinates of the needle (starting position is the center)
    float angle = 0;  // Rotation angle of the needle
    float rotateSpeed = PI/360;  // Rotation speed of the needle
    setlinestyle(PS_SOLID, 3);  // Line width is 3, making the needle more visible
    while (1) {
        cleardevice();  // Clear the background with the background color
        angle = angle + rotateSpeed;  // Increase the angle
        // Prevent angle data from increasing indefinitely
        if (angle > 2 * PI) {
            angle = angle - 2 * PI;
        }
        xEnd = lineLength * cos(-angle) + width/2;  // Calculate the tip coordinates of the needle
        yEnd = lineLength * sin(-angle) + height/2;        setlinecolor(RGB(0, 0, 0));  // Set needle color to black
        line(width/2, height/2, xEnd, yEnd);  // Draw a needle
        setlinecolor(HSVtoRGB(0, 0.9, 0.8));  // Set disk line color to red
        circle(width/2, height/2, 60);  // Draw the central disk
        Sleep(10);  // Pause for 10 milliseconds
    }
    closegraph();    return 0;
}

C++ Needle Insertion Game: From Basic Drawing to Collision Detection

3. Using Arrays to Implement Multiple Needles Drawing

To achieve the effect of multiple needles, we need to use an array to store the angle values of each needle. An array is a collection of data of the same type, which can be accessed through an index.

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <math.h>
int main() {
    const float PI = 3.1415926;  // PI constant
    int width = 800;  // Screen width
    int height = 600;  // Screen height
    initgraph(width, height);  // Open a new screen
    setbkcolor(RGB(255, 255, 255));  // Background color is white
    setlinestyle(PS_SOLID, 3);  // Line width is 3, making the needle more visible
    float lineLength = 160;  // Length of the needle
    float xEnd, yEnd;  // Tip coordinates of the needle (starting position is the center)
    float rotateSpeed = PI/360;  // Rotation speed of the needle
    int lineNum = 20;  // Number of needles
    float Angles[20];  // Float array to store the rotation angles of all needles
    int i;
    // Start to evenly distribute the angles of the needles in the array
    for (i = 0; i < lineNum; i++) {
        Angles[i] = i * 2 * PI / lineNum;
    }
    while (1) {  // Repeat loop
        cleardevice();  // Clear the background with the background color
        setlinecolor(RGB(0, 0, 0));  // Set needle color to black
        for (i = 0; i < lineNum; i++) {  // Iterate through all rotating needles
            Angles[i] = Angles[i] + rotateSpeed;  // Increase the angle
            // If it exceeds 2*PI, subtract 2*PI to prevent angle data from increasing indefinitely
            if (Angles[i] > 2 * PI) {
                Angles[i] = Angles[i] - 2 * PI;
            }
            xEnd = lineLength * cos(-Angles[i]) + width/2;  // Calculate the tip coordinates of the needle
            yEnd = lineLength * sin(-Angles[i]) + height/2;            line(width/2, height/2, xEnd, yEnd);  // Draw a needle        }
        setlinecolor(HSVtoRGB(0, 0.9, 0.8));  // Set disk line color to red
        circle(width/2, height/2, 60);  // Draw the central disk
        Sleep(10);  // Pause for 10 milliseconds    }
    closegraph();    return 0;
}

C++ Needle Insertion Game: From Basic Drawing to Collision Detection

4. Optimizing Drawing Performance: Batch Drawing

After running the above program, we may find that when there are many elements being drawn, the screen may flicker. In this case, we can use batch drawing functions to optimize:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <math.h>
int main() {
    const float PI = 3.1415926;  // PI constant
    int width = 800;  // Screen width
    int height = 600;  // Screen height
    initgraph(width, height);  // Open a new screen
    setbkcolor(RGB(255, 255, 255));  // Background color is white
    setlinestyle(PS_SOLID, 3);  // Line width is 3, making the needle more visible
    float lineLength = 160;  // Length of the needle
    float xEnd, yEnd;  // Tip coordinates of the needle (starting position is the center)
    float rotateSpeed = PI/360;  // Rotation speed of the needle
    int lineNum = 20;  // Number of needles
    float Angles[20];  // Float array to store the rotation angles of all needles
    int i;
    for (i = 0; i < lineNum; i++) {
        Angles[i] = i * 2 * PI / lineNum;
    }
    BeginBatchDraw();  // Start batch drawing
    while (1) {
        cleardevice();
        setlinecolor(RGB(0, 0, 0));
        for (i = 0; i < lineNum; i++) {
            Angles[i] = Angles[i] + rotateSpeed;
            if (Angles[i] > 2 * PI) {
                Angles[i] = Angles[i] - 2 * PI;
            }
            xEnd = lineLength * cos(-Angles[i]) + width/2;
            yEnd = lineLength * sin(-Angles[i]) + height/2;
            line(width/2, height/2, xEnd, yEnd);
        }
        setlinecolor(HSVtoRGB(0, 0.9, 0.8));
        circle(width/2, height/2, 60);
        FlushBatchDraw();  // Batch draw
        Sleep(10);
    }
    closegraph();    return 0;
}

C++ Needle Insertion Game: From Basic Drawing to Collision Detection

5. Implementing Needle Launching Functionality
Now we will implement the core functionality of the game—launching a new needle by pressing the space bar.
static bool lastSpaceState = false;  // Record the last frame's space key state
// Current frame state
bool currentSpaceState = (GetAsyncKeyState(VK_SPACE) & 0x8000) != 0;
// State change detection
if (currentSpaceState && !lastSpaceState && rotateSpeed != 0) {
    // Trigger only when the key changes from released to pressed
    // Logic for launching a needle
}
// Update last frame state
lastSpaceState = currentSpaceState;
GetAsyncKeyState is a function in the Windows API used to detect the current state of a specified virtual key. VK_SPACE: virtual key code representing the space bar.
The bitmask operation & 0x8000 is used to check if the key is pressed: a non-zero return value indicates the key is pressed, while zero indicates the key is not pressed.

6. Game Failure Judgment: Collision Detection

The key rule of the game is: if a newly launched needle collides with an existing needle, the game ends. We determine the collision by comparing the angle difference:

bool collision = false;
for (i = 0; i < lineNum - 1; i++) {
    if (fabs(Angles[lineNum-1] - Angles[i]) < PI/60) {
        collision = true;
        rotateSpeed = 0;  // Game over
        break;
    }
}

Here, we use the <span><span>abs()</span></span> function to calculate the absolute value. When the angle difference between two needles is less than PI/60, a collision is considered to have occurred.

7. Enhancing Game Effects: Scoring and Visual Effects

Finally, we add a scoring system and better visual effects. Below is the complete program:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <math.h>
#include <windows.h>
int main() {
    const float PI = 3.1415926;  // PI constant
    int width = 800;  // Screen width
    int height = 600;  // Screen height
    initgraph(width, height);  // Open a new screen
    setbkcolor(RGB(255, 255, 255));  // Background color is white
    setlinestyle(PS_SOLID, 3);  // Line width is 3, making the needle more visible
    float lineLength = 160;  // Length of the needle
    float xEnd, yEnd;  // Tip coordinates of the needle (starting position is the center)
    float rotateSpeed = PI/360;  // Rotation speed of the needle
    int lineNum = 0;  // Number of rotating needles
    float Angles[1000];  // Float array to store the rotation angles of all needles, up to 1000 needles
    int score = 0;  // Score
    int i;
    BeginBatchDraw();  // Start batch drawing
    while (1) {  // Repeat loop
        cleardevice();  // Clear the background with the background color
        setlinecolor(RGB(0, 0, 0));  // Set needle color to black
        line(0, height/2, lineLength, height/2);  // Draw a needle in the left launch area
        for (i = 0; i < lineNum; i++) {  // Iterate through all rotating needles
            Angles[i] = Angles[i] + rotateSpeed;  // Increase the angle
            if (Angles[i] > 2 * PI) {  // If it exceeds 2*PI, subtract 2*PI
                Angles[i] = Angles[i] - 2 * PI;
            }
            xEnd = lineLength * cos(-Angles[i]) + width/2;  // Calculate the tip coordinates of the needle
            yEnd = lineLength * sin(-Angles[i]) + height/2;            line(width/2, height/2, xEnd, yEnd);  // Draw a needle        }
static bool lastSpaceState = false;  // Record the last frame's space key state
        bool currentSpaceState = (GetAsyncKeyState(VK_SPACE) & 0x8000) != 0;          // Use GetAsyncKeyState to detect the key, trigger only when the key changes from released to pressed
        if (currentSpaceState && !lastSpaceState && rotateSpeed != 0){  // If the key is pressed
            lineNum++;  // Increase the number of needles by 1
            Angles[lineNum-1] = PI;  // The initial angle of the newly added needle
bool collision = false;
for (i = 0; i < lineNum - 1; i++) {
    if (fabs(Angles[lineNum-1] - Angles[i]) < PI/60) {
        collision = true;
        rotateSpeed = 0;  // Game over
        break;
    }
}
if (rotateSpeed != 0) {  // If no collision occurs
    score = score + 1;  // Increase score by 1
}
}
lastSpaceState = currentSpaceState;  // Update state
        setlinecolor(HSVtoRGB(0, 0.9, 0.8));  // Set disk line color to red
        fillcircle(width/2, height/2, 60);  // Draw the central disk
        TCHAR s[20];  // Define string array
        _stprintf(s, _T("%d"), score);  // Convert score to string
        settextstyle(50, 0, _T("Times"));  // Set text size and font
        settextcolor(RGB(50, 50, 50));  // Set font color
        outtextxy(65, 200, s);  // Output score text
        FlushBatchDraw();  // Batch draw
        Sleep(10);  // Pause for 10 milliseconds
    }
    closegraph();    return 0;
}

C++ Needle Insertion Game: From Basic Drawing to Collision Detection

Leave a Comment