天天酷跑-C语言搭建童年游戏(easyx)

news2024/10/2 15:21:49

游戏索引

游戏名称:天天酷跑



游戏介绍:

本游戏是在B站博主<程序员Rock>的视频指导下完成

想学的更详细的小伙伴可以移步到<程序员Rock>视频

【程序员Rock】C语言项目:手写天天酷跑丨大一课程设计首选项目,手把手带你用C语言游戏开发
 


游戏效果展示#

天天酷跑


游戏模块:

<1>安装easyx图形库

<2>创建项目
<3>导入素材
<4>创建游戏开始界面
<5>循环滚动背景实现
<6>创建人物实现奔跑

<7>实现人物的跳跃和下蹲

<8>创建障碍物

<9>实现人物的血条

<10>人物和障碍物的碰撞

<11>实现游戏的积分

<12>游戏输赢的判定


目录

游戏索引

 写代码前的准备工作 

搭建项目环境easyx:

导入游戏素材 : 

修改项目属性:

导入我们的辅助项目: 

tools.h

tools.cpp

导入操作#

实现游戏开始界面 

 游戏的定义与声明

游戏的初始化 

 实现游戏背景展示

图片背景的显示

图片移动的实现

实现用户的按键操作

实现人物的展示 

​编辑

奔跑人物的显示

人物的跳跃和下蹲的实现

渲染障碍物 

显示障碍物

更新障碍物的位置

随机生成小乌龟

人物与障碍物的碰撞检测

人物血量的显示 

游戏分数的显示 

游戏输赢的判定 

游戏赢的判定

游戏输的判定

游戏主函数 

全部代码的实现 


素材已上传至百度网盘:
百度网盘 请输入提取码

提取码:ABCD

 写代码前的准备工作 

搭建项目环境easyx:

要想在我们的窗口实现图片交互

应该给编译器安装 easyx 图形库

这边我用图片展示详细的安装操作

(1)我们先在网页找到官网

(2)然后点击下载

(3)将 easyx 安装到目标编译器

(4)出现安装成功就代表可以使用了

接下来我们就可以写放心写代码啦!!!

导入游戏素材 : 

在当前项目的目录下创建文件夹,并把解压好的文件 res 拷贝进去

修改项目属性:

 <1>点击项目找到项目属性

<2>将字符集改成多字符集

这里图片操作的时候需要

<3>将编译器对SDL的检查关掉

导入我们的辅助项目: 

tools.h
#pragma once
#include <graphics.h>

//返回距离上一次调用间隔的时间(单位:ms),第一次调用时返回0
int getDelay(); 

void putimagePNG(int  picture_x, int picture_y, IMAGE* picture);

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

// 判断两个矩形是否相交
bool rectIntersect(int a1X, int a1Y, int a2X, int a2Y,
    int b1X, int b1Y, int b2X, int b2Y);

void  preLoadSound(const char* name);
void  playSound(const char* name);

void drawBloodBar(int x, int y, int width, int height, int lineWidth, int boardColor, int emptyColor, int fillColor, float percent);
tools.cpp
#include <stdio.h>
#include <Windows.h>
#include "tools.h"

#include <mmsystem.h>//播放音乐
#pragma comment(lib, "winmm.lib")


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

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

//设A[x01,y01,x02,y02]  B[x11,y11,x12,y12].
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  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  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 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);
}
导入操作#

<1>点击项目添加现有项

<2>选定我们要添加的辅助项目选择添加就好啦

实现游戏开始界面 

按钮放置的位置我们可以通过 画图软件 或者 微信截图 来获取

void startUI() {
	// 添加背景音乐
	mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
	IMAGE imgMenu, imgMenu1;
	int	flag = 0;
	loadimage(&imgMenu, "res/Menu1.png");
	loadimage(&imgMenu1, "res/menu.png");
	while (1) {
		BeginBatchDraw();
		putimage(0, 0, &imgMenu);
		putimagePNG(474, 150, &imgMenu1);
		ExMessage	msg;
		if (peekmessage(&msg)) {
			//判断是否有左键按下
			if (msg.message == WM_LBUTTONDOWN &&msg.x > 474 && msg.x < 720 && msg.y>150 && msg.y < 170) {
            //按键点击的范围
				flag = 1;
			}
		}
		    //判断鼠标按下后有没有松开
		else if (msg.message == WM_LBUTTONUP && flag == 1) {
			mciSendString("close res/bg.mp3", 0, 0, 0);
			EndBatchDraw();
			return;
		}
		EndBatchDraw();
	}
}

 游戏的定义与声明

//这里采用宏定义为的是更好的维护
#define WIN_WIDTH 1012     //窗口的宽度
#define	 WIN_HEIGHT 396    //窗口的高度
#define OBSTACLE_COUNT 10  //障碍物数量
#define WIN_SCORE 20       //获胜的分数

IMAGE imaBgs[3];            //图片类型的指针
int bgX[3];		            //背景图片
int bgspeed[3] = { 1,2,4 }; //背景移动的速度进行控制

IMAGE bgHeros[12];          //人物帧数-图片张数
int HeroX;                  //人物坐标
int HeroY;
int HeroIndex;              //玩家奔跑的图片帧序号

//判断人物的跳跃和下蹲
bool heroJump;              
bool herodown;

int jumpHeightMax;
int jumpHeightOff;//跳跃偏移量

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

int heroBlood;
int score;

//枚举障碍物,计算障碍物的种类
typedef enum {
	TORTOISE,//乌龟	
	LION,    //狮子
	HOOK1,   //挡板
	HOOK2,
	HOOK3,
	HOOK4,
	OBSTACLE_TYPE_COUNT     //直接表示障碍物的个数		
}obstacle_type; 

obstacle_type type;

vector<vector<IMAGE>> obstacleImags; //动态二维数组存放各个障碍物的图片


typedef struct obstacle
{
	obstacle_type type;	       //障碍物类型
	int imaIndex;              //当前显示图片的序号
	int x, y;	               //障碍物的坐标
	int speed;                 //障碍物速度
	int power;                 //杀伤力
	bool exist;                //存在检测
	bool hited;	               //碰撞检测
	bool passed;               //通过检测
} obstacle_t;

obstacle_t obstacles[OBSTACLE_COUNT];

int lastObsIndex;


IMAGE imgHeroDown[2];
IMAGE imgSZ[10];

游戏的初始化 

void init()
{
	//创建游戏界面
	initgraph(WIN_WIDTH, WIN_HEIGHT);

	//循环来读取用数组来接收
	char name[64];
	for (int i = 0; i < 3; i++) {
		sprintf(name, "res/bg%03d.png", i + 1);
		loadimage(&imaBgs[i], name);//加载背景资源 
		bgX[i] = 0;
	}

	//加载人物奔跑的图片帧数
	for (int i = 0; i < 12; i++) {
		sprintf(name, "res/hero%d.png", i + 1);
		loadimage(&bgHeros[i], name);
	}
	//设置人物奔跑的位置
	HeroX = WIN_WIDTH * 0.5 - bgHeros[0].getwidth() * 0.5;
	HeroY = 345 - bgHeros[0].getheight();
	HeroIndex = 0;

	heroJump = false;

	jumpHeightMax = 345 - bgHeros[0].getheight() - 120;//跳跃的最大高度
	jumpHeightOff = -4; 

	update = true;
	//加载小乌龟
	IMAGE imgTort;
	loadimage(&imgTort, "res/t1.png");    //加载乌龟图片,这里只用了一张
	vector<IMAGE>imgTortArray;            //定义乌龟障碍图片空数组
	imgTortArray.push_back(imgTort);      //将imgTort尾插到空数组,若多个图片可利用循环添加见LION
	obstacleImags.push_back(imgTortArray);//将乌龟图片数组尾插到上面的二维数组中
	//加载小狮子
	IMAGE imaLion;
	vector<IMAGE> imaLionArray;
	for (int i = 0; i < 6; i++) {
		sprintf(name, "res/p%d.png", i + 1);
		loadimage(&imaLion, name);
		imaLionArray.push_back(imaLion);
	}
	obstacleImags.push_back(imaLionArray);


	//初始化障碍物池
	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;

	for (int i = 0; i < 4; i++) {
		vector<IMAGE> imgHookArray;
		sprintf(name, "res/sz/h%d.png", i + 1);
		loadimage(&imgH, name, 70, 260, true);
		imgHookArray.push_back(imgH);
		obstacleImags.push_back(imgHookArray);
	}
	//血量
	heroBlood = 100;
	//预加载音效
	preLoadSound("res/hit.mp3");

	lastObsIndex = -1;
	//分数初始化
	score = 0;

	//加载数字图片
	for (int i = 0; i < 10; i++) {
		sprintf(name, "res/sz/%d.png", i);
		loadimage(&imgSZ[i], name);
	}
}

 实现游戏背景展示

图片背景的显示

void updateBg()
{
	putimagePNG2(bgX[0], 0, &imaBgs[0]);
	putimagePNG2(bgX[1], 119, &imaBgs[1]);
	putimagePNG2(bgX[2], 330, &imaBgs[2]);
}

图片移动的实现

void fly()
{
	for (int i = 0; i < 3; i++) 
	{
		bgX[i] -= bgspeed[i];     //调整背景移速
		if (bgX[i] < -WIN_WIDTH) 
		{
			bgX[i] = 0;
		}
	}
}

实现用户的按键操作

void keyEvent()
{
	char ch;
	if (_kbhit())      //如果有按键按下kbhit返回true
	{    
		ch = _getch(); //getch()不需要按空格就可以直接读取
		if (ch == 'w'||ch == 'W') {
			jump();    
		}
		else if (ch == 's' || ch == 'S') {
			down();
		}
	}
}

实现人物的展示 

奔跑人物的显示

void updateHero()
{
	if (!herodown) {
		putimagePNG2(HeroX, HeroY, &bgHeros[HeroIndex]);//显示奔跑人物
	}
	else {
		int y = 345 - imgHeroDown[HeroIndex].getheight();
		putimagePNG2(HeroX, y, &imgHeroDown[HeroIndex]);//显示下蹲人物
	}
}

人物的跳跃和下蹲的实现

void jump()//跳跃分装函数
{
	heroJump = true;  //启动跳跃功能
	update = true;    //若启动跳跃开关则立马刷新启动功能
}

void down()//下蹲分装函数
{
	herodown = true;
	update = true;
	HeroIndex = 0;
}

void fly()
{
	//实现跳跃
	if (heroJump) {
		if (HeroY < jumpHeightMax) {
			jumpHeightOff = 4;              
		}
		HeroY += jumpHeightOff;

		if (HeroY > 345 - bgHeros[0].getheight()) {
			heroJump = false;  
			jumpHeightOff = -4; 
		}
	}

	//实现下蹲
	else if (herodown) {
		static int count = 0;
		count++;
		int delays[2] = { 8,30 };
		if (count >= delays[HeroIndex]) {
			count = 0;
			HeroIndex++;
			if (HeroIndex == 2) {
				HeroIndex = 0;
				herodown = false;
			}
		}

	}
	else {
		HeroIndex = (HeroIndex + 1) % 12;//跳跃时腿不动
	}

渲染障碍物 

显示障碍物

void updateEnemy()
{

	//渲染障碍物
	for (int i = 0; i < OBSTACLE_COUNT; i++)
	{
		if (obstacles[i].exist) 
		{
			putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH, &obstacleImags[obstacles[i].type][obstacles[i].imaIndex]);
		}
	}
}

更新障碍物的位置

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

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

随机生成小乌龟

	//随机生成小乌龟
	static int frameCount = 0;     //静态出函数不会销毁
	static int torToiseFre = 50;
	frameCount++;
	if (frameCount > torToiseFre) 
	{
    //帧数够计数器,就生成小乌龟
		frameCount = 0; 
		torToiseFre = 50 + rand() % 60;
		creatObstacle();
	}
         

人物与障碍物的碰撞检测

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 = 30; 
			if (!herodown) {		//非下蹲状态
				a1x = HeroX + off;
				a1y = HeroY + off;
				a2x = HeroX + bgHeros[HeroIndex].getwidth() - off;
				a2y = HeroY + bgHeros[HeroIndex].getheight();
			}
			else
			{
				a1x = HeroX + off;
				a1y = 345 - imgHeroDown[HeroIndex].getheight();
				a2x = HeroX + imgHeroDown[HeroIndex].getwidth() - off;
				a2y = 345;
			}

			//障碍物的区域
			IMAGE img = obstacleImags[obstacles[i].type][obstacles[i].imaIndex];
			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() - off;
			if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {
				heroBlood -= obstacles[i].power;
				playSound("res/hit.mp3");
				obstacles[i].hited = true;
			}
		}
	}
}

人物血量的显示 

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

游戏分数的显示 

void checkScore() 
{
    //每躲避一个障碍物就加一分
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist &&obstacles[i].passed == false &&obstacles[i].hited == false &&obstacles[i].x + obstacleImags[obstacles[i].type][0].getwidth() < HeroX)
		{
			score++;
			obstacles[i].passed = true;

		}
	}
}


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

游戏输赢的判定 

游戏赢的判定

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

游戏输的判定

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

		//暂停之后,若选择直接开始下一局
		heroBlood = 100;
		score = 0;
		mciSendString("play res/Maclk.mp3 repeat", 0, 0, 0);
	}
}

游戏主函数 

双缓冲绘图

BeginBatchDraw() - 开始双缓冲

EndBatchDraw()    - 结束双缓冲

双缓冲区:打印一个的同时显示另一个,不断重复这个过程,避免了屏幕闪耀问题

int main(void)
{
	init(); 
	startUI();
	mciSendString("play Music.mp3 repeat", 0, 0, 0);
	int timer = 0;

	while (1) {
		keyEvent();         //按键接收
		timer += getDelay();
		if (timer > 30) {
			timer = 0;
			update = true;	//30次刷新一次,帧休眠,不然速度太快
		}

		if (update) {
			update = false; 
			BeginBatchDraw();
			updateBg();       //渲染游戏背景
			updateHero();     //渲染人物图片
			updateEnemy();    //渲染障碍物
			updateBloodBar();
			updateScore();    //渲染分数图片
			checkWin();       //检查游戏是否胜利
			EndBatchDraw();

			checkOver();      //检查游戏是否结束
			checkScore();     //计算得分

			fly();            //实现人物跳跃,下蹲,障碍物的移动效果
		}
	}

	system("pause");
	return 0;
}

全部代码的实现 

#include<stdio.h>
#include<graphics.h>
#include"tools.h"
#include<conio.h>
#include<vector>
using namespace std;

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

IMAGE imaBgs[3];            
int bgX[3];		            
int bgspeed[3] = { 1,2,4 }; 

IMAGE bgHeros[12];          
int HeroX;                  
int HeroY;
int HeroIndex;            


bool heroJump;              
bool herodown;

int jumpHeightMax;
int jumpHeightOff;

int update; 

int heroBlood;
int score;

typedef enum {
	TORTOISE,
	LION,   
	HOOK1,  
	HOOK2,
	HOOK3,
	HOOK4,
	OBSTACLE_TYPE_COUNT    	
}obstacle_type; 

obstacle_type type;

vector<vector<IMAGE>> obstacleImags; 


typedef struct obstacle
{
	obstacle_type type;	   
	int imaIndex;              
	int x, y;	              
	int speed;                 
	int power;                 
	bool exist;               
	bool hited;	               
	bool passed;            
} obstacle_t;

obstacle_t obstacles[OBSTACLE_COUNT];

int lastObsIndex;


IMAGE imgHeroDown[2];
IMAGE imgSZ[10];

void init()
{
	initgraph(WIN_WIDTH, WIN_HEIGHT);
	char name[64];
	for (int i = 0; i < 3; i++) {
		sprintf(name, "res/bg%03d.png", i + 1);
		loadimage(&imaBgs[i], name);
		bgX[i] = 0;
	}

	
	for (int i = 0; i < 12; i++) {
		sprintf(name, "res/hero%d.png", i + 1);
		loadimage(&bgHeros[i], name);
	}
	HeroX = WIN_WIDTH * 0.5 - bgHeros[0].getwidth() * 0.5;
	HeroY = 345 - bgHeros[0].getheight();
	HeroIndex = 0;

	heroJump = false;

	jumpHeightMax = 345 - bgHeros[0].getheight() - 120;
	jumpHeightOff = -4; 

	update = true;
	IMAGE imgTort;
	loadimage(&imgTort, "res/t1.png");    
	vector<IMAGE>imgTortArray;            
	imgTortArray.push_back(imgTort);      
	obstacleImags.push_back(imgTortArray);
	IMAGE imaLion;
	vector<IMAGE> imaLionArray;
	for (int i = 0; i < 6; i++) {
		sprintf(name, "res/p%d.png", i + 1);
		loadimage(&imaLion, name);
		imaLionArray.push_back(imaLion);
	}
	obstacleImags.push_back(imaLionArray);


	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;

	for (int i = 0; i < 4; i++) {
		vector<IMAGE> imgHookArray;
		sprintf(name, "res/sz/h%d.png", i + 1);
		loadimage(&imgH, name, 70, 260, true);
		imgHookArray.push_back(imgH);
		obstacleImags.push_back(imgHookArray);
	}
	
	heroBlood = 100;
	
	preLoadSound("res/hit.mp3");

	lastObsIndex = -1;
	
	score = 0;

	for (int i = 0; i < 10; i++) {
		sprintf(name, "res/sz/%d.png", i);
		loadimage(&imgSZ[i], name);
	}
}

void creatObstacle()
{
	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].hited = false;
	obstacles[i].imaIndex = 0;
	obstacles[i].type = (obstacle_type)(rand() % 3);

	if (lastObsIndex >= 0 &&
		obstacles[lastObsIndex].type >= HOOK1 &&
		obstacles[lastObsIndex].type <= 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);
	}
	obstacles[i].x = WIN_WIDTH; 
	obstacles[i].y = 350 - obstacleImags[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;
	}

	else if (obstacles[i].type >= HOOK1 && obstacles[i].type <= HOOK4) {
		obstacles[i].speed = 0;
		obstacles[i].power = 20;
		obstacles[i].y = 0; 
	}

	obstacles[i].passed == false;
}

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 = 30; 
			if (!herodown) {		
				a1x = HeroX + off;
				a1y = HeroY + off;
				a2x = HeroX + bgHeros[HeroIndex].getwidth() - off;
				a2y = HeroY + bgHeros[HeroIndex].getheight();
			}
			else
			{
				a1x = HeroX + off;
				a1y = 345 - imgHeroDown[HeroIndex].getheight();
				a2x = HeroX + imgHeroDown[HeroIndex].getwidth() - off;
				a2y = 345;
			}

			IMAGE img = obstacleImags[obstacles[i].type][obstacles[i].imaIndex];
			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() - off;
			if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {
				heroBlood -= obstacles[i].power;
				playSound("res/hit.mp3");
				obstacles[i].hited = true;
			}
		}
	}
}


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

	if (heroJump) {
		if (HeroY < jumpHeightMax) {
			jumpHeightOff = 4;              
		}
		HeroY += jumpHeightOff;

		if (HeroY > 345 - bgHeros[0].getheight()) {
			heroJump = false;  
			jumpHeightOff = -4; 
		}
	}

	else if (herodown) {
		static int count = 0;
		count++;
		int delays[2] = { 8,30 };
		if (count >= delays[HeroIndex]) {
			count = 0;
			HeroIndex++;
			if (HeroIndex == 2) {
				HeroIndex = 0;
				herodown = false;
			}
		}

	}
	else {
		HeroIndex = (HeroIndex + 1) % 12;
	}

	
	static int frameCount = 0;     
	static int torToiseFre = 50;
	frameCount++;
	if (frameCount > torToiseFre) 
	{
		frameCount = 0; 
		torToiseFre = 50 + rand() % 60;
		creatObstacle();
	}
                    
	
	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist) {
			obstacles[i].x -= obstacles[i].speed + bgspeed[2];
			if (obstacles[i].x < -obstacleImags[obstacles[i].type][0].getwidth() * 2) {
				obstacles[i].exist = false;
			}

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

	
	checkHit();
}


    
void updateBg()
{
	putimagePNG2(bgX[0], 0, &imaBgs[0]);
	putimagePNG2(bgX[1], 119, &imaBgs[1]);
	putimagePNG2(bgX[2], 330, &imaBgs[2]);
}

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

void down()
{
	herodown = true;
	update = true;
	HeroIndex = 0;
}

void keyEvent()
{
	char ch;
	if (_kbhit())      
	{    
		ch = _getch(); 
		if (ch == 'w'||ch == 'W') {
			jump();    
		}
		else if (ch == 's' || ch == 'S') {
			down();
		}
	}
}

void updateEnemy()
{


	for (int i = 0; i < OBSTACLE_COUNT; i++)
	{
		if (obstacles[i].exist) 
		{
			putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH, &obstacleImags[obstacles[i].type][obstacles[i].imaIndex]);
		}
	}
}

void updateHero()
{
	if (!herodown) {
		putimagePNG2(HeroX, HeroY, &bgHeros[HeroIndex]);
	}
	else {
		int y = 345 - imgHeroDown[HeroIndex].getheight();
		putimagePNG2(HeroX, y, &imgHeroDown[HeroIndex]);
	}
}

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

void checkOver()
{
	if (heroBlood <= 0)
	{
		loadimage(0, "res/Maclk.png");
		FlushBatchDraw();
		mciSendString("stop res/Maclk.mp3", 0, 0, 0);
		system("pause");
		heroBlood = 100;
		score = 0;
		mciSendString("play res/Maclk.mp3 repeat", 0, 0, 0);
	}
}


void checkScore() 
{

	for (int i = 0; i < OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist &&obstacles[i].passed == false &&obstacles[i].hited == false &&obstacles[i].x + obstacleImags[obstacles[i].type][0].getwidth() < HeroX)
		{
			score++;
			obstacles[i].passed = true;

		}
	}
}


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

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

void startUI() {
	mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
	IMAGE imgMenu, imgMenu1;
	int	flag = 0;
	loadimage(&imgMenu, "res/Menu1.png");
	loadimage(&imgMenu1, "res/menu.png");
	while (1) {
		BeginBatchDraw();
		putimage(0, 0, &imgMenu);
		putimagePNG(474, 150, &imgMenu1);
		ExMessage	msg;
		if (peekmessage(&msg)) {
			
			if (msg.message == WM_LBUTTONDOWN &&msg.x > 474 && msg.x < 720 && msg.y>150 && msg.y < 170) {
				flag = 1;
			}
		}
		   
		else if (msg.message == WM_LBUTTONUP && flag == 1) {
			mciSendString("close res/bg.mp3", 0, 0, 0);
			EndBatchDraw();
			return;
		}
		EndBatchDraw();
	}
}

int main(void)
{
	init(); 
	startUI();
	mciSendString("play Music.mp3 repeat", 0, 0, 0);
	int timer = 0;

	while (1) {
		keyEvent();
		timer += getDelay();
		if (timer > 30) {
			timer = 0;
			update = true;	
		}

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

			checkOver();
			checkScore();

			fly();
		}
	}

	system("pause");

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高效减少组织自发荧光,提高信噪比

在免疫组化检测过程中&#xff0c;许多样本组织会产生可通过各种波长滤光片的组织内源性自发荧光&#xff0c;干扰抗体标记的目的蛋白荧光的观察&#xff0c;甚至导致实验失败。为了解决免疫组化实验中的自发荧光&#xff0c;VectorLabs公司&#xff08;国内代理商欣博盛生物&a…

C#,入门教程(24)——类索引器(this)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(23)——数据类型转换的一点基础知识https://blog.csdn.net/beijinghorn/article/details/124187182 工业软件首先要求高可靠性、高可维护性。 作为工业软件的开发者&#xff0c;我们对语言重载的需求是&#xff1a;“不可或缺”。 …

为什么说CRM行业化是大势所趋?有哪些判断因素

很多企业、很多销售对CRM的负面评价集中在不够贴合行业、不人性化&#xff0c;也就是功能不够细分和实用。因为CRM几乎是所有企业管理系统中最贴近业务实际的&#xff0c;但各行各业的业务千差万别&#xff0c;所以功能完备、使用满意度高的CRM一定是深度行业化、与不同行业业务…

canvas绘制六芒星

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

C++ Primer 6.5 特殊用途语言特性 6.6 函数匹配 知识点+练习题

C Primer6.5 特殊用途语言特性 6.6 函数匹配 默认实参内联函数constexpr函数调试帮助assert预处理宏NDBUG预处理变量 函数匹配练习题 默认实参 string screen(int hz24,int wid80,char c) windowscreen( , ,?)&#xff1b;//错误&#xff01;&#xff0c;只有尾部的实参可以省…

固态硬盘优化设置

目录 前言&#xff1a; 关闭Windows Search 禁用系统保护&#xff08;不建议&#xff09; 不建议禁用系统保护原因 关闭碎片整理 提升固态硬盘速度 开启TRIM 合理使用固态硬盘的容量 正确关机 关闭开机自启 前言&#xff1a; 电脑配备固态硬盘就能一劳永逸吗&#…

Oracle1 数据库管理

Oracle的安装 一、基础表的创建 1.1 切换到scott用户 用sys 账户 登录 解锁scott账户 alter user scott account unlock;conn scott/tiger;发现并不存在scott账户&#xff0c;自己创建一个&#xff1f; 查找资料后发现&#xff0c;scott用户的脚本需要自己执行一下 C:\ap…

一个golang小白使用vscode搭建Ununtu20.04下的go开发环境

文章目录 前言搭建go环境下载go安装包解压go压缩包完成安装配置环境变量编写一个helloword程序 安装VSCode插件安装智能提示插件安装go依赖包修改代理并重新安装依赖包 go.mod 和 go.workgo.modgo.work小试一下go.work 总结 前言 先交代一下背景&#xff0c;距离正式接触golan…

[IPv6]常用的几种IPv4和IPv6地址格式

记录一下常用的IPv4和IPv6地址格式 IPv4 IPv4地址是32位的&#xff0c;通常表示为“192.168.1.12”这样点分成4段的形式。 一个IP地址可以分为两部分&#xff0c;网络ID和主机ID。如何区分哪些位是网络ID&#xff0c;哪些位是主机ID&#xff0c;就需要子网掩码。 子网掩码 …

利用STM32CubeMX和keil模拟器,3天入门FreeRTOS(1.1) —— 创建多个静态任务实操和简单讲解

前言 &#xff08;1&#xff09;FreeRTOS是我一天过完的&#xff0c;由此回忆并且记录一下。个人认为&#xff0c;如果只是入门&#xff0c;利用STM32CubeMX是一个非常好的选择。学习完本系列课程之后&#xff0c;再去学习网上的一些其他课程也许会简单很多。 &#xff08;2&am…

基于LSTM的负荷预测,基于BILSTM的负荷预测,基于GRU的负荷预测,基于BIGRU的负荷预测,基于BP神经网络的负荷预测

目录 背影 摘要 代码和数据下载&#xff1a;基于LSTM的负荷预测&#xff0c;基于BILSTM的负荷预测&#xff0c;基于GRU的负荷预测&#xff0c;基于BIGRU的负荷预测&#xff0c;基于BP神经网络的负荷预测资源-CSDN文库 https://download.csdn.net/download/abc991835105/8876806…

HCIA——22DNS:DNS层次域名空间、域名服务器、域名解析的原理

学习目标&#xff1a; 计算机网络 1.掌握计算机网络的基本概念、基本原理和基本方法。 2.掌握计算机网络的体系结构和典型网络协议&#xff0c;了解典型网络设备的组成和特点&#xff0c;理解典型网络设备的工作原理。 3.能够运用计算机网络的基本概念、基本原理和基本方法进行…

YOLOv8改进 | 主干篇 | 低照度图像增强网络SCINet改进黑暗目标检测(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是低照度图像增强网络SCINet,SCINet(自校正照明网络)是一种专为低光照图像增强设计的框架。它通过级联照明学习过程和权重共享机制来处理图像,优化了照明部分以提升图像质量。我将该网络集成在YOLOv8的主干上针对于图像的输入进行增…

以太坊账户地址与比特B地址生成方法对比

作者 张群&#xff08;赛联区块链教育首席讲师&#xff0c;工信部赛迪特聘资深专家&#xff0c;CSDN认证业界专家&#xff0c;微软认证专家&#xff0c;多家企业区块链产品顾问&#xff09;关注张群&#xff0c;为您提供一站式区块链技术和方案咨询。 以太坊和比特B地址在生成方…

关于js的BigInt的使用与注意事项

说明 BigInt是一种内置对象&#xff0c;提供了一种方法来表示大于2^53 - 1 的整数&#xff0c;2^53 - 1 为Number可以表示的最大数字&#xff0c;BigInt可以突破限制&#xff0c;可以用任意精度表示整数&#xff0c;超出Number的安全整数限制&#xff0c;也可以安全地存储和操…

【AI】小白入门笔记

前言 2024年&#xff0c;愿新年胜旧年&#xff01;作为AI世界的小白&#xff0c;今天先来从一些概念讲起&#xff0c;希望路过的朋友们多多指教&#xff01; 正文 AI (人工智能) 提起AI, 大家可能会想起各种机器人&#xff0c;移动手机的“Siri”,"小爱同学", 是语…

GIS项目实战08:JetBrains IntelliJ IDEA 2022 激活

为什么选择 IntelliJ IDEA 使用编码辅助功能更快地编写高质量代码&#xff0c;这些功能可在您键入时搜索可能的错误并提供改进建议&#xff0c;同时无缝地向您介绍编码、新语言功能等方面的社区最佳实践。 IntelliJ IDEA 了解您的代码&#xff0c;并利用这些知识通过在每种上…

[java基础揉碎]位运算符

java中有7个位运算&#xff08;&、|、^、~、>>、<<和>>>&#xff09; 第一组 分别是按位与&、按位或|、按位异或^&#xff0c;按位取反~&#xff0c;它们的运算规则是&#xff1a; 按位与& : 两位全为1&#xff0c;结果为1&#xff0c;否则…

Debian11下编译ADAravis和Motor模块的一条龙过程

Debian11编译EPICS ADAravis记录 一年前整理的上面文&#xff0c;这几天重新走了一遍&#xff0c;有些地方会碰到问题&#xff0c;需要补充些环节&#xff0c;motor模块以前和areaDetector一条龙编译时&#xff0c;总是有问题&#xff0c;当时就没尝试了&#xff0c;这几天尝试…

生成当天递增唯一的流水号的几种方式

说明&#xff1a;当开发中&#xff0c;如交易、文件传输过程中的文件名&#xff0c;可能需要我们使用一串唯一的数字来锁定这一条“交互记录”&#xff0c;即流水号。 本文介绍几种生成6位递增唯一&#xff0c;且每日重置的流水号的方式。 方式一&#xff1a;使用Redis 我们…