C++入门——实现“火柴人跑酷”游戏

参考

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

火柴人跑酷

游戏的思路是,玩家通过键盘控制火柴人的奔跑和跳跃,躲避蝙蝠到达终点。游戏地图随机生成,随着关卡数增加,游戏难度越来越大

在这里插入图片描述

定义Player类

class Player
{
public:
	IMAGE im_show;                               // 当前时刻要显示的图像
	float x_left, y_bottom;                      // 左下角位置
	float vx, vy;                                // 速度
	float width, height;                         // 图片的宽度、高度

	void draw()
	{
		putimagePng(x_left, y_bottom - height, &im_show);
	}

	void initialize()
	{
		loadimage(&im_show, _T("standright.png"));
		width = im_show.getwidth();
		height = im_show.getheight();
		updateXY(WIDTH / 2, HEIGHT / 2);
		vx = 10;
		vy = 10;
	}

	void updateXY(float mx, float my)
	{
		x_left = mx;
		y_bottom = my;
	}
};

定义全局变量,初始化并显示:

IMAGE im_land;
IMAGE im_bk;
Player player;

void startup()
{
	player.initialize();
	loadimage(&im_land, _T("land.png"));
	loadimage(&im_bk, _T("landscape1.png"));

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

void show()
{
	putimage(-100, -100, &im_bk);
	putimage(WIDTH / 2, HEIGHT / 2, &im_land);
	player.draw();
	FlushBatchDraw();
}

在这里插入图片描述

通过按键控制角色移动:

void updateWithInput()
{
	if (_kbhit())
	{
		char input = getch();
		if (input == 'A')
		{
			player.x_left -= player.vx;
		}
		if (input == 'D')
		{
			player.x_left += player.vx;
		}
		if (input == 'W')
		{
			player.y_bottom -= player.vy;
		}
		if (input == 'S')
		{
			player.y_bottom += player.vy;
		}
	}
}

异步输入

利用异步输入函数GetAsyncKeyState(),可以同时识别两个按键被按下的情况,玩家可以用A、S、D、W或左、右、上、下方向键控制火柴人移动:

void updateWithInput()
{
	if (_kbhit())
	{
		if (GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState('A'))
		{
			player.x_left -= player.vx;
		}
		if (GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState('D'))
		{
			player.x_left += player.vx;
		}
		if (GetAsyncKeyState(VK_UP) || GetAsyncKeyState('W'))
		{
			player.y_bottom -= player.vy;
		}
		if (GetAsyncKeyState(VK_DOWN) || GetAsyncKeyState('S'))
		{
			player.y_bottom += player.vy;
		}
	}
}

枚举类型状态切换

火柴人会有向左站立、向右站立、向左奔跑、向右奔跑、向左跳跃、向右跳跃、死亡这7种状态:

在Player类中新增成员变量存储向右站立图像、向左站立图像和角色状态:

IMAGE im_standright;
IMAGE im_standleft;
PlayerStatus playerStatus;                   // 当前状态

在initialize()中对新增成员变量初始化:

loadimage(&im_standright, _T("standright.png"));
loadimage(&im_standleft, _T("standleft.png"));

playerStatus = standright;
im_show = im_standright;

在updateWithInput()中,当键盘控制角色移动时,角色站立方向也应变化:

if (GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState('A'))
{
	player.x_left -= player.vx;
	player.playerStatus = standleft;
}
if (GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState('D'))
{
	player.x_left += player.vx;
	player.playerStatus = standright;
}

添加奔跑动画

在Player类中添加成员变量ims_runright存储向右奔跑的8张图片,并初始化:

vector<IMAGE> ims_runright;
int animId;

void initialize()
{
	loadimage(&im_standright, _T("standright.png"));
	loadimage(&im_standleft, _T("standleft.png"));

	playerStatus = standright;
	im_show = im_standright;
	width = im_standright.getwidth();
	height = im_standright.getheight();

	TCHAR filename[80];
	for (int i = 0; i <= 7; i++)
	{
		swprintf_s(filename, _T("runright%d.png"), i);
		IMAGE im;
		loadimage(&im, filename);
		ims_runright.push_back(im);
	}

	animId = 0;

	updateXY(WIDTH / 2, HEIGHT / 2);
	vx = 10;
	vy = 10;
}

新增成员函数runRight(),把角色向右奔跑涉及的坐标更新,状态切换和动画效果实现:

void runRight()
{
	x_left += vx;
	if (playerStatus != runright)
	{
		playerStatus = runright;
		animId = 0;
	}
	else
	{
		animId++;
		if (animId >= ims_runright.size())
		{
			animId = 0;
		}
	}
	im_show = ims_runright[animId];
}

实现跳跃

在Player类中添加成员变量存储向右、向左跳跃的图片和重力加速度,并初始化:

IMAGE im_jumpright;
IMAGE im_jumpleft;
float gravity;

void initialize()
{
	loadimage(&im_jumpright, _T("jumpright.png"));
	loadimage(&im_jumpleft, _T("jumpleft.png"));

	vx = 10;
	vy = 0;
	gravity = 3;
}

添加beginJump(),控制动画切换并给角色一个向上初速度:

void beginJump()
{
	if (playerStatus != jumpleft && playerStatus != jumpright)           // 已在空中不能起跳
	{
		if (playerStatus == runleft || playerStatus == standleft)
		{
			im_show = im_jumpleft;
			playerStatus = jumpleft;
		}
		else if (playerStatus == runright || playerStatus == standright)
		{
			im_show = im_jumpright;
			playerStatus = jumpright;
		}
		vy = -30;
	}
}

新增成员函数updateYcoordinate(),自动完成自由落体运动:

void updateYcoordinate()
{
	if (playerStatus == jumpleft || playerStatus == jumpright)
	{
		vy += gravity;
		y_bottom += vy;

		if (y_bottom > HEIGHT / 2)
		{
			y_bottom = HEIGHT / 2;                     // 保证正好落在地面上
			if (playerStatus == jumpleft)
			{
				playerStatus = standleft;
			}
			if (playerStatus == jumpright)
			{
				playerStatus = standright;
			}
		}
	}
}

地面类

class Land
{
public:
	IMAGE im_land;
	float left_x, right_x, top_y;
	float land_width, land_height;

	void initialize()
	{
		loadimage(&im_land, _T("land.png"));
		land_width = im_land.getwidth();
		land_height = im_land.getheight();
		left_x = WIDTH / 2;
		right_x = left_x + land_width;
		top_y = HEIGHT / 2;
	}

	void draw()
	{
		putimage(left_x, top_y, &im_land);
	}
};

场景类

场景类包括背景图片和多个地面对象

class Scene
{
public:
	IMAGE im_bk;
	vector<Land> lands;

	void draw()
	{
		putimage(-100, -100, &im_bk);
		for (int i = 0; i < lands.size(); i++)
		{
			lands[i].draw();
		}
	}

	void initialize()
	{
		loadimage(&im_bk, _T("landscape1.png"));
		for (int i = 1; i <= 6; i++)
		{
			Land land;
			land.initialize();
			land.left_x = i * land.land_width;
			land.top_y = (i - 1) / 6.0 * HEIGHT;
			lands.push_back(land);
		}
	}
};

在这里插入图片描述

碰撞检测

产生一些随机地面:

void initialize()
{
	loadimage(&im_bk, _T("landscape1.png"));
	lands.clear();
	for (int i = 1; i < 10; i++)
	{
		Land land;
		land.initialize();
		land.left_x = i * land.land_width;
		land.right_x = land.left_x + land.land_width;
		land.top_y = HEIGHT / 2 + rand() % 2 * HEIGHT / 10;
		lands.push_back(land);
	}
}

为Player类添加成员函数isOnLand(),判断角色是否在地面上:

int isOnLand(Land& land, float ySpeed)
{
	float x_right = x_left + width;
	if (ySpeed <= 0)
	{
		ySpeed = 0;
	}
	if (land.left_x - x_left <= width * 0.6 && x_right - land.right_x <= width * 0.6 && abs(y_bottom - land.top_y) <= 5 + ySpeed)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

添加成员函数isNotAllLands(),判断角色是否不在所有地面上:

int isNotOnAllLands(vector<Land>& lands, float speed)
{
	for (int i = 0; i < lands.size(); i++)
	{
		if (isOnLand(lands[i], speed))
		{
			return 0;
		}
	}
	return 1;
}

修改runRight()和runLeft(),如果角色不在任何地面上,进入跳跃状态:

void runRight(Scene& scene)
{
	x_left += vx;
	if (isNotOnAllLands(scene.lands, vy))
	{
		im_show = im_jumpright;
		playerStatus = jumpright;
		return;
	}
}

void runLeft(Scene& scene)
{
	x_left -= vx;
	if (isNotOnAllLands(scene.lands, vy))
	{
		im_show = im_jumpleft;
		playerStatus = jumpright;
		return;
	}
}

修改updateYcoordinate(),使角色碰到地面再停止下落:

void updateYcoordinate(Scene& scene)
{
	if (playerStatus == jumpleft || playerStatus == jumpright)
	{
		vy += gravity;
		y_bottom += vy;
		for (int i = 0; i < scene.lands.size(); i++)
		{
			if (isOnLand(scene.lands[i], vy))
			{
				y_bottom = scene.lands[i].top_y;
				if (playerStatus == jumpleft)
				{
					playerStatus = standleft;
				}
				if (playerStatus == jumpright)
				{
					playerStatus = standright;
				}
				break;
			}
		}
	}
}

在这里插入图片描述

实现相对运动

让角色位置不变,而背景、地面做相对运动:

class Land
{
	void draw(float px, float py)
	{
		putimage(left_x - px, top_y - py, &im_land);      // 绘制地面有一个偏移量
	}
};

class Scene
{
	void draw(float px, float py)
	{
		putimage(-px / 20, -100 - py / 20, &im_bk);       // 绘制背景有一个偏移量,实现前后景
		for (int i = 0; i < lands.size(); i++)
		{
			lands[i].draw(px, py);
		}
	}
}

class Player
{
	void draw()
	{
		putimagePng(WIDTH / 2, HEIGHT / 2 - height, &im_show);  // 角色位置不动
	}
}

无尽关卡与胜负判断

在updateWithoutInput()中,角色跑到最后一个地面上,这一关游戏胜利,level加1,进入下一关;角色掉落画面底部,游戏失败,重新开始这一关:

void updateWithoutInput()
{
	player.updateYcoordinate(scene);

	int landSize = scene.lands.size();
	if (player.x_left > scene.lands[landSize - 1].left_x && abs(player.y_bottom - scene.lands[landSize - 1].top_y) <= 2) // 游戏胜利
	{
		scene.lastlevel = scene.level;
		scene.level++;
		scene.initialize();
		player.initialize();
	}
	else if (player.y_bottom > 1.5 * HEIGHT)                   // 角色落到画面底部,游戏失败
	{
		scene.lastlevel = scene.level;
		scene.initialize();
		player.initialize();
	}
}

随着level增加,生成的地图更大、地面之间更不连续:

void initialize()
{
	loadimage(&im_bk, _T("landscape1.png"));
	if (lands.size() == 0)                             // 默认从第一关开始
	{
		level = 1;
		lastlevel = 1;
	}

	if (lands.size() == 0 || level > lastlevel)        // 如果不升级,则不重新生成场景
	{
		lands.clear();

		Land land1;                                    // 第一关land要在游戏角色下方
		land1.initialize();
		lands.push_back(land1);

		for (int i = 1; i < 10 + level * 2; i++)
		{
			Land land2;
			land2.initialize();
			int r1 = randBetweenMinMax(1, 30);
			if (r1 > level)
			{
				land2.left_x = land1.left_x + land2.land_width;
			}
			else
			{
				land2.left_x = land1.left_x + 2 * land2.land_width;
			}

			int r3 = randBetweenMinMax(1, 20);
			if (r1 > level)
			{
				land2.top_y = land1.top_y;
			}
			else
			{
				int r3 = randBetweenMinMax(-1, 1);
				land2.top_y = WIDTH / 2 + HEIGHT / 10 * r3;
			}
			land2.right_x = land2.left_x + land2.land_width;
			lands.push_back(land2);
			land1 = land2;
		}
	}
}

敌人类

class Enemy
{
public:
	IMAGE im_enemy;
	float x, y;
	float enemy_width, enemy_height;
	float x_min, x_max;                                   // 敌人移动的x坐标最大值、最小值
	float vx;                                             // x方向速度

	void initialize()
	{
		loadimage(&im_enemy, _T("bat.png"));
		enemy_width = im_enemy.getwidth();
		enemy_height = im_enemy.getheight();
	}

	void draw(float px, float py)
	{
		putimagePng(x - enemy_width / 2 - px, y - enemy_height / 2 - py, &im_enemy);
	}

	void update()
	{
		x += vx;
		if (x > x_max || x < x_min)
		{
			vx = -vx;
		}
	}
};

在场景中添加Enemy变量,并初始化:

vector<Enemy> enemies;

void draw(float px, float py)
{
	putimage(-px / 20, -100 - py / 20, &im_bk);       // 绘制背景有一个偏移量,实现前后景
	for (int i = 0; i < lands.size(); i++)
	{
		lands[i].draw(px, py);
	}

	for (int i = 0; i < enemies.size(); i++)
	{
		enemies[i].draw(px, py);
	}
}

void initialize()
{
	if (lands.size() == 0 || level > lastlevel)        // 如果不升级,则不重新生成场景
	{
		enemies.clear();
		int numEnemy = (level + 3) / 5;
		int idStep = lands.size() / (numEnemy + 1);       // 敌人在scene中均匀分布
		for (int j = 1; j <= numEnemy; j++)
		{
			Enemy enemy;
			enemy.initialize();
			int landId = j * idStep;
			enemy.x = lands[landId].left_x + lands[landId].land_width / 2;
			enemy.y = lands[landId].top_y - enemy.enemy_height;

			float movingRange = enemy.enemy_width * (3 + level / 15.0);

			enemy.x_min = enemy.x - movingRange;
			enemy.x_max = enemy.x + movingRange;
			enemy.vx = 2 + level / 10.0;

			enemies.push_back(enemy);
		}

	}
}

在Player类中,添加成员函数isCollideEnemy()判断角色是否和蝙蝠发生碰撞:

int isCollideEnemy(Enemy& enemy)
{
	float x_center = x_left + width / 2;
	float y_center = y_bottom - height / 2;
	if (abs(enemy.x - x_center) <= enemy.enemy_width * 0.5 && abs(enemy.y - y_center) <= enemy.enemy_height * 0.5)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

在updateWithoutInput()中,让所有敌人水平移动,如果与角色碰撞,游戏失败:

for (int i = 0; i < scene.enemies.size(); i++)
{
	scene.enemies[i].update();                              // 敌人自动更新位置
	if (player.isCollideEnemy(scene.enemies[i]))
	{
		scene.lastlevel = scene.level;
		scene.initialize();
		player.initialize();
	}
}

添加音效

在startup()中添加背景音乐:

mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL);
mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);

添加胜利音效:

PlayMusicOnce(_T("success.mp3"));

添加失败音效:

PlayMusicOnce(_T("warning.mp3"));

添加五角星

在每一关的终点处放置五角星:

IMAGE im_star;
void draw(float px, float py)
{
	// 在最后一个地面上方,放置五角星
	putimagePng(lands[lands.size() - 1].left_x + im_star.getwidth() - px, lands[lands.size() - 1].top_y - im_star.getheight() - py, &im_star);
}

在这里插入图片描述

完整代码

#include <graphics.h>
#include <conio.h>
#include <time.h>
#include "EasyXPng.h"
#include "Timer.h"
#include <vector>
using namespace std;
#pragma comment(lib,"Winmm.lib")

#define WIDTH 800
#define HEIGHT 600

enum PlayerStatus
{
	standleft, standright, runleft, runright, jumpleft, jumpright, die
};

int randBetweenMinMax(int min, int max)
{
	int r = rand() % (max - min + 1) + min;
	return r;
}

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 Land
{
public:
	IMAGE im_land;
	float left_x, right_x, top_y;
	float land_width, land_height;

	void initialize()
	{
		loadimage(&im_land, _T("land.png"));
		land_width = im_land.getwidth();
		land_height = im_land.getheight();
		left_x = WIDTH / 2;
		right_x = left_x + land_width;
		top_y = HEIGHT / 2;
	}

	void draw(float px, float py)
	{
		putimage(left_x - px, top_y - py, &im_land);      // 绘制地面有一个偏移量
	}
};

class Enemy
{
public:
	IMAGE im_enemy;
	float x, y;
	float enemy_width, enemy_height;
	float x_min, x_max;                                   // 敌人移动的x坐标最大值、最小值
	float vx;                                             // x方向速度

	void initialize()
	{
		loadimage(&im_enemy, _T("bat.png"));
		enemy_width = im_enemy.getwidth();
		enemy_height = im_enemy.getheight();
	}

	void draw(float px, float py)
	{
		putimagePng(x - enemy_width / 2 - px, y - enemy_height / 2 - py, &im_enemy);
	}

	void update()
	{
		x += vx;
		if (x > x_max || x < x_min)
		{
			vx = -vx;
		}
	}
};

class Scene
{
public:
	IMAGE im_bk;
	IMAGE im_star;
	vector<Land> lands;
	vector<Enemy> enemies;
	int level;
	int lastlevel;

	void draw(float px, float py)
	{
		putimage(-px / 20, -100 - py / 20, &im_bk);       // 绘制背景有一个偏移量,实现前后景
		for (int i = 0; i < lands.size(); i++)
		{
			lands[i].draw(px, py);
		}

		for (int i = 0; i < enemies.size(); i++)
		{
			enemies[i].draw(px, py);
		}
		// 在最后一个地面上方,放置五角星
		putimagePng(lands[lands.size() - 1].left_x + im_star.getwidth() - px, lands[lands.size() - 1].top_y - im_star.getheight() - py, &im_star);

		// 显示这是第几关
		TCHAR s[20]; // 字符串
		setbkmode(TRANSPARENT); // 文字透明显示
		swprintf_s(s, _T("第 %d 关"), level); // 生成文字字符串
		settextcolor(RGB(0, 50, 200));  // 设置文字颜色
		settextstyle(30, 0, _T("黑体")); // 设置文字大小、字体
		outtextxy(WIDTH * 0.45, 30, s); // 输出文字
	}

	void initialize()
	{
		TCHAR filename[80];
		int i = level % 9 + 1;
		swprintf_s(filename, _T("landscape%d.png"), i);
		loadimage(&im_bk, filename);
		loadimage(&im_star, _T("star.png"));

		if (lands.size() == 0)                             // 默认从第一关开始
		{
			level = 1;
			lastlevel = 1;
		}

		if (lands.size() == 0 || level > lastlevel)        // 如果不升级,则不重新生成场景
		{
			lands.clear();

			Land land1;                                    // 第一关land要在游戏角色下方
			land1.initialize();
			lands.push_back(land1);

			for (int i = 1; i < 10 + level * 2; i++)
			{
				Land land2;
				land2.initialize();
				int r1 = randBetweenMinMax(1, 30);
				if (r1 > level)
				{
					land2.left_x = land1.left_x + land2.land_width;
				}
				else
				{
					land2.left_x = land1.left_x + 2 * land2.land_width;
				}

				int r3 = randBetweenMinMax(1, 20);
				if (r1 > level)
				{
					land2.top_y = land1.top_y;
				}
				else
				{
					int r3 = randBetweenMinMax(-1, 1);
					land2.top_y = WIDTH / 2 + HEIGHT / 10 * r3;
				}
				land2.right_x = land2.left_x + land2.land_width;
				lands.push_back(land2);
				land1 = land2;
			}

			enemies.clear();
			int numEnemy = (level + 3) / 5;
			int idStep = lands.size() / (numEnemy + 1);       // 敌人在scene中均匀分布
			for (int j = 1; j <= numEnemy; j++)
			{
				Enemy enemy;
				enemy.initialize();
				int landId = j * idStep;
				enemy.x = lands[landId].left_x + lands[landId].land_width / 2;
				enemy.y = lands[landId].top_y - enemy.enemy_height;

				float movingRange = enemy.enemy_width * (3 + level / 15.0);

				enemy.x_min = enemy.x - movingRange;
				enemy.x_max = enemy.x + movingRange;
				enemy.vx = 2 + level / 10.0;

				enemies.push_back(enemy);
			}

		}
	}
};

class Player
{
public:
	IMAGE im_show;                               // 当前时刻要显示的图像
	IMAGE im_standright;
	IMAGE im_standleft;
	IMAGE im_jumpright;
	IMAGE im_jumpleft;
	vector<IMAGE> ims_runright;
	vector<IMAGE> ims_runleft;
	int animId;

	PlayerStatus playerStatus;                   // 当前状态

	float x_left, y_bottom;                      // 左下角位置
	float vx, vy;                                // 速度
	float gravity;
	float width, height;                         // 图片的宽度、高度

	void draw()
	{
		putimagePng(WIDTH / 2, HEIGHT / 2 - height, &im_show);  // 角色位置不动
	}

	void initialize()
	{
		ims_runleft.clear();
		ims_runright.clear();
		loadimage(&im_standright, _T("standright.png"));
		loadimage(&im_standleft, _T("standleft.png"));
		loadimage(&im_jumpright, _T("jumpright.png"));
		loadimage(&im_jumpleft, _T("jumpleft.png"));

		playerStatus = standright;
		im_show = im_standright;
		width = im_standright.getwidth();
		height = im_standright.getheight();

		TCHAR filename[80];
		for (int i = 0; i <= 7; i++)
		{
			swprintf_s(filename, _T("runright%d.png"), i);
			IMAGE im;
			loadimage(&im, filename);
			ims_runright.push_back(im);
		}
		for (int i = 0; i <= 7; i++)
		{
			swprintf_s(filename, _T("runleft%d.png"), i);
			IMAGE im;
			loadimage(&im, filename);
			ims_runleft.push_back(im);
		}

		animId = 0;

		updateXY(WIDTH / 2, HEIGHT / 2);
		vx = 10;
		vy = 0;
		gravity = 3;
	}

	void updateXY(float mx, float my)
	{
		x_left = mx;
		y_bottom = my;
	}

	void runRight(Scene& scene)
	{
		x_left += vx;
		if (isNotOnAllLands(scene.lands, vy))
		{
			im_show = im_jumpright;
			playerStatus = jumpright;
			return;
		}

		if (playerStatus == jumpleft || playerStatus == jumpright)    // 如果是起跳状态
		{
			im_show = im_jumpright;
		}
		else
		{
			if (playerStatus != runright)
			{
				playerStatus = runright;
				animId = 0;
			}
			else
			{
				animId++;
				if (animId >= ims_runright.size())
				{
					animId = 0;
				}
			}
			im_show = ims_runright[animId];
		}
	}

	void runLeft(Scene& scene)
	{
		x_left -= vx;
		if (isNotOnAllLands(scene.lands, vy))
		{
			im_show = im_jumpleft;
			playerStatus = jumpright;
			return;
		}

		if (playerStatus == jumpleft || playerStatus == jumpright)      // 如果是起跳状态
		{
			im_show = im_jumpleft;
		}
		else
		{
			if (playerStatus != runleft)
			{
				playerStatus = runleft;
				animId = 0;
			}
			else
			{
				animId++;
				if (animId >= ims_runleft.size())
				{
					animId = 0;
				}
			}
			im_show = ims_runleft[animId];
		}
	}

	void standStill()
	{
		if (playerStatus == runleft || playerStatus == standleft)
		{
			im_show = im_standleft;
		}
		else if (playerStatus == runright || playerStatus == standright)
		{
			im_show = im_standright;
		}
	}

	void beginJump()
	{
		if (playerStatus != jumpleft && playerStatus != jumpright)           // 已在空中不能起跳
		{
			if (playerStatus == runleft || playerStatus == standleft)
			{
				im_show = im_jumpleft;
				playerStatus = jumpleft;
			}
			else if (playerStatus == runright || playerStatus == standright)
			{
				im_show = im_jumpright;
				playerStatus = jumpright;
			}
			vy = -30;
		}
	}

	int isCollideEnemy(Enemy& enemy)
	{
		float x_center = x_left + width / 2;
		float y_center = y_bottom - height / 2;
		if (abs(enemy.x - x_center) <= enemy.enemy_width * 0.5 && abs(enemy.y - y_center) <= enemy.enemy_height * 0.5)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	int isOnLand(Land& land, float ySpeed)
	{
		float x_right = x_left + width;
		if (ySpeed <= 0)
		{
			ySpeed = 0;
		}
		if (land.left_x - x_left <= width * 0.6 && x_right - land.right_x <= width * 0.6 && abs(y_bottom - land.top_y) <= 5 + ySpeed)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	int isNotOnAllLands(vector<Land>& lands, float speed)
	{
		for (int i = 0; i < lands.size(); i++)
		{
			if (isOnLand(lands[i], speed))
			{
				return 0;
			}
		}
		return 1;
	}

	void updateYcoordinate(Scene& scene)
	{
		if (playerStatus == jumpleft || playerStatus == jumpright)
		{
			vy += gravity;
			y_bottom += vy;
			for (int i = 0; i < scene.lands.size(); i++)
			{
				if (isOnLand(scene.lands[i], vy))
				{
					y_bottom = scene.lands[i].top_y;
					if (playerStatus == jumpleft)
					{
						playerStatus = standleft;
					}
					if (playerStatus == jumpright)
					{
						playerStatus = standright;
					}
					break;
				}
			}
		}
	}
};

Player player;
Scene scene;
Timer timer;

void startup()
{
	mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL);
	mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);

	srand(time(0));
	scene.initialize();
	player.initialize();
	initgraph(WIDTH, HEIGHT);
	BeginBatchDraw();
}

void show()
{
	scene.draw(player.x_left - WIDTH / 2, player.y_bottom - HEIGHT / 2);
	player.draw();
	FlushBatchDraw();
	timer.Sleep(50);
}

void updateWithoutInput()
{
	player.updateYcoordinate(scene);

	int landSize = scene.lands.size();
	if (player.x_left > scene.lands[landSize - 1].left_x && abs(player.y_bottom - scene.lands[landSize - 1].top_y) <= 2) // 游戏胜利
	{
		PlayMusicOnce((TCHAR*)_T("success.mp3"));
		scene.lastlevel = scene.level;
		scene.level++;
		scene.initialize();
		player.initialize();
	}
	else if (player.y_bottom > 1.5 * HEIGHT)                   // 角色落到画面底部,游戏失败
	{
		PlayMusicOnce((TCHAR*)_T("warning.mp3"));
		scene.lastlevel = scene.level;
		scene.initialize();
		player.initialize();
	}

	for (int i = 0; i < scene.enemies.size(); i++)
	{
		scene.enemies[i].update();                              // 敌人自动更新位置
		if (player.isCollideEnemy(scene.enemies[i]))
		{
			PlayMusicOnce((TCHAR*)_T("warning.mp3"));
			scene.lastlevel = scene.level;
			scene.initialize();
			player.initialize();
		}
	}
}

void updateWithInput()
{
	player.standStill(); // 游戏角色默认为向左或向右静止站立
	if (_kbhit())
	{
		if (GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState('D'))
		{
			player.runRight(scene);
		}
		else if (GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState('A'))
		{
			player.runLeft(scene);
		}
		if (GetAsyncKeyState(VK_UP) || GetAsyncKeyState('W'))
		{
			player.beginJump();
		}
	}
}

int main()
{
	startup();
	while (1)
	{
		show();
		updateWithoutInput();
		updateWithInput();
	}
	return 0;
}
  • 34
    点赞
  • 277
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
好的,这里是一份简单的火柴游戏的代码,你可以在 Dev-C++ 中运行它。请将代码复制到 Dev-C++ 辑器中,然后按 `F9` 键运行。 ```c++ #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <time.h> // 火柴游戏 int main() { int score = 0; // 得分 int speed = 10; // 游戏速度 int position = 0; // 火柴人位置 int obstacle_pos = 80; // 障碍物位置 int obstacle_gap = 5; // 障碍物间隔 int count = 0; // 计数器 int jump = 0; // 是否在跳跃状态 int jump_pos = 0; // 跳跃高度 char input; // 用户输入 // 设置随机数种子 srand(time(NULL)); // 游戏循环 while (1) { // 清屏 system("cls"); // 输出火柴人位置和得分 printf("Score: %d\n", score); for (int i = 0; i < position; i++) putchar(' '); printf("0\n"); // 输出障碍物位置 for (int i = 0; i < obstacle_pos; i++) putchar(' '); for (int i = 0; i < obstacle_gap; i++) putchar('#'); putchar('\n'); // 计数器累加 count++; if (count >= speed) { count = 0; // 随机生成障碍物位置 obstacle_pos--; if (obstacle_pos < 0) { obstacle_pos = 80; obstacle_gap = rand() % 10 + 5; score++; } // 火柴人跳跃 if (jump) { jump_pos++; if (jump_pos >= 5) { jump = 0; jump_pos = 0; } } // 判断是否碰到障碍物 if (obstacle_pos <= position && obstacle_pos + obstacle_gap > position) { printf("Game Over! Your score is %d\n", score); return 0; } } // 用户输入 if (kbhit()) { input = getch(); if (input == ' ') { jump = 1; } } // 火柴人移动 if (!jump) { position++; if (position >= 80) { printf("You Win! Your score is %d\n", score); return 0; } } // 延时 Sleep(10); } return 0; } ``` 上面的代码实现一个简单的火柴游戏,玩家需要控制火柴人跳跃,躲避障碍物,并尽可能地前进,直到到达终点或者碰到障碍物为止。你可以根据需要修改代码中的一些参数,比如游戏速度、障碍物位置、障碍物间隔等等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值