Introduction to C + + - realize the game of "persistence for 100 seconds"

Keywords: C++ Class easyx

reference resources

  1. Interesting programming of C and C + + Games by Tong Jing

Stick to the 100 second game

The main idea of the game is that players use the mouse to control rockets to avoid a UFO and more and more rebound bullets

Background display

Use the prepared background.png picture to display the background picture in the picture:

#include <graphics.h>
#include <conio.h>
#define WIDTH 560
#define HEIGHT 800

int main()
{
	IMAGE im_bk;                     // Define image objects
	loadimage(&im_bk, _T("background.png"));
	initgraph(WIDTH, HEIGHT);
	putimage(0, 0, &im_bk);          // Show background picture
	_getch();
	return 0;
}

Display rocket

Like the background display, define im first_ Rocket image object, and then import rocket image rocket.png. However, the rocket image has a transparent channel. If it is drawn with putimage(), the transparent part will be displayed in black, so import the EasyXPng.h header file to solve the problem:

bullet

Define the bullet class, add an IMAGE image object to the class, then define a draw() member function to display bullets, and define an update() member function to update the position and speed of bullets:

class Bullet
{
public:
	IMAGE im_bullet;
	float x, y;                                 // Bullet coordinates
	float vx, vy;                               // Bullet speed
	float radius;                               // Bullet radius

	void draw()
	{
		putimagePng(x - radius, y - radius, &im_bullet);
	}

	void update()
	{
		x += vx;
		y += vy;
		if (x <= 0 || x >= WIDTH)
		{
			vx = -vx;
		}
		if (y <= 0 || y >= HEIGHT)
		{
			vy = -vy;
		}
	}
};

IMAGE im_bk, im_bullet;                         // Define image objects
Bullet bullet;

void startup()
{
	loadimage(&im_bk, _T("background.png"));
	loadimage(&im_bullet, _T("bullet.png"));
	bullet.x = WIDTH / 2;
	bullet.y = HEIGHT / 2;
	bullet.vx = 2;
	bullet.vy = 2;
	bullet.im_bullet = im_bullet;
	bullet.radius = im_bullet.getwidth() / 2;           // Set the radius of the bullet to half the width of its picture

	initgraph(WIDTH, HEIGHT);
	BeginBatchDraw();
}

void show()
{
	putimage(0, 0, &im_bk);                             // Display background
	bullet.draw();                                      // Show bullets
	FlushBatchDraw();                                   // Batch drawing
	Sleep(10);
}

void updateWithoutInput()
{
	bullet.update();
}

int main()
{
	startup();
	while (1)
	{
		show();
		updateWithoutInput();
	}
	return 0;
}

Add a bullet every 2 seconds

Define the array bullet [] to store existing bullets, initialize a new bullet randomly every 2 seconds, update the speed and position of existing bullets, and display:

Bullet bullet[MaxBulletNum];
int bulletNum = 0;

void updateWithoutInput()
{
	static int lastSecond = 0;                          // Record how many seconds the previous program ran
	static int nowSecond = 0;                           // Record how many seconds the current program has run
	static clock_t start = clock();                     // Record the first running time
	clock_t now = clock();                              // Get the current moment

	nowSecond = (int(now - start) / CLOCKS_PER_SEC);
	if (nowSecond == lastSecond + 2)
	{
		lastSecond = nowSecond;
		if (bulletNum < MaxBulletNum)
		{
			bullet[bulletNum].x = WIDTH / 2;
			bullet[bulletNum].y = 10;
			float angle = (rand() / double(RAND_MAX) - 0.5) * 0.9 * PI;
			float scalar = 2 * rand() / double(RAND_MAX) + 2;
			bullet[bulletNum].vx = scalar * sin(angle);
			bullet[bulletNum].vy = scalar * cos(angle);
			bullet[bulletNum].im_bullet = im_bullet;
			bullet[bulletNum].radius = im_bullet.getwidth() / 2;
		}
		bulletNum++;
	}
	
	for (int i = 0; i < bulletNum; i++)
	{
		bullet[i].update();
	}
}

rocket

Define the rocket class, similar to the bullet class:

class Rocket
{
public:
	IMAGE im_rocket;
	float x, y;                                 // Rocket coordinates
	float width, height;                        // Width and height of rocket picture

	void draw()
	{
		putimagePng(x - width / 2, y - height / 2, &im_rocket);
	}

	void update(float mx, float my)
	{
		x = mx;
		y = my;
	}
};

Initialize the rocket in startup():

loadimage(&im_rocket, _T("rocket.png"));

rocket.im_rocket = im_rocket;
rocket.width = im_rocket.getwidth();
rocket.height = im_rocket.getheight();

In updateWithInput(), when the mouse moves, the position of the rocket is updated:

void updateWithInput()
{
	ExMessage e;
	while (peekmessage(&e))
	{
		if (e.message == WM_MOUSEMOVE)
		{
			rocket.update(e.x, e.y);
		}
	}
}

Collision judgment and rocket explosion

Add the member function isCollideRocket() in the Bullet class:

int isCollideRocket(Rocket rocket)
{
	float distance_x = fabs(rocket.x - x);
	float distance_y = fabs(rocket.y - y);
	if (distance_x < rocket.width / 2 && distance_y < rocket.height / 2)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

Add a rocket explosion image object in the rocket class and modify the draw() that displays the rocket:

IMAGE im_blowup;
int islive;

void draw()
{
	if (islive > 0)
	{
		putimagePng(x - width / 2, y - height / 2, &im_rocket);
	}
	else
	{
		putimagePng(x - width / 2, y - height / 2, &im_blowup);
	}
}

During initialization, the islive of the rocket is set to 1, and all bullets are traversed in updateWithoutInput(). If any bullet collides with the rocket, the islive of the rocket is set to 0:

for (int i = 0; i < bulletNum; i++)
{
	bullet[i].update();
	if (bullet[i].isCollideRocket(rocket))
	{
		rocket.islive = 0;
		bullet[i].x = 5;                            // Move the current bullet away to prevent repeated collision
		bullet[i].y = 5;
		break;
	}
}

Adhere to the display of time and multiple lives

Add the member variable liveSecond in the Rocket class, record the number of seconds the Rocket survived, and draw it in draw():

int liveSecond;

void draw()
{
	if (islive > 0)
	{
		putimagePng(x - width / 2, y - height / 2, &im_rocket);
	}
	else
	{
		putimagePng(x - width / 2, y - height / 2, &im_blowup);
	}
	TCHAR s[20];
	setbkmode(TRANSPARENT);
	swprintf_s(s, _T("%d second"), liveSecond);
	settextcolor(WHITE);
	settextstyle(40, 0, _T("Blackbody"));
	outtextxy(WIDTH * 0.85, 20, s);
}

Then, add the member variable life to record the life of the rocket, add the member function updateWhenLifeLoss() to handle the operation when the life of the rocket decreases, and display the life of the rocket in the upper left corner of the implementation window in draw():

int life;

void updateWithLifeLost()
{
	life--;
}

void draw()
{
	for (int i = 0; i < life; i++)          // A picture of life rocket is displayed in the upper left corner
	{
		putimagePng(i * width * 0.9, 0, &im_rocket);
	}

	TCHAR s[20];                            // Just above the window shows how many seconds it lasted
	setbkmode(TRANSPARENT);
	swprintf_s(s, _T("%d second"), liveSecond);
	settextcolor(WHITE);
	settextstyle(40, 0, _T("Blackbody"));
	outtextxy(WIDTH * 0.85, 20, s);

	if (life > 0)
	{
		putimagePng(x - width / 2, y - height / 2, &im_rocket);
	}
	else
	{
		putimagePng(x - width / 2, y - height / 2, &im_blowup);
	}
}

Add sound effects

Import winmm.lib library to support windows multimedia programming:

#pragma comment(lib, "Winmm.lib")

background music

Add code in startup() and play the game repeatedly_ music.mp3:

mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL); // Turn on background music
mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);               // Loop Playback

Explosion sound

Define the function to play music once:

void PlayMusicOnce(TCHAR fileName[80])
{
	TCHAR cmdString1[50];
	swprintf_s(cmdString1, _T("open %s alias tmpmusic"), fileName);    // Generate command string
	mciSendString(_T("close tmpmusic"), NULL, 0, NULL);                // Turn off the previous music first
	mciSendString(cmdString1, NULL, 0, NULL);                          // Turn on the music
	mciSendString(_T("play tmpmusic"), NULL, 0, NULL);                 // Play only once
}

In the updateWhenLifeLost() member function of Rocket() class, we call:

void updateWithLifeLost()
{
	PlayMusicOnce((TCHAR*)_T("explode.mp3"));
	life--;
}

Add smart UFO class

Define a new class SmartUFO, which inherits from bullet class:

class SmartUFO : public Bullet
{
public:
	void updateVelforTarget(Rocket targetRocket)
	{
		float scalar = 1 * rand() / double(RAND_MAX) + 1;             // Let the speed of the flying saucer aim at the target rocket
		if (targetRocket.x > x)
		{
			vx = scalar;
		}
		else if (targetRocket.x < x)
		{
			vx = -scalar;
		}
		if (targetRocket.y > y)
		{
			vy = scalar;
		}
		else if (targetRocket.y < y)
		{
			vy = -scalar;
		}
	}
};

Initialize UFO in startup():

ufo.x = WIDTH / 2;
ufo.y = 10;
ufo.im_bullet = im_UFO;
ufo.radius = im_UFO.getwidth() / 2;
ufo.updateVelforTarget(rocket);

In updateWithoutInput(), set the ufo speed according to the rocket position every 1 second:

if (nowSecond == lastSecond + 1)
{
	ufo.updateVelforTarget(rocket);
}

Judge whether the flying saucer collides with the rocket. If it collides, the rocket will lose its life:

ufo.update();
if (ufo.isCollideRocket(rocket))
{
	rocket.updateWithLifeLost();
	ufo.x = 5;
	ufo.y = 5;
}

Complete code

#include <graphics.h>
#include <conio.h>
#include <time.h>
#include "EasyXPng.h"
#define WIDTH 560
#define HEIGHT 800
#define MaxBulletNum 200
#pragma comment(lib, "Winmm.lib")

void sleep(DWORD ms)                           // Exact delay function
{
	static DWORD oldtime = GetTickCount();
	while (GetTickCount() - oldtime < ms)
	{
		Sleep(1);
	}
	oldtime = GetTickCount();
}

void PlayMusicOnce(TCHAR fileName[80])
{
	TCHAR cmdString1[50];
	swprintf_s(cmdString1, _T("open %s alias tmpmusic"), fileName);    // Generate command string
	mciSendString(_T("close tmpmusic"), NULL, 0, NULL);                // Turn off the previous music first
	mciSendString(cmdString1, NULL, 0, NULL);                          // Turn on the music
	mciSendString(_T("play tmpmusic"), NULL, 0, NULL);                 // Play only once
}

class Rocket
{
public:
	IMAGE im_rocket;
	IMAGE im_blowup;
	float x, y;                                 // Rocket coordinates
	float width, height;                        // Width and height of rocket picture
	int liveSecond;
	int life;

	void draw()
	{
		for (int i = 0; i < life; i++)          // A picture of life rocket is displayed in the upper left corner
		{
			putimagePng(i * width * 0.9, 0, &im_rocket);
		}

		TCHAR s[20];                            // Just above the window shows how many seconds it lasted
		setbkmode(TRANSPARENT);
		swprintf_s(s, _T("%d second"), liveSecond);
		settextcolor(WHITE);
		settextstyle(40, 0, _T("Blackbody"));
		outtextxy(WIDTH * 0.85, 20, s);

		if (life > 0)
		{
			putimagePng(x - width / 2, y - height / 2, &im_rocket);
		}
		else
		{
			putimagePng(x - width / 2, y - height / 2, &im_blowup);
		}
	}

	void update(float mx, float my)
	{
		x = mx;
		y = my;
	}

	void updateWithLifeLost()
	{
		PlayMusicOnce((TCHAR*)_T("explode.mp3"));
		life--;
	}
};

class Bullet
{
public:
	IMAGE im_bullet;
	float x, y;                                 // Bullet coordinates
	float vx, vy;                               // Bullet speed
	float radius;                               // Bullet radius

	void draw()
	{
		putimagePng(x - radius, y - radius, &im_bullet);
	}

	void update()
	{
		x += vx;
		y += vy;
		if (x <= 0 || x >= WIDTH)
		{
			vx = -vx;
		}
		if (y <= 0 || y >= HEIGHT)
		{
			vy = -vy;
		}
	}

	int isCollideRocket(Rocket rocket)
	{
		float distance_x = fabs(rocket.x - x);
		float distance_y = fabs(rocket.y - y);
		if (distance_x < rocket.width / 2 && distance_y < rocket.height / 2)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
};

class SmartUFO : public Bullet
{
public:
	void updateVelforTarget(Rocket targetRocket)
	{
		float scalar = 1 * rand() / double(RAND_MAX) + 1;             // Let the speed of the flying saucer aim at the target rocket
		if (targetRocket.x > x)
		{
			vx = scalar;
		}
		else if (targetRocket.x < x)
		{
			vx = -scalar;
		}
		if (targetRocket.y > y)
		{
			vy = scalar;
		}
		else if (targetRocket.y < y)
		{
			vy = -scalar;
		}
	}
};

IMAGE im_bk, im_bullet, im_rocket, im_blowup, im_UFO;                         // Define image objects
Bullet bullet[MaxBulletNum];
Rocket rocket;
SmartUFO ufo;
int bulletNum = 0;

void startup()
{
	srand(time(0));
	loadimage(&im_bk, _T("background.png"));
	loadimage(&im_bullet, _T("bullet.png"));
	loadimage(&im_rocket, _T("rocket.png"));
	loadimage(&im_blowup, _T("blowup.png"));
	loadimage(&im_UFO, _T("ufo.png"));

	rocket.im_rocket = im_rocket;
	rocket.im_blowup = im_blowup;
	rocket.width = im_rocket.getwidth();
	rocket.height = im_rocket.getheight();
	rocket.life = 5;

	ufo.x = WIDTH / 2;
	ufo.y = 10;
	ufo.im_bullet = im_UFO;
	ufo.radius = im_UFO.getwidth() / 2;
	ufo.updateVelforTarget(rocket);

	initgraph(WIDTH, HEIGHT);
	BeginBatchDraw();

	mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL); // Turn on background music
	mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);               // Loop Playback
}

void show()
{
	putimage(0, 0, &im_bk);                             // Display background
	for (int i = 0; i < bulletNum; i++)
	{
		bullet[i].draw();                               // Show bullets
	}
	rocket.draw();
	ufo.draw();
	FlushBatchDraw();                                   // Batch drawing
	sleep(10);
}

void updateWithoutInput()
{
	if (rocket.life <= 0)
	{
		return;
	}

	static int lastSecond = 0;                          // Record how many seconds the previous program ran
	static int nowSecond = 0;                           // Record how many seconds the current program has run
	static clock_t start = clock();                     // Record the first running time
	clock_t now = clock();                              // Get the current moment

	nowSecond = (int(now - start) / CLOCKS_PER_SEC);
	rocket.liveSecond = nowSecond;
	if (nowSecond == lastSecond + 2)
	{
		lastSecond = nowSecond;
		if (bulletNum < MaxBulletNum)
		{
			bullet[bulletNum].x = WIDTH / 2;
			bullet[bulletNum].y = 10;
			float angle = (rand() / double(RAND_MAX) - 0.5) * 0.9 * PI;
			float scalar = 2 * rand() / double(RAND_MAX) + 2;
			bullet[bulletNum].vx = scalar * sin(angle);
			bullet[bulletNum].vy = scalar * cos(angle);
			bullet[bulletNum].im_bullet = im_bullet;
			bullet[bulletNum].radius = im_bullet.getwidth() / 2;
		}
		bulletNum++;
	}
	if (nowSecond == lastSecond + 1)
	{
		ufo.updateVelforTarget(rocket);
	}
	
	for (int i = 0; i < bulletNum; i++)
	{
		bullet[i].update();
		if (bullet[i].isCollideRocket(rocket))
		{
			rocket.updateWithLifeLost();
			bullet[i].x = 5;                            // Move the current bullet away to prevent repeated collision
			bullet[i].y = 5;
			break;
		}
	}

	ufo.update();
	if (ufo.isCollideRocket(rocket))
	{
		rocket.updateWithLifeLost();
		ufo.x = 5;
		ufo.y = 5;
	}
}

void updateWithInput()
{
	if (rocket.life <= 0)
	{
		return;
	}

	ExMessage e;
	while (peekmessage(&e))
	{
		if (e.message == WM_MOUSEMOVE)
		{
			rocket.update(e.x, e.y);
		}
	}
}

int main()
{
	startup();
	while (1)
	{
		show();
		updateWithoutInput();
		updateWithInput();
	}
	return 0;
}

Posted by rpmorrow on Thu, 21 Oct 2021 00:17:31 -0700