开发日志
2023/10/13 17:04
- √ 1、背景的添加(可以分成上下两层,一个bg,一个map)
- √ 2、颜色在update时的改变,包括背景,前景,人物等
- √3、enemy的添加
- 4、加速度的添加(可以把纵向和横向的updatetime分开来写,以实现重力加速度和横向加速度的效果)
2023/10/14 13:09
- √1、把输入处理、更新游戏状态、渲染画面分开来写
- √2、把C语言的代码改为C++
- 3、把游戏状态改为枚举类型
- √4、状态机
2023/10/15 17:04
- √1、拆分为多文件
- √2、多线程update以实现enemy和player的不同速度
- √3、循环依赖问题
- √4、加入互斥锁,以解决多线程导致的光标混乱问题
- √5、碰到enemy时的判负问题
2023/10/15 20:48
- 1、实现背景的滚动、背景的动画效果
- √2、camera
2023/10/16 00:00
- 1、靠近敌人的时候在上方显示血条
- 2、开场动画
- 3、敌人死亡后显示爆炸效果
- 4、音乐的添加(分形音乐)
- 5、打击感
- 6、关卡的设置
- 7、装备系统
- 8、游戏的剧情以及动画
- 9、rpg 肉鸽 横板过关
- 10、可以做成俯视图(主城)和横板过关结合的形式
2023/10/16 22:09
- √1、实现游戏的循环
- √2、第三次及以后的choice输入时的上下键自动切换为曾输入值的现象————Windows的cmd窗口的功能
- √3、第二次及以后的scanf choice自动填充一个y的问题————原因同上,是因为按了→键
2023/10/17 17:26
- √1、清空输入缓冲区、getchar、_getch等等
- √2、把颜色转换单独写成一个函数
2023/10/18 11:47 21:12
- √1、关于标准输入缓冲区和控制台输入缓冲区————FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); // 清空输入缓冲区
- 2、添加pos_ prepos,把update和render完全分开————player似乎无法实现,因为player类是通过不断接收用户输入而产生移动,一旦用户停止输入,prepos会自动更新为最新的pos,导致bug,反观enemy类,无需接收用户输入,所以不用担心prepos的更新行为
- ***3、双缓冲更新背景
- √4、实现多颜色
2023/10/19 10:18
- 1、可以加入伪3d
- √2、添加边框
- *√3、冲刺————残影拖尾还未添加
按下j,isdash=true,角色横纵速度3,/或者瞬间完成三次更新,或者updatetime/100/,留下拖尾,一秒后拖尾消失
最终决定用纵横速度3,虽然这样写麻烦一点,但是其他两种方法只是单纯的加快速度,并不能达到冲刺突进效果,比如跳跃的时候,它只是加快了跳跃和下坠
2023/10/20 15:23
- √1、有一个小bug,在跳跃到最高点的瞬间松开w键然后按j,会毫无反应————在去掉向下冲刺的功能后,这个bug随之解决
- 2、game类的添加,架构的规范
- √3、bg背景颜色的改变
- √4、把player的每个技能和检测都抽离成单独的函数,不要全都直接写在update里
- √5、还有一个很久以前就遇到的bug,当MAXHOP为3时(可以刚好跳四格高),角色跳不上高度为3的墙,但是后来在修改代码过程中这个bug消失了
- √6、把map类和background类里存放地图和背景的二维数组由char类型改为结构体类型,在存储字符的同时也存储它的颜色,方便以后的开发
(也可以直接用int类型的二维数组数组,每个元素里直接存储这个位置的颜色,然后根据颜色去判断它是什么类型的方块,是WALL还是其他之类的。但是这里我仍然选择前者) - √7、把所有的render都放到camera_render线程,player和enemy等线程只负责update————删除prepos
2023/10/21 11:34
- √1、冲刺突进技能改为只能向上和左右使用,不能向下
- √2、关于字体颜色的问题:
目前所使用的SetConsoleTextAttribute来设置颜色,似乎只能支持有限的颜色,这样后期想用控制台去绘制一些背景的时候效果会不好
解决方法有两个
一是使用关于ansi转义序列的CONSOLE_SCREEN_BUFFER_INFOEX、colortable、rgb等win api来以rgb格式设置字体和背景颜色,但是刚刚尝试了一下,似乎不行
详见https://learn.microsoft.cameraom/zh-cn/windows/console/console-virtual-terminal-sequences中的“扩展颜色”
似乎windows默认自带的控制台不支持那么多颜色,而是某些虚拟终端仿真器支持比 Windows 控制台提供的 16 种颜色更多的颜色调色板。
二是用脚本把图片转换为字符画的时候,把不同颜色的像素聚类,这样会使呈现出来的效果差一点,但是也别无他法了
关于ansi转义序列和虚拟终端的参考资料
https://blog.walterlv.cameraom/post/enable-virtual-terminal-processing.html
https://learn.microsoft.cameraom/zh-cn/windows/console/setconsolemode?wt.mc_id=MVP
https://learn.microsoft.cameraom/zh-cn/windows/console/getconsolemode
https://en.wikipedia.org/wiki/ANSI_escape_code
https://learn.microsoft.cameraom/zh-cn/windows/console/console-virtual-terminal-sequences
https://learn.microsoft.cameraom/zh-cn/windows/console/console-screen-buffer-infoex#members
16:03
关于字体颜色的问题,经过几个小时的探索,找到了两个方法:
其一:首先通过GetConsoleScreenBufferInfoEx获取标准输出的CONSOLE_SCREEN_BUFFER_INFOEX,然后以RGB格式修改其中的ColorTable属性,最后用SetConsoleTextAttribute设置
但是探索很长时间后,发现怎么修改都是没有效果。遂放弃
其二:直接用ansi转义符,设置背景和前景颜色,这个方法成功了,通过printw("\x1B[38;2;%d;%d;%dm\x1B[48;2;%d;%d;%dm", i, j, k, k, j, i);设置前景和背景颜色
打印字符之后,再用printw("\x1B[0m");恢复默认状态即可
详细的测试代码见文件夹test里的color_test.cpp
(改是改好了,但是卡顿和闪烁问题变得很严重,双缓冲问题亟待解决)
- √3、颜色修改过程中,用到了"整数打包"和"整数解包"的技术,测试代码见test\data_pack.cpp
- 4、修改vscode工作区里2dgame子目录test下的cpp文件单独使用一个.vscode配置
- 5、在cmd控制台中光标偶尔会莫名其妙自己显现出来,但是似乎不是代码的问题而是系统的问题
2023/10/22 12:41
- 1、今天开始着手双缓冲的优化
测试代码见exe\double_buffer_test.cpp,里面实现了基本的双缓冲逻辑
但是其实测试代码中的方法不能用于我的2dgame,因为其中对于颜色的修改还是通过修改Attributes属性来实现,这样输出的颜色有很大的局限性
而我要通过ansi转义符来实现颜色的更改
目前所想的解决办法是在consoleBuffer中预留出空间,专门用于存储颜色转换的ANSI转义符和恢复默认的ANSI转义符
(这样会使screen的内存占用增多42倍,但是应该对性能影响不会太大)
还有一种思路,就是用两个线程交替渲染————错误,交替渲染也需要用mutex来阻塞,防止指针混乱,这样不但不能防止卡顿,还会影响性能
甚至可以把画面拆分成两块,启动两个线程,一个渲染左边,一个渲染右边,似乎也可以达到一样的效果
还有一个小问题,那就是以前的代码中是直接用MOVECURSOR来进行绘制,无需考虑换行问题,而改用双缓冲后,需要考虑换行
- 2、或者用NCurses重写代码
16:33
尝试了很长时间的方法(见exe\double_buffer_test2.cpp),代码的难度太大,计算比较复杂
2023/10/23 10:38
今天开始进行蓝桥杯准备,此项目暂时搁置
2023/10/24 18:59
白天又调试了一下Double_Buffer_test2.cpp,仍然存在一些小问题
2023/11/6 19:57
今天再次调试了Double_Buffer_test3.cpp,bug仍然存在,估计就是因为缓冲区中的字符数远远超出了控制台长度(即使转义字符根本不会在控制台中实际显示出来)
其实我也不确定错误原因究竟是什么,而且我感觉很可能不是这个原因👆
不管了,放弃这个方法
下面简单阐述一下思路
首先是一个 返回值类型 为 字符串 的 Color 函数,接收的参数为 前景色rgb 和 背景色rgb共六个(其实可以优化成两个int类型,用位运算解码),具体功能就是把颜色信息转成ANSI转义符,然后返回这个字符串
然后是 返回值为 空 的 SetChar 函数,接收的参数为横坐标x,纵坐标y,要打印的字符c,和 前景色rgb 背景色rgb,具体功能就是在缓冲区字符串中写入带有颜色转义序列的字符,和换行符
我本来是打算用结构体来表示一个字符,成员有 char c;字符 int forecolor;前景色 int backcolor;背景色,然后用一个二维数组来表示缓冲区,在SetChar函数中,先根据forecolor和backcolor来生成颜色转义序列,然后把字符c和换行符写入缓冲区
最终失败
放弃了,继续探索其他方法
也就是说,目前游戏中的颜色仍然有局限,想要在屏幕上绘制像素画仍然是有局限性的
并且也没有实现双缓冲,刷新机制仍然不平滑
2023/11/8 14:03
👆👆👆不不不,记错了,其实颜色已经实现了ansi转义序列控制,现在唯一的问题就是无法在使用转义序列控制颜色的同时使用双缓冲
2023/11/9 19:24
地图的生成与存储
2023/11/10 17:00
用python脚本生成地图的时候,前景地图的字符为 W ,背景为 . ,主要的任务是读取图片中的颜色,并以RGB的格式存储
2023/11/11 15:11
守恒,不平衡则趋向毁灭
2023/11/12 1:37
I've found that in ncurses, after using init_color, all colors that appear on the screen are immediately changed to the newly defined ones.
So, I've abandoned the previous method of "calling init_color before printing each character and then applying the color"
and instead, during game initialization, I define all the colors I'll need, and then I directly use them when printing characters.
The first parameter of init_color and init_pair only supports the range 0-255
Therefore, in terms of color change, ncurses are discarded and ansi escape sequences are used
but ncurses doesn't supports ANSI!!!!!!!
The getch of ncurses cannot detect multiple keys simultaneously and has latency, which is a fatal issue
The solution is to use other libraries that can detect multiple keys simultaneously on Linux systems,
while separating keyboard detection on Windows and Linux
Currently, we plan to first implement the existing functions using ncurses (without considering color scalability),
while also adding dual buffering function
Rewrite to a version that supports full color in the future
Adding Double Buffers
Separate control modules under Linux and Windows
2024/1/26 11:42
游戏的画面渲染方式存在一些问题,需要更改架构
- 1、需要指定窗口大小以及(所有字符的出现位置:也许可以忽略)
- 2、优化各个类,例如camera,要使其在俯视角2d和横板2d中都能使用
- 3、
查阅了很多资料,还是没有找到在linux系统下实现同时检测多个键值输入的简单方法。
其实目前在读取键盘输入这方面,有两个问题:
- 1、键盘的停止有延迟,比如我长按一段时间的→之后,即使我松开,角色也不会立马停下,而是继续移动一段时间后才停止(初步推断是ch没有及时被归回ERR,也有可能是在linux系统下的的线程时间的冲突导致的,或者是渲染问题)。
这个bug的检测很好做,在player::input函数中添加一个count变量并打印,然后看是否出现了“count在变化的同时ch仍然没有被归回ERR”,但是这种情况的概率很小。
我暂时还没有做检测,因为即使这个问题解决了,也还会面临下面这个更严峻的问题。 - 2、linux系统下无法同时检测多个键值的输入,或者说:linux系统下还没有现成的方法去实现这种检测。
解决办法有两个:
- 1、监听/dev/input/event事件 从底层去剖析键值检测的逻辑,然后实现多个键值同时检测
- 2、多线程检测
2024/1/27 23:45
今天终于解决了linux系统下无法同时检测多个键值输入的问题
在国内外搜了好久,相关的资料真的屈指可数(毕竟也很少有人会在linux下做这种游戏)
和gpt探讨了好久,最终选用如下方法
使用一个单独的线程,用select去获取键盘的状态,然后在每个键的按下或弹起的时候,把全局变量中的key_state数组对应的键状态改变,再在角色控制线程中去读取这个数组中指定键的状态,从而达到和windows系统下的GetAsyncKeyState相同的效果
其实也可以每个键开一个线程去检测,但是这样太消耗资源,而且以后加入新的键的功能的时候也不好扩展
目前发现的一个新的问题是,在游戏结束后,提示用户继续游戏还是退出游戏的时候,即使用户选择了退出游戏(N/n),仍然会继续游戏,我推测是在游戏结束后,开启阻塞模式,虽然程序会等待用户输入,但是其实输入缓冲区中仍然有键值残余,即使用户输入了,录入的choice值也不是用户输入的,而是以前的键值残余,不过这个bug倒是不难修改
g++ -fdiagnostics-color=always -g /media/fsw/data/github/2Dgame/*.cpp -o /media/fsw/data/github/2Dgame/exe/game -lncurses
2024/1/28 14:07
今天完成了程序在linux和windows双系统的适配
妈的,游戏在vscode集成终端运行的时候会有很大的显示bug,搞得我找了半天bug,最后找正常的试了好久才发现是它的问题。
以前明明没有这个问题来着。
在linux系统下有个问题:在纯文本模式(init 3)中,运行程序后,会有一些颜色显示bug
2024/1/29 10:48
- 1、把player类和enemy类的共同部分抽象成object类,方便程序的扩展和维护
- √2、把碰撞检测单独抽象为一个函数
- √3、把接收玩家输入单独抽象为一个类
- 4、感觉“在一个单独的线程中循环调用每个角色和摄像机的渲染函数来进行绘制”这种做法有些问题,但是和gpt交流了一下,他说没问题,那就没问题吧
- √5、从main函数中抽象出game类,input、update和render添加为成员函数
2024/1/29 21:29
把角色的每一个方块都抽象成一个类:Roleblock,然后在具体的角色类中去确定这个角色的Roleblock数量和初始位置,这样就可以搭建任意形状的角色
在碰撞(重合)检测中,用循环把角色的每一个roleblock都检测一遍就好了
2024/1/30 1:01
目前初步修改了代码,但是带来了很多的问题
经过调试,发现问题的主要原因是用循环按顺序去判断/更新/渲染roleblock时,由于不能同步进行,会导致后来遍历的roleblock在判断完毕,改变isAirborne等属性后,直接进行更新。
修改了一下,把update中的判断和位置更新分成了两个循环,这样就会在全都判断完成之后再按照最后的属性进行更新
但这样就导致update函数和加入roleblock之前无异,最终还是按照其中的某一个block去更新
注意:而且除此之外仍会有渲染bug
解决办法应该似乎只有给每个roleblock都添加属性值,然后每次判断都只改变特定的roleblock的属性值,再整体更新
或者直接完全重写update逻辑,适应这种架构
比如找到角色的最上最下最左最右四个roleblock,然后通过判断他们去获得属性值的最终结果
2024/1/30 11:15
👆但是这样就失去了“判断角色的每一个roleblock”的效果,如果是一个不规则形状,那么就会出现问题
有办法了:不去判断角色中的所有roleblock,也不去单纯地判断最上最下最左最右四个值,而是判断所有作为边框的roleblock
区分边框的roleblock的方法有很多,比如手动添加一个标记,或者用pos去判断
不仅要区分边框,还要区分它代表哪个边(上下左右)
可以在最外层先去判断它的(上、下、左、右)位置是否为自己,如果是,则跳过,如果不是,则去判断并修改相应的(上、下、左、右)碰撞状态
主要修改的地方是把每一个roleblock的判断都添加到角色状态中,而非单纯地使用最后一个roleblock的判断
注意整体与个体的界限,哪些属性值应该放在整体里,哪些属性值应该放在个体里
2024/1/31 13:20
今天开始着手Player::update的重写
目前思路:
首先把Player::isTopReached改为了Player::canJump,并且把原先的true和false反过来,这样清晰一点
然后,先把move()中的x轴和y轴的判定分开,再分别在x轴和y轴判定中确定具体方向(上、下、左、右),并给每个方向添加一个此方向上的下一个是否是自己的判断
如果是自己,则直接跳过判断,如果不是,则进行判断,并修改属性值
2024/2/1 0:38
目前存在非常非常多的问题
首先,update中的move部分的逻辑虽然看似正常运行,但是逻辑极其混乱,对跳跃和下落的判断仍然存在问题,而且有时候会出现“吃墙”的情况
并且其中的dash还没有做任何适应性修改
其次,位置更新部分也存在问题:循环中后渲染的部分的preblock会覆盖掉先渲染部分的 P 为 .
目前对于这个问题,有三种思路:
- 1、对于先渲染的部分,当把它渲染到下一个位置的时候,发现下一个位置也是自己时,做出标记。(但是这个标记必须只对运动趋势上的下一个位置生效)
- 2、思考算法:根据运动方向,把所有roleblock按照从 运动方向的反方向 到 运动方向 的顺序排序,然后依次进行渲染,这样就可以防止出现覆盖问题(不过修改起来十分麻烦)
- 3、对于先渲染的部分,当把它渲染到下一个位置的时候,发现下一个位置也是自己时,想办法把下一个位置的preblock改为P即可,但是这样的话需要修改除了自己以外的其他roleblock,就得放弃lambda表达式的使用
肯定还有很多其他问题,比如当角色一个方向上的roleblock在三个或三个以上时,或者角色的形状不单纯是一个矩形时,还没有进行测试
归根结底,这些问题全都是“roleblock不能同时进行判断,只能进行顺序判断”所引起的检测和渲染冲突
2024/2/2 14:59
https://blog.n0p3.cn/2020/04/05/InfantGameEngine/
👆找遍全网终于找到一个和我遇到差不多问题的人(渲染部分)
搞两个大小为player_blocksize的canJump和canFall的数组,记录每一个roleblock的状态
👆略作修改,把canJump和canFall全部放到Roleblock类里
2024/2/2 18:45
初步修改后,代码似乎正常运行,只是还存在如下问题:
- 1、当角色跳跃完,在落地之前,此时canJump == 0;若此时按住←或→,则canJump不会被置为1,除非松开←和→
- 2、有时左右移动会出现roleblock混乱的情况
2024/2/7 18:45
最近过年比较忙,暂时搁置
2024/2/11 15:06
bug:卡墙角、吃墙、两侧roleblock下坠判断过于提前导致左右手下方有flag的时候直接结束
2024/3/11 13:27
最近琐事繁多,生活也步入正轨,以后有时间再搞吧。
下面记录几个暂时注释的地方和问题:
- 1、player.cpp:39暂时注释(敌人碰撞判断)
- √2、冲刺后的颜色更新问题
(2024/6/13 17:24)
(问题主要出现在player.cpp中102行update中循环调用dash的时候,在第一次调用时就把dash_cooling_time置为非0。
但奇怪的是,roleblock的颜色没有变,位置却正常在dash)
解决方法1:把dash_cooling_time != 0 单独拉出来专门给颜色改变做判断,放到dash函数的最后
解决方法2:把对isdash和dash_cooling_time==0的判断拿出来放在update中循环调用dash之前,这样只判断一次,就直接渲染所有roleblock
目前暂时使用解决方法1 - 3、以前说过的很多问题
2024/3/28 15:32
- 1、player.cpp:136
- 2、角色类role的抽象,但是要解决不同角色的形状、block数量等不同的问题(可用动态数组等解决)
2024/6/13 16:44
main函数中的一些初始化操作可以集成为init函数