C++入门——实现“坚持一百秒”游戏

参考

  1. 《C和C++游戏趣味编程》 童晶

坚持一百秒游戏

游戏主要思路是玩家通过鼠标控制火箭躲避一架UFO和越来越多的反弹子弹
在这里插入图片描述

背景显示

利用事先准备的background.png图片,在画面中显示背景图片:

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

int main()
{
	IMAGE im_bk;                     // 定义图像对象
	loadimage(&im_bk, _T("background.png"));
	initgraph(WIDTH, HEIGHT);
	putimage(0, 0, &im_bk);          // 显示背景图片
	_getch();
	return 0;
}

在这里插入图片描述

显示火箭

同背景显示一样,先定义im_rocket图像对象,然后导入火箭图片rocket.png。但火箭图片带有透明通道,使用putimage()绘制会使透明部分显示为黑色,于是导入EasyXPng.h头文件解决:

在这里插入图片描述

子弹

定义子弹类,在类中增加IMAGE图片对象,然后定义一个draw()成员函数显示子弹,并定义一个update()成员函数用于更新子弹的位置、速度:

class Bullet
{
public:
	IMAGE im_bullet;
	float x, y;                                 // 子弹坐标
	float vx, vy;                               // 子弹速度
	float 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;                         // 定义图像对象
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;           // 设置子弹的半径为其图片宽度的一半

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

void show()
{
	putimage(0, 0, &im_bk);                             // 显示背景
	bullet.draw();                                      // 显示子弹
	FlushBatchDraw();                                   // 批量绘制
	Sleep(10);
}

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

int main()
{
	startup();
	while (1)
	{
		show();
		updateWithoutInput();
	}
	return 0;
}
每隔2秒增加一颗子弹

定义数组bullet[]存储现有的子弹,每隔2秒随机初始化一颗新子弹,并更新现有子弹的速度和位置,并显示:

Bullet bullet[MaxBulletNum];
int bulletNum = 0;

void updateWithoutInput()
{
	static int lastSecond = 0;                          // 记录前一次程序运行了多少秒
	static int nowSecond = 0;                           // 记录当前程序运行了多少秒
	static clock_t start = clock();                     // 记录第一次运行时刻
	clock_t now = clock();                              // 获得当前时刻

	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();
	}
}

火箭

定义火箭类,与子弹类类似:

class Rocket
{
public:
	IMAGE im_rocket;
	float x, y;                                 // 火箭坐标
	float width, height;                        // 火箭图片的宽度、高度

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

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

在startup()中初始化火箭:

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

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

在updateWithInput()中,当鼠标移动时,更新火箭的位置:

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

碰撞判断与火箭爆炸

在Bullet类中添加成员函数isCollideRocket():

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;
	}
}

在火箭类中添加火箭爆炸图像对象,并修改显示火箭的draw():

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);
	}
}

初始化时火箭的islive设为1,在updateWithoutInput()中遍历所有子弹,任一子弹和火箭发生碰撞,则将火箭的islive设为0:

for (int i = 0; i < bulletNum; i++)
{
	bullet[i].update();
	if (bullet[i].isCollideRocket(rocket))
	{
		rocket.islive = 0;
		bullet[i].x = 5;                            // 当前子弹移开,防止重复碰撞
		bullet[i].y = 5;
		break;
	}
}

坚持时间与多条生命的显示

在Rocket类添加成员变量liveSecond,记录火箭存活了多少秒,并在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秒"), liveSecond);
	settextcolor(WHITE);
	settextstyle(40, 0, _T("黑体"));
	outtextxy(WIDTH * 0.85, 20, s);
}

然后,添加成员变量life记录火箭的生命数,添加成员函数updateWhenLifeLoss()处理火箭生命减少时的操作,另外,在draw()中实现在窗口左上角显示火箭生命数:

int life;

void updateWithLifeLost()
{
	life--;
}

void draw()
{
	for (int i = 0; i < life; i++)          // 左上角显示life个火箭图片
	{
		putimagePng(i * width * 0.9, 0, &im_rocket);
	}

	TCHAR s[20];                            // 窗口正上方显示坚持了多少秒
	setbkmode(TRANSPARENT);
	swprintf_s(s, _T("%d秒"), liveSecond);
	settextcolor(WHITE);
	settextstyle(40, 0, _T("黑体"));
	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);
	}
}

在这里插入图片描述

添加音效

导入winmm.lib库以支持对windows多媒体的编程:

#pragma comment(lib, "Winmm.lib")
背景音乐

在startup()中添加代码,重复播放game_music.mp3:

mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL); // 打开背景音乐
mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);               // 循环播放
爆炸音效

定义播放一次音乐函数:

void PlayMusicOnce(TCHAR fileName[80])
{
	TCHAR cmdString1[50];
	swprintf_s(cmdString1, _T("open %s alias tmpmusic"), fileName);    // 生成命令字符串
	mciSendString(_T("close tmpmusic"), NULL, 0, NULL);                // 先把前面一次的音乐关闭
	mciSendString(cmdString1, NULL, 0, NULL);                          // 打开音乐
	mciSendString(_T("play tmpmusic"), NULL, 0, NULL);                 // 仅播放一次
}

在Rocket()类的updateWhenLifeLost()成员函数中调用:

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

添加智能飞碟类

定义新类SmartUFO,继承自子弹类:

class SmartUFO : public Bullet
{
public:
	void updateVelforTarget(Rocket targetRocket)
	{
		float scalar = 1 * rand() / double(RAND_MAX) + 1;             // 让飞碟的速度瞄向目标火箭
		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;
		}
	}
};

在startup()中初始化飞碟:

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

在updateWithoutInput()中,每隔1秒,根据火箭位置设定ufo的速度:

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

判断飞碟是否和火箭碰撞,如果碰撞就让火箭减命:

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

完整代码

#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)                           // 精确延时函数
{
	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);    // 生成命令字符串
	mciSendString(_T("close tmpmusic"), NULL, 0, NULL);                // 先把前面一次的音乐关闭
	mciSendString(cmdString1, NULL, 0, NULL);                          // 打开音乐
	mciSendString(_T("play tmpmusic"), NULL, 0, NULL);                 // 仅播放一次
}

class Rocket
{
public:
	IMAGE im_rocket;
	IMAGE im_blowup;
	float x, y;                                 // 火箭坐标
	float width, height;                        // 火箭图片的宽度、高度
	int liveSecond;
	int life;

	void draw()
	{
		for (int i = 0; i < life; i++)          // 左上角显示life个火箭图片
		{
			putimagePng(i * width * 0.9, 0, &im_rocket);
		}

		TCHAR s[20];                            // 窗口正上方显示坚持了多少秒
		setbkmode(TRANSPARENT);
		swprintf_s(s, _T("%d秒"), liveSecond);
		settextcolor(WHITE);
		settextstyle(40, 0, _T("黑体"));
		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;                                 // 子弹坐标
	float vx, vy;                               // 子弹速度
	float 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;             // 让飞碟的速度瞄向目标火箭
		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;                         // 定义图像对象
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); // 打开背景音乐
	mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);               // 循环播放
}

void show()
{
	putimage(0, 0, &im_bk);                             // 显示背景
	for (int i = 0; i < bulletNum; i++)
	{
		bullet[i].draw();                               // 显示子弹
	}
	rocket.draw();
	ufo.draw();
	FlushBatchDraw();                                   // 批量绘制
	sleep(10);
}

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

	static int lastSecond = 0;                          // 记录前一次程序运行了多少秒
	static int nowSecond = 0;                           // 记录当前程序运行了多少秒
	static clock_t start = clock();                     // 记录第一次运行时刻
	clock_t now = clock();                              // 获得当前时刻

	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;                            // 当前子弹移开,防止重复碰撞
			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;
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值