C语言用EGE库简单实现别踩白块游戏
本项目已开源:https://github.com/wmpscc/AvoidBlank
关于 EGE
- 介绍:EGE(Easy Graphics Engine),是 windows 下的简易绘图库,是一个类似 BGI(graphics.h)的面向 C/C++语言新手的图形库,它的目标也是为了替代 TC 的 BGI 库而存在。
详情见EGE 官网
简单示例
1 |
|
别踩白块 -准备篇
开始之前先介绍一下相关点。
本篇文章假定你已经准备好游戏每个按键对应的音频文件和游戏关卡的歌词按键谱。
绘图函数
- setinitmode(RENDER_MANUAL); //设置窗口模式
- 功能:
这个函数用于设置初始化图形的选项和模式。
- 声明:
void setinitmode(int mode, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT);
- 参数:
mode
初始化模式,是二进制组合的值。如果为 INIT_DEFAULT 表示使用默认值。
可以使用的值的组合:
INIT_NOBORDER 为无边框窗口
INIT_CHILD 为子窗口(需要使用 attachHWND 指定要依附的父窗口,此函数不另说明)
INIT_TOPMOST 使窗口总在最前
INIT_RENDERMANUAL 手动更新标志,即调用 delay_fps/delay_ms 等会等待操作的函数时会更新窗口,否则保持窗口内容
INIT_WITHLOGO 使 initgraph 的时候显示开场动画 logo
INIT_NOFORCEEXIT 使关闭窗口的时候不强制退出程序,但窗口会消失,需要配合 is_run 函数
INIT_DEFAULT 默认参数,不调用本函数时即使用此参数
INIT_ANIMATION 是 INIT_DEFAULT, INIT_RENDERMANUAL, INIT_NOFORCEEXIT 的组合,用于动画编写
- x, y
初始化时窗口左上角在屏幕的坐标,默认为系统分配。
- 返回值:
(无)
- 说明:
本函数只能在 initgraph 前调用。
- initgraph(WINDOW_WIDTH, WINDOW_HIGHT, INIT_RENDERMANUAL); //初始化窗口
- 功能:
这个函数用于初始化绘图环境。
- 声明:
1 | void initgraph( |
- 参数:
- Width
绘图环境的宽度。如果为-1,则使用屏幕的宽度
- Height
绘图环境的高度。如果为-1,则使用屏幕的高度
- Style
请留空,为保留参数
- 返回值:
(无)
- setbkcolor(EGERGB(0xff, 0xff, 0xff)); //背景色
- 功能:
这个函数用于设置当前背景色。并且会把当前图片上是原背景色的像素,转变为新的背景色。
- 声明:
1 | void setbkcolor( |
- 参数:
color
指定要设置的背景颜色。注意,该设置会同时影响文字背景色。
- 返回值:
(无)
- setfillstyle(SOLID_FILL, Blank_COLOR); //设置方块颜色
- 功能:
这个函数用于设置当前填充类型。该函数的自定义填充部分尚不支持。
- 声明:
1 | void setfillstyle( |
- 参数:
- pattern
填充类型,可以是以下宏或值:
NULL_FILL 不填充
SOLID_FILL 固实填充
pupattern
color
填充颜色。
- 返回值:
(无)
- fillpoly(4, point, totalimg); //绘制方块
- 功能:
这个函数用于画填充的多边形。边线颜色由 setcolor 函数决定,填充颜色由 setfillstyle 函数决定
- 声明:
1 | void fillpoly( |
- 参数:
numpoints
多边形点的个数。
polypoints
每个点的坐标,数组元素个数为 numpoints * 2。
该函数会自动连接多边形首尾。
- 返回值:
(无)
- setfont(40, 0, “宋体”); //设定字体样式
- 功能:
这个函数用于设置当前字体样式。
- 声明:
1 | void setfont( |
- sprintf(mCharsScore, “%d”, mScore); //int 转 char*
- setfontbkcolor(EGERGB(0xff, 0x00, 0x00)); //设置字体背景色
- outtextxy(WINDOW_WIDTH / 2, 0, mCharsScore); //在窗口输出字体
- 功能:
这个函数用于在指定位置输出字符串。
- 声明:
1 | void outtextxy( |
- 参数:
x
字符串输出时头字母的 x 轴的坐标值
y
字符串输出时头字母的 y 轴的坐标值。
textstring
要输出的字符串的指针。
c
要输出的字符。
- 返回值:
(无)
- cleardevice(); //清屏
- setcaption(“别踩白块”); //设置窗口标题
时间和随机数
- api_sleep(500); //传入参数为延时多少毫秒,注意只有该函数可在线程中使用
- delay_fps(FPS) //根据 FPS 设置延迟
- randomize(); //初始化随机数
- random(max); //产生[0,max)的随机数,注意不包括最大值
mp3 音频播放
filename 为要播放的文件的路径名
- 方法一:mciSendString
1 | mciSendString(filename, NULL, 0, NULL); //低效播放方式 |
- 方法二:mciSendCommand
1 | //高效率无阻塞播放 |
线程
举个例子
1 | typedef struct ThreadParameter { //音频播放线程参数 |
启动线程
1 | CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)music_play, &tpp, 0, NULL); //启动音乐播放线程 |
解释如下:
结构体是用来传参数的,线程函数返回值类型必须为 DWORD WINAPI,函数参数必须为 LPVOID 类型。形式如下:
- DWORD WINAPI functionName(LPVOID pParam){}
启动函数第四个参数传入传参结构体指针,此处为&tpp。形式如下:
- CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)functionName, &tpp, 0, NULL); //启动音乐播放线程
文件读入
1 | FILE *fp = NULL; |
无回显读取按键
- getch(); //返回值为按下的键对应 ASII 码
别踩白块 -启程
效果图
我已经准备好每个按键的音频文件和每个关卡对应音乐的键谱了,相关定义如下
1 |
|
整体思路(这可能是最简便的方法了)
- 启动时读入相关的音乐键谱,记录总按键数
- 别踩白块可看成竖向的四条平行轨道
- 根据总按键数创建一个尺寸为 窗口宽度(按键数黑块高度)的 PIMAGE 对象(此处代称总画布)
- 生成 1 到 4 的随机数,对应的数为所在的轨道。并且记录随机数,为后面评判做准备
- 按照所在轨道在总画布上错位绘制黑块
- 游戏启动后,使总画布左下角定点与窗口(0,0)顶点对齐
- 使总画布在可见区域由底向上绘制,并记录下移坐标。
- 根据下移的坐标除以黑块的高度可确定是哪个方块
- 根据按键以及之前的随机数数组即答案来计分
具体实现
- 主函数
下面代码已有注解
1 | int main() //主函数 |
- 文件读入
键谱文件中的内容(片段供参考)
d2 d2 #f2 e2 a1 #c2 e2 d2 d (#f.a) (#f.a) d (#f.a) (#f.a) d (#f.a) (#f.a) A-1 (e.g) (e.g)
对应的音频文件名为黄色部分
d2 d (#f.a) A-1
1 | char track[1024][12]; //乐谱 |
从文件中读入每个按键的标识码存到数组 track 中,并记录有多少个键存到变量 mLength 中。
- 绘制总画布
根据总按键数创建一个尺寸为窗口宽度(按键数黑块高度)的 PIMAGE 对象。生成 1 到 4 的随机数,对应的数为所在的轨道。按照所在轨道在总画布上错位绘制黑块
1 | PIMAGE totalimg; //总画布存储对象 <全局变量> |
- 启动监听按键线程
根据按键以及之前的随机数数组即答案来计分
由于监听按键和绘制动画是异步操作的,所有我们给按键控制单独开一个线程来监听。线程的使用在上面已经介绍过了,下面来看看代码。
1 |
|
代码中用大写字母 S、D 和数字键 4、5 分别控制四条轨道,可随意修改。
failpaly 为游戏失败标志,若为真则播放失败音乐,延迟 500 毫秒后结束线程。
- 计数器
游戏窗口中有显示得分情况,以下是绘制得分函数,其中 mScore 为 int 型变量,程序中通过改变 mScore 变量的值来改变得分。
1 | void drawCountText() //绘制计数器 |
- 音频播放
该部分分为两个函数,一个是预处理一个是播放。
预处理(写法比较暴力哈)
1 | void music_play_control(char key[]) //控制音乐播放 |
实际播放音乐的线程函数
1 | DWORD WINAPI music_play(LPVOID pParam) //播放音乐 |
- 启动时绘制窗口函数
1 | void GameView() // 绘图 |
- 游戏开始后画面绘制线程函数
游戏启动后,使总画布左下角定点与窗口(0,0)顶点对齐
使总画布在可见区域由底向上绘制,并记录下移坐标。
根据下移的坐标除以黑块的高度可确定是哪个方块
1 | DWORD WINAPI ThreadDrawTrack() |
总结
个人感觉 EGE 绘制效率确实有点低,这是我写的五总方法中画面最流畅,且最简单的方法。缺点也很明显,不能单独控制每个按键。
欢迎关注我的 github。
我也是初学者,代码上有很多不足欢迎提出指正,感谢阅览。
完整实现代码如下
1 |
|
本文作者 : HeoLis
原文链接 : https://ishero.net/C%E8%AF%AD%E8%A8%80%E7%94%A8EGE%E5%BA%93%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%E5%88%AB%E8%B8%A9%E7%99%BD%E5%9D%97%E6%B8%B8%E6%88%8F.html
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
学习、记录、分享、获得