【C语言】实现天天酷跑游戏

news/2025/2/22 18:38:47/

天天酷跑游戏开发日志及源码

**

纯c语言开发的游戏项目,与天天酷跑玩法与贴图类似,日志中有着详细的开发过程,从零开始手把手带你,解决游戏开发的问题;体验开发的乐趣!!!

ps:源码在文章最下面,有详细的注释内容。

**请添加图片描述

1.创建项目

2.导入素材

3.创建游戏界面

基于C语言和Easyx图形库
1)创建游戏窗口
2)设计游戏背景
a)三重背景以不同的速度同时移动
b)循环滚动背景的实现
3)实现游戏背景
a)加载背景资源
b)打印背景图片(背景知识:坐标)

​ 遇到问题:背景图片的png格式图片出现黑色

​ 解决方案:导入tools.h和tools.cpp,包含到头文件并引用putimagePNG2

c)滚动效果加循环fly

​ 遇到问题:背景闪烁抖动(不断打印与渲染)

​ 解决方案:双缓冲机制(一次性准备好后一起渲染)

​ BeginBatchDraw();EndBatchDraw();

4.实现角色奔跑效果

1)加载角色奔跑的图片素材
2)设置角色的初始位置
a) 定义并取值角色X坐标,Y坐标,图片帧序号
3)实现角色跑动效果
a) 图片帧序号数组+1取余实现

​ heroIndex = (heroIndex + 1) % 12;

5.实现角色跳跃效果

1)处理用户输入的按键
a)如果有按键按下,kbhit()返回 ture
b)_getch不需要按回车即可直接读取,如果是 ‘ ’ 引用jump
2)实现跳跃
a)定义跳跃开关:跳跃Y坐标最大高度,偏移量(值为负)
b)打开跳跃开关

​ 1> 角色Y坐标 > 最大高度,则向上偏移

​ 2> 角色Y坐标 < 最大高度,将偏移量改为正,则向下偏移

c)关闭跳跃开关:当角色Y坐标 > 角色自身高度(最小高度),偏移量还原默认值
3)优化帧等待效果
a)sleep缺点:会导致程序休眠失去响应,在30s内无法触发按键响应
b)getDelay优化:返回距离上一次调用间隔的时间,第一次调用时返回。在30s内可以触发按键响应
c)update开关:用来实时刷新画面

6.实现敌人和障碍物

1)实现小乌龟的随机出现
a)加载小乌龟素材
b)出现小乌龟:(每3s刷新一个小乌龟)同时保证窗口只存在一个乌龟
c)渲染小乌龟

​ 1> 定义并取值小乌龟X坐标,Y坐标,图片帧序号

​ 2> 图片帧序号数组+1取余实现小乌龟旋转效果

​ 3>实现小乌龟水平移动,当小乌龟移出窗口后消失

​ 4>定义小乌龟的出现频率,使小乌龟随2s~5s随机出现、

2)使用结构体封装障碍物
a)定义结构体obstacle_t
b)用枚举存放障碍物类型,并定义为obstacle_type
c)使用c++中vector定义动态数组用来存储各障碍物图片(相当于c中:IMAGE obstacleImgs【3】【12】)
3)封装后障碍物的初始化
a)用结构体加载小乌龟素材
b)用结构体加载狮子素材
C)定义并初始化障碍物池
4)显示多个障碍物
a)出现障碍物:每隔一段时间刷新一个障碍物
b)创建障碍物的各个参数

​ 1>定义exist,imgIndex,type,x,y

​ 2>乌龟的数值,狮子的数值

c)更新所有障碍物的坐标
d)实现障碍物动态效果

7.实现玩家下蹲技能

1)加载下蹲素材
2)下蹲按键实现
3)定义下蹲开关和更新下蹲
a)用updateHero封装更新跳跃和下蹲
b)如果不下蹲则实现跳跃,否则实现下蹲
c)在滚动效果中实现下蹲

​ 遇到问题:下蹲速度太快

​ 解决方案:使用count增加帧数让图片变慢,增加delay延时

8.添加柱子障碍物

1)加载柱子障碍物素材
2)创建柱子障碍物的各个参数
3)优化障碍物的出现频率
a)对3取余降低柱子出现概率

9.实现碰撞检测

1)计算角色和障碍物的矩形大小(在tool.h中)
2)角色的矩形计算
a)奔跑与跳跃状态(通过偏移量来计算)
b)下蹲状态
2)障碍物的矩形计算
3)检测玩家与障碍物是否相交
a)相交:玩家掉血+碰撞声音

​ 遇到问题:角色碰撞后持续掉血

​ 解决方案:新定义hited,控制其开关来解决持续掉血

10.解决音效BUG–优化下蹲–增加血条

1)预加载音效,解决音效BUG
2)优化下蹲
3)使用封装好的血条

11.实现游戏结束,增加初始界面

1)播放背景音乐
2)判断游戏结束

a)血量<= 0,加载结束图片,0表示加载到界面;关闭背景音乐;游戏暂停

b)暂停以后,直接开始下一局;血量回复;播放背景音乐

3)增加初始界面

12.解决死亡障碍(柱子与狮子同时出现)

1)记录上一次障碍物,如果上一次障碍物时柱子&&下一次出现狮子&&上一个障碍物没走远
则将下一次障碍物替换成乌龟

13.通过障碍物后自动加分机制

1)比较x坐标:障碍物x坐标 + 障碍物第一章图片的宽度 < 玩家坐标
2)障碍物通过玩家加一分

14.实现界面界面显示分数

1)加载得分数字图片
2)显示分数
a)实现方法:50 =>‘50’ ‘5’ ‘5’-‘0’=5

15.判断游戏胜利

1)当分数 > 100分,播放胜利音效及图片
2)回复玩家血条,清零分数
3)注意:胜利前最后一分不显示就胜利,用”手动“刷新解决

注意:错误 C4996 'sprintf’要改为sprintf_s

右键文件名->属性->C/C++中常规->SDL检查关闭

源码如下:

#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include <vector>
#include "tools.h"using namespace std;	//声明命名空间#define WIN_SCORE  3#define WIN_WIDTH 1012
#define WIN_HEIGHT 396	//宏定义,便于维护和更改
#define OBSTACLE_COUNT 10	//宏定义障碍物池数量IMAGE imgBgs[3];	//(全局数组)存放背景图片
int bgX[3];		//背景图片的x坐标
int bgSpeed[3] = {2, 3, 5};		//三张图不同的速度IMAGE imgHeros[12];		//存放角色奔跑图片
int heroX;	//角色的x坐标
int heroY;	//角色的y坐标
int heroIndex;	//角色奔跑的图片帧序号bool heroJump;	//表示角色正在跳跃开关
int jumpHeighetMax;	//跳跃的最大值
int heroJumpOff;	//表示偏移量
int update;	//表示是否马上刷新画面//	IMAGE imgTortoise[7];	//小乌龟
//	int torToiseX;	//小乌龟的水平坐标
//	int torToiseY;	//小乌龟的垂直坐标
//	bool torToiseExist;	//当前窗口是否存在小乌龟
//	int tortoiseIndex;//小乌龟旋转的图片帧序号int heroBlood;	//玩家血量
int score;	//得分//枚举封装障碍物的类型
typedef enum {TORTOISE,	//乌龟 0LION,	//狮子 1HOOK1,	//四个柱子障碍物HOOK2,HOOK3,HOOK4,OBSTACLE_TYPE_CONST // 6 便于查看枚举数
}obstacle_type; vector<vector<IMAGE>>obstacleImgs;//c++存放所用障碍物的各个图片
//相当于c中:IMAGE obstacleImgs[3][12]//结构体封装障碍物
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];//得分数字图片//游戏初始化
void init()	{initgraph(WIN_WIDTH, WIN_HEIGHT, EW_SHOWCONSOLE);	//创建游戏窗口//加载背景资源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 = 355 - imgHeros[0].getheight();heroIndex = 0;heroJump = false;jumpHeighetMax = 355 - imgHeros[0].getheight() - 120;heroJumpOff = -5;update = true;//	6.1 加载小乌龟素材//	for (int i = 0; i < 7; i++) {//		//	"res/t1.png" ... "res/t7.png"//		sprintf(name, "res/t%d.png", i + 1);//		loadimage(&imgTortoise[i], name);//	}//	torToiseExist = false;//	torToiseY = 355 - imgTortoise[0].getheight();//用结构体加载小乌龟素材IMAGE imgTort;vector<IMAGE> imgTortArray;for (int i = 0; i < 7; i++) {//	"res/t1.png" ... "res/t7.png"sprintf(name, "res/t%d.png", i + 1);loadimage(&imgTort, name);imgTortArray.push_back(imgTort);	//存放到一维}obstacleImgs.push_back(imgTortArray);	//存放到二维//用结构体加载狮子素材IMAGE imgLion;vector<IMAGE> imgLionArray;for (int i = 0; i < 6; i++) {//	"res/p1.png" ... "res/p6.png"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, 63, 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;//加载得分数字图片for (int i = 0; i < 10; i++) {sprintf(name, "res/sz/%d.png", i);loadimage(&imgSZ[i],name);}
}//创建障碍物的各个参数
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].hited = false;obstacles[i].imgIndex = 0;//obstacles[i].type = (obstacle_type)(rand() % OBSTACLE_TYPE_CONST);	//使障碍物随机出现obstacles[i].type = (obstacle_type)(rand() % 3);	//对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 += rand() % 4;	//0...3}obstacles[i].x = WIN_WIDTH;	obstacles[i].y = 355 - obstacleImgs[obstacles[i].type][0].getheight();//乌龟的数值if (obstacles[i].type == TORTOISE) {obstacles[i].speed = 1;	//乌龟速度值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;		//a1x,a1y为左上角坐标:a2x,a2y为右下角坐标int off = 30;	//偏移量//角色碰撞检测if (!heroDown) {	//非下蹲状态(奔跑,跳跃状态)a1x = heroX + off;a1y = heroY + off;a2x = heroX + imgHeros[heroIndex].getwidth() - off;a2y = heroY + imgHeros[heroIndex].getheight();}else {a1x = heroX + off;a1y = 355 - imgHeroDown[heroIndex].getheight();a2x = heroX + imgHeroDown[heroIndex].getwidth() - off;a2y = 355;}//障碍物碰撞检测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 go()
{for (int i = 0; i < 3; i++) {bgX[i] -= bgSpeed[i];if (bgX[i] < -WIN_WIDTH) {bgX[i] = 0;		//循环实现}}//实现跳跃if (heroJump) {if (heroY < jumpHeighetMax) {heroJumpOff = 6;}heroY += heroJumpOff;if (heroY > 355 - imgHeros[0].getheight()) {heroJump = false;heroJumpOff = -6;}}else if (heroDown) {	//实现下蹲static int count = 0;int delays[2] = {6, 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) {		//25ms刷新一次,刷新一个障碍物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;}}//	6.1 出现小乌龟//if (!torToiseExist) {//	torToiseExist = true;//	torToiseX = WIN_WIDTH;	//小乌龟从窗口宽度边缘出现//	enemyFre = 200 + rand() % 300;	//小乌龟在200 ~ 500(2s~5s)随机出现//}//	6.1 实现小乌龟水平移动//	if (torToiseExist) {//	tortoiseIndex = (tortoiseIndex + 1) % 7;	//实现乌龟旋转效果//	torToiseX -= bgSpeed[2];//	if (torToiseX < -imgTortoise[0].getwidth()) {	//当小乌龟移出窗口后消失//		torToiseExist = false;//	}//}//玩家和障碍物的“碰撞检测”处理checkHit();}//渲染游戏背景
void updateBg() 
{putimagePNG2(bgX[0], 0, &imgBgs[0]);putimagePNG2(bgX[1], 119, &imgBgs[1]);putimagePNG2(bgX[2], 330, &imgBgs[2]);
} void jump() {heroJump = true;	//打开跳跃开关update = true;
}void down() {heroDown = true;	//打开下蹲开关update = true;heroIndex = 0;
}//处理用户输入的按键
void keyEvent() 
{char ch;if (_kbhit()) {		//如果有按键按下,kbhit()返回 turech = _getch();		//_getch不需要按回车即可直接读取if (ch == ' ') {jump();}else if (ch == 'z') {	//按z下蹲down();}}
}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]);}}
}//	6.1 渲染小乌龟//	if (torToiseExist) {//		putimagePNG2(torToiseX, torToiseY, WIN_WIDTH, &imgTortoise[tortoiseIndex]);//	}//更新跳跃和下蹲
void updateHero() {if (!heroDown) {putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);}else {int y = 355 - 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/over.png");	//加载结束图片,0表示加载到界面FlushBatchDraw();mciSendString("stop res/bg.mp3", 0, 0, 0);	//	关闭背景音乐system("pause");	//游戏暂停//暂停以后,直接开始下一局heroBlood = 100;score = 0;mciSendString("play res/bg.mp3 repeat", 0, 0, 0);}
}//添加初始界面
void checkStart() {loadimage(0, "res/over.png");system("pause");
}//越过障碍物自动得分机制
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);}}
}//显示分数
void updateScore() {//实现方法:50 =>'50' '5'   '5'-'0'=5char 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/win.png");FlushBatchDraw();	//刷新界面	mciSendString("stop res/bg.mp3", 0, 0, 0);system("pause");heroBlood = 100;score = 0;mciSendString("play res/bg.mp3", 0, 0, 0);}
}int main(void) 
{init();checkStart();int timer = 0;while (1) {keyEvent();timer += getDelay();	//返回距离上一次调用间隔的时间(单位:ms),第一次调用时返回0if (timer > 25) {		//实现sleep25ms循环效果timer = 0;update = true;}if (update) {	//如果没到30ms触发按键,直接刷新update = false;BeginBatchDraw();	//双缓冲机制(防背景闪屏)updateBg();//putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);updateHero();updateEnemy();updateBloodBar();updateScore();checkWin();EndBatchDraw();checkOver();checkScore();go();}//Sleep(25);	//导致休眠}system("pause");return 0;
}

实机演示:

请添加图片描述

请添加图片描述


http://www.ppmy.cn/news/252447.html

相关文章

《天天酷跑》是不是真的不火了?Python发布“酷跑+”计划。

导语 “ 急速奔跑——享受生活中的美好时光&#xff01;” ——顾木子吖 重磅消息&#xff01;木木子今天又要给大家介绍一款新游戏啦&#xff01; 期待不期待呀~来我们先来看看是什么游戏让大家这么沸腾~ 《天天酷跑》这款游戏有好几年的历史了&#xff0c;当初确实是十分…

基于Springboot的会员制医疗预约服务管理信息系统

摘要 会员制医疗预约服务管理信息系统是针对会员制医疗预约服务管理方面必不可少的一个部分。在会员制医疗预约服务管理的整个过程中&#xff0c;会员制医疗预约服务管理系统担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类的管理系统也在不断改进。本课题所…

拼音输入法技巧-智能ABC

在目前使用的输入法当中&#xff0c;拼音输入法凭借入门简单、无需背字根等特点被很多的电脑用户所使用。这其中&#xff0c;紫光、智能ABC、微软三种拼音输入法使用得比较多。下面是笔者在长期使用过程中&#xff0c;收集的各种技巧&#xff0c;供大家分享。   一、智能ABC输…

怎样用计算机打出Abc,电脑智能ABC输入法怎么设置输入功能?

windows系统自带的智能ABC输入法&#xff0c;虽然功能少&#xff0c;但设置简单、实用。不会拼的字在设置开启后&#xff0c;也可以用笔形输入。小编为大家介绍智能ABC输入法的设置方法及功能。 设置 1、在输入法状态栏中按鼠标右键&#xff0c;在弹出的菜单中选择“属性设置”…

智能ABC

题目描述 人工智能是计算机科学的一个分支&#xff0c;它企图了解智能的实质&#xff0c;并生产出一种新的能以人类智能相似的方式做出反应的智能机器&#xff0c;该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。图像识别是人工智能中很重要的一门技…

怎样用计算机打出Abc,49.智能ABC输入秘籍四招:

智能ABC输入"秘籍"四招 一.[词组输入]:在输入词语的时后,大多数人习惯把整个词组的全部汉语拼音字母都输入.这样做有一个很大缺点,就是按键次数太多.实际上我们在输入词组的时后可以只输入该词组的拼音字母或者声母.例如输入"xx"可以很快的得到词组"学…

【智能ABC】WIN10智能ABC的替换方案

下载QQ输入法输入风格改为智能ABC 属性设置->输入风格->选择【智能ABC方案】 选择使用智能ABC皮肤 下载并选择智能ABC皮肤 再点击【确认】即可效果

新华拼音 输入法4.03(免费)

软件名称&#xff1a;新华拼音输入法软件版本&#xff1a;4.03软件类型&#xff1a; 免费软件 使用平台&#xff1a;PWin98/Me/NT/2000/XP软件大小&#xff1a;5,235KB软件等级&#xff1a; ★★★★★ 软件开发&#xff1a;新华拼音研究组发布日期&#xff1a;2004.06.12网…