前言
这里讲述这个天天酷跑是怎么实现的,我会在天天酷跑的下篇添加源代码,这里会讲述天天酷跑这个项目是如何实现的每一个思路,都是作者自己学习于别人的代码而创作的项目和思路,这个代码和网上有些许不一样,因为掺杂了作者的思路
建立一个窗口
我们要先安装EaxyX图库,安装到我们的开发工具里面去,这样我们才可以实现窗口的实现和图片的加载
这个头文件我们一定要调用,这样才可以实现我们一系列的EaxyX图库函数的实现,对于一个小型的项目设计,我们不可以全部都堆在main函数里面,要不然可读性极差,后面编写项目的时候,内容会很乱,我们也不好去查找错误,我们应该把每一个功能都分装到一个函数里面去,后者创建更多的cpp源文件来是实现多功能
窗口的设计
这里我们在main函数里面写一个init();函数,把所有的初始化分装到这个函数里面去,把窗口的长宽都用宏定义来定义,这样后面方便使用,还具有很高的可读性
根据以上,我们就有了
Main函数-----控制台函数
Init函数-----初始化函数
EaxyX图形库函数的学习
我们可以转到initgraph函数的定义来看里面的参数是什么
这里的参数HWND是一个是窗口句手柄(包括程序窗口,对话框,按钮等)都有一个唯一的标识符号,这个就是窗口句柄
参数:
Width 这个是窗口的宽 height 这个是窗口的高
Flag:
作用:可以取不同的值来实现特定的功能或效果,常见的取值及作用如下:
0:默认模式,通常表示普通的图形显示模式,会创建一个标准的图形窗口,根据width和height指定的大小进行显示。
EW_SHOWCONSOLE:在创建图形窗口的同时显示控制台窗口。这对于在图形程序中需要同时在控制台输出调试信息或与用户进行命令行交互的情况非常有用。
EW_NOCLOSE:创建的图形窗口没有关闭按钮,用户无法通过点击关闭按钮来关闭窗口。这种模式通常用于需要限制用户对窗口关闭操作的特定应用场景
初始化背景
设计思路
我们在实现玩窗口的设置的时候,我们接下来的目的就是要把背景画出来,这就是初始化背景,但是我们要知道这个该怎么设计,我们角色是原地奔跑的,只要背景进行移动,我们就可以达到角色奔跑的效果,所以我们的代码就是要实现背景的挪动,我们可以近处的景色移动快一点,远处的移动的慢一点
这个是我们要实现的一个程序,我们分别把图片的右上角放到x为0,y为对应的位置即可,接下来这个就是我们的思路,我们用代码来实现一波
定义背景图的全局变量
这里我们就用这三个全局变量来存放背景图片,这里就以两个背景图片为例子
第一个为IMAGE类型的图片数组
第二个为存放背景的对于x的坐标
第三个为背景移动的速度,快的为远的物体,慢的为进的物体
初始化背景属性
我们有两个图片就是要实现两次的循环(init函数)
Name 数组:我们利用该数组来存放这个文件的名
Sprintf:这个是把我们输入的内容格式化,可以存放到char类型的数组里面
Loadimage:是把这个name的东西加载到这个imgbgs数组里面
然后初始化一下这个背景的X坐标为0,因为都是在0处,只需要改y即可
(如果想加全局变量的y不加x也是可以的,后面只需再putimage函数加上x)
加载图片的内容到窗口
Putimage是放置到窗口的特定位置,这里就是实现窗口放置图片
背景的奔跑
我们已经放置好图片在窗口处,我们这个时候就要实现图片的移动,我们就写一个run函数运行所有可以运动的东西
这里上面的srand函数是后面用的,这里不用看,我们按照我们的步骤走
我们需要执行两次循环,然后让图片向后面移动,由于我们执行的是一个小游戏,所以这个几毫秒是微乎其微的,不会影响我们的游戏的性能,注意我们是减不是加,因为我们背景往后面移动,我们的人才可以往前面跑动,我们的图片的长度设计应该为窗口长度的两倍,当这个图片运动到—长,那么我们就重置我们背景图片的位置
这样我们就可以实现一个背景的奔跑,利用死循环来不断是实现背景奔跑
目前为止我们实现了一下的函数
Main函数-----控制台函数
Init函数-----初始化函数
Updatebg-----更新背景函数
Run-----所有的运行程序函数
EaxyX图形库的函数学习
这里是loadimage函数参数
pDstImg保存图像的 IMAGE 对象指针。如果为 NULL,表示图片将读取至绘图窗口
(一般这个都是NULL,直接绘制到窗口里面)
pImgFile图片文件名。支持 bmp / gif / jpg / png / tif / emf / wmf / ico 格式的图片
gif 格式的图片仅加载第一帧;gif 与 png 均不支持透明
(我们一般都是用png,jpg,但是图片在这个函数一直都是有一个bug,不可以重叠图片)
nWidth图片的拉伸宽度。加载图片后,会拉伸至该宽度。如果为 0,表示使用原图的宽度
nHeight图片的拉伸高度。加载图片后,会拉伸至该高度。如果为 0,表示使用原图的高度
bResize
是否调整 IMAGE 的大小以适应图片
void putimage(
int dstX, // 绘制位置的 x 坐标
int dstY, // 绘制位置的 y 坐标
IMAGE *pSrcImg, // 要绘制的 IMAGE 对象指针
DWORD dwRop = SRCCOPY // 三元光栅操作码
);
void putimage(
int dstX, // 绘制位置的 x 坐标
int dstY, // 绘制位置的 y 坐标
int dstWidth, // 绘制的宽度
int dstHeight, // 绘制的高度
IMAGE *pSrcImg, // 要绘制的 IMAGE 对象指针
int srcX, // 绘制内容在 IMAGE 对象中的左上角 x 坐标
int srcY, // 绘制内容在 IMAGE 对象中的左上角 y 坐标
DWORD dwRop = SRCCOPY // 三元光栅操作码
);
这里我们一般都是写出x和y的坐标,然后利用指针指向那个图片的位置,然后进行放置图片
实现角色的跑动
根据我们实现背景的步骤,我们就有了一个很清晰的认知
定义相关属性(全局变量)—>初始化相关属性(init)-->加载相关图片(update…)
-->运行该物体(run)
定义角色的全局变量
角色的图片(12帧)
角色的x与y坐标
角色加载的帧数
初始化角色属性
这里就是同样的操作,把图片加载到name数组里面,然后初始化这个图片和角色的坐标和角色的帧
加载角色的图片
下面那个是下蹲所需要,这个后面会讲,我们理解下面这个就好
我们这里是放置一个角色在(heroX,heroY)这个坐标的位置,放置的是这个imgHero这个数组里面的图片,那么我们现在最后的一步就是不断逐帧的显示图片即可
逐帧显示图片
我们利用这个可以进行逐帧动画,实现玩家的不断奔跑,用取模运算改变数组的下角标来实现角色的运动
目前我们设计的函数有
Main函数-----主控制台
Init-----初始化每一个东西的属性
Updatebg-----加载背景
UpdateHero-----加载角色
Run-----运行每一个事物
实现玩家的跳跃
这里我们利用这几个全局变量来是实现跳跃的功能
Herojump 这个是一个布尔变量,实现是否进行跳跃
Jump HeightMax 这个每一次进行的循环,这个角色该上移和下移动多少
初始化跳跃的属性
herojump是进行判断是否进行跳跃
jump HeightMax 是跳跃的最大限度
herojumpOff 是每次所减少的量
输入空格键来实现玩家的跳跃
这里就是如果有键盘输入的话,这个_kbhit()这个就会返回为真,否则返回为假
我们有了这个然后判断一下输入的是什么键盘就可以进行操作的执行了,ch是用来接受这个他输入的是什么东西就执行什么东西,我们如果输入空格之后就会跳到jump函数的里面
Jump的开关函数
跳跃的实现
这个是我们实现跳跃的函数,如果jump被打开,那么就让heroY先上升,但是我们是往0靠近的,所以我要加负数,然后下降的时候是要加正数来实现下下降然后当我们角色下降到合适的位置,我们就可以关掉这个角色跳跃的开关即可
优化帧
这个就是我们用来实现这个优化帧的
这个是我们一般来写的,每间隔30毫秒执行一次循环里面的语句,但是我们在这30毫秒里面去输入空格的话,这个电脑是执行休眠期的,所以这个空格就不会执行,胡出现掉帧的情况,虽然我们这种小型的游戏看不出什么,但是大型游戏就很明显,所以我们来优化一下
我们定义了一个update来执行是否进行更新,虽然跟sleep的功能很相似,但是这个是受我们控制的keyEvent(),在更新的前面,方便控制更新getdaly()执行的就是不断的输入1毫秒,当这个到达30次就执行一次清零,然后就update打开,跟sleep很像,但是当我们输入空格的时候,那个jump开关函数里面也有一个update打开,这样就可以在30毫秒里执行跳跃,不至于掉帧
到目前我们所实现的程序:
Main-----控制台函数
Init-----初始化函数
Update(xxx)---加载各种图片
keyEvent—判断输入的键盘的功能-
jump的开关函数
实现多种障碍物的出现
我们把定义一个枚举,里面放置各种类型的障碍物
这里是定义一个存储每一个障碍物的数组,这里使用的c++的容器写的,大家也可也用c语言的二维数组来进行编写
这里定义了每一个障碍物的的属性,用于后面的编写
这个是定义的是,我们用于存放障碍物的,便于我们设置他们的属性
接下来我们就要把这所有的进行初始化,用于后面的操作里
这个是初始化小奶龙的
这个是我们现加载一下这个图片,创建一个容器类的一维数组用来存放图片然后把在容器里用push_back功能来实现把图片压入容器,然后在把这个一维数组放入到我们先前创建好的二维容器数组里面,这个TOTRTOISE是我们之前用枚举定义过的,用这个可读性很高,这样我们就可以分清楚哪一行对应的那一种的障碍物
这个是我们kunkun的图片初始化,跟小奶龙的初始化是一样的,只不过kunkun是一个动图,所以才需要for循环来执行逐一把图片压入到二维数组里面
这个是柱子的初始化,由于不知道为什么这个用for循环逐个压入会有问题,可能是尺寸问题,所以采取了逐一的压入的方法
OBSTACLE_CUONT是障碍物的最大个数
然后我们把每一个障碍物的存在都设置为false
这里生声明一下,我们定义了两种数组:
一个是存放照片的
vector<vector<IMAGE>>obstacleImags(OBSTACLE_TYPE_COUNT,vector<IMAGE>(12));
一个是存放每个障碍物的,这个是用于管理每一个障碍物的属性
obstacle_t obstacles[OBSTACLE_CUONT];
当我们进行图片的初始化,我们就要创造障碍物
除了伤害和移动速度对于每一个角色都不一样,其他的属性都是一样的
这里就是如果障碍物不存在的化,这个就是跳出循环,但是如果单单这样就是每一次都是第一个障碍物出现,所以我们就要在后面实现一个随机取的一个功能
这里就是随机取这个障碍物的功能,由于vs比较严谨,所以整形和枚举类型是不会兼容的,所以需要强制转换一下,这个rand前面要写一个strand函数,随机种子要写time,这样才可以实现随机出现一个数字,要不然是一直出现一种的数字,所以我们分为了两个区域一个是取奶龙,kunkun,柱子,但是柱子有出现了很多种,为了让每一个柱子出现的频率相同,下面在写一个对于柱子的随机取值
知识点:rand()%x这个是取0~(x-1)范围的值,在前面我们已经定义了枚举的类型,所以这个是十分方便的,其他与初始化反着来就可以了
我们现在初始化了每一个障碍物和实现了他们的创建,但是我们还要实现他们的出现和调整频率,要不然一直出来就完蛋了在这样就没有操作空间了
所以我们要在run函数里面进行操作
我们定义一个静态变量,这个是每一次都是加1,达到50次就刷新怪物来调整这个怪物之后就进行创造这个障碍物,这个频率肯定不可以一直不变嘛,所以我们后面再写一个rand函数前面也写一个srand函数来以time为种子实现随机,但是创造这个障碍物还要实现移动和关闭,要不然不动还不一移动就不是游戏了
实现障碍物的移动和开关的关闭
这里我们也是在run函数里写,创建完障碍物,我们就逐一的判断每一个障碍物的存在性,存在就调用他们之前定义的速度,记住要加背景的速度,因为小奶龙是不动的,然后这个当到达这个-300的时候,就把这个图片关掉,但是由于我们的kunkun使用一个动图,所以我们就要加下面这个,计算他数组的长度,然后利用取模运算来
方法:
取模:(x+1)% n这个是确保不超出n如果等于n就自动为0了,但是小于n就是他本身加1,比如(1+1)%12为2,就是为(x+1)的值,只是为了不超过一直在0~n的区间内执行
我们现在已经初始化障碍物,创造障碍物,实现障碍物移动与关闭,现在就差一个放置在窗口了,所以我们就要写一个updatezhang这个来放置图片
这个是放置障碍物所用的看是否存在然后进行放置窗口
这样也就实现了障碍物了
实现玩家的下蹲
定义相关下蹲属性
用于下蹲的开关和存放这个角色下蹲图片的数组
初始化角色的相关属性
Down的输入设置
当我们输入s的时候就可以打开这个down的开关
这个时候我们是读取这个角色下蹲数组的图片,所以这个下角标一定要进行更新为0,然后再进去,要不然会出现bug
这里是下蹲功能的实现
我们用静态变量来实现这个次数,我们要这两个图片之间要有一定的时间间隔,要不然会很快的起来,所以我们这里定义一个delays是用于延迟的,然后延迟到一定的时间的时候,我们就可以实现这个图片的更换,完成之后,我们有初始化这个图片的下角标,这样就可以实现下蹲和然后再关掉这个开关
当下蹲和跳跃都没有实现的时候,就执行这个奔跑
当目前为止我们已经完成了大半的进度了实现了玩家的下蹲,奔跑,背景的移动,障碍物的出现,调频,帧的优化
接下来就是实现这个血条,得分,撞到检测,打包,这个会在下一篇讲到
总结
今天我们学习到了障碍物,背景,人物的制作
对于大部分的物件的实现
定义这个物件的相关属性-->初始化这个程序的相关的属性-->加载这个物件到这个窗口-->运行这个程序
对于大部分功能的实现
定义这个共能再物件的相关属性(如跳多高)-->定义这个功能的开关-->再运行函数写出这个程序-->是否需要别的函数来辅助实现(如这个障碍物就是需要creatObstacle函数)-->放置物体
关于背景
我们再实现东西的时候要知道拿一些放入到死循环里面,那些不需要,这个背景是每时每刻都是存在的,所以我们不需要放到死循环里面,放到外面即可
关于人物
我们再写人物的时候,有很多的动作,我们这个时候先创建这个人物的相关的属性,然后这个根据功能写出相关的函数,比如我们在这里就是要跳跃,下蹲,奔跑,这里我们可以用if语句来实现三种功能,在奔跑是奔跑,跳跃时不奔跑等,我们还设计了一个函数是关于键盘的输入还有开关的公控制是否需要打开,这样我们的逻辑思路是很清晰的
知识点(x+1)%n这个公式就是循环取值的时候永远都不会超过n,一直在(x+1)~n跳动这个就为我们逐帧动画提供了很好的帮助
关于障碍物
我们在写障碍物的时候,我们要写出很多的属性,这里需要用到结构体就可以简化很多的代码,结构体里面全部都是障碍物的相关属性,然后利用枚举类型,在实现数组的时候,初始化用枚举具有很高的可读性,然后到后面我们存了这个图片不知道在哪一行是这个障碍物的图片是,可以去到枚举里面查看,而且枚举在最后面定义一个sum就是障碍物的总和
障碍物的功能总结
我们在实现障碍物的时候,我们需要随机出现所以创造障碍物的时候,我们需要用到rand和strand函数来实现实现,后面就是频率的控制和障碍物的放置和移动了
知识点 rand()%n这个数字永远是0~n-1这个区间里面的,这样也可以用于调整出现频率什么时候出现,也可以用于随机出现障碍物
优化帧
我们一般都是用sleep这个来写,但是容易掉帧,所以我们需要写一个自己可以控制那30毫秒里面的东西