ComputerGraphics(UCS505)
Project Name:
Tetris Game
Branch B. E. 3rd Year– COE
Submitted By:
Vivek Vansh (102383070)
Vivek Kumar Bhaskar (102383069)
Om Aman Srivastava (102383047)
Submitted To Mrs Kudratdeep Aulakh
Computer Science and Engineering Department
Thapar Institute of Engineering and Technology Patiala–
147001
1. Introduction
The Tank Shooting Game is a 2D arcade-style shooting game created using
OpenGL and GLUT in C++. This game demonstrates key principles of computer
graphics by providing a dynamic and interactive environment in which a player
controls a tank to destroy enemy tanks. The gameplay involves navigating through
a predefined 2D space, aiming, shooting, and avoiding enemy fire. The simplicity
of the graphics and mechanics allows the focus to remain on real-time rendering,
transformation, collision detection, and interactive controls, making the project an
ideal demonstration of foundational graphics programming skills. The game is
intended to offer both an entertaining experience and an educational exploration
of graphics-based game development.
2. Project Overview
This project is a straightforward 2D tank shooting game that simulates a combat
environment from a top-down perspective. The player controls a single tank and
must navigate a battlefield to eliminate a fixed number of enemy tanks. The game
mechanics are based on a basic real-time simulation where the player must shoot
and dodge incoming enemy bullets while trying to achieve the highest possible
score.
The game includes the following main features:
• Player Tank: Fully controllable using the keyboard, with support for
directional movement and shooting.
• Enemy Tanks: Randomly placed and programmed with basic AI to move and
shoot at the player.
• Bullets: Spawn from both player and enemy tanks with a set speed and
direction.
• Health System: Player has limited health, which decreases when hit by
enemy bullets.
• Scoring: Each enemy destroyed increases the player's score.
• Game States: The game displays clear messages for "Game Over" or "You
Win" states and allows restarting.
The goal is to destroy all enemy tanks while maintaining the player's tank health
above zero. The game's simplicity is purposeful, focusing on the integration of
core graphics techniques and game loop logic.
3. Scope of the Project
The scope of the Tank Shooting Game project centers around demonstrating a
working knowledge of 2D computer graphics and game programming using
OpenGL and GLUT. The design remains simple and structured to serve as an
academic or beginner-level project while still offering meaningful functionality and
interaction. The project covers:
• Game Setup: Initialization of OpenGL environment, setting up window
dimensions, and defining orthographic projection.
• Player Interactions: Movement control, bullet firing, and collision response.
• Enemy AI: Simplified logic for movement and bullet targeting, enabling
enemies to react dynamically to player location.
• Collision and Game Physics: Basic distance-based collision detection
between bullets and tanks.
• Game State Management: Transitions between different states like active
gameplay, win, and game over.
• UI Elements: Real-time score and health display using bitmap text
rendering.
The project does not include advanced features such as pathfinding algorithms,
multiplayer networking, complex terrain, or 3D rendering. These exclusions help
keep the focus on mastering 2D rendering and interaction basics, making the
game an effective learning platform for students and beginners in graphics
programming.
4. Computer Graphics Concepts Used
This game incorporates several essential computer graphics principles:
• 2D Transformations: Position and orientation of tanks and bullets are
handled via translations and rotations.
o glTranslatef() and glRotatef() are used to move and rotate tanks.
• Orthographic Projection:
o gluOrtho2D() defines a 2D viewing area, making the game rendering
independent of depth.
• Rendering Primitives:
o GL_QUADS for drawing tanks and bullets.
o GL_LINES for the gun barrel of the tanks.
• Coloring:
o Different colors distinguish player (green) and enemy (red) tanks.
• Double Buffering:
o Ensures smooth animations using GLUT_DOUBLE with
glutSwapBuffers().
• Real-time Rendering and Animation:
o Game updates occur via glutTimerFunc() to maintain a frame rate
(~60 FPS).
• Text Rendering:
o Bitmap font rendering using glutBitmapCharacter() to display score,
health, and messages.
5. User Defined Functions
Function Name Description
drawTank() Renders a tank with given position and angle. Draws
differently for player/enemy.
drawBullet() Renders a bullet if it is active. Color differs for player/enemy.
drawText() Displays on-screen text using GLUT bitmap fonts.
fireBullet() Triggers when the player presses SPACE. Creates and adds a
bullet.
enemyFire() Automatically called to fire bullets from enemies towards the
player.
spawnEnemies() Randomly positions enemy tanks at game start/reset.
checkCollision() Determines if two circular areas overlap (basic distance
check).
resetGame() Reinitializes all game variables, enemies, and player state.
update() Main game logic: moves tanks, processes bullets, handles
collisions.
display() Draws all game objects and UI text every frame.
timer() Schedules periodic updates (approx. every 16ms) to ensure
game loop timing.
keyboardDown() Captures key presses; handles movement, shooting, and
reset.
keyboardUp() Captures key releases to stop movement.
reshape() Updates the viewport and projection matrix on window size
changes.
6. Code Snippets
#include <GL/glut.h>
#include <cmath>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <sstream>
const int WIDTH = 800;
const int HEIGHT = 600;
const float PI = 3.14159265f;
const int TANK_SIZE = 20;
const float BULLET_SPEED = 10.0f;
const int ENEMY_COUNT = 5;
const int MAX_HEALTH = 500;
struct Bullet {
float x, y;
float angle;
bool active;
bool ownerIsEnemy;
};
struct Tank {
float x, y;
float angle;
bool alive;
};
Tank player = { WIDTH / 2.0f, HEIGHT / 2.0f, 0.0f, true };
std::vector<Bullet> bullets;
std::vector<Tank> enemies;
int score = 0;
int health = MAX_HEALTH;
bool keys[256] = { false };
bool gameOver = false;
bool gameWin = false;
int enemyFireCooldown = 0;
void drawText(float x, float y, const std::string& text) {
glRasterPos2f(x, y);
for (char c : text)
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, c);
}
void drawTank(float x, float y, float angle, bool isPlayer) {
glPushMatrix();
glTranslatef(x, y, 0);
glRotatef(angle * 180 / PI, 0, 0, 1);
glColor3f(isPlayer ? 0 : 1, isPlayer ? 1 : 0, 0); // Player: green, Enemy: red
glBegin(GL_QUADS);
glVertex2f(-TANK_SIZE / 2, -TANK_SIZE / 2);
glVertex2f(TANK_SIZE / 2, -TANK_SIZE / 2);
glVertex2f(TANK_SIZE / 2, TANK_SIZE / 2);
glVertex2f(-TANK_SIZE / 2, TANK_SIZE / 2);
glEnd();
glColor3f(0, 0, 0); // Gun
glBegin(GL_LINES);
glVertex2f(0, 0);
glVertex2f(TANK_SIZE, 0);
glEnd();
glPopMatrix();
}
void drawBullet(Bullet& b) {
if (![Link]) return;
glPushMatrix();
glTranslatef(b.x, b.y, 0);
glColor3f([Link] ? 1 : 1, [Link] ? 0 : 1, 0); // Enemy: red-
yellow
glBegin(GL_QUADS);
glVertex2f(-3, -3);
glVertex2f(3, -3);
glVertex2f(3, 3);
glVertex2f(-3, 3);
glEnd();
glPopMatrix();
}
void fireBullet() {
if (gameOver || gameWin) return;
Bullet b;
b.x = player.x + TANK_SIZE * cos([Link]);
b.y = player.y + TANK_SIZE * sin([Link]);
[Link] = [Link];
[Link] = true;
[Link] = false;
bullets.push_back(b);
}
void enemyFire(Tank& enemy) {
Bullet b;
b.x = enemy.x + TANK_SIZE * cos([Link]);
b.y = enemy.y + TANK_SIZE * sin([Link]);
[Link] = atan2(player.y - enemy.y, player.x - enemy.x);
[Link] = true;
[Link] = true;
bullets.push_back(b);
}
void spawnEnemies() {
[Link]();
for (int i = 0; i < ENEMY_COUNT; i++) {
Tank e;
e.x = rand() % (WIDTH - 100) + 50;
e.y = rand() % (HEIGHT - 100) + 50;
[Link] = (rand() % 360) * PI / 180.0f;
[Link] = true;
enemies.push_back(e);
}
}
bool checkCollision(float x1, float y1, float x2, float y2, float range) {
float dx = x1 - x2;
float dy = y1 - y2;
return (dx * dx + dy * dy) <= (range * range);
}
void resetGame() {
player = { WIDTH / 2.0f, HEIGHT / 2.0f, 0.0f, true };
[Link]();
score = 0;
health = MAX_HEALTH;
gameOver = false;
gameWin = false;
enemyFireCooldown = 0;
spawnEnemies();
}
void update() {
if (gameOver || gameWin) return;
float speed = 4.0f;
if (keys['w']) {
player.x += speed * cos([Link]);
player.y += speed * sin([Link]);
}
if (keys['s']) {
player.x -= speed * cos([Link]);
player.y -= speed * sin([Link]);
}
if (keys['a']) [Link] -= 0.07f;
if (keys['d']) [Link] += 0.07f;
for (auto& b : bullets) {
if ([Link]) {
b.x += BULLET_SPEED * cos([Link]);
b.y += BULLET_SPEED * sin([Link]);
if (b.x < 0 || b.x > WIDTH || b.y < 0 || b.y > HEIGHT)
[Link] = false;
}
}
enemyFireCooldown++;
if (enemyFireCooldown > 60) {
for (auto& e : enemies) {
if ([Link])
enemyFire(e);
}
enemyFireCooldown = 0;
}
for (auto& e : enemies) {
if (![Link]) continue;
e.x += 1.5f * cos([Link]);
e.y += 1.5f * sin([Link]);
if (e.x < 0 || e.x > WIDTH || e.y < 0 || e.y > HEIGHT)
[Link] += PI;
if (checkCollision(player.x, player.y, e.x, e.y, TANK_SIZE)) {
gameOver = true;
}
for (auto& b : bullets) {
if ([Link] && ![Link] && checkCollision(b.x, b.y, e.x, e.y,
TANK_SIZE)) {
[Link] = false;
[Link] = false;
score += 10;
if (score >= 50) {
gameWin = true;
}
}
}
}
for (auto& b : bullets) {
if ([Link] && [Link] && checkCollision(b.x, b.y, player.x,
player.y, TANK_SIZE)) {
[Link] = false;
health -= 5;
if (health <= 0) {
gameOver = true;
health = 0;
}
}
}
}
void display() {
glClear(GL_COLOR_BUFFER_BIT);
drawTank(player.x, player.y, [Link], true);
for (auto& b : bullets)
drawBullet(b);
for (auto& e : enemies)
if ([Link])
drawTank(e.x, e.y, [Link], false);
// Score and Health
std::stringstream ss;
ss << "Score: " << score << " Health: " << health;
glColor3f(1, 1, 1);
drawText(10, HEIGHT - 20, [Link]());
if (gameOver) {
drawText(WIDTH / 2 - 50, HEIGHT / 2, "GAME OVER!");
drawText(WIDTH / 2 - 100, HEIGHT / 2 - 30, "Press ENTER to continue...");
}
else if (gameWin) {
drawText(WIDTH / 2 - 50, HEIGHT / 2, "YOU WIN!");
drawText(WIDTH / 2 - 100, HEIGHT / 2 - 30, "Press ENTER to continue...");
}
glutSwapBuffers();
}
void timer(int) {
update();
glutPostRedisplay();
glutTimerFunc(16, timer, 0); // ~60 FPS
}
void keyboardDown(unsigned char key, int, int) {
keys[key] = true;
if (key == 32) // Spacebar
fireBullet();
else if ((gameOver || gameWin) && key == 13) // Enter
resetGame();
}
void keyboardUp(unsigned char key, int, int) {
keys[key] = false;
}
void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, WIDTH, 0, HEIGHT);
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char** argv) {
srand(time(0));
spawnEnemies();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("OpenGL Tank Game");
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, WIDTH, 0, HEIGHT);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboardDown);
glutKeyboardUpFunc(keyboardUp);
glutTimerFunc(0, timer, 0);
glutMainLoop();
return 0;
}