——211850016 写于2021/12/15晚
-
请先配置好**
SDL2
+SDL2_image
+SDL2_ttf
**的环境,Linux下可直接通过命令行配置,Windows下可参考自制的配置SDL2环境的教程。 -
命令行编译请使用:
gcc Sakiyary_2048.c -o Sakiyary_2048 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf
使用Clion编译时,请注意文件夹中的
CMakeLists.txt
,可根据情况微调。另外,使用Clion编译时,请先点开右上角“锤子键”与“运行键”之间的配置,点击编辑配置,在跳出的窗口中将**“工作目录”那一栏改为本文件的绝对路径**。
详情可见自制的SDL2教程的p2中06:35所演示。
-
注意: 设置的窗口大小为800*1000,当使用1080p的屏幕并将屏幕缩放调至大于100%时,游戏窗口会超出屏幕!同理,使用2k、4k屏幕时请将屏幕缩放调小一些,使得游戏窗口可在屏幕内完全展现。
-
当您运行游戏,在您面前呈现的是开始界面。
此时,您可通过鼠标点击"START"或按下"Enter"回车键或按下空格键来进入游戏。
也可以通过按下"Esc"键或**鼠标点击窗口右上角的"×"**来退出游戏。
-
第一次进入游戏界面,在您面前呈现的是供测试与演示用的几乎填满的游戏面板。
您可以通过按下"w" "a" "s" "d"键或按下方向键或在窗口内上下左右拖动鼠标一定距离以上(>100pt)来进行2048的数字移动操作。
- 您在游戏界面正常游玩时也可以随时进行以下操作:
(1). 通过按下"Esc"键或按下"Backspace"键或鼠标点击"退出"图标来退回到开始界面,此时游戏面板与游戏时长将不会发生改变。
(2). 通过按下"z"键或鼠标点击"撤回"图标来返回上一步。注意,您只能撤回一步,且不产生数字移动的操作并不会记录为"上一步"。开始游戏/重开游戏后的第一步操作前是不能撤回的。
(3). 通过按下"right"键或鼠标点击"重置"图标来重开游戏,此时您的游戏分数与游戏时长会清零,但最高分不会。
(4). 通过按下"Space"空格键或鼠标点击"暂停"图标来暂停游戏,此时会有游戏暂停对话框呈现在您的面前。
(5). 通过按下"Enter"回车键来随机方向进行一次数字移动操作。此操作必定会产生数字移动。
- 游戏共有三种对话框,对话框出现时,您在游玩时可进行的操作将无效化,且暂停游戏时长记录(即对话框停留的时长不会计入游戏时长中)。三种对话框及操作分别为:
(1). 游戏结束对话框:当您游玩时进行完一次数字移动操作,且新的数字随机生成后,系统判定您游戏已经无法继续时,此对话框会出现在您的眼前。您此时有三个选项:退回开始界面(按下"Esc"键或按下"Backspace"键或鼠标点击"退出"图标,此操作会重置游戏面板),撤回一步(按下"z"键或鼠标点击"撤回"图标),重新开始(按下"right"键或鼠标点击"重置"图标)。
(2). 游戏胜利对话框:当您游玩时进行完一次数字移动操作,在本局游戏中第一次成功合成出2048这个数字时,此对话框会出现在您的眼前。您此时有三个选项:退回开始界面(按下"Esc"键或按下"Backspace"键或鼠标点击"退出"图标,此操作不会重置游戏面板),继续游戏(按下"Space"空格键或鼠标点击"继续"图标),重新开始(按下"right"键或鼠标点击"重置"图标)。若您选择了继续游戏(包括退回开始界面再重进),当您第二次及更多次合成出2048时,此对话框不会再次出现。在开始界面停留的时长同样不会计入游戏时长中。
(3). 游戏暂停对话框:出现条件如二.3.(4)所写。您此时有三个选项:退回开始界面(按下"Esc"键或按下"Backspace"键或鼠标点击"退出"图标,此操作不会重置游戏面板),继续游戏(按下"Space"空格键或鼠标点击"继续"图标),重新开始(按下"right"键或鼠标点击"重置"图标)。在开始界面停留的时长同样不会计入游戏时长中。
-
每一次产生数字移动的数字移动操作之后所随机生成的新的数字,90%为"2",10%为"4"。
-
当您合并出大于65536的数时,该数字方块将会显示为纯黑,但其本质与所计分数不会改变。
-
实测用部分Linux系统(Ubuntu)来运行时鼠标类的交互会非常卡顿,不明原因。已修复!! -
当您在游玩中进行某些窗口外操作(如按下Ctrl+Alt+Delete调出任务管理器),会让游戏卡死,此时只能关闭窗口再重开。已修复!!
图形库真好(fan)玩(suo)!
有些地方不可避免地写成了屎山(大片的switch-case),但我还是尽力缩减重复代码了。例如加载图片时用指针数组来循环加载数字图片、将四种方向的移动(用大量的三目运算符)统一进一个函数(写报告的时候发现我是不是可以用数组旋转来避免可读性很差的三目运算符呢,说不定会优化一下)、统一对话框在一个函数中等等。
所有随机数所用种子为srand((unsigned)time(NULL))
,并用类似于rand()>RAND_MAX/2
而不是rand()%2
的判断语句来进行随机的操作。
内存巨大泄漏的bug应该已经修复,但我测试时还是会占用100MB左右的内存,不知道该如何缩减。随着游玩时间的增加,内存占用会缓慢上升,玩10分钟后会占用到400MB以上,且不会减少,不知该如何优化。(21/12/20更新: 可能已修复,见下)
这次是全程使用了git,虽说没有/remake过,但确实在写代码的时候变得很安心。
所有图形,除了温迪人物相关与背景,均为自制,有git存档与工程文件.psd留档。
感谢游玩!
2021/12/17更新: 本来的Move函数是用三目运算符堆出来的(如下),可读性实在是不忍直视,故更新了代码,改用了数组旋转。
void Move(int Dir1, int Dir2) {
for (int i = 0; i < 4; ++i) {
if (!Map[Dir1 ? 0 : i][Dir1 ? i : 0] && !Map[Dir1 ? 1 : i][Dir1 ? i : 1] && !Map[Dir1 ? 2 : i][Dir1 ? i : 2] && !Map[Dir1 ? 3 : i][Dir1 ? i : 3])
continue;
for (int j = Dir2 ? 0 : 3; (Dir2 && j < 3) || (!Dir2 && j > 0); j += Dir2 ? 1 : -1)
for (int k = 0; (Dir2 && k < 3 - j) || (!Dir2 && k < j); ++k)
if (!Map[Dir1 ? j : i][Dir1 ? i : j]) {
for (int left = j; (Dir2 && left < 3) || (!Dir2 && left > 0); left += Dir2 ? 1 : -1)
Map[Dir1 ? left : i][Dir1 ? i : left] = Map[Dir1 ? (Dir2 ? left + 1 : left - 1) : i][Dir1 ? i : (Dir2 ? left + 1 : left - 1)];
Map[Dir1 ? (Dir2 ? 3 : 0) : i][Dir1 ? i : (Dir2 ? 3 : 0)] = 0;
}
for (int j = Dir2 ? 0 : 3; (Dir2 && j < 3) || (!Dir2 && j > 0); j += Dir2 ? 1 : -1) {
if (Map[Dir1 ? j : i][Dir1 ? i : j] == Map[Dir1 ? (Dir2 ? j + 1 : j - 1) : i][Dir1 ? i : (Dir2 ? j + 1 : j - 1)] && Map[Dir1 ? j : i][Dir1 ? i : j] != 0) {
Map[Dir1 ? j : i][Dir1 ? i : j]++;
Score += 1 << Map[Dir1 ? j : i][Dir1 ? i : j];
BestScore = BestScore < Score ? Score : BestScore;
if (Map[Dir1 ? j : i][Dir1 ? i : j] == 11 && !IfWin)IfWin = 1;
for (int k = Dir2 ? j + 1 : j - 1; (Dir2 && k < 3) || (!Dir2 && k > 0); k += Dir2 ? 1 : -1)
Map[Dir1 ? k : i][Dir1 ? i : k] = Map[Dir1 ? (Dir2 ? k + 1 : k - 1) : i][Dir1 ? i : (Dir2 ? k + 1 : k - 1)];
Map[Dir1 ? (Dir2 ? 3 : 0) : i][Dir1 ? i : (Dir2 ? 3 : 0)] = 0;
IfMove = 1;
}
}
}
}
2021/12/20更新1: 在翻SDL2库的时候发现一个美妙的函数:
SDL_COMPILE_TIME_ASSERT(SDL_Event, sizeof(SDL_Event) == sizeof(((SDL_Event *) NULL)->padding));
按照我的理解,此函数可以更新事件队列与内部输入状态,感觉和fflush()
有相似之处(?)。
运用此函数后程序整体内存占用可以动态控制在60MB左右。
但是当快速按下方向键或wasd时操作还是会记录每一个操作并按序反应出来(虽说在本游戏问题并不大)。
(2021/12/22更新: 实测,上述没什么实际用处,没解决问题本质)
2021/12/20更新2: 试图写成多文件联合,但由于一开始接口没做好,现在改动太复杂了,故只把定义都集中到了Sakiyary_2048.h
中。
2021/12/22更新: 在Bob(211250070)同学的帮助下,发现编译选项中的-lmingw32
可以删除,通过加上#undef main
来使编译器找到主函数在哪(这是SDL2的一个坑)。同时,修复了由于鼠标移动引起的游戏卡顿与内存泄漏。(因为MOUSEMOTION是SDL会一直识别的一个事件)目前**二. 7.**大概也许可能已经修复了。 同时由于内存泄漏已修复,可以稳定在30MB~50MB内存占用,一并修复了游戏会卡死的问题,二. 8修复了!