c语言练手项目【编写天天酷跑游戏2.0】EASYX图形库的运用。代码开源,素材已打包

天天酷跑项目的开发

项目前言

项目是基于Windows,easyX图形库进行开发的,

开发环境:Visual Studio 2022

项目技术最低要求:

常量,变量,数组,循环,函数。
在这里插入图片描述

文章目录

  • 天天酷跑项目的开发
    • 项目前言
    • 游戏背景的实现
    • 实现玩家的奔跑
    • 实现人物的跳跃
      • 获取玩家的输入
    • 优化帧等待
    • 随机出现小乌龟障碍
    • 使用结构体优化障碍
      • 使用结构体后,重新初始化游戏
    • 实现下蹲技能
    • 添加柱子障碍物
    • 碰撞检测
    • 实现血条
    • 判断游戏结束 添加背景音乐 添加初始界面
    • 优化死亡BUG
    • 跨越障碍后计算得分
    • 判断游戏胜利
    • 源代码

游戏背景的实现

  • 使用initgraph()初始化界面;
  • 加载背景资源

写好游戏的图像化界面的框架

在这里插入图片描述

由于easyX不支持透明图片的的贴图,所以我们要自己写一个透明贴图的函数这里给出3个

// 适用于 y <0 以及y>0的任何情况
void putimagePNG2(int x, int y, IMAGE* picture);
void putimagePNG2(int x, int y, int winWidth, IMAGE* picture);
void putimagePNG(int  picture_x, int picture_y, IMAGE* picture);

// 适用于 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);
}


// 载入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
            }
        }
    }
}



//定义相关的变量
IMAGE imgBgs[3]; // 背景图片
int bgX[3];  //背景图片的x坐标
int bgSpeed[3] = { 1, 2, 4 };//三重背景以不同速度行驶

三张背景图片需要载入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//直接在初始化中加载到内存
    char name[64];
    for (int i = 0; i < 3; i++) {
        // "res/bg001.png"    "res/bg002.png"     "res/bg003.png"   
        sprintf(name, "res/bg%03d.png", i + 1);
        loadimage(&imgBgs[i], name);

        bgX[i] = 0;
    }

注:这里的图片在main.cpp文件夹中的res文件夹下。

再在updateBg的函数中进行渲染;

  	putimagePNG2(bgX[0], 0, &imgBgs[0]);
    putimagePNG2(bgX[1], 119, &imgBgs[1]);
    putimagePNG2(bgX[2], 330, &imgBgs[2]);

编译如果遇到C4996错误只需要在项目属性c/c++中关掉SDL检查即可。

如果出现loadimage()的错误,可能是字符集的问题;只需要在项目属性-高级-字符集-使用多字节字符集即可

看一下执行结果。

在这里插入图片描述

如果不使用透明贴图函数

在这里插入图片描述

图片成功渲染,但是在实际的游戏中,背景是会移动的且遵守基本的物理。

接下来我们需要移动背景。远处的移动慢,中处更快,最近处看起来最快,所以速度可以取int bgSpeed[3] = { 1, 2, 4 };

void moveBg() {
    for (int i = 0; i < 3; i++) {
        bgX[i] -= bgSpeed[i];
        if (bgX[i] < -WIN_WIDTH) {
            bgX[i] = 0;
        }
    }

在main函数中载入死循环 这样背景就可以无休止的移动了

int main(void) {
    init();
    while (1) {
        BeginBatchDraw();
        updateBg();
        EndBatchDraw();
        moveBg();

        Sleep(30);
    }
//使用beginbatchdraw 和endbatchdraw进行优化渲染。
    
}

实现玩家的奔跑

IMAGE imgHeros[12];//玩家的帧图片
int heroX; //玩家的x坐标
int heroY; //玩家的y坐标
int heroIndex; //玩家奔跑的图片帧序号

在这里插入图片描述

初始化的时候也初始化玩家

    // 加载Hero奔跑的图片帧素材
    for (int i = 0; i < 12; i++) {
        // "res/hero1.png"  ... "res/hero12.png"
        sprintf(name, "res/hero%d.png", i + 1);
        loadimage(&imgHeros[i], name);
    }
// 设置玩家的初始位置
    heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;
    heroY = 345 - imgHeros[0].getheight();
    heroIndex = 0;

在Main函数中给hero贴图

   putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);

在这里插入图片描述

更改人物帧图片,让人物看起来会动

  heroIndex = (heroIndex + 1) % 12;

在这里插入图片描述

RUN:

在这里插入图片描述

实现人物的跳跃

初始化函数中

在这里插入图片描述

heroJump = false;
jumpHeightMax = 345 - imgHeros[0].getheight() - 120;
heroJumpOff = -4;
bool heroJump; //表示玩家正在跳跃
int jumpHeightMax;
int heroJumpOff;

在这里插入图片描述

int jump()
{
    // 实现跳跃
    if (heroJump) {
        if (heroY < jumpHeightMax) {
            heroJumpOff = 4;
        }

        heroY += heroJumpOff;

        if (heroY > 345 - imgHeros[0].getheight()) {
            heroJump = false;
            heroJumpOff = -4;
        }
    }
    else { //不跳跃
        heroIndex = (heroIndex + 1) % 12;
    }
}
void jump() {
    heroJump = true;
}

获取玩家的输入

// 处理用户按键的输入
void keyEvent() {
    char ch;

    if (_kbhit()) { //如果有按键按下,_kbhit()返回  true  
        ch = _getch();    // _getch()不需要按下回车即可直接读取
        if (ch == ' ') {
            jump();
        }
    } 
}

main()

int main(void) {
    init();
    while (1) {
        keyEvent();
        BeginBatchDraw();

        updateBg();
        putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
        EndBatchDraw();
        moveBg();
        HeroIndex();
        jump(heroJump);
        Sleep(15);
    }

    
}

在这里插入图片描述

优化帧等待

bool update;   //表示是否需要马上刷新画面

init()尾部

update = true;

void jump() {
    heroJump = true;
    update = true;
}
int getDelay() {
    static unsigned long long lastTime = 0;
    unsigned long long currentTime = GetTickCount();
    if (lastTime == 0) {
        lastTime = currentTime;
        return 0;
    }
    else {
        int ret = currentTime - lastTime;
        lastTime = currentTime;
        return ret;
    }
}

main函数主体

int main(void) {
    int timer = 0;
    init();
    while (1) {
        keyEvent();
      
        timer += getDelay();//10
        if (timer > 30) {
            timer = 0;
            update = true;
        }

        if (update)
        {
            update = false;
            BeginBatchDraw();
            updateBg();
            putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
            EndBatchDraw();
            moveBg();
            HeroIndex();
            jump(heroJump);
         

        } 
       
    }

    return 0;
}


随机出现小乌龟障碍

定义乌龟相关的变量

IMAGE imgTortoise; //小乌龟
int torToiseX; //小乌龟的水平坐标
int torToiseY; //小乌龟的垂直坐标
bool torToiseExist; //当前窗口是否有小乌龟

在init()函数中,初始化乌龟

    // 加载小乌龟素材
    loadimage(&imgTortoise, "res/t1.png");
    torToiseExist = false;
    torToiseY = 345 - imgTortoise.getheight() + 5;

定义一个函数创建小乌龟

void creatTortoise() {
    // 创建小乌龟
    static int frameCount = 0;
    static int torToiseFre = 100;
    frameCount++;
    if (frameCount > torToiseFre) {
        frameCount = 0;
        if (!torToiseExist) {
            torToiseExist = true;
            torToiseX = WIN_WIDTH;
            torToiseFre = 200 + rand() % 300;
        }
    }

    if (torToiseExist) {
        torToiseX -= bgSpeed[2];
        if (torToiseX < -imgTortoise.getwidth()) {
            torToiseExist = false;
        }
    }
}


创建函数对乌龟进行渲染

void updateEnemy() {
    // 渲染小乌龟
    if (torToiseExist) {
        putimagePNG2(torToiseX, torToiseY, WIN_WIDTH, &imgTortoise);
    }
}

再把creattortorise

在这里插入图片描述

在main函数中调用if语句中

    update = false;
            BeginBatchDraw();
            updateBg();
            putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
            updateEnemy();
            EndBatchDraw();
            moveBg();
            HeroIndex();
            creatTortoise();
            jump(heroJump);
          

使用结构体优化障碍

添加头文件\#include <vector>

#define OBSTACLE_COUNT 10
typedef enum {
    TORTOISE, //乌龟  0
    LION, //狮子 1
    OBSTACLE_TYPE_COUNT  // 2
} obstacle_type;

vector<vector<IMAGE>>obstacleImgs; //存放所有障碍物的各个图片

typedef struct obstacle {
    obstacle_type type; //障碍物的类型 
    int imgIndex; //当前显示的图片的序号
    int x, y; //障碍物的坐标
    int speed;
    int power; //杀伤力
    bool exist;
}obstacle_t;

使用结构体后,重新初始化游戏

obstacle_t obstacles[OBSTACLE_COUNT];

// 游戏的初始化
void init() {
	// 创建游戏窗口
	initgraph(WIN_WIDTH, WIN_HEIGHT);

	// 加载背景资源
	char name[64];
	for (int i = 0; i < 3; i++) {
		// "res/bg001.png"    "res/bg002.png"     "res/bg003.png"   
		sprintf(name, "res/bg%03d.png", i + 1);
		loadimage(&imgBgs[i], name);

		bgX[i] = 0;
	}

	// 加载Hero奔跑的图片帧素材
	for (int i = 0; i < 12; i++) {
		// "res/hero1.png"  ... "res/hero12.png"
		sprintf(name, "res/hero%d.png", i + 1);
		loadimage(&imgHeros[i], name);
	}

	// 设置玩家的初始位置
	heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;
	heroY = 345 - imgHeros[0].getheight();
	heroIndex = 0;

	heroJump = false;
	jumpHeightMax = 345 - imgHeros[0].getheight() - 120;
	heroJumpOff = -4;

	update = true;

 
	IMAGE imgTort;
	loadimage(&imgTort, "res/t1.png");
	vector<IMAGE> imgTortArray;
	imgTortArray.push_back(imgTort);
	obstacleImgs.push_back(imgTortArray);

	IMAGE imgLion;
	vector<IMAGE> imgLionArray;
	for (int i = 0; i < 6; i++) {
		sprintf(name, "res/p%d.png", i + 1);
		loadimage(&imgLion, name);
		imgLionArray.push_back(imgLion);
	}
	obstacleImgs.push_back(imgLionArray);

	// 初始化障碍物池
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		obstacles[i].exist = false;
	}
}

创建函数初始化障碍物(我们将用此函数创建所有的障碍物)

void createObstacle() {
    int i;
    for (i = 0; i < OBSTACLE_COUNT; i++) {
        if (obstacles[i].exist == false) {
            break;
        }
    }
    if (i >= OBSTACLE_COUNT) {
        return;
    }

    obstacles[i].exist = true;
    obstacles[i].imgIndex = 0;
    obstacles[i].type = (obstacle_type)(rand() % OBSTACLE_TYPE_COUNT);
    obstacles[i].x = WIN_WIDTH;
    obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();
    if (obstacles[i].type == TORTOISE) {
        obstacles[i].speed = 0;
        obstacles[i].power = 5; //自己修改
    }
    else if (obstacles[i].type == LION) {
        obstacles[i].speed = 4;
        obstacles[i].power = 20;
    }
}

再写一个函数进行创建障碍物的数据计算

void createObstacleData() {
 
		static int frameCount = 0;
	static int enemyFre = 50;
	frameCount++;
	if (frameCount > enemyFre) {
		frameCount = 0;
		enemyFre = 50 + rand() % 50; // 50..99
		createObstacle();
	}

 

	// 更新所有障碍物的坐标
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist) {
			obstacles[i].x -= obstacles[i].speed + bgSpeed[2];
			if (obstacles[i].x < -obstacleImgs[obstacles[i].type][0].getwidth() * 2) {
				obstacles[i].exist = false;
			}

			int len = obstacleImgs[obstacles[i].type].size();
			obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;
		}
	}
}

在对敌人初始化

void updateEnemy() {
	// 渲染小乌龟
	//if (torToiseExist) {
	//    putimagePNG2(torToiseX, torToiseY, WIN_WIDTH, &imgTortoise);
	//}
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist) {
			putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH,
				&obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]);
		}
	}
}

一下是main函数的主体

		if (update)
		{
			update = false;
			BeginBatchDraw();
			updateBg();
			putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
			updateEnemy();
			EndBatchDraw();
			updateEnemy();
			moveBg();
			HeroIndex();
			createObstacleData();
			jump(heroJump);
		}

实现下蹲技能

IMAGE imgHeroDown[2];
bool heroDown; //表示玩家是否处于下蹲状态

init()中对其初始化

// 初始化障碍物池
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		obstacles[i].exist = false;
	}
	// 加载下蹲素材
	loadimage(&imgHeroDown[0], "res/d1.png");
	loadimage(&imgHeroDown[1], "res/d2.png");
	heroDown = false;

我们写一个heroupdown函数集成up和down方便进行管理

void heroupdown()
{
	 // 实现跳跃
		if (heroJump) {
			if (heroY < jumpHeightMax) {
				heroJumpOff = 4;
			}

			heroY += heroJumpOff;

			if (heroY > 345 - imgHeros[0].getheight()) {
				heroJump = false;
				heroJumpOff = -4;
			}
		}
		else if (heroDown) {
			static int count = 0;
			int delays[2] = { 4, 10 };
			count++;
			if (count >= delays[heroIndex]) {
				count = 0;
				heroIndex++;
				if (heroIndex >= 2) {
					heroIndex = 0;
					heroDown = false;
				}
			}
		}
		else { //不跳跃
			heroIndex = (heroIndex + 1) % 12;
		}
}
 
void down() {
	update = true;
	heroDown = true;
	heroIndex = 0;
}

在接受键盘进行响应

void keyEvent() {
	char ch;

	if (_kbhit()) { //如果有按键按下,_kbhit()返回  true  
		ch = _getch();    // _getch()不需要按下回车即可直接读取
		if (ch == ' ') {
			jump();
		}
		else if (ch == 'a') {
			down();
		}
	}
}
void updateHero() {
    if (!heroDown) {
        putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
    }
    else {
        int y = 345 - imgHeroDown[heroIndex].getheight();
        putimagePNG2(heroX, y, &imgHeroDown[heroIndex]);
    }
    
}

main函数主体

int main(void) {
	int timer = 0;
	init();
	while (1) {
		keyEvent();

		timer += getDelay();//10
		if (timer > 20) {
			timer = 0;
			update = true;
		}

		if (update)
		{
			update = false;
			BeginBatchDraw();
			updateBg();
			//putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
			
			updateHero(); 
			updateEnemy();
			EndBatchDraw();

			
			moveBg();
			
			heroupdown();

 		 createObstacleData();
		}
	}

	return 0;
}

添加柱子障碍物

老规矩 加载-初始化

typedef enum {
	TORTOISE, //乌龟  0
	LION, //狮子 1
	OBSTACLE_TYPE_COUNT  // 2
	HOOK1,      //勾子
	HOOK2,
	HOOK3,
	HOOK4,
} obstacle_type;

void createObstacle() {

	srand((unsigned)time(NULL));
	int i;
	for (i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist == false) {
			break;
		}
	}
	if (i >= OBSTACLE_COUNT) {
		return;
	}

	obstacles[i].exist = true;
	obstacles[i].imgIndex = 0;
	//obstacles[i].type = (obstacle_type)(rand() % OBSTACLE_TYPE_COUNT);
	obstacles[i].type = (obstacle_type)(rand() % 3);
	 
	obstacles[i].x = WIN_WIDTH;
	obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();
	if (obstacles[i].type == HOOK1) {
		obstacles[i].type = (obstacle_type)((int)(obstacles[i].type) + rand() % 4);
	 }
	if (obstacles[i].type == TORTOISE) {
		obstacles[i].speed = 0;
		obstacles[i].power = 5; //自己修改
	}
	else if (obstacles[i].type == LION) {
		obstacles[i].speed = 4;
		obstacles[i].power = 20;
	}
	else if (obstacles[i].type >= HOOK1 && obstacles[i].type <= HOOK4) {
		obstacles[i].speed = 0;
		obstacles[i].power = 20;
		obstacles[i].y = 0;
	}
	
}


碰撞检测

typedef struct obstacle {
	obstacle_type type; //障碍物的类型
	int imgIndex; //当前显示的图片的序号
	int x, y; //障碍物的坐标
	int speed;
	int power; //杀伤力
	bool exist;
	bool hited;//是否撞击
}obstacle_t;

void  preLoadSound(const char* name) {
    char cmd[512];
    sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
    mciSendString(cmd, 0, 0, 0);
    sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
    mciSendString(cmd, 0, 0, 0);
}

bool rectIntersect(int x01, int y01, int x02, int y02,
	int x11, int y11, int x12, int y12)
{
	int zx = abs(x01 + x02 - x11 - x12);
	int x = abs(x01 - x02) + abs(x11 - x12);
	int zy = abs(y01 + y02 - y11 - y12);
	int y = abs(y01 - y02) + abs(y11 - y12);
	return  (zx <= x && zy <= y);
}

void  playSound(const char* name) {
    static int index = 1;
    char cmd[512];

    if (index == 1) {
        sprintf_s(cmd, sizeof(cmd), "play %s-1", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "close %s-2", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
        mciSendString(cmd, 0, 0, 0);
        index++;
    }
    else if (index == 2) {
        sprintf_s(cmd, sizeof(cmd), "play %s-2", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "close %s-1", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
        mciSendString(cmd, 0, 0, 0);
        index = 1;
    }
}

void checkHit()
{
	for (int i = 0; i < OBSTACLE_COUNT; i++)
	{
		if (obstacles[i].exist && obstacles[i].hited == false)
		{

			int a1x, a1y, a2x, a2y;
			int off = 20;
			if (!heroDown)//非下蹲 奔跑 跳跃
			{
				a1x = heroX + off;
				a1y = heroY + off;

				a2x = heroX + imgHeros[heroIndex].getwidth() - off;
				a2y = heroY + imgHeros[heroIndex].getheight();


			}
			else { //下蹲状态
				a1x = heroX + off;
				a1y = 345 - imgHeroDown[heroIndex].getheight();

				a2x = heroX + imgHeroDown[heroIndex].getwidth() - off;
				a2y = 345;
			}

			IMAGE img = obstacleImgs[obstacles[i].type][obstacles[i].imgIndex];

			int b1x = obstacles[i].x + off;
			int b1y = obstacles[i].y + off;
			int b2x = obstacles[i].x + img.getwidth() - off;
			int b2y = obstacles[i].y + img.getheight() - 10;

			if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y))
			{
			
				heroBlood -= obstacles[i].power;
				 
				playSound("res/hit.mp3");
				obstacles[i].hited = true;
			}
		}
	}

}

在这里插入图片描述

实现血条

优化下蹲:

int delays[2] = { 6, 10 };

改成
int delays[2] = { 8, 30 };

头文件

#include <mmsystem.h>
#pragma comment(lib, “winmm.lib”)

void updateBloodBar() {
    drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBlood / 100.0);
}

void drawBloodBar(int x, int y, int width, int height, int lineWidth, int boardColor, int emptyColor, int fillColor, float percent) {
    LINESTYLE lineStyle;
    getlinestyle(&lineStyle);
    int lineColor = getlinecolor();
    int fileColor = getfillcolor();

    if (percent < 0) {
        percent = 0;
    }

    setlinecolor(BLUE);
    setlinestyle(PS_SOLID | PS_ENDCAP_ROUND, lineWidth);
    setfillcolor(emptyColor);
    fillrectangle(x, y, x + width, y + height);
    setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 0);
    setfillcolor(fillColor);
    setlinecolor(fillColor);
    if (percent > 0) {
        fillrectangle(x + 0.5 * lineWidth, y + lineWidth * 0.5, x + width * percent, y + height - 0.5 * lineWidth);
    }
    
    setlinecolor(lineColor);
    setfillcolor(fillColor);
    setlinestyle(&lineStyle);
}
void init() {
	// 创建游戏窗口
	initgraph(WIN_WIDTH, WIN_HEIGHT);

	// 加载背景资源
	char name[64];
	for (int i = 0; i < 3; i++) {
		// "res/bg001.png"    "res/bg002.png"     "res/bg003.png"   
		sprintf(name, "res/bg%03d.png", i + 1);
		loadimage(&imgBgs[i], name);

		bgX[i] = 0;
	}

	// 加载Hero奔跑的图片帧素材
	for (int i = 0; i < 12; i++) {
		// "res/hero1.png"  ... "res/hero12.png"
		sprintf(name, "res/hero%d.png", i + 1);
		loadimage(&imgHeros[i], name);
	}

	// 设置玩家的初始位置
	heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;
	heroY = 345 - imgHeros[0].getheight();
	heroIndex = 0;

	heroJump = false;
	jumpHeightMax = 345 - imgHeros[0].getheight() - 120;
	heroJumpOff = -4;

	update = true;

 
	IMAGE imgTort;
	loadimage(&imgTort, "res/t1.png");
	vector<IMAGE> imgTortArray;
	imgTortArray.push_back(imgTort);
	obstacleImgs.push_back(imgTortArray);

	IMAGE imgLion;
	vector<IMAGE> imgLionArray;
	for (int i = 0; i < 6; i++) {
		sprintf(name, "res/p%d.png", i + 1);
		loadimage(&imgLion, name);
		imgLionArray.push_back(imgLion);
	}
	obstacleImgs.push_back(imgLionArray);

	// 初始化障碍物池
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		obstacles[i].exist = false;
	}
	// 加载下蹲素材
	loadimage(&imgHeroDown[0], "res/d1.png");
	loadimage(&imgHeroDown[1], "res/d2.png");
	heroDown = false;

	// 加载“柱子”障碍物
	IMAGE imgH;
	vector<IMAGE> imgHookArray;
	for (int i = 0; i < 4; i++) {
		sprintf_s(name, sizeof(name), "res/h%d.png", i + 1); //帧图片不够,补帧
		loadimage(&imgH, name, 63, 260, true);
		imgHookArray.push_back(imgH);
		obstacleImgs.push_back(imgHookArray);
		imgHookArray.pop_back();
	}
	heroBlood = 100;
	// 预加载音效
	preLoadSound("res/hit.mp3");

}

main : updateBloodBar();

判断游戏结束 添加背景音乐 添加初始界面

用屁股想 是初始化函数中

    mciSendString("play res/bg.mp3", 0, 0, 0);

在这里插入图片描述

    if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {
                heroBlood -= obstacles[i].power;
                printf("血量剩余 %d\n", heroBlood);
                playSound("res/hit.mp3");
                obstacles[i].hited = true;
            }
void checkOver() {
    if (heroBlood <= 0) {
        loadimage(0, "res/over.png");
        FlushBatchDraw();
        mciSendString("stop res/bg.mp3", 0, 0, 0);
        system("pause");

        // 暂停之后,充币复活,或者直接开始下一局
        heroBlood = 100;
        mciSendString("play res/bg.mp3", 0, 0, 0);
    }
}
int main(void) {
	int timer = 0;
	init();
	// 显示初始画面
	loadimage(0, "res/over.png");
	system("pause");
	while (1) {
		keyEvent();

		timer += getDelay();//10
		if (timer > 20) {
			timer = 0;
			update = true;
		}

		if (update)
		{
			update = false;
			BeginBatchDraw();
			updateBg();
			 
			updateHero(); 
			updateEnemy();
			updateBloodBar();
			EndBatchDraw();

		
			moveBg();
			
			heroupdown();

 		  createObstacleData();
		  checkHit();
		  checkOver();
		}
	}

	return 0;
}

优化死亡BUG

int lastObsIndex; //last obstacle index
void createObstacle() {

	srand((unsigned)time(NULL));
	int i;
	for (i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist == false) {
			break;
		}
	}
	if (i >= OBSTACLE_COUNT) {
		return;
	}

	obstacles[i].exist = true;
	obstacles[i].imgIndex = 0;
	//obstacles[i].type = (obstacle_type)(rand() % OBSTACLE_TYPE_COUNT);
	obstacles[i].type = (obstacle_type)(rand() % 3);
	 
	obstacles[i].x = WIN_WIDTH;
	obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();
	if (lastObsIndex >= HOOK1 && lastObsIndex <= HOOK4 &&
		obstacles[i].type == LION &&
		obstacles[lastObsIndex].x > (WIN_WIDTH - 500)) {
		obstacles[i].type = TORTOISE;
	}
	lastObsIndex = i;
	if (obstacles[i].type == HOOK1) {
		obstacles[i].type = (obstacle_type)((int)(obstacles[i].type) + rand() % 4);
	 }
	if (obstacles[i].type == TORTOISE) {
		obstacles[i].speed = 0;
		obstacles[i].power = 5; //自己修改
	}
	else if (obstacles[i].type == LION) {
		obstacles[i].speed = 4;
		obstacles[i].power = 20;
	}
	else if (obstacles[i].type >= HOOK1 && obstacles[i].type <= HOOK4) {
		obstacles[i].speed = 0;
		obstacles[i].power = 20;
		obstacles[i].y = 0;
	}
	
}

 

跨越障碍后计算得分

int score; //分数
lastObsIndex = -1;
	score = 0;
void checkScore() {
    for (int i = 0; i < OBSTACLE_COUNT; i++) {
        if (obstacles[i].exist &&
                obstacles[i].passed == false &&
                obstacles[i].x + obstacleImgs[obstacles[i].type][0].getwidth() < heroX) {
            score++;
            obstacles[i].passed = true;
            printf("score: %d\n", score);
        }
    }
}
typedef struct obstacle {
	obstacle_type type; //障碍物的类型
	int imgIndex; //当前显示的图片的序号
	int x, y; //障碍物的坐标
	int speed;
	int power; //杀伤力
	bool exist;
	bool hited;
	bool passed;
}obstacle_t;
int main(void) {
	int timer = 0;
	init();
	// 显示初始画面
	loadimage(0, "res/over.png");
	system("pause");
	while (1) {
		keyEvent();

		timer += getDelay();//10
		if (timer > 20) {
			timer = 0;
			update = true;
		}

		if (update)
		{
			update = false;
			BeginBatchDraw();
			updateBg();
			 
			updateHero(); 
			updateEnemy();
			updateBloodBar();
			EndBatchDraw();

		
			moveBg();
			
			heroupdown();

 		  createObstacleData();
		  checkHit();
		  checkOver();
		  checkScore();
		}
	}

	return 0;
}
void updateScore() {
    // 50 => "50"   '5'     '5'-'0' == 5   
    char str[8];
    sprintf(str, "%d", score);

    int x = 20;
    int y = 25;

    for (int i = 0; str[i]; i++) {
        int sz = str[i] - '0';
        putimagePNG(x, y, &imgSZ[sz]);
        x += imgSZ[sz].getwidth() + 5;
    }
}

 		   updateBloodBar();
            updateScore();
            EndBatchDraw();

            checkOver();
            checkScore();

判断游戏胜利

写main函数中的位置 这里不再赘述

#define WIN_SCORE   100
void checkWin() {
    if (score >= WIN_SCORE) {
        FlushBatchDraw();
        mciSendString("play res/win.mp3", 0, 0, 0);
        Sleep(2000);
        loadimage(0, "res/win.png");
        FlushBatchDraw();
        mciSendString("stop res/bg.mp3", 0, 0, 0);
        system("pause");

        heroBlood = 100;
        score = 0;
        mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
    }
}

源代码

#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include <vector>
#include <time.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")


#define WIN_SCORE   100
#define WIN_WIDTH 1012
#define WIN_HEIGHT 396
#define OBSTACLE_COUNT 10

using namespace std;


IMAGE imgHeros[12];
IMAGE imgBgs[3];
IMAGE imgHeroDown[2];
int bgX[3]; 
int bgSpeed[3] = { 1, 2, 4 };
int heroX;
int heroY;
int heroIndex;
bool heroJump;
int jumpHeightMax;
int heroJumpOff;
bool update;
bool heroDown; 
int heroBlood;
int score;
int lastObsIndex;


typedef enum {
	TORTOISE, //乌龟  0
	LION, //狮子 1	 
	HOOK1,      //勾子
	HOOK2,
	HOOK3,
	HOOK4,
	OBSTACLE_TYPE_COUNT ,
} obstacle_type;

vector<vector<IMAGE>>obstacleImgs; //存放所有障碍物的各个图片

typedef struct obstacle {
	obstacle_type type; //障碍物的类型
	int imgIndex; //当前显示的图片的序号
	int x, y; //障碍物的坐标
	int speed;
	int power; //杀伤力
	bool exist;
	bool hited;
	bool passed;
}obstacle_t;

obstacle_t obstacles[OBSTACLE_COUNT];



void  preLoadSound(const char* name);
void drawBloodBar(int x, int y, int width, int height, int lineWidth, int boardColor, int emptyColor, int fillColor, float percent);

void init();
void moveBg();
void jump();
void heroupdown();
int getDelay();
void keyEvent();
void updateBg();
void updateEnemy();
void jump(bool herJump);
bool rectIntersect(int a1X, int a1Y, int a2X, int a2Y,
	int b1X, int b1Y, int b2X, int b2Y);
void updateEnemy();
void updateBloodBar();
void createObstacleData();
void createObstacle();
 void putimagePNG2(int x, int y, IMAGE* picture);
void putimagePNG2(int x, int y, int winWidth, IMAGE* picture);
void putimagePNG(int  picture_x, int picture_y, IMAGE* picture);
void HeroIndex();
void  playSound(const char* name);

void  preLoadSound(const char* name) {
	char cmd[512];
	sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
	mciSendString(cmd, 0, 0, 0);
	sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
	mciSendString(cmd, 0, 0, 0);
}


void drawBloodBar(int x, int y, int width, int height, int lineWidth, int boardColor, int emptyColor, int fillColor, float percent) {
	LINESTYLE lineStyle;
	getlinestyle(&lineStyle);
	int lineColor = getlinecolor();
	int fileColor = getfillcolor();

	if (percent < 0) {
		percent = 0;
	}

	setlinecolor(BLUE);
	setlinestyle(PS_SOLID | PS_ENDCAP_ROUND, lineWidth);
	setfillcolor(emptyColor);
	fillrectangle(x, y, x + width, y + height);
	setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 0);
	setfillcolor(fillColor);
	setlinecolor(fillColor);
	if (percent > 0) {
		fillrectangle(x + 0.5 * lineWidth, y + lineWidth * 0.5, x + width * percent, y + height - 0.5 * lineWidth);
	}

	setlinecolor(lineColor);
	setfillcolor(fillColor);
	setlinestyle(&lineStyle);
}


bool rectIntersect(int x01, int y01, int x02, int y02,
	int x11, int y11, int x12, int y12)
{
	int zx = abs(x01 + x02 - x11 - x12);
	int x = abs(x01 - x02) + abs(x11 - x12);
	int zy = abs(y01 + y02 - y11 - y12);
	int y = abs(y01 - y02) + abs(y11 - y12);
	return  (zx <= x && zy <= y);
}


void  playSound(const char* name) {
	static int index = 1;
	char cmd[512];

	if (index == 1) {
		sprintf_s(cmd, sizeof(cmd), "play %s-1", name);
		mciSendString(cmd, 0, 0, 0);
		sprintf_s(cmd, sizeof(cmd), "close %s-2", name);
		mciSendString(cmd, 0, 0, 0);
		sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
		mciSendString(cmd, 0, 0, 0);
		index++;
	}
	else if (index == 2) {
		sprintf_s(cmd, sizeof(cmd), "play %s-2", name);
		mciSendString(cmd, 0, 0, 0);
		sprintf_s(cmd, sizeof(cmd), "close %s-1", name);
		mciSendString(cmd, 0, 0, 0);
		sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
		mciSendString(cmd, 0, 0, 0);
		index = 1;
	}
}


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

void jump() {
	heroJump = true;
	update = true;
}

void down() {
	update = true;
	heroDown = true;
	heroIndex = 0;
}

void heroupdown()
{
	 // 实现跳跃
		if (heroJump) {
			if (heroY < jumpHeightMax) {
				heroJumpOff = 4;
			}

			heroY += heroJumpOff;

			if (heroY > 345 - imgHeros[0].getheight()) {
				heroJump = false;
				heroJumpOff = -4;
			}
		}
		else if (heroDown) {
			static int count = 0;
			int delays[2] = { 8, 30 };
			count++;
			if (count >= delays[heroIndex]) {
				count = 0;
				heroIndex++;
				if (heroIndex >= 2) {
					heroIndex = 0;
					heroDown = false;
				}
			}
		}
		else { //不跳跃
			heroIndex = (heroIndex + 1) % 12;
		}
}
 
void updateBloodBar() {
	drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBlood / 100.0);
}
void moveBg() {
	for (int i = 0; i < 3; i++) {
		bgX[i] -= bgSpeed[i];
		if (bgX[i] < -WIN_WIDTH) {
			bgX[i] = 0;
		}
	}
}


void createObstacleData() {
 
	static int frameCount = 0;
	static int enemyFre = 50;
	frameCount++;
	if (frameCount > enemyFre) {
		frameCount = 0;
		enemyFre = 50 + rand() % 50; // 50..99
		createObstacle();
	}

 

	// 更新所有障碍物的坐标
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist) {
			obstacles[i].x -= obstacles[i].speed + bgSpeed[2];
			if (obstacles[i].x < -obstacleImgs[obstacles[i].type][0].getwidth() * 2) {
				obstacles[i].exist = false;
			}

			int len = obstacleImgs[obstacles[i].type].size();
			obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;
		}
	}
}
void checkHit()
{
	for (int i = 0; i < OBSTACLE_COUNT; i++)
	{
		if (obstacles[i].exist && obstacles[i].hited == false)
		{

			int a1x, a1y, a2x, a2y;
			int off = 20;
			if (!heroDown)//非下蹲 奔跑 跳跃
			{
				a1x = heroX + off;
				a1y = heroY + off;

				a2x = heroX + imgHeros[heroIndex].getwidth() - off;
				a2y = heroY + imgHeros[heroIndex].getheight();


			}
			else { //下蹲状态
				a1x = heroX + off;
				a1y = 345 - imgHeroDown[heroIndex].getheight();

				a2x = heroX + imgHeroDown[heroIndex].getwidth() - off;
				a2y = 345;
			}

			IMAGE img = obstacleImgs[obstacles[i].type][obstacles[i].imgIndex];

			int b1x = obstacles[i].x + off;
			int b1y = obstacles[i].y + off;
			int b2x = obstacles[i].x + img.getwidth() - off;
			int b2y = obstacles[i].y + img.getheight() - 10;

			if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {
				heroBlood -= obstacles[i].power;
				printf("血量剩余 %d\n", heroBlood);
				playSound("res/hit.mp3");
				obstacles[i].hited = true;
			}
		}
	}

}


void checkOver() {
	if (heroBlood <= 0) {
		loadimage(0, "res/over.png");
		FlushBatchDraw();
		mciSendString("stop res/bg.mp3", 0, 0, 0);
		system("pause");

		// 暂停之后,充币复活,或者直接开始下一局
		heroBlood = 100;
		mciSendString("play res/bg.mp3", 0, 0, 0);
	}
}

void createObstacle() {

	srand((unsigned)time(NULL));
	int i;
	for (i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist == false) {
			break;
		}
	}
	if (i >= OBSTACLE_COUNT) {
		return;
	}

	obstacles[i].exist = true;
	obstacles[i].imgIndex = 0;
	//obstacles[i].type = (obstacle_type)(rand() % OBSTACLE_TYPE_COUNT);
	obstacles[i].type = (obstacle_type)(rand() % 3);
	 
	obstacles[i].x = WIN_WIDTH;
	obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();
	if (lastObsIndex >= HOOK1 && lastObsIndex <= HOOK4 &&
		obstacles[i].type == LION &&
		obstacles[lastObsIndex].x > (WIN_WIDTH - 500)) {
		obstacles[i].type = TORTOISE;
	}
	lastObsIndex = i;
	if (obstacles[i].type == HOOK1) {
		obstacles[i].type = (obstacle_type)((int)(obstacles[i].type) + rand() % 4);
	 }
	if (obstacles[i].type == TORTOISE) {
		obstacles[i].speed = 0;
		obstacles[i].power = 5; //自己修改
	}
	else if (obstacles[i].type == LION) {
		obstacles[i].speed = 4;
		obstacles[i].power = 20;
	}
	else if (obstacles[i].type >= HOOK1 && obstacles[i].type <= HOOK4) {
		obstacles[i].speed = 0;
		obstacles[i].power = 20;
		obstacles[i].y = 0;
	}
	
}

 

//渲染游戏背景
void updateBg()
{
	putimagePNG2(bgX[0], 0, &imgBgs[0]);
	putimagePNG2(bgX[1], 119, &imgBgs[1]);
	putimagePNG2(bgX[2], 330, &imgBgs[2]);
}
//
 处理用户按键的输入
//void keyEvent() {
//	char ch;
//
//	if (_kbhit()) { //如果有按键按下,_kbhit()返回  true
//		ch = _getch();    // _getch()不需要按下回车即可直接读取
//		if (ch == ' ') {
//			jump();
//		}
//	}
//}

 // 处理用户按键的输入
void keyEvent() {
	char ch;

	if (_kbhit()) { //如果有按键按下,_kbhit()返回  true  
		ch = _getch();    // _getch()不需要按下回车即可直接读取
		if (ch == ' ') {
			jump();
		}
		else if (ch == 's') {
			down();
		}
	}
}

void jump(bool heroJump)
{
	// 实现跳跃
	if (heroJump) {
		if (heroY < jumpHeightMax) {
			heroJumpOff = 4;
		}

		heroY += heroJumpOff;

		if (heroY > 345 - imgHeros[0].getheight()) {
			heroJump = false;
			heroJumpOff = -4;
		}
	}
	else { //不跳跃
		heroIndex = (heroIndex + 1) % 12;
	}
	update = true;
}
 
void updateEnemy() {
	 
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist) {
			putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH,
				&obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]);
		}
	}
}
// 游戏的初始化
void init() {
	// 创建游戏窗口
	initgraph(WIN_WIDTH, WIN_HEIGHT);

	// 加载背景资源
	char name[64];
	for (int i = 0; i < 3; i++) {
		// "res/bg001.png"    "res/bg002.png"     "res/bg003.png"   
		sprintf(name, "res/bg%03d.png", i + 1);
		loadimage(&imgBgs[i], name);

		bgX[i] = 0;
	}

	// 加载Hero奔跑的图片帧素材
	for (int i = 0; i < 12; i++) {
		// "res/hero1.png"  ... "res/hero12.png"
		sprintf(name, "res/hero%d.png", i + 1);
		loadimage(&imgHeros[i], name);
	}

	// 设置玩家的初始位置
	heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;
	heroY = 345 - imgHeros[0].getheight();
	heroIndex = 0;

	heroJump = false;
	jumpHeightMax = 345 - imgHeros[0].getheight() - 120;
	heroJumpOff = -4;

	update = true;

 
	IMAGE imgTort;
	loadimage(&imgTort, "res/t1.png");
	vector<IMAGE> imgTortArray;
	imgTortArray.push_back(imgTort);
	obstacleImgs.push_back(imgTortArray);

	IMAGE imgLion;
	vector<IMAGE> imgLionArray;
	for (int i = 0; i < 6; i++) {
		sprintf(name, "res/p%d.png", i + 1);
		loadimage(&imgLion, name);
		imgLionArray.push_back(imgLion);
	}
	obstacleImgs.push_back(imgLionArray);

	// 初始化障碍物池
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		obstacles[i].exist = false;
	}
	// 加载下蹲素材
	loadimage(&imgHeroDown[0], "res/d1.png");
	loadimage(&imgHeroDown[1], "res/d2.png");
	heroDown = false;

	// 加载“柱子”障碍物
	IMAGE imgH;
	vector<IMAGE> imgHookArray;
	for (int i = 0; i < 4; i++) {
		sprintf_s(name, sizeof(name), "res/h%d.png", i + 1); //帧图片不够,补帧
		loadimage(&imgH, name, 63, 260, true);
		imgHookArray.push_back(imgH);
		obstacleImgs.push_back(imgHookArray);
		imgHookArray.pop_back();
	}
	heroBlood = 100;
	// 预加载音效
	preLoadSound("res/hit.mp3");
	lastObsIndex = -1;
	score = 0;

}

void checkScore() {
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist &&
			obstacles[i].passed == false &&
			obstacles[i].x + obstacleImgs[obstacles[i].type][0].getwidth() < heroX) {
			score++;
			obstacles[i].passed = true;
			printf("score: %d\n", score);
		}
	}
}

void updateHero() {
	if (!heroDown) {
		putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
	}
	else {
		int y = 345 - imgHeroDown[heroIndex].getheight();
		putimagePNG2(heroX, y, &imgHeroDown[heroIndex]);
	}

}


void HeroIndex()
{
	heroIndex = (heroIndex + 1) % 12;
}


void checkWin() {
	if (score >= WIN_SCORE) {
		FlushBatchDraw();
		mciSendString("play res/win.mp3", 0, 0, 0);
		Sleep(2000);
		loadimage(0, "res/win.png");
		FlushBatchDraw();
		mciSendString("stop res/bg.mp3", 0, 0, 0);
		system("pause");

		heroBlood = 100;
		score = 0;
		mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
	}
}

int getDelay() {
	static unsigned long long lastTime = 0;
	unsigned long long currentTime = GetTickCount();
	if (lastTime == 0) {
		lastTime = currentTime;
		return 0;
	}
	else {
		int ret = currentTime - lastTime;
		lastTime = currentTime;
		return ret;
	}
}

int main(void) {
	int timer = 0;
	init();
	// 显示初始画面
	loadimage(0, "res/over.png");
	system("pause");
	while (1) {
		keyEvent();

		timer += getDelay();//10
		if (timer > 20) {
			timer = 0;
			update = true;
		}

		if (update)
		{
			update = false;
			BeginBatchDraw();
			updateBg();
			 
			updateHero(); 
			updateEnemy();
			updateBloodBar();
			checkWin();
			EndBatchDraw();

		
			moveBg();
			
			heroupdown();

 		  createObstacleData();
		  checkHit();
		  checkOver();
		  checkScore();
		  
		}
	}

	return 0;
}




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/43975.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop概念学习(无spring集成)

Hadoop 分布式的文件存储系统 三个核心组件 但是现在已经发展到很多组件的s 或者这个图 官网地址: https://hadoop.apache.org 历史 hadoop历史可以看这个: https://zhuanlan.zhihu.com/p/54994736 优点 高可靠性&#xff1a; Hadoop 底层维护多个数据副本&#xff0c;所…

初识网络 --- 浅了解一些基础概念

文章目录 初识网络局域网与广域网 初识协议协议分层 OSI七层模型TCP/IP 四层&#xff08;五层&#xff09;模型网络传输基本流程协议报头局域网通信原理传输流程图数据包封装和分用 初识网络 在每台计算机独立的情况下&#xff1a;假设现在有三台计算机&#xff0c;每台计算机…

2024考研408-操作系统 第五章-输入输出IO管理 学习笔记

文章目录 一、I/O管理概述1.1、I/O设备的概念与分类1.1.1、什么是I/O设备&#xff1f;1.1.2、I/O设备的分类&#xff1a;按照使用特性1.1.2、I/O设备的分类&#xff1a;按传输速率分类1.1.3、I/O设备的分类&#xff1a;按照信息交换的单位分类知识点回顾与重要考点 1.2、I/O控制…

SpringBoot Redis 使用Lettuce和Jedis配置哨兵模式

Redis 从入门到精通【应用篇】之SpringBoot Redis 配置哨兵模式 Lettuce 和Jedis 文章目录 Redis 从入门到精通【应用篇】之SpringBoot Redis 配置哨兵模式 Lettuce 和Jedis前言Lettuce和Jedis区别1. 连接方式2. 线程安全性 教程如下1. Lettuce 方式配置1.1. 添加 Redis 和 Let…

【目标跟踪】1、基础知识

文章目录 一、卡尔曼滤波二、匈牙利匹配 一、卡尔曼滤波 什么是卡尔曼滤波&#xff1f;——状态估计器 卡尔曼滤波用于在包含不确定信息的系统中做出预测&#xff0c;对系统下一步要做什么进行推测&#xff0c;且会结合推测值和观测值来得到修正后的最优值卡尔曼滤波就是利用…

git 和adb

一、git 1、git的作用 git是一个版本控制系统&#xff0c;是一种记录一个或若干文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 我的理解就是代码管理器&#xff1a; 第一点你可将代码备份到git仓上&#xff1b; 第二点可记录的你修改记录&#xff1b; 第三点…

周训龙老兵参观广西森林安全紧急救援装备演练

7月21日上午&#xff0c;周训龙老兵参观广西紧急救援促进中心在南宁市青秀山举行森林安全紧急救援装备演练&#xff0c;多功能水罐消防车、无人救援机等先进设备轮番上阵&#xff0c;展示了广西应对突发事件的紧急救援速度和水平。广西壮族自治区应急厅不情愿参此次演练活动。 …

Python Flask构建微信小程序订餐系统 (十)

🔥 编辑会员信息 🔥 编辑会员信息可以通过点击会员列表操作,也可以点击会员信息详情点击进行操作 🔥 修改编程会员信息列表布局 🔥 修改 web/templates/member/index.html 文件,添加跳转到编辑会员信息的页面 web/templates/member/set.html 🔥 创建用于会员…

Mybatis单元测试,不使用spring

平时开发过程中需要对mybatis的Mapper类做单元测试&#xff0c;主要是验证语法是否正确&#xff0c;尤其是一些复杂的动态sql&#xff0c;一般项目都集成了spring或springboot&#xff0c;当项比较大时&#xff0c;每次单元测试启动相当慢&#xff0c;可能需要好几分钟&#xf…

OpenCV图像处理-图像分割-MeanShift

MeanShift 1. 基本概念2.代码示例 1. 基本概念 MeanShift严格说来并不是用来对图像进行分割的&#xff0c;而是在色彩层面的平滑滤波。它会中和色彩分布相近的颜色&#xff0c;平滑色彩细节&#xff0c;侵蚀掉面积较小的的颜色区域&#xff0c;它以图像上任意一点P为圆心&…

优思学院|六西格玛案例分析 - 降低焊接缺陷率

大家都知道六西格玛方法中的控制图有助于监测流程的稳定性和识别特有原因的发生。对流程周期性地采样&#xff0c;当测量结果在控制上限和下限内&#xff0c;而且围绕着一条中心线时&#xff0c;我们就说流程是受控的。注意上述控制上限和下限有别于规范限。 我们来看看一家工…

电脑安装双系统ubuntu18.04+windows后开机直接进入Windows解决方法

电脑型号&#xff1a;联想拯救者Y9000K2021H 系统&#xff1a;Windows11Ubuntu18.04双系统 问题&#xff1a;笔记本安装双系统后&#xff0c;Windows系统下处理word或者看论文&#xff1b;Ubuntu18.04系统安装ros进行机械臂控制等的研究。但最近开机后发现没有系统选项了&#…

知识库数据导出为excel-使用JavaScript实现在浏览器中导出Excel文件

我们智能客服知识库机器人已经开发完成&#xff0c;后端数据库是使用的qdrant向量数据库&#xff0c;但是该数据库并没有导出备份功能&#xff0c;所以我按简单的纯前端实现知识库导出excel数据 使用第三方库(如SheetJS) SheetJS是一个流行的JavaScript库&#xff0c;可帮助处理…

App隐私及合规评估服务

随着移动应用种类和数量呈爆发式增长&#xff0c;APP侵害用户权益事件层出不穷&#xff0c;为规范个人信息的收集使用&#xff0c;打击涉及个人信息违法犯罪行为&#xff0c;我国相继出台多个涉及个人信息保护相关法律法规。与此同时&#xff0c;中央网信办、工信部、公安部、市…

获取大疆无人机的飞控记录数据并绘制曲线

机型M350RTK&#xff0c;其飞行记录文件为加密的&#xff0c;我的完善代码如下 gitgithub.com:huashu996/DJFlightRecordParsing2TXT.git 一、下载安装官方的DJIFlightRecord git clone gitgithub.com:dji-sdk/FlightRecordParsingLib.git飞行记录文件在打开【我的电脑】&am…

Istio Pilot源码学习(二):ServiceController服务发现

本文基于Istio 1.18.0版本进行源码学习 4、服务发现&#xff1a;ServiceController ServiceController是服务发现的核心模块&#xff0c;主要功能是监听底层平台的服务注册中心&#xff0c;将平台服务模型转换成Istio服务模型并缓存&#xff1b;同时根据服务的变化&#xff0c…

OpenHarmony与HarmonyOS联系与区别

目录 1. 背景 2.OpenHarmony 3.HarmonyOS 4.鸿蒙生态 5.OpenHarmony与HarmonyOS的技术上实现区别 1.语言支持 2.SDK 的不同 3.运行调测方式不同 4.对APK的兼容性不同 5.包含关系 6.调试命令 6.何时选择OpenHarmony或是HarmonyOS&#xff1f; 1. 背景 开篇就说“关于…

2023最新谷粒商城笔记之Sentinel概述篇(全文总共13万字,超详细)

Sentinel概述 服务流控、熔断和降级 什么是熔断 当扇出链路的某个微服务不可用或者响应时间太长时&#xff0c;会进行服务的降级&#xff0c;**进而熔断该节点微服务的调用&#xff0c;快速返回错误的响应信息。**检测到该节点微服务调用响应正常后恢复调用链路。A服务调用B服…

Spring Security 身份验证的基本类/架构

目录 1、SecurityContextHolder 核心类 2、SecurityContext 接口 3、Authentication 用户认证信息接口 4、GrantedAuthority 拥有权限接口 5、AuthenticationManager 身份认证管理器接口 6、ProviderManager 身份认证管理器的实现 7、AuthenticationProvider 特定类型的…

数字孪生管控系统,智慧园区楼宇合集

智慧园区是指将物联网、大数据、人工智能等技术应用于传统建筑和基础设施&#xff0c;以实现对园区的全面监控、管理和服务的一种建筑形态。通过将园区内设备、设施和系统联网&#xff0c;实现数据的传输、共享和响应&#xff0c;提高园区的管理效率和运营效益&#xff0c;为居…