用C语言写一个类似天天酷跑游戏(图形库用easyx)

1.头文件、全局变量和结构体

  a.玩家结构体

  b.枚举玩家三种状态:奔跑、跳跃、滑行

  c.障碍物结构体

  d.障碍物结点

  e.枚举出障碍物类型

#include
#include
#include
#include
#include
#include"tools.h"

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

using namespace std;
#define WIN_WIDTH 1012
#define WIN_HEIGHT 396
#define OBSTACLE_COUNT 10

IMAGE img_bk[3];
IMAGE people_run[12];//人物跑步图片
IMAGE people_leap[12];//人物跑步图片
IMAGE people_slide[2];//人物滑行



int bk_x[3];//背景坐标
int bk_y;
int bk_speed[3] = { 1,4,8 };//背景运动速度
int obst_count;//增加障碍物时计数
bool update;//是否立即刷新
bool switch_kb;//是否需要打开按键开关
int scored;//得分

//枚举人物状态,奔跑,跳跃,滑行
enum PEOPLE_STATE
{
	run,
	leap,
	slide
};
//人物结构体
struct People
{
	int x;
	int y;
	int frame_run;//跑步时帧
	int motion_state;//人物运动状态
	int leap_height;//跳跃高度
	int frame_leap;//人物跳跃帧
	int jump_offset;//人物跳跃偏移量
	int frame_slide;//人物滑行帧
	int slide_distance;//人物滑行距离
	int exist;
}player;
//障碍物结点
struct Node
{
	struct Obstacle* obstacle_t;
	struct Node* next;
};
//障碍物类型枚举
enum OBSTACLE_TYPE
{
	tortoise,
	lion,
	fire,
	hock1,
	hock2,
	hock3,
	hock4,
	obstacle_type_count
};

//障碍物结构体
struct Obstacle
{
	OBSTACLE_TYPE type;//障碍物类型
	int x;//坐标
	int y;
	int frame_index;//图片帧
	bool exist;//判断是否存在
	int speed;//运动速度
};

vector>obstacleImgs;//用C++的动态数组

void LoadObstImg();
struct Obstacle* InitOb();
void PrintObImg(struct Obstacle* p_ob);
void MoveOb(struct Obstacle* p_ob);
struct Node* CreateList();
struct Node* CreateNode(Obstacle* p_ob);
void InsertNode(struct Node* p_head, struct Obstacle* p_ob);
void PrintList(struct Node* p_head);
void UpdateOb();
bool CheckCollision(struct Obstacle* p_ob);

struct Node* list = NULL;

2.制作背景和玩家

  a.加载资源和数据初始化

  b.背景绘制以及运动

  c.玩家三种状态绘制与运动

  d.键盘响应

  e.判断玩家与障碍物碰撞

//加载图片
void LoadImg()
{
	
	//加载背景
	char bk_path[20] = {0};//背景图片路径
	for (int i = 0; i < 3; i++)
	{
		sprintf(bk_path, "resource/bg%03d.png", i + 1);
		loadimage(&img_bk[i], bk_path);
	}

	//加载背景音乐
	mciSendString("open resource/bg.mp3 alias bgm", NULL, 0, NULL);
	mciSendString("play bgm repeat", NULL, 0, NULL);
	//加载人物
	char people_path1[20] = { 0 };//人物奔跑图片路径
	for (int i = 0; i < 12; i++)
	{
		sprintf(people_path1, "resource/hero%d.png", i + 1);
		loadimage(&people_run[i], people_path1);
	}
	char people_path2[20] = { 0 };//人物跳跃图片路径
	for (int i = 0; i < 12; i++)
	{
		sprintf(people_path1, "resource/g%02d.png", i + 1);
		loadimage(&people_leap[i], people_path1);
	}
	char people_path3[20] = { 0 };//人物滑行图片路径
	for (int i = 0; i < 2; i++)
	{
		sprintf(people_path3, "resource/d%d.png", i + 1);
		loadimage(&people_slide[i], people_path3);
	}
	LoadObstImg();
}

//初始化数据
void InitData()
{
	//加载窗口
	initgraph(WIN_WIDTH, WIN_HEIGHT,1);
	//背景X坐标初始为0
	bk_x[3] = { 0 };

	update = false;
	switch_kb = true;

	player.x = WIN_WIDTH*0.5 - people_run[0].getwidth()*0.5;
	player.y = 360 - people_run[0].getheight();
	player.frame_run = 0;
	player.motion_state = run;
	player.frame_leap = 0;
	player.leap_height = 100;
	player.jump_offset = -10;
	player.frame_slide = 0;
	player.slide_distance = 0;
	player.exist = true;

	scored = 0;

	 list = CreateList();//将头结点地址赋予list

	 for (int i = 0; i < OBSTACLE_COUNT; i++)
	 {
		 InsertNode(list, InitOb());
	 }
}

//打印图片
void PrintdImg()
{
	//打印背景图片
	putimagePNG2(bk_x[0], 0, &img_bk[0]);
	putimagePNG2(bk_x[1], 119, &img_bk[1]);
	putimagePNG2(bk_x[2], 330, &img_bk[2]);
	
	//打印人物奔跑图片
	if (run == player.motion_state)
	{
		putimagePNG2(player.x, player.y, &people_run[player.frame_run]);
		//人物奔跑帧改变
		player.frame_run = (player.frame_run + 1) % 12;
	}
	
	//打印人物跳跃图片
	if (leap == player.motion_state)
	{
		putimagePNG2(player.x, player.y, &people_leap[player.frame_leap]);
	}

	//打印人物滑行图片
	if (slide == player.motion_state)//如果是滑行就打印
	{
		player.y = 360 - people_slide[player.frame_slide].getheight();//打印当前图片帧的高度
		putimagePNG2(player.x, player.y, &people_slide[player.frame_slide]);
	}

	//绘制跑步分数
	settextcolor(BLACK);
	settextstyle(20, 25, "黑体");
	setbkmode(TRANSPARENT);
	char count[40];
	sprintf(count, "%d", scored);
	outtextxy(10, 20, count);
	outtextxy(textwidth(count)+10, 20, "分");

}

//图片更新运动
void Update()
{
	for (int i = 0; i < 3; i++)
	{
		bk_x[i] -= bk_speed[i];
		//当背景图片运动一个窗口距离时,重新回到原点
		if (bk_x[i] <= -WIN_WIDTH)
		{
			bk_x[i] = 0;
		}
	}

	scored++;//得分
	
	//人物跳跃运动
	if (leap == player.motion_state)
	{
		if (player.y < player.leap_height)//如果人物高度小于要求高度时,偏移量为正
		{
			player.jump_offset = 10;
		}
		player.y += player.jump_offset;//如果人物高度大于要求高度时,偏移量为负
		if (player.frame_leap != 12)
		{
			player.frame_leap++;//跳跃图片帧改变
		}
		if (player.y >= 360 - people_run[0].getheight())
		{
			player.motion_state = run;
			player.jump_offset = -10;
			player.frame_leap = 0;
			player.frame_run = 0;
			player.frame_slide = 0;
			player.y = 360 - people_run[0].getheight();
		}
	}

	//人物滑行动作
	if (slide == player.motion_state )
	{
		if (0 == player.frame_slide && player.slide_distance >= 4)
		{
			player.frame_slide += 1;
		}
		player.slide_distance++;//滑行距离
		
		if (20 == player.slide_distance)
		{
			player.frame_slide = 0;
			player.motion_state = run;
			player.frame_run = 0;
			player.slide_distance = 0;
			player.y = 360 - people_run[0].getheight();
		}
	}
	
	//检测是否碰撞,碰撞了人物和障碍物一起后移。
	struct Node* p_check = list->next;
	int one = 0;//用于标记当向前一步之后,while循环不再向前移动
	while (p_check!=NULL)
	{
		if (p_check->obstacle_t->exist == false)
		{
			p_check = p_check->next;
			continue;
		}
		if (CheckCollision(p_check->obstacle_t))
		{
			player.x -= p_check->obstacle_t->speed;
			if (player.x <= -people_run->getwidth())
			{
				player.exist = false;
			}
			return;
		}
		else
		{
			if (one<=0 && player.x <= WIN_WIDTH * 0.5 - people_run[0].getwidth() * 0.5)
			{
				player.x += 2;//当人物不在当初位置时向前移动
				one++;
			}
		}
		p_check = p_check->next;
	}
}

//按键响应
void KbControl()
{
	char keyboard = {0};
	if (_kbhit())
	{
		update = true;
		keyboard = getch();
		switch (keyboard)
		{
		case ' ':
			if (slide == player.motion_state)//滑行时可以跳跃,但是Y坐标不在奔跑时高度,需要重新初始化
			{
				player.y = 360 - people_run[0].getheight();
			}
			player.motion_state = leap;
			break;
		case 80:
			if (player.motion_state != leap)//跳跃时不允许滑行
			{
				player.motion_state = slide;
			}
			break;
		default:
			break;
		}
	}
}

3.制作障碍物

  a.创建障碍物链表

  b.加载资源

  c.障碍物运动和绘制d

  d.碰撞检测

//加载障碍物图片
void LoadObstImg()
{
	char obstacle_path[20];

	IMAGE img_tortoise;
	vectortortoiseArr;
	for (int i = 0; i < 7; i++)
	{
		sprintf(obstacle_path, "resource/t%d.png", i + 1);
		loadimage(&img_tortoise, obstacle_path);
		tortoiseArr.push_back(img_tortoise);
	}
	obstacleImgs.push_back(tortoiseArr);


	IMAGE img_lion;
	vectorlionArr;
	for (int i = 0; i < 6; i++)
	{
		sprintf(obstacle_path, "resource/p%d.png", i + 1);
		loadimage(&img_lion, obstacle_path);
		lionArr.push_back(img_lion);
	}
	obstacleImgs.push_back(lionArr);

	IMAGE img_fire;
	vectorfireArr;
	for (int i = 0; i < 9; i++)
	{
		sprintf(obstacle_path, "resource/fire%d.png", i + 1);
		loadimage(&img_fire, obstacle_path, 46, 68, true);
		fireArr.push_back(img_fire);
	}
	obstacleImgs.push_back(fireArr);

	IMAGE img_hock;
	for (int i = 0; i < 4; i++)
	{
		vectorhockArr;
		sprintf(obstacle_path, "resource/h%d.png", i + 1);
		loadimage(&img_hock, obstacle_path,84,260,true);
		hockArr.push_back(img_hock);
		obstacleImgs.push_back(hockArr);
	}
}
//初始化障碍物
struct Obstacle* InitOb()
{
	//srand((unsigned int)time(NULL));
	struct Obstacle* p_ob = (struct Obstacle*)malloc(sizeof(struct Obstacle));
	assert(p_ob);
	
	p_ob->exist = false;
	
	return p_ob;
}
//打印障碍物
void PrintObImg(struct Obstacle* p_ob)
{
	putimagePNG2(p_ob->x, p_ob->y, &obstacleImgs[p_ob->type][p_ob->frame_index]);
}
//障碍物运动
void MoveOb(struct Obstacle* p_ob)
{
	p_ob->x -= p_ob->speed;
}
//创建头节点
struct Node* CreateList()
{
	struct Node* p_head = (struct Node*)malloc(sizeof(struct Node));
	assert(p_head);
	p_head->obstacle_t = NULL;
	p_head->next = NULL;
	return p_head;
}
//创建结点
struct Node* CreateNode(Obstacle* p_ob)
{
	struct Node* p_node = (struct Node*)malloc(sizeof(struct Node));
	assert(p_node);
	p_node->obstacle_t = p_ob;
	p_node->next = NULL;
	return p_node;
}
//连接结点
void InsertNode(struct Node* p_head, struct Obstacle* p_ob)
{
	struct Node* p_newnode = CreateNode(p_ob);
	p_newnode->next = p_head->next;
	p_head->next = p_newnode;
}
//打印结点
void PrintList(struct Node* p_head)
{
	struct Node* p_move = p_head->next;
	while (p_move != NULL)
	{
		if (false == p_move->obstacle_t->exist)
		{
			p_move = p_move->next;
			continue;
		}
		PrintObImg(p_move->obstacle_t);
		MoveOb(p_move->obstacle_t);
		//减缓障碍物帧率
		static int currFrame = 0;
		static int frame = 5;
		currFrame++;
		if (currFrame >= frame)
		{
			currFrame = 0;
			int len = obstacleImgs[p_move->obstacle_t->type].size();//获取一种障碍物的图片数量
			p_move->obstacle_t->frame_index = (p_move->obstacle_t->frame_index + 1) % len;
		}
		
		if (-obstacleImgs[p_move->obstacle_t->type][p_move->obstacle_t->frame_index].getwidth() >= p_move->obstacle_t->x)
		{
			p_move->obstacle_t->x = WIN_WIDTH;
			p_move->obstacle_t->exist = false;
		}
		
		p_move = p_move->next;
	}
}
//更新障碍物状态类型位置
void UpdateOb()
{
	//srand((unsigned int)(time(NULL)));
	struct Node* p_curr = list->next;
	while (p_curr != NULL)
	{
		if (true == p_curr->obstacle_t->exist)
		{
			p_curr = p_curr->next;
			continue;
		}
		p_curr->obstacle_t->exist = (bool)(rand() % 2);//随机产生障碍物的存在与否
		if (true == p_curr->obstacle_t->exist)
		{
			p_curr->obstacle_t->type = (OBSTACLE_TYPE)(rand() % 4);//随机产生障碍物类型
			if (hock1 == p_curr->obstacle_t->type)
			{
				p_curr->obstacle_t->type = (OBSTACLE_TYPE)(p_curr->obstacle_t->type + (rand() % 4));
			}
			p_curr->obstacle_t->frame_index = 0;
			p_curr->obstacle_t->x = WIN_WIDTH;
			p_curr->obstacle_t->y = 360 - obstacleImgs[p_curr->obstacle_t->type][p_curr->obstacle_t->frame_index].getheight();
			if (lion == p_curr->obstacle_t->type)
			{
				p_curr->obstacle_t->speed = 10;
			}
			else if (tortoise == p_curr->obstacle_t->type)
			{
				p_curr->obstacle_t->speed = 8;
				p_curr->obstacle_t->frame_index = rand() % 7;
			}
			else if (fire == p_curr->obstacle_t->type)
			{
				p_curr->obstacle_t->speed = 8;
				p_curr->obstacle_t->frame_index = rand() % 7;
			}
			else if (p_curr->obstacle_t->type >= hock1 && p_curr->obstacle_t->type <= hock4)
			{
				p_curr->obstacle_t->speed = 8;
				p_curr->obstacle_t->y = 0;
			}
			return;
		}
		p_curr = p_curr->next;
	}
}
//判断碰撞
bool CheckCollision(struct Obstacle* p_ob)
{
	int x01 = player.x + 25;
	int y01 = player.y;
	int x02 = 0;
	int y02 = 0;
	if (run == player.motion_state)
	{
		x02 = player.x + people_run->getwidth() - 20;
		 y02 = player.y + people_run->getheight();
	}
	else if (leap == player.motion_state)
	{
		 x02 = player.x + people_leap->getwidth() - 20;
		 y02 = player.y + people_leap->getheight();
	}
	else if (slide == player.motion_state)
	{
		 x02 = player.x + people_slide->getwidth() - 20;
		 y02 = player.y + people_slide->getheight();
	}
	int x11 = p_ob->x;
	int x12 = p_ob->x + obstacleImgs[p_ob->type][p_ob->frame_index].getwidth();
	int y11 = p_ob->y;
	int y12 = p_ob->y + obstacleImgs[p_ob->type][p_ob->frame_index].getheight();

	int zx = abs(abs(x01) + x02 - x11 - x12);
	int x = abs(x02 - x01) + abs(x12 - x11);
	int zy = abs(y01 + y02 - y11 - y12);
	int y = abs(y02 - y01) + abs(y12 - y11);

	return (x >= zx && y >= zy);
}

4.游戏运行

//游戏运行
void RunGame()
{
	srand(unsigned int(time(NULL)));
	while (player.exist == true)
	{
		if (timer(40, 0))
		{
			update = true;
		}
		KbControl();
		if (update)
		{
			static int currTime = 0;
			static int Time = 40 + rand() % 60;
			currTime++;
			if (currTime >= Time)
			{
				currTime = 0;
				UpdateOb();
			}
			update = false;
			BeginBatchDraw();
			PrintdImg();
			PrintList(list);
			FlushBatchDraw();
			Update();
		}
	}
}


int main(void)
{
	LoadImg();
	InitData();
	RunGame();

	getchar();
	closegraph();
	
	return 0;
}

5.tools用于透明贴图

#include 
#include 

#include 
#pragma comment(lib, "winmm.lib")
// 载入PNG图并去透明部分
void putimagePNG(int  picture_x, int picture_y, IMAGE* picture) //x为载入图片的X坐标,y为Y坐标
{
    DWORD* dst = GetImageBuffer();    // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
    DWORD* draw = GetImageBuffer();
    DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
    int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
    int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
    int graphWidth = getwidth();       //获取绘图区的宽度,EASYX自带
    int graphHeight = getheight();     //获取绘图区的高度,EASYX自带
    int dstX = 0;    //在显存里像素的角标

    // 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
    for (int iy = 0; iy < picture_height; iy++)
    {
        for (int ix = 0; ix < picture_width; ix++)
        {
            int srcX = ix + iy * picture_width; //在显存里像素的角标
            int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
            int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
            int sg = ((src[srcX] & 0xff00) >> 8);   //G
            int sb = src[srcX] & 0xff;              //B
            if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
            {
                dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
                int dr = ((dst[dstX] & 0xff0000) >> 16);
                int dg = ((dst[dstX] & 0xff00) >> 8);
                int db = dst[dstX] & 0xff;
                draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)  //公式: Cp=αp*FP+(1-αp)*BP  ; αp=sa/255 , FP=sr , BP=dr
                    | ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)         //αp=sa/255 , FP=sg , BP=dg
                    | (sb * sa / 255 + db * (255 - sa) / 255);              //αp=sa/255 , FP=sb , BP=db
            }
        }
    }
}

// 适用于 y <0 以及x<0的任何情况
void putimagePNG2(int x, int y, IMAGE* picture) {
    IMAGE imgTmp;
    if (y < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, 0, -y,
            picture->getwidth(), picture->getheight() + y);
        SetWorkingImage();
        y = 0;
        picture = &imgTmp;
    }

    if (x < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, -x, 0, picture->getwidth() + x, picture->getheight());
        SetWorkingImage();
        x = 0;
        picture = &imgTmp;
    } 

    putimagePNG(x, y, picture);
}

// 适用于 y <0 以及y>0的任何情况
void putimagePNG2(int x, int y, int winWidth, IMAGE* picture) {
    IMAGE imgTmp;
    if (y < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, 0, -y,
            picture->getwidth(), picture->getheight() + y);
        SetWorkingImage();
        y = 0;
        picture = &imgTmp;
    }

    if (x < 0) {
        SetWorkingImage(picture);
        getimage(&imgTmp, -x, 0, picture->getwidth() + x, picture->getheight());
        SetWorkingImage();
        x = 0;
        picture = &imgTmp;
    }
    else if (x >= winWidth) {
        return;
    }
    else if (x > winWidth-picture->getwidth()) {
        SetWorkingImage(picture);
        getimage(&imgTmp, 0, 0, winWidth - x, picture->getheight());
        SetWorkingImage();
        picture = &imgTmp;
    }

    putimagePNG(x, y, picture);
}

本游戏开发基于rock老师视频内容(视频学习网站上都能找到)以及自己学到的知识编写,可以简单学习链表和图形工具的运用,根据自己的喜好加以修改,仅供学习参考,不足之处还望指出,大家一起学习进步。

资源:链接:https://pan.baidu.com/s/1sLC0f1D77iQ-EUQrTbnu-Q 
提取码:0lp8

你可能感兴趣的:(c语言,游戏,c++)