C项目 天天酷跑(下篇)
上篇再博客里面有,接下来我们实现我们剩下要实现的功能
文章目录
碰撞检测
血条的实现
积分计数器
前言
我们现在要继续优化我们的程序才可以使这个程序更加的全面
碰撞的检测
定义全局变量
实现全局变量
void checkHit() {
for (int i = 0; i < OBSTACLE_CUONT; i++) {
if (obstacles[i].exist && obstacles[i].hited == false) {
int ax1, ay1, ax2, ay2;
//角色的碰撞宽度高度设置
if (!heroDown) {
ax1 = heroX;
ay1 = heroY;
ax2 = heroX + imgHero[i].getwidth();
ay2 = heroY - imgHero[i].getheight();
}
else {
ax1 = heroX;
ay1 = 397 - imgHeroDown[i].getheight();
ax2 = heroX + imgHeroDown[i].getwidth();
ay2 = 397;
}
//障碍物的碰撞宽度高度设置
int bx1 = obstacles[i].x;
int by1 = obstacles[i].y;
int bx2 = obstacles[i].x + obstacleImags[obstacles[i].type][obstacles[i].imgIndex].getwidth();
int by2 = obstacles[i].y - obstacleImags[obstacles[i].type][obstacles[i].imgIndex].getheight()-10;
if (rectIntersect(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2)) {
//写到这里,只要我们有图片和障碍物相交在一起就会扣血,因为在屏幕里面,怪是要到屏幕之外才是结束存在的
heroBoold -= obstacles[i].power;
printf("血量剩余%d\n", heroBoold);
obstacles[i].hited = true;
}
}
}
}
我们来看到这个代码是实现碰撞检测的,我们来画一个图
我们要找出这两个人的x1,x2,y1,y2的坐标后面的by-10是因为我这图片的问题,就是kunkun和别的尺寸有问题才-10,一般是不需要减10的,正常写就好接下来我们就要判断是否发生碰撞了
接下里我们就要学习是否发生碰撞这个判断条件了
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);
}
这个是我们要用到的函数,这个函数参数
x01--人的x1 y01--人的y1 x02--人的x2 y02--人的y2 x11--障碍物的x1 y11--障碍物的y1
x12--障碍物的x2 y12--障碍物的y2
zx和x的理解
这个就是zx和x之间的原理
这个是zy和y的原理跟x差不多
当我们实现这个碰撞检测的时候,我们就可以继续写了,我们就利用这个碰撞检测来实现这个血条的扣血
在run函数里面实现进行每一次的检测
这里我们打开了hitd这个开关,我们不需要写一个关闭这个开关的程序,因为每一次创建一个障碍物的时候,他都会进行一次初始化,这个开关它会关闭,碰到之后再继续打开
血条的扣血
我们定义一个这个函数来显示血条
这个是血条扣除的函数
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);
}
参数部分:
x, y
: 血条左上角的坐标width, height
: 血条的宽度和高度lineWidth
: 边框的宽度boardColor
: 背景色(未使用,但可以用于描边)emptyColor
: 进度条背景(未填充部分)的颜色fillColor
: 进度条已填充部分的颜色percent
: 进度条已填充部分的百分比,取值范围为0到1
接下来分析这个函数里面的代码内容
LINESTYLE
是一个数据类型,用于表示线条的样式。它通常在图形编程库(比如 EasyX)中用来存储与线条相关的属性,比如线条的宽度、样式、端点样式等
典型的 LINESTYLE
结构体可能包含:
- 线条宽度(
width
):指定线条的宽度 - 线条样式(
style
):指定线条的样式,常见的样式包括实线、虚线等 - 线条端点样式(
cap
):设置线条的端点样式,比如圆形端点、方形端点等 - 线条连接样式(
join
):设置线段连接点的样式,如圆形连接、尖角连接等
示例:
在 EasyX 中,LINESTYLE
的定义可能类似于:
typedef struct _LINESTYLE {
int width; // 线条宽度
int style; // 线条样式
int cap; // 端点样式
int join; // 连接点样式
} LINESTYLE;
这个是eaxyx里面的
总结来说,LINESTYLE
是一个结构体,用来存储和管理与线条绘制相关的多种样式设置
LINESTYLE lineStyle;
getlinestyle(&lineStyle);
int lineColor = getlinecolor();
int fileColor = getfillcolor();
这个代码是
getlinestyle(&lineStyle)
:获取当前的线条样式getlinecolor()
:获取当前的线条颜色getfillcolor()
:获取当前的填充颜色
这个是获取你系统自带的默认的颜色,避免你后面没有颜色和样例
这个是一个浮点数,就是血条的百分比含量 ,当为小于0的时候,就归属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);
-
setlinecolor(BLUE);
- 这行代码设置 线条的颜色为蓝色。通常,这会影响矩形的边框颜色(即轮廓)。
BLUE
是 EasyX 图形库中的预定义颜色常量,表示蓝色。
-
setlinestyle(PS_SOLID | PS_ENDCAP_ROUND, lineWidth);
- 这行代码设置 线条的样式。
PS_SOLID
表示线条是 实线(不是虚线或点线)。PS_ENDCAP_ROUND
表示线条的端点是 圆形,即线段的两端是圆头,而不是方头或平头。lineWidth
是线条的宽度,通常是一个整数,表示线条的粗细(例如 2 表示线条宽度为 2 像素)。
- 这行代码设置 线条的样式。
-
setfillcolor(emptyColor);
- 这行代码设置 填充颜色,即矩形内部的颜色。
emptyColor
是传入的一个变量,表示血条的背景颜色,通常是透明或灰色,表示血条没有被填充的部分。
-
fillrectangle(x, y, x + width, y + height);
- 这行代码用于 绘制矩形。
x, y
是矩形的左上角坐标,x + width, y + height
是矩形的右下角坐标。这样,矩形的大小和位置就由这些坐标确定了。- 这个矩形会被 填充为之前设置的
emptyColor
,即矩形的背景颜色。
这个下一个就是恢复样例和颜色了,避免下一次使用不是默认的样例和颜色
-
setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 0);
- 这行代码 恢复线条样式:
PS_SOLID
表示线条是 实线。PS_ENDCAP_FLAT
表示线条的端点是 平头,即线段的两端是直角,而不是圆形端点。0
表示 线宽为 0,这意味着不会显示边框,这通常用于消除矩形或其他图形的外部轮廓,只显示填充
- 这行代码 恢复线条样式:
这个后面就是填充血条的颜色,上面那个是空血条的颜色
-
setfillcolor(fillColor);
- 这行代码设置 填充颜色 为
fillColor
,也就是血条的 实际填充颜色,即显示血量的颜色。通常这个颜色是 红色、绿色 或其他表示血量的颜色。
- 这行代码设置 填充颜色 为
-
setlinecolor(fillColor);
- 这行代码将 线条颜色 设置为
fillColor
,即设置血条边框的颜色为填充颜色。这个通常是为了确保血条的边框与填充颜色一致,给人一致的视觉效果。
- 这行代码将 线条颜色 设置为
核心句子
if (percent > 0) {
fillrectangle(x + 0.5 * lineWidth, y + lineWidth * 0.5, x + width * percent, y + height - 0.5 * lineWidth);
}
- 左上角坐标:
x + 0.5 * lineWidth
和y + lineWidth * 0.5
x + 0.5 * lineWidth
:由于在绘制矩形时需要考虑线条宽度,所以x
坐标偏移了0.5 * lineWidth
,确保边框的线宽(lineWidth
)不会影响矩形的显示位置,确保绘制的矩形与边框之间有一定的间隔。y + lineWidth * 0.5
:同样,y
坐标也进行了偏移,确保矩形的上边界与线条宽度相适应。
- 右下角坐标:
x + width * percent
和y + height - 0.5 * lineWidth
x + width * percent
:这里的x + width * percent
计算了矩形的 实际宽度,它是根据当前血量的百分比percent
来决定的。如果percent
为 0,矩形的宽度为 0;如果percent
为 1,矩形的宽度将是血条的最大宽度width
。y + height - 0.5 * lineWidth
:y + height
为矩形的下边界,减去0.5 * lineWidth
是为了避免线条的宽度影响矩形的底部显示。
我们这里不需要修改这个左上角标的值的,只需要修改右下角标的值,这样就可以实现动态绘画这个血条了
setlinecolor(lineColor);
setfillcolor(fillColor);
setlinestyle(&lineStyle);
这个是恢复之前的颜色,避免下一次使用不是默认的值,我们之前就把初始化的东西用变量储存起来了
积分计数器
void checkpassed() {
for (int i = 0; i < OBSTACLE_CUONT; i++) {
if (obstacles[i].exist &&
obstacles[i].passed == false &&
obstacles[i].x + obstacleImags[obstacles[i].type][0].getwidth()) {
score++;
obstacles[i].passed = true;
printf("%d", score);
}
}
}
这个就是我们来检测是否跳过这个障碍物,我们只需要检测这个开关是否关闭,还有这个长度是否超过这个图片的长度还有这个障碍物是否存在,执行完成之后,就打开这个开关,防止一直加分,然后就是我们要加一个分数绘制在这个窗口
void updateScore() {
//ASCLL码值 ‘5’-‘0’=5
char str[8];
sprintf(str, "%d", score);
int x = 20;
int y = 25;
for (int i = 0; str[i] != 0; i++) {
int num = str[i] - '0';
putimage(x, y, &imgSZ[num]);
x += imgSZ[num].getwidth();
}
}
我们只需要把这个score格式化一下从int到char类型,然后利用sprintf来读取对应的数字到数组里面,然后就是用for循环来来便利 ,这里也运用了ASCII值的转换,后买你x+=这一个代码是为了是图片连起来
源码
//图片的长宽
#define _CRT_SECURE_NO_WARNINGS 1
#define WIN_LONG 1150
#define WIN_WIDTH 538
#define OBSTACLE_CUONT 20
#include<time.h>
#include<stdio.h>
#include<graphics.h>
//这个是用来调用按键是否输入的
#include<conio.h>
//这个是用来使用变长数组的
#include<vector>
#include<windows.h>
//播放音乐的时候用的两行代码
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
#include"tools.h"
using namespace std; //声明命名空间
/* 背 景 制 作 */
//背景图数组
IMAGE imgBgs[2];
//背景图的x
int bgX[2];
//用来存放速度
int bgspeed[2] = { 4,8 };
/* 角 色 制 作*/
//角色奔跑
IMAGE imgHero[12];
//玩家会被弹飞还有下蹲啥的,所以要设置全局变量
int heroX; //x坐标
int heroY; //y坐标
int heroIndex; //玩家奔跑时图片的序号
bool heroJump; //表示玩家正在跳跃(跳跃开关)
bool heroDown; //表示玩家正在下蹲(下蹲开关)
int heroBoold;
int jumpHeightMax;
int herojumpOff;
int update; //表示是否需要马上刷新画面
int score;
/* 乌 龟 制 作*/
//IMAGE imgTortoise; //小乌龟
//int torToiseX; //小乌龟的x坐标
//int torToiseY; //小乌龟的y坐标
//bool torToiseExist;//当前窗口是否有小乌龟
//由于障碍物越来越多,则需要进行封装,这样就可以简化代码行数,实现代码不拥挤
typedef enum {
TORTOISE, //奶龙0
LION, //狮子1
//四个柱子
HOOK1,
HOOK2,
HOOK3,
HOOK4,
OBSTACLE_TYPE_COUNT//常看有几种障碍物(因为是从0开始,所以更加方便看有几种类型)ho
}obstacle_type;
//IMAGE obstacleImage[3][12];用这个的话会浪费些许内存,因为乌龟只有一个图片
vector<vector<IMAGE>>obstacleImags(OBSTACLE_TYPE_COUNT,vector<IMAGE>(12));//里面一个成员的又一个成员(可变数组)
//相当于int data[ int ]
typedef struct obstacle {
obstacle_type type;//障碍物的类型
int imgIndex; //当前显示的图片的序号
//但是这样有障碍物的数字代表不是特别可读,所以我们可以利用枚举类型,更加可读
int x, y; //障碍物的x与y坐标
int speed;
int power; //伤害
bool exist;
bool hited; //碰撞
bool passed;
}obstacle_t;
//由于我们不可以把障碍物直接全部都是弄出来,要逐个逐个的,就像捕鱼达人一样,所以我们要建立一个池子来存储这个预备队员,就像篮球,一个上场一个下场
obstacle_t obstacles[OBSTACLE_CUONT];
//玩家下滑图片储存的数组
IMAGE imgHeroDown[2];
//创建窗口
IMAGE imgSZ[10];
void init() {
//创建窗口
//第三个参数是为了显示控制台的参数
//第三个参数是为了显示控制台的参数
//在initgraph后面, EX_SHOWCONSOLE这个在打包删除了
initgraph(WIN_LONG, WIN_WIDTH);
/*背 景 制 作*/
//加载背景图 loadimage函数(取地址,具体的文件名字)
char name[64];
for (int i = 0; i < 2; i++) {
//"ret/bg001.png" "ret/bg002.png" "ret/bg003.png"
sprintf(name, "ret/bg%03d.png", i + 1);
//这个是要在win32平台和多字节字符串的情况下才可以运行
loadimage(&imgBgs[i], name);
//初始化背景图的x轴坐标
bgX[i] = 0;
}
/*人 物 制 作*/
//加载玩家奔跑
for (int i = 0; i < 12; i++) {
//"跑步1" "跑步2"
sprintf(name, "ret/跑步%d.png", i + 1);
loadimage(&imgHero[i], name);
}
//设置玩家初始位置
//窗口宽度*0.5-自己宽度*0.5
heroX = 600 * 0.5 - 200 * 0.5 - 80;
heroY = 300;
heroIndex = 0;
//玩家跳跃
heroJump = false;
jumpHeightMax = 80;//记得是减,不是加,注意坐标原点
herojumpOff = -6;
update = true;
/*小 乌 龟 制 作*/
//loadimage(&imgTortoise, "ret/奶龙.png");
//torToiseExist = false;
//torToiseY = 400;
/*小 奶 龙 改 版*/
IMAGE imgTort;
loadimage(&imgTort, "ret/奶龙.png");
vector<IMAGE>imgTorArray;
imgTorArray.push_back(imgTort);
obstacleImags[TORTOISE] = imgTorArray;
/*kunkun 图片制作*/
IMAGE imgLion;
vector<IMAGE>imgLionArray;
for (int i = 0; i < 12; i++) {
sprintf(name, "ret/kun%d.png", i + 1);
loadimage(&imgLion, name);
imgLionArray.push_back(imgLion);
}
obstacleImags[LION] = imgLionArray;
//初始化障碍物池
for (int i = 0; i < OBSTACLE_CUONT; i++) {
obstacles[i].exist = false;
}
//加载下蹲素材
loadimage(&imgHeroDown[0], "ret/d1.png");
loadimage(&imgHeroDown[1], "ret/d2.png");
heroDown = false;
//加载柱子
// 创建单独的数组或 vector 存储每个障碍物的图片
IMAGE imgH1;
loadimage(&imgH1, "ret/hook1.png", 63, 310);
vector<IMAGE> imgHook1Array;
imgHook1Array.push_back(imgH1);
obstacleImags[HOOK1] = imgHook1Array;
IMAGE imgH2;
loadimage(&imgH2, "ret/hook2.png", 63, 310);
vector<IMAGE> imgHook2Array;
imgHook2Array.push_back(imgH2);
obstacleImags[HOOK2] = imgHook2Array;
IMAGE imgH3;
loadimage(&imgH3, "ret/hook3.png", 63, 310);
vector<IMAGE> imgHook3Array;
imgHook3Array.push_back(imgH3);
obstacleImags[HOOK3] = imgHook3Array;
IMAGE imgH4;
loadimage(&imgH4, "ret/hook4.png", 63, 310);
vector<IMAGE> imgHook4Array;
imgHook4Array.push_back(imgH4);
obstacleImags[HOOK4] = imgHook4Array;
heroBoold = 100;
mciSendString("open ret/bk.mp3", NULL, 0, NULL);
mciSendString("play ret/bk.mp3 repeat", NULL, 0, NULL);
//加载数字
for (int i = 0; i < 10; i++) {
sprintf(name, "ret/sz/%d.png", i);
loadimage(&imgSZ[i], name);
}
}
void creatObstacle() {
srand((unsigned)time(NULL));
int i = 0;
for ( i = 0; i < OBSTACLE_CUONT; i++) {
if (obstacles[i].exist == false) {
break;
}
}
if (i >= OBSTACLE_CUONT) {
return;
}
obstacles[i].exist = true;
obstacles[i].hited = false;
obstacles[i].imgIndex = 0;
obstacles[i].passed = false;
//obstacles[i].type = (obstacle_type)(rand() % OBSTACLE_TYPE_COUNT);//由于vs的语法要求比较严,所以我们进行强制转换为枚举类型
obstacles[i].x = WIN_LONG;
obstacles[i].y = 380;
obstacles[i].type=(obstacle_type)(rand() % 3);
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 = 10;
}
else if (obstacles[i].type >= HOOK1 && obstacles[i].type <= HOOK4) {
obstacles[i].speed = 0;
obstacles[i].power = 10;
obstacles[i].y = 0;
}
}
//玩家和障碍物的碰撞检测处理
void checkHit() {
for (int i = 0; i < OBSTACLE_CUONT; i++) {
if (obstacles[i].exist && obstacles[i].hited == false) {
int ax1, ay1, ax2, ay2;
//角色的碰撞宽度高度设置
if (!heroDown) {
ax1 = heroX;
ay1 = heroY;
ax2 = heroX + imgHero[i].getwidth();
ay2 = heroY - imgHero[i].getheight();
}
else {
ax1 = heroX;
ay1 = 397 - imgHeroDown[i].getheight();
ax2 = heroX + imgHeroDown[i].getwidth();
ay2 = 397;
}
//障碍物的碰撞宽度高度设置
int bx1 = obstacles[i].x;
int by1 = obstacles[i].y;
int bx2 = obstacles[i].x + obstacleImags[obstacles[i].type][obstacles[i].imgIndex].getwidth();
int by2 = obstacles[i].y - obstacleImags[obstacles[i].type][obstacles[i].imgIndex].getheight()-10;
if (rectIntersect(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2)) {
//写到这里,只要我们有图片和障碍物相交在一起就会扣血,因为在屏幕里面,怪是要到屏幕之外才是结束存在的
heroBoold -= obstacles[i].power;
printf("血量剩余%d\n", heroBoold);
obstacles[i].hited = true;
}
}
}
}
void run() {
srand((unsigned)time(NULL));
for (int i = 0; i < 2; i++) {
bgX[i] -= bgspeed[i];
//用于返回到当时图片的位置,这样才可以在后面继续奔跑
if (bgX[i] < -WIN_WIDTH) {
bgX[i] = 0;
}
}
//玩家帧序列
heroIndex = (heroIndex + 1) % 12;
//0~11的移动(因为就这么多图片)
//实现跳跃(改变玩家的i坐标)
//有个上升下降的过程()
//因为这个是套在while里面的,随着函数的不断推进,他会一直跳跃,直到触发另外一个if开关
if (heroJump) {
//下降阶段
if (heroY < jumpHeightMax) {
herojumpOff = 6;
}
//一开始处于上升,后面处于下降
heroY += herojumpOff;
//280初始值
if (heroY > 280) {
heroJump = false;
herojumpOff = -6;
}
}
else if (heroDown) {
static int count = 0;
int delays[2] = { 15,20 };
count++;
//由于下蹲就两张图片,所以我们要延续时间(这里可以用静态变量,来延缓帧数)
if (count >= delays[heroIndex]) {
count = 0;
heroIndex++;
if (heroIndex >= 2) {
heroIndex = 0;
heroDown = false;
}
}
}
else{
heroIndex = (heroIndex + 1) % 12;
}
//生成障碍物(如果跟着系统的30ms来整出小奶龙的话,那就满篇小奶龙了)
//我们可以计算,这个函数调用的次数*30ms来决定这个乌龟是否生成,比如我们要3s生成一次,那我们就调用100次*30ms就可以了进率1000
//那我们要知道调用多少次,用静态变量,因为这样调用完之后销毁也会保存在静态区
static int frameCount = 0;
static int enemyFre = 50;
frameCount++;
if (frameCount > enemyFre) {
frameCount = 0;
enemyFre = 50 + rand() % 50; // 50~99
creatObstacle();
}
//更新奶龙位置
//if (torToiseExist) {
// torToiseX -= 4;
// //当奶龙完全让自己出去的时候
// if (torToiseX < -151) {
// torToiseExist = false;
// }
//}
//更新障碍物的坐标
for (int i = 0; i < OBSTACLE_CUONT; i++) {
if (obstacles[i].exist) {
obstacles[i].x -= obstacles[i].speed + bgspeed[1];
if (obstacles[i].x < -300) {
obstacles[i].exist = false;
}
//由于我们的坤坤为了可以出现变动,所以哦我们要加这个变化图片
int len = obstacleImags[obstacles[i].type].size(); //type为第几行
obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;//(我们不用障碍物的图片数量不一样,所以我们要用len)
}
}
//玩家和障碍物的碰撞检测处理
checkHit();
}
//渲染游戏背景
void updateBg() {
//x轴是确定图片滚到哪里的 y轴是确定位置的
//这里的滚动是有这个bgX来变化来进行图片的滚动
//由于这个是一次一次吧图片加载出来的,但是我们想一次性一起加载出来这个时候就要添加
//BeginBatchDraw()和EndBatchDraw()
//一个是开始批量绘制图形,一个是结束开始批量绘制图形
putimage(bgX[0], 0, &imgBgs[0]);
putimage(bgX[1],397, &imgBgs[1]);
}
void jump() {
//实现跳跃的时候,坐标是慢慢移动的,而不是直接蹦上去瞬移了
heroJump = true;//启动开关
update = true; //在30毫秒过程中,如果在30毫秒点击空格跳跃,则这个立马刷新,直接步入到下面那个if语句然后直接进入,这样可以接受按键
}
void down() {
heroDown = true;
update = true;
heroIndex = 0;
}
//处理用户按键输入
void keyEvent() {
char ch;
//如果有按键按下,则这个kbhit函数是返回为真,没有则反之
if (_kbhit()) {
/*scanf("%c", &c);*/ //这里用scanf的话这里是要按下回车才可以继续执行,降低了可玩性,所以不用scanf
ch = _getch(); //这个是scanf的pilus版,这个是不需要按下回车,直接读取这个输入的字符
//由于vs版本,这个getch和kbhit都是要加_这个的,不加会报警告
if (ch == ' ') {
jump();//功能相对独立的要封装成一个函数
}
else if (ch == 's') {
down();
}
}
/*char c;
scanf("%c", &c);*///这里会卡死直接这样写,因为他会等待这个玩家输入
//解决方法就是判断玩家到底有没有按键按下去,写一个if语句
}
void checkover() {
if (heroBoold <= 0) {
loadimage(0, "ret/kunkunjie.png");
FlushBatchDraw();
mciSendString("stop ret/bk.mp3", 0, 0, 0);
system("pause");
//暂停之后刷新游戏角色数据
heroBoold = 100;
score = 0;
mciSendString("play ret/bk.mp3 repeat", 0, 0, 0);
}
}
void checkpassed() {
for (int i = 0; i < OBSTACLE_CUONT; i++) {
if (obstacles[i].exist &&
obstacles[i].passed == false &&
obstacles[i].x + obstacleImags[obstacles[i].type][0].getwidth()) {
score++;
obstacles[i].passed = true;
printf("%d", score);
}
}
}
void updatezhang()
{
//渲染奶龙
//if (torToiseExist) {
// putimage(torToiseX, torToiseY, &imgTortoise);
//}
for (int i = 0; i < OBSTACLE_CUONT;i++) {
if (obstacles[i].exist) {
//我们要取走第几行呢?我们可以利用我们的枚举类型,这个是对应我们的图片的行数(type对应着枚举->行数)
putimage(obstacles[i].x, obstacles[i].y, &obstacleImags[obstacles[i].type][obstacles[i].imgIndex]);
}
}
}
void updateHero() {
//这个奔跑和跳跃这个可以实现,但是下蹲不行了,所以需要加一个判断
if (!heroDown) {
putimage(heroX, heroY, &imgHero[heroIndex]);
}
else {
int y = 395 - 15;
putimage(heroX, y, &imgHeroDown[heroIndex]);
}
}
void updateBoold() {
drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBoold / 100.0);
}
void updateScore() {
//ASCLL码值 ‘5’-‘0’=5
char str[8];
sprintf(str, "%d", score);
int x = 20;
int y = 25;
for (int i = 0; str[i] != 0; i++) {
int num = str[i] - '0';
putimage(x, y, &imgSZ[num]);
x += imgSZ[num].getwidth();
}
}
int main() {
init();
//显示菜单
loadimage(0, "ret/kunkunkai.png");
system("pause");
int timer = 0;
while (1) {
/*由于根据游戏设计,这里是要循环多次才可以跳好
所以我们这里需要设计一个按键来执行立马跳跃,就
不用循环多次了(按键跳跃)
*/
keyEvent();
//这个的好处就是可以随时接受按键接受
timer += getDelay(); //升级版sleep
if (timer > 30) {
timer = 0;
update = true;//这里是sleep的升级版本,没进过30秒钟刷新一次,然后进入游戏,在30秒的过程中,这个画面在逐帧加载,这样就可以持续进行画面显示
//没有这个,程序就没有逐帧的慢速显示,就会很快很快
}
if (update) {
update = false;
BeginBatchDraw();
updateBg();
//这个里面不要直接放图片,因为我们后续还有撞出窗外的操作要实现
//所以不要直接放putimage()在这里
//由于玩家需要下蹲则这个代码不行了
/*putimage(heroX, heroY, &imgHero[heroIndex]);*/
updateHero();
updateBoold();
updatezhang();
updateScore();
EndBatchDraw();
//这个run是改变这个图片下一次所在的位置的
checkover();
checkpassed();
run();
}
//这个休眠是让他暂停一会,要不然太快了显示的是一个快影子
//休眠的话,如果在30毫秒钟之内按按键的话是不会响应得,所以这个sleep在这个小游戏里面没有什么实质的影响,但是在大型游戏里面是有影响的
/*Sleep(30);*///所以我们一般不用sleep
}//定义一个死循环不断的对图片进行滚动
//system("pause");//用于暂停观察
//int system(const char* 命令);
return 0;
}
总结
接下来我们的项目就基本结束了,打包安装需要安装插件,感兴趣的可以取收一下微软本地的自带插件,可以在vs管理扩展包下载,这里的源码你直接复制会报错,你得自己学习调节属性和安装插件还有文件的学习,因为天底下哪有免费的午餐