C语言手写天天酷跑代码详解
- 项目总览:
- 一、项目开发日志
- 二、引入库与宏编译
- 三、全局变量与结构体的定义
- 四、主函数中的内容
- 五、逐个自定义函数拆解分析
- 1.初始化init()函数
- 2.处理用户按键输入keyEvent() 函数
- 3.渲染游戏背景 updateBg()函数
- 4.渲染下蹲图片 updateHero()函数
- 5.渲染障碍物图片 updateEnemy()函数
- 6.渲染血条 updateBloodBar()函数
- 7.渲染分数图片显示 updateScore()函数
- 8.检查游戏是否胜利 checkWin()函数
- 9.检查游戏是否结束(死亡) checkOver()函数
- 10.计算得分 checkScore()函数
- 11.实现跳跃,下蹲,障碍物的移动等动态效果fly() 函数
- 六、效果展示:
- 1.视频效果:[点击链接直达](https://www.bilibili.com/video/BV1Vr4y1B7bn?spm_id_from=333.999.0.0)
- 2.图片效果:
项目总览:
1.开发语言:C语言加上一点点C++的函数
2.IDE:VS2022(点击跳转到配置环境教程)
3.开发用时:3天左右
4.代码所需要使用的第三方函数与所有图形素材及本项目源码文件均已经上传到我的GitHub,链接:https://github.com/2394799692/Cool-running-every-day1.0 或点此跳转
以下是本篇文章正文内容,欢迎朋友们进行指正,一起探讨,共同进步。——来自考研路上的lwj。QQ:2394799692
一、项目开发日志
/*
* 天天酷跑开发日志
* 1.创建项目
* 2.导入素材
* 3.实际的开发流程:从用户界面入手
* 1)创建游戏窗口
* 2)实现游戏背景(基于“easyx”图形库)
* a:三重背景不同的速度
* b:循环滚动背景的实现
* 3)实现游戏背景
* a.加载背景资源
* b,渲染(坐标)
* 遇到问题:背景图片的png格式图片出现黑色
* 解决办法:百度引入第三方头文件中的putimagePNG2函数解决
* 4.实现玩家的奔跑
* 5.实现玩家的跳跃(空格)
* 6.实现随机小乌龟
* 7.创建障碍物结构体数据类型
* 8.使用障碍物结构体后重新初始化
* 9.封装后多个障碍物的显示
* 10.实现玩家下蹲技能
* 11.实现“柱子”障碍物
* 12.实现血条与计分
* 13.判断游戏胜利
*/
二、引入库与宏编译
#define _CRT_SECURE_NO_WARNINGS//使用scanf函数防止报错
#define WIN_SCORE 20//用于定义游戏获胜的分数条件
#define WIDTH 1012//游戏背景的宽度
#define HEIGHT 396//高度
#define OBSTACLE_COUNT 10//障碍物数量
#include<stdio.h>//标准输入输出库函数
#include<graphics.h>//引入图形库
#include<conio.h>//按键输入库
#include"tools.h"//添加本地头文件
#include<vector>//引入c++库用于使用容器变长数组
三、全局变量与结构体的定义
using namespace std;//声明命名空间
//游戏初始化
IMAGE imgBgs[3];//全局变量,存放三重背景
int bgX[3];//背景图片的x坐标
int bgSpeed[3] = { 1,2,4 };//背景移动的速度
IMAGE imgHeros[12];//奔跑
int heroX;//玩家的x坐标
int heroY;
int heroIndex;//玩家奔跑的图片帧序号
bool heroJump;//玩家跳跃状态
int jumpHeightMax;//设置跳跃最大高度
int heroJumoOff;//设置偏移量
int update;//表示是否需要马上刷新
//IMAGE imgTortoise;//小乌龟
//int torToiseX;//小乌龟的水平坐标
//int torToiseY;
//bool torToiseXExist;//当前窗口是否有小乌龟
int heroBlood;//玩家血量
int score;//分数
typedef enum {//定义障碍物类型结构体TORTOISE,LION,HOOK1,HOOK2,HOOK3,HOOK4,OBSTACLE_TYPE_COUNT
}obstacle_type;
vector<vector<IMAGE>>obstacleImgs;//相当于IMAGE obstacleImgs[][]
typedef struct obstacle {//定义障碍物结构体int type;//障碍物的类型int imgIndex;//当前显示的图片的序号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];
bool heroDown;//表示玩家是否处于下蹲状态
IMAGE imgSZ[10];
四、主函数中的内容
int main(void) { init();//初始化函数loadimage(0, "res/over.png");//初始画面system("pause");//调用DOS系统的暂停命令 pause 来暂停程序执行,按任意一个键后将继续执行。int timer = 0;while (1) {keyEvent();//用于接收空格和tab键来去执行对应的跳跃/下蹲函数timer+=getDelay();//第三方封装函数用于返回距离上次间隔调用的时间if (timer > 30) {//间隔30帧刷新一次页面timer = 0;update = true;}if (update) {//刷新页面update = false;BeginBatchDraw();//去除闪烁updateBg();//渲染游戏背景//putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);updateHero();//渲染下蹲图片updateEnemy();//渲染障碍物updateBloodBar();//调用第三方接口渲染血条updateScore();//渲染分数图片输出checkWin();//检查游戏是否胜利EndBatchDraw();//去除闪烁checkOver();//检查游戏是否结束checkScore();//计算得分fly();//实现跳跃,下蹲,障碍物的移动等动态效果}}system("pause");//调用DOS系统的暂停命令 pause 来暂停程序执行,按任意一个键后将继续执行。return 0;
}
五、逐个自定义函数拆解分析
1.初始化init()函数
void init() {//创建游戏窗口initgraph(WIDTH, HEIGHT,EW_SHOWCONSOLE);//调用“easyx”图形库中的函数实现页面大小char name[64];//加载游戏背景资源for (int i = 0; i < 3; i++) {sprintf(name, "res/bg%03d.png", i + 1);//宽3位不够前面补充0loadimage(&imgBgs[i], name);//载入游戏背景bgX[i] = 0;//背景图片的x坐标设为0heroJump = false;//跳跃状态初始化为否}//加载玩家Hero奔跑的图片帧素材for (int i = 0; i < 12; i++) {sprintf(name, "res/hero%d.png", i + 1);loadimage(&imgHeros[i], name);}//设置玩家的初始位置heroX = WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;heroY = 345 - imgHeros[0].getheight();heroIndex = 0;heroJump = false;jumpHeightMax = 345 - imgHeros[0].getheight() - 120;heroJumoOff = -4;update = true;//加载小乌龟/*loadimage(&imgTortoise, "res/t1.png");torToiseXExist = false;//小乌龟初始化torToiseY = 345 - imgTortoise.getheight()+5;*///用结构体加载小乌龟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;//加载柱子素材for (int i = 0; i < 4; i++) {vector<IMAGE>imgHookArray;sprintf(name, "res/h%d.png", i + 1);loadimage(&imgH, name,60,260,true);imgHookArray.push_back(imgH);obstacleImgs.push_back(imgHookArray);}heroBlood = 100;//初始化血量为满血//预加载音效preLoadSound("res/hit.mp3");mciSendString("play res/bg.mp3 repeat", 0, 0, 0);//重复播放lastObsIndex = -1;score = 0;//起始分数为0//加载数字得分图片for (int i = 0; i < 10; i++) {sprintf(name, "res/sz/%d.png", i);loadimage(&imgSZ[i], name);}
}
2.处理用户按键输入keyEvent() 函数
//处理用户按键输入
void jump() {heroJump = true;update = true;
}
void down() {update = true;heroDown = true;heroIndex = 0;
}
void keyEvent() {char ch;if (_kbhit()) {//如果有按键按下,返回truech = getch();//不需要按回车即可读取if (ch == ' ') {//如果按下空格执行跳跃函数jump();}else if (ch == ' ') {//如果按下tab键执行下蹲函数down();}}
}
3.渲染游戏背景 updateBg()函数
void updateBg() {putimagePNG2(bgX[0],0,&imgBgs[0]);putimagePNG2(bgX[1], 119, &imgBgs[1]);putimagePNG2(bgX[2], 330, &imgBgs[2]);
}
4.渲染下蹲图片 updateHero()函数
void updateHero() {if (!heroDown) {//下蹲图片渲染putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);}else {int y = 345 - imgHeroDown[heroIndex].getheight();putimagePNG2(heroX, y, &imgHeroDown[heroIndex]);}
}
5.渲染障碍物图片 updateEnemy()函数
void updateEnemy() {//渲染小乌龟/*if (torToiseXExist) {putimagePNG2(torToiseX, torToiseY,WIDTH,&imgTortoise);}*/for (int i = 0; i < OBSTACLE_COUNT; i++) {if (obstacles[i].exist) {putimagePNG2(obstacles[i].x, obstacles[i].y, WIDTH, &obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]);}}
}
6.渲染血条 updateBloodBar()函数
void updateBloodBar() {//调用第三方接口drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBlood / 100.0);
}
7.渲染分数图片显示 updateScore()函数
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;}
}
8.检查游戏是否胜利 checkWin()函数
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);}
}
9.检查游戏是否结束(死亡) checkOver()函数
void checkOver() {if (heroBlood <= 0) {loadimage(0, "res/over.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);}
}
10.计算得分 checkScore()函数
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 + obstacleImgs[obstacles[i].type][0].getwidth() < heroX) {score++;obstacles[i].passed = true;printf("score:%d\n", score);}}
}
11.实现跳跃,下蹲,障碍物的移动等动态效果fly() 函数
void fly() {for (int i = 0; i < 3; i++) {bgX[i] -=bgSpeed[i];if (bgX[i] < -WIDTH) {bgX[i] = 0;}}//实现跳跃if (heroJump) {if (heroY < jumpHeightMax) {//防止飞天heroJumoOff = 4;}heroY += heroJumoOff;//上升if (heroY > 345 - imgHeros[0].getheight()) {//防止遁地heroJump = false;heroJumoOff = -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;}//创建障碍物static int frameCount = 0;//静态变量永久有效static int enemyFre = 50;frameCount++;if (frameCount > enemyFre) {frameCount = 0;enemyFre =50 + rand() % 50;//50-99随机数createObstacle();}//if (torToiseXExist) {// //设置小乌龟的移动// torToiseX -= bgSpeed[2];// if (torToiseX < -imgTortoise.getwidth()) {// torToiseXExist = false;// }//}//更新所有障碍物的坐标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) {//obstacleImgs[obstacles[i].type][0].getwidth() * 2obstacles[i].exist = false;}int len = obstacleImgs[obstacles[i].type].size();obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;}}//玩家和障碍物的碰撞检测checkHit();}
六、效果展示:
1.视频效果:点击链接直达
2.图片效果:
1)开始页面:
2)进入游戏页面:
3)跳跃页面:
4)下蹲页面:
5)游戏结束页面: