+1 (315) 557-6473 

Creating Interactive 2D Games with C++

In this guide, we will embark on an exciting journey into the realm of 2D game development using C++. Together, we will dissect a C++ code snippet that breathes life into a 2D game featuring circles, bricks, and a paddle. Join us as we explore the intricacies of collision detection, physics, and graphics within this simple yet exhilarating gaming environment. Whether you're a budding game developer or an inquisitive coder seeking to expand your skills, this guide is your ticket to a hands-on experience in the core principles of crafting interactive and captivating 2D games. By the end of this guide, you'll possess a deeper understanding of how to create a game from the ground up, complete with dynamic interactions and captivating visuals.

Building a 2D Game from Scratch in C++

Exploring 2D Game Development in C++ is a great way to enhance your programming skills. Our comprehensive guide not only helps you create captivating games but also equips you with the knowledge to help with your C++ assignment. Dive into the world of game development and strengthen your expertise. Whether you're a student seeking to excel in your coursework or an enthusiast looking to turn your passion into a practical skill, this resource is your gateway to building exciting games and mastering C++ programming for academic excellence.

Block 1: Header Inclusions

```cpp #include < glfw\glfw3.h > #include < stdlib.h > #include < stdio.h > #include < conio.h > #include < iostream > #include < vector > #include < time.h > ```

These lines include the necessary header files for the program. They provide functionality for graphics (using GLFW), standard input/output operations, console input (conio.h), and containers (vector) to store game objects.

Block 2: Constants and Helper Functions

```cpp const float DEG2RAD = 3.14159f / 180; float clampFloat(float f, float minValue, float maxValue) { // Constrain a value between a minimum and maximum values if (f < minValue) return minValue; if (f > maxValue) return maxValue; return f; } float randomFloat(float minValue, float maxValue) { // Generate a random value between 0.0 and 1.0 float r = (rand() % 1000) * 0.001f; // Scale and shift the value to lie between a minimum and maximum values return minValue + (maxValue - minValue) * r; } ```

Here, some constants are defined, such as `DEG2RAD`, which is used to convert degrees to radians. Two helper functions, `clampFloat` and `randomFloat`, are also declared. `clampFloat` ensures a value is within a specified range, and `randomFloat` generates a random float value within a given range.

Block 3: `Brick` Class Definition

```cpp class Brick { public: float red, green, blue; float x, y, width, height; BRICKTYPE type; BRICKSTATE state; vector crack; // (x,y) positions of crack points Brick(BRICKTYPE bt, float xx, float yy, float ww, float hh, float rr, float gg, float bb) { type = bt; x = xx; y = yy, width = ww; height = hh; red = rr, green = gg, blue = bb; state = ON; }; bool inside(float px, float py) { // check if a point (px, py) is inside the brick return px > x - width / 2 && px < x + width / 2 && py > y - height / 2 && py < y + height / 2; } void draw() { if (state == OFF) return; float halfWidth = width / 2; float halfHeight = height / 2; // draw the brick as a rectangle glColor3d(red, green, blue); glBegin(GL_POLYGON); glVertex2f(x + halfWidth, y + halfHeight); glVertex2f(x + halfWidth, y - halfHeight); glVertex2f(x - halfWidth, y - halfHeight); glVertex2f(x - halfWidth, y + halfHeight); glEnd(); // draw the crack as a strip of lines glLineWidth(5); glColor3d(0, 0, 0); glBegin(GL_LINE_STRIP); for (int i = 0; i < (int)crack.size() / 2; i++) glVertex2f(crack[i * 2], crack[i * 2 + 1]); glEnd(); } void hit(float px, float py) { if (type != DESTRUCTABLE) return; if (state == ON) { // when hit, the intact brick becomes cracked state = CRACKED; createCrack(px, py); } else if (state == CRACKED) { // when hit, the cracked brick becomes hidden state = OFF; } } void createCrack(float px, float py) { // start cracking from the collision point crack.push_back(px); crack.push_back(py); // angle in the direction to the brick center float angle = atan2f(y - py, x - px) + 0.5f; // create a sequence of crack points for (int i = 0; ; i++) { // make a step float step = randomFloat(0.1f, 0.2f); px += width * step * cosf(angle); py += height * step * sinf(angle); // stop if outside the brick if (!inside(px, py)) break; // append another point of the crack crack.push_back(px); crack.push_back(py); // update the angle in a zigzag pattern float turn = randomFloat(0.5f, 1.0f); if (i % 2) angle += turn; else angle -= turn; } } }; ```

This class represents a brick in the game. It contains properties like position, color, type (reflective or destructible), and state (on, cracked, off). The class also has methods for drawing, checking if a point is inside the brick, and handling collisions with the ball.

Block 4: `Paddle` Class Definition (Inheritance)

```cpp class Paddle : public Brick { public: Paddle(float xx, float yy, float ww, float hh, float rr, float gg, float bb) : Brick(REFLECTIVE, xx, yy, ww, hh, rr, gg, bb) { }; }; ```

This class inherits from the `Brick` class and represents the paddle in the game. It adds no new properties but defines a constructor.

Block 5: `Circle` Class Definition

```cpp class Circle { public: float red, green, blue; float radius; float x; float y; float xspeed; float yspeed; Circle(float xx, float yy, float xxspeed, float yyspeed, float rad, float r, float g, float b) { x = xx; y = yy; xspeed = xxspeed; yspeed = yyspeed; radius = rad; red = r; green = g; blue = b; } bool checkCollision(Circle* crc) { // Squared distance to another circle float dx = crc->x - x; float dy = crc->y - y; float dd = dx * dx + dy * dy; // Distance test float minDist = radius + crc->radius; return dd < minDist * minDist; } void checkCollision(Brick* brk) { // Ignore hidden bricks if (brk->state == OFF) return; // Closest point to the circle within the brick float bx = clampFloat(x, brk->x - brk->width / 2, brk->x + brk->width / 2); float by = clampFloat(y, brk->y - brk->height / 2, brk->y + brk->height / 2); // Squared distance from the closest point to the circle center float dx = x - bx; float dy = y - by; float dd = dx * dx + dy * dy; // Distance test if (dd > radius * radius || dd == 0) return; // Project the circle's speed vector on the direction vector float sd = xspeed * dx + yspeed * dy; if (sd >= 0) return; // For a reflective or uncracked brick, bounce off the closest point if (brk->type == REFLECTIVE || brk->state == ON) { xspeed -= 2 * sd * dx / dd; yspeed -= 2 * sd * dy / dd; } // Crack or hide the brick brk->hit(bx, by); } void moveOneStep() { // Update position x += xspeed * timeStep; y += yspeed * timeStep; // Collide with walls if (x < -1 + radius && xspeed < 0) // Left wall xspeed *= -1; if (x > +1 - radius && xspeed > 0) // Right wall xspeed *= -1; if (y < -1 + radius && yspeed < 0) // Bottom wall yspeed *= -1; if (y > +1 - radius && yspeed > 0) // Top wall yspeed *= -1; } void draw() { glColor3f(red, green, blue); glBegin(GL_POLYGON); for (int i = 0; i < 360; i++) { float degInRad = i * DEG2RAD; glVertex2f((cos(degInRad) * radius) + x, (sin(degInRad) * radius) + y); } glEnd(); } }; ```

The `Circle` class represents a circular game object (likely a ball). It contains properties like position, color, radius, and speed. It has methods for checking collisions with other circles or bricks, updating its position, and drawing itself.

Block 6: Global Variables

```cpp Paddle paddle(0, -0.9f, 0.5f, 0.1f, 1, 1, 1); vector bricks; vector circles; ```

Here, the global game objects are declared and initialized. The `paddle` is an instance of the `Paddle` class, and there are vectors to store `bricks` and `circles`.

Block 7: `main` Function

```cpp int main(void) { srand((unsigned int)time(NULL)); if (!glfwInit()) exit(EXIT_FAILURE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); GLFWwindow* window = glfwCreateWindow(480, 480, "Random World of Circles", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } glfwMakeContextCurrent(window); glfwSwapInterval(1); // Create bricks bricks.push_back(Brick(DESTRUCTABLE, 0.5f, -0.33f, 0.20f, 0.20f, 1, 1, 0)); bricks.push_back(Brick(DESTRUCTABLE, -0.5f, 0.33f, 0.25f, 0.25f, 0, 1, 0)); bricks.push_back(Brick(DESTRUCTABLE, -0.5f, -0.33f, 0.30f, 0.30f, 0, 1, 1)); bricks.push_back(Brick(REFLECTIVE, 0.5f, 0.33f, 0.35f, 0.35f, 1, 0.5f, 0.5f)); while (!glfwWindowShouldClose(window)) { // Manage time float t = (float)glfwGetTime(); timeStep = t - currentTime; currentTime = t; // Setup view int width, height; glfwGetFramebufferSize(window, &width, &height); float ratio = width / (float)height; glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT); processInput(window); // Alter the state of the circles upon collision vector uncollidedCircles; for (int i = 0; i < (int)circles.size(); i++) { // Collide with other circles bool hit = false; for (int j = 0; j < (int)circles.size(); j++) if (i != j && circles[i].checkCollision(&circles[j])) hit = true; // Store uncollided circles if (!hit) uncollidedCircles.push_back(circles[i]); } circles = uncollidedCircles; // Update and draw the circles for (int i = 0; i < (int)circles.size(); i++) { // Collide with the bricks/paddle for (int j = 0; j < (int)bricks.size(); j++) circles[i].checkCollision(&bricks[j]); circles[i].checkCollision(&paddle); circles[i].moveOneStep(); circles[i].draw(); } // Draw the bricks/paddle for (int i = 0; i < (int)bricks.size(); i++) bricks[i].draw(); paddle.draw(); glfwSwapBuffers(window); glfwPollEvents(); } glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); } ```

The `main` function is the entry point of the program. It initializes the game, creates the window, manages time, processes user input, and runs the game loop, handling the collision, update, and rendering of objects.

Block 8: `processInput` Function

```cpp void processInput(GLFWwindow *window) { // Close the window if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); // Add circles if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) { float x = randomFloat(-0.9f, 0.9f); float y = randomFloat(0.8f, 0.9f); float angle = randomFloat(0, 360 * DEG2RAD); float xspeed = cosf(angle); float yspeed = sinf(angle); float r = randomFloat(0.5f, 1.0f); float g = randomFloat(0.5f, 1.0f); float b = randomFloat(0.5f, 1.0f); Circle B(x, y, xspeed, yspeed, 0.05f, r, g, b); circles.push_back(B); } // Control the paddle const float paddleSpeed = 2.0f; if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) // Move left paddle.x -= paddleSpeed * timeStep; if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) // Move right paddle.x += paddleSpeed * timeStep; if (paddle.x < -1 + paddle.width / 2) // Left constraint paddle.x = -1 + paddle.width / 2; if (paddle.x > 1 - paddle.width / 2) // Right constraint paddle.x = 1 - paddle.width / 2; } ```

The `processInput` function is responsible for handling user input, such as closing the window, adding circles, and controlling the paddle's movement.

Block 9: Game Loop

```cpp while (!glfwWindowShouldClose(window)) { // Manage time float t = (float)glfwGetTime(); timeStep = t - currentTime; currentTime = t; // Setup view int width, height; glfwGetFramebufferSize(window, &width, &height); float ratio = width / (float)height; glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT); processInput(window); // Alter the state of the circles upon collision vector uncollidedCircles; for (int i = 0; i < (int)circles.size(); i++) { // Collide with other circles bool hit = false; for (int j = 0; j < (int)circles.size(); j++) if (i != j && circles[i].checkCollision(&circles[j])) hit = true; // Store uncollided circles if (!hit) uncollidedCircles.push_back(circles[i]); } circles = uncollidedCircles; // Update and draw the circles for (int i = 0; i < (int)circles.size(); i++) { // Collide with the bricks/paddle for (int j = 0; j < (int)bricks.size(); j++) circles[i].checkCollision(&bricks[j]); circles[i].checkCollision(&paddle); circles[i].moveOneStep(); circles[i].draw(); } // Draw the bricks/paddle for (int i = 0; i < (int)bricks.size(); i++) bricks[i].draw(); paddle.draw(); glfwSwapBuffers(window); glfwPollEvents(); } ```

This is the core of the game loop. It manages time, handles collisions between circles and bricks, updates positions, and draws the game objects. The loop continues until the user closes the window.

Block 10: Cleanup and Termination

```cpp glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); ```

After the game loop ends, these lines clean up resources and terminate the program.

Conclusion

In conclusion, this guide has taken you on a comprehensive journey into the fascinating world of 2D game development using C++. From understanding the code to unraveling the complexities of collision detection, physics, and graphics, you've gained essential knowledge. Whether you're a novice game developer or an aspiring coder, this guide equips you with the fundamentals to create engaging 2D games from scratch. By the end of this guide, you're not just familiar with the intricacies; you're well-prepared to bring your own game ideas to life, complete with dynamic interactions and captivating visuals. As you continue on your game development journey, remember that creativity and persistence are your greatest assets in shaping the digital worlds of the future. So, dive in, experiment, and turn your gaming dreams into reality with the power of C++.