LCUI 0.15.0 开发日志

发表于2013年06月14日

2013-6-14

为照片查看器新建了个Win32工程,测试编译时,报错说未找到FreeType的头文件。话说,最近发布的LCUI二进制文件包中没有FreeType的头文件,没法用LCUI的头文件。解决方法的话,可以下载LCUI的源码包,把里面的FreeType库的头文件拷出来即可。

头文件的话,感觉还需要进行修改。

修改了LCUI.h,主要是修改里面的WinMain函数的定义,考虑到文件路径会带中文,为保证照片查看器能够获取到正确的文件路径,需要让WinMain函数能够处理宽字符版的字符串参数。

照片查看器需要遍历目录里的文件,并获取图片文件列表,而现在的代码,是针对linux环境的文件目录的操作,为了让照片查看器跨平台,需要对linux和windows下的文件目录操作函数进行封装。

封装好后进行测试,出现问题,无法获得完整文件列表,经过与win32目录操作示例代码进行对比后,发现问题原因是传给FindFirstFile函数的文件路径需要有通配符(例如: .*),不然该函数找不到文件。

打算在完成win32版的照片查看器后,准备做个游戏。在2011年的暑假,花了一些时间在做游戏,但未完成,那时的游戏架构设计还不成熟,代码凌乱不堪,可读性较差,考虑到的问题也不周全。现在,想完成之前未完成的任务,在完成后,打算分享自己写的游戏。除了源代码外,如果能完成的话,附上游戏设计原理,也就和LCUI的设计原理文档类似,简单的说明即可。

游戏功能需求及个人想法如下:

  1. 游戏属于横版格斗类。
  2. 后期的游戏角色,能够在不修改游戏源代码的情况下,被添加进游戏。思路:可将游戏角色的数据搞成动态库,让游戏扫描目录里的动态库,调用库里的相应函数,以应用新角色。
  3. 支持人机对战、双机对战、局域网双人对战,看来,游戏角色的控制模块代码需要好好设计;反正本学期的网络编程没怎么学,通过实现局域网双人对战这个功能,可以补充一些知识。
  4. 游戏中用到的图形素材来自互联网,为避免侵权问题,暂时决定不将素材与游戏源码一同公开,素材需整理进一个文件里,而网上公开的游戏源码,里面用简单的图片代替原来的图形素材,比如:只有一个游戏角色,角色的图形是个方块。

上述想法和游戏需求,在后面实际开发中会有变更。

写了个测试程序,用于将含中文的字符串写进文本文件里,源文件使用UTF-8编码,并带BOM信息。运行测试程序后,生成的文本文件中的文本内容是ASCII编码,中文能显示。看来,对于VS2012的编译器,字符串的编码不受源文件的编码方式影响。

将一张图片转换成代码,源文件1MB多,用VS2012编译,居然编译不出来,等了几分钟,编译器都没编译完,编译器还占了200MB左右的内存,把图像数组转为静态全局变量,再进行编译,几秒就完成了。

2013-6-18

内存模块中,选用红黑树作为记录内存块信息的数据结构,看了一些相关文章,感觉还是无法理解,有些文章写那么多,没有哪个内容是说结点被染成红色或者黑色需要满足什么条件。

据说linux的源代码中有红黑树的实现代码,于是到github上去看,在头文件中发现,它的红黑树结点的结构体中居然没有用于存储数据的成员变量,数据与结构完全分离?

红黑树中,主要难的就是保持树的平衡,以及满足红黑树的那几个特征。如果直接用二叉树的话,当结点多的时候,就会出现问题:有的结点到根结点的距离很长,而有的结点到根结点的距离又很短。这就会导致有时耗时很多,有时耗时很少,耗时量取决于遍历的结点数。而红黑树,在插入、删除等操作时,会进行调整,以保持红黑树的性质。

内存块的类别需要一个ID,该ID必须唯一,且与类别名对应,不同类别名有不同的ID。找到了这篇文章:http://www.byvoid.com/blog/string-hash-compare/,决定用BKDRHash函数来为字符串生成一个ID。

内存管理的代码算是大致完成了,主要代码修改自:http://blog.csdn.net/v_JULY_v/article/details/6114226,要完全靠自己写出来的话,还有点难度,必须对红黑树的原理及实现有一定程度的了解才能写出来。

写了个测试程序,用于分配N次内存空间,把分配次数调整至400多万次,一运行程序就报错,进行调试时,VS2012给出的提示是栈溢出。

呃,忘记局部变量是存在栈上的了,局部变量占用的内存空间不能太大,否则会导致栈溢出。

改用了动态内存,又进行测试,结果让整个系统卡住了,等了几分钟,无奈之下强制关机。

将分配次数调整至1600000次,运行测试程序,结果如下:

进行1600000次内存分配,每次分配128个字节的内存,共耗时3.01秒,把这些内存全部释放,耗时2.606秒;而用malloc和free函数直接进行同样次数的内存分配和内存释放,耗时分别为:0.951秒、0.936秒。

内存管理模块的代码已经推送至github,还有部分功能未添加。

2013-6-20

修改LCUI_Graph结构体,改用一维指针记录像素数据的地址,这也就意味着,RGBA这三个色彩值是混合存储的,而不是之前那样用单独的内存空间保存单一通道的色彩值。既然数据结构做了修改,图形处理代码也就需要进行修改。

图片文件的像素数据的载入功能代码,也需要进行修改,好麻烦。

linux下的帧缓冲、win32上的BITMAP的像素数据是按BGRA顺序存储的,bmp文件的是BGR,而jpeg和png的是RGBA。

LCUI_Graph的像素数据该按什么顺序存储?名字叫RGB,存的时候却不是按RGB顺序存,而是翻转的。经过考虑,决定以BGRA顺序存储像素数据。

修改量较大,图形绘制代码都要修改,还是算了。只是删除了LCUI_Graph结构体的部分成员变量,顺便删减了LCUI_Graph.c里面的部分函数,调整了部分代码。

2013-6-21

纠正了Graph_Mix和Graph_Replace函数存在的一些问题。

游戏已进入开发阶段,先从启动动画开始。

游戏运行后,先显示LOGO,然后显示载入动画,动画播放过程中,在另一个线程上载入游戏资源,载入完后,结束动画的播放。

程序已经完成,程序样品:lcui-game-dev-01.zip,可下载它来体验效果。

2013-6-22

准备做游戏菜单,正纠结菜单及界面的设计,看来需要找些游戏来做参考。

用定时器实现部件的闪烁,只能通过全局变量的方式让定时器响应函数操作部件。觉得定时器的响应函数需要一个参数。

游戏菜单中的选择框靠按键来移动,进入游戏后,需要控制游戏,这时,需要将之前关联的按键事件取消掉,再为游戏重新关联按键事件。但目前没有提供相应功能的函数,需要添加一个。

主菜单已经完成,效果和以前做的那个游戏大致一样,光标有闪烁效果。

程序样品:lcui-game-dev-02.zip

2013-6-23

需要思考角色控制模块的具体实现思路,由于需要实现局域网对战,因此,两台计算机之间的角色控制就要保持同步。例如:A要移动位置,并切换为行走动画,就需要向B提交信息,等B确认收到后,A就应用角色移动和角色动画的切换。

2013-6-24

决定采用消息来实现。

用UDP广播房间信息,这样局域网中的其它计算机就能看到已创建的房间了。房间信息包括:房间名、创建者的IP,加入者的IP。

2013-6-25

现在纠结的是,若有N台计算机创建了房间,那么,其它计算机获得这N个房间信息需要耗时多久?主机广播房间信息的频率要多少?

用个线程,专门接收其它主机广播的房间信息,并生成列表。但是,怎么知道其它主机退出(删除)了房间?难道是要等一段时间,若没再次接收到该主机广播的房间信息,就判定该房间已经不存在,并从列表中删除该房间的记录?

已经完成了房间的创建与列表显示功能,还只是个控制台版本。接收端的房间信息列表是动态更新的,已设置了时限,超过一段时间没收到房间信息后,就会更新房间列表里的记录,让所有房间的“未存在次数”加一,当“未存在次数”的值大于一定值时,就会删除该房间记录,因为它可能不存在了。当接收端接收到主机广播的房间信息时,会在列表中查询该房间,若房间记录存在,则重置“未存在次数”为0,不存在则添加新记录。

2013-6-27

MessageBox的按钮上的文字是乱码的,问题原因是MessageBox的源文件messagebox.c是UTF-8编码的,当没有BOM信息,VS2012把源文件内容当成GB2312编码的,导致转换成宽字符时未转换正确,已经加上了BOM信息。

房间信息列表显示需要一个列表框部件,要是专为游戏做个列表框,以后游戏界面要改动,列表框的风格及效果也要改动,感觉有点麻烦。

2013-6-29

局域网对战功能可以在以后添加,目前主要是实现游戏角色的显示和控制。

角色动画可以用ActiveBox部件实现,ActiveBox部件目前只能绑定一个动画进行播放,需要让它支持多动画,并能切换播放。

在控制角色改变动作时,会打断当前角色动画,并切换至与新动作对应的角色动画,由于动画帧的处理问题,动画的切换需要等当前帧更新完后才能完成,因为动画的下帧更新的等待时间是预先计算好的,并用定时器等待这段时间后再进行动画帧的更新。需要一个函数,能够立刻切换动画,并更新当前帧的图形。

由于某些动作需要在指定帧动作上才会触发事件(例如:在角色挥出刀的时候才会触发攻击),因此,需要有个函数能够获取角色当前帧动作的序号。

2013-6-30

正纠结角色资源该如何处理。角色资源编译进动态库,游戏启动时扫描动态库,获取指定函数的函数指针,调用它来获取角色信息。但如何载入角色动画?需要指定一个函数,通过调用该函数并传递相应参数以获取相应的角色动作动画,然后添加至玩家角色对应的ActiveBox部件,供它播放。

2013-7-1

ActiveBox部件的修改已经基本完成。

角色在不同的动作中,脚底都必须是踩在同一直线上,而身体中心也要在同一位置。为了达到这一要求,除了在处理角色每帧图形时进行手动调整外,动画的位置也需要调整。从源游戏图形素材中提取出来的角色动作图,并没有该动作动画的全局尺寸,以及每帧的位置,需要自己在用PS处理时,调整每帧动作图的坐标,再根据所有帧动作图的坐标及尺寸,得出整个动画的尺寸,也就是能够完全显示每一帧动作图的最佳尺寸。最后将它们一帧一帧的另存为png图片。

游戏在用的时候,不同动作动画的尺寸也不同,人物的脚底并要不一定踩在图像底边上,例如下图:

旋转的刀刃已经超出脚底直线,针对这种情况,需要为动画设定底线Y轴坐标以及中心点的X轴坐标,让ActiveBox部件根据这个坐标,调整动画的显示位置。显示位置是调整了,但会超出ActiveBox部件的容器范围,部分区域的图像无法显示,调整尺寸可以让图像完全显示,但是怎么获取角色的坐标?

与其在ActiveBox部件上纠结该如何修改功能以适应游戏需求,不如直接写个专门用于游戏角色动画播放的部件,包含ActiveBox部件的功能,并提供适用于游戏的一些功能,ActiveBox部件还是保持它的简单功能为好。

2013-7-2

AnimationData结构体中不应该记录动画的状态和当前的帧,假设两个ActiveBox部件共用一个动画的话,那会有冲突的,这两个信息应该由动画帧更新处理功能去记录。

一个动画只能在一个ActiveBox部件中存在一个动画播放实例。

这样的话,动画帧缓冲、回调函数也需要从AnimationData结构体中移出去。

已经完成对ActiveBox部件的改动。

2013-7-4

用于显示游戏对象动画的部件的名字暂定为GameObject。

用PS做了个LCUI的LOGO,主要用于在游戏启动时显示,效果如下图所示:

但感觉有点土,没有清新和活力的感觉,有待继续编辑。

GameObject部件在游戏对象切换至新动作时,会计算该动作所需的合适容器大小、中心点,如下图所示:

每帧动作的图像尺寸和对象中心点可能会不同,需要为该动作计算出容器的全局尺寸和中点坐标。

GameObject部件的坐标,并不是游戏对象的坐标,假设游戏对象是人,一般人的脚底踩的就是对象的底线,那么人的坐标则是他脚踩的位置。

添加了GameObject部件,已经实现了基本功能,能够播放角色动画,还需继续完善。

2013-7-5

当前对象的动作动画还是用ID标识好些,添加动作动画的时候给定它的ID,在切换动作动画时传个ID就行了,不用传个动作动画的指针。

切换动作动画的时候,还要将当前播放至的帧号重置为零,以保证下次播放该动画时还是从头开始播放,而不是继续未播放完的动作动画。

在切换动作动画后,需要标记数据为无效,让GameObject部件在更新时,重新根据当前播放的动作计算出合适的尺寸及底线中点的坐标。

GameObject部件的动作动画切换功能已经完善,可能存在延迟,因为只是在改当前播放的帧号,更新动画帧的函数要在等待响应定时器后才进行更新。

接下来就是响应按键控制,让游戏对象切换动作动画,并能通过方向键移动游戏对象。

有个想法:添加一个出招表功能,将单一或组合按键与一个招式关联,这样,在处理按键输入时,游戏就会查出招表,并根据之前按的键,将游戏对象切换至相应动作。

试玩了 死神VS火影 1.6版,感觉不是很好,至少可玩性比 拳皇wing 1.85 差点。游戏我想搞成 热血格斗 这样,但网上找不到 热血格斗 的游戏素材,这很让人费解。

当按下某个键后,与该键对应的动作动画开始播放,当按键释放时,若该动作还未完成,需要等该动作完成后才进行动作切换。为解决这一问题,需要为“动作”添加一个属性,用于表示该动作是否能够被打断,能被打断就直接切换动作。

看来还需要添加一个机制,直接通过响应按键来直接控制角色移动和动作切换有些不妥。

有了个想法,游戏主要工作流程改为:按键处理-》动作处理-》消息处理-》角色控制。当按住某键后,游戏响应按键事件,然后提交给动作处理机制,让它处理什么时候切换动作,什么时候移动游戏对象的坐标,之后转为消息,让游戏在消息循环里处理消息,最后就是根据消息内容进行角色控制。

项目主页所在LCUI的gh-pages分支应该独立出来,作为一个单独的项目,这样在clone LCUI时,没了gh-pages分支,不用再把项目主页的文件下载下来,省了很多时间。

2013-7-6

哦对了,热血格斗 的人物动作动画素材,可以靠截图来提取,打开NES模拟器,运行游戏,然后截图,就这样,还好游戏的人物动作帧数不多,提取出来再去除背景,用不了多少时间。

做成 热血格斗 这样的游戏的话,角色除了左右移动外还要能上下移动,那么就需要添加一个属性,用于记录Z轴坐标,X轴坐标代表人物的左右位置,Y轴坐标代表人物的高度,Z轴坐标代表人物的前后位置。

哪个键是控制哪个玩家?需要添加一个函数,用于根据按键键值,获取由该按键控制的角色。

局域网对战中,用ID来标识各位玩家控制的角色,为消息添加一个成员变量,用于记录该消息针对的玩家的ID,这样,在解析消息时,就知道要控制哪个玩家了。

角色在切换为行走动作后,应该由一个线程来更新该角色的坐标,如果是在响应按键时直接改坐标,那么,角色移动不会流畅,会时断时续,因为按住按键后,不会连续收到按键消息,会有时间间隔的;角色在行走时的坐标更新需要有时间间隔,移动距离由该角色的相关属性决定。

在局域网对战中,部分角色是由其他玩家控制的,因此,游戏应该只控制当前计算机上的角色,而其他角色,则由其他玩家的计算机控制时,把处理后的数据以消息形式发送过来,由游戏进行处理。

关于角色的按键控制,并不是只响应按键的按下和释放就行了,需要让游戏根据当前记录的各个按键的状态,来控制游戏中的角色。例如:先按住A键,角色向左方移动;然后再按住D键,角色向右方移动;之后,又按住W键,角色向右上方移动;释放D键后,角色向左上方移动;释放A键后,角色向上方移动;释放W键后,角色停止移动。

按键释放后,按键的记录有时会无法成功删除,现已修复。

2013-7-7

重新整理一下游戏设计思路:
游戏分为三个子系统:

FC 版的 热血格斗 游戏中的战斗场景,有个是在冷冻库,由于站在冰面上,摩擦力减少,玩家会受惯性的影响,会滑行一段距离。有个是在仓库里,玩家角色停站在传送带上会被传送带朝滚动方向移动,若逆着传送带滚动的方向奔跑,移动速度会被传送带抵消一部分。还有个场景里有水流,玩家会被水推着移动,顺水移动和逆水移动的速度不一样。

战斗场景有地形,如何让玩家角色能够站在不同地形上?看来是物理系统的工作。但目前还是以平地作为场景,这些有地形的场景等以后完善了物理系统再添加。

物理系统,先实现匀速直线运动,到时候再实现抛物线运动,比如人物的跳跃、击飞,运动轨迹就是抛物线的。

已经添加物理系统,还只有简单的功能,角色控制代码做了些修改。目前可用WSAD这四个键控制角色移动,按住后,游戏会将移动速度赋给物理对象,并让物理系统的线程去更新物理对象的坐标,而游戏也会定时获取物理对象的坐标,并更新屏幕上显示的游戏角色的坐标。松开按键后,速度就会消失,坐标停止变化。

程序样品:游戏角色控制-01.zip,以下是测试程序的效果:

2013-7-8

动画每帧的停留时间为20的倍数毫秒,动画更新机制可以更改为每隔20毫秒更新各个动画的当前帧的剩余停留时间。每秒50帧就够了。

GameObject部件目前只会播放动画,与名字不符,那么,可以将物理系统整合进来,线程就取消了,由游戏自己调用函数来更新对象的物理属性。

已经完成调整。

在切换动作动画时,GameObject部件的位置和图形内容更新不及时,估计是部件消息处理有问题,在更新当前帧屏幕内容时没有完全处理完消息队列里的部件消息。

已经解决此问题

2013-7-10

假设要人物朝右方奔跑,那就按两下D键以切换至奔跑状态;处于奔跑状态后,可以不用通过按住D键以维持奔跑状态;奔跑状态中,可以按W或S键控制人物向右上、右下方奔跑。若要停止奔跑,则需按A键,朝与奔跑方向相反的左边移动,就会停止奔跑。原版游戏中,停止奔跑后还会减速并滑行一小段距离,不能立刻站住,这时不能进行其它动作。

修改了keyboard.c,添加LCUIKey_IsDoubleHit函数,用于检测指定键值的按键是否按了两次,为了实现这个功能,用于记录按键状态的数据结构也做了修改。

上述效果,除了奔跑停止后的减速效果,其余的都已经实现。奔跑停止后的减速效果该用什么来实现?目前纠结的就是角色的控制问题,按什么键,切换至什么动作,有些动作可以被打断,而有些则不能,在某些动作进行时,其它部分动作就不能进行。

LCUI_KEYDOWN事件改为在按键按下时触发,一直处于按住状态的按键不会重复触发LCUI_KEYDOWN事件。

调整了角色控制代码

奔跑停止后的减速效果已经实现,在停止奔跑时,设置加速度和回调函数,游戏在循环中更新玩家数据时,会检测对象在匀变移动中,速度是否到了0,当速度达到或近似于0后,就调用预先设置好的回调函数,以改变对象的状态和动作动画。

程序样品:游戏角色控制-02.zip,换了人物动作图。WASD是方向控制键(这个应该不用提醒了)。

移动速度、加速度和当前坐标需要用double类型的。

2013-7-11

有的时候需要在特定条件下进行响应,比如:在动作动画播放结束时、在角色落地时。先考虑前者,怎么在当前动作结束时及时响应并切换至其它动作?

动作动画的更新机制,需要改用GameObject部件作为更新处理的对象,以方便实现上述功能,之前用动作动画作为对象的方法显然是难以实现的。

已经完成修改,添加了一个函数,用于设置回调函数以在当前动作结束时调用。

添加了 READY 动作,当维持该动作一段时间后,就会恢复成STANCE动作(站立)。

添加了 A_ATTACK 和 B_ATTACK 动作,通过按J、K键可使用这两个动作。

程序样品:游戏角色控制-03.zip

2013-7-12

模拟器没帧率的设置功能,那只好用变速齿轮来调整程序运行速度了;经测试可行,模拟器的游戏画面帧率可设置成2帧/秒,y要捕获动作图还是有点困难。

添加了冲刺+A攻击、冲刺+B攻击。

需要添加角色动作动画的水平翻转功能,向右移动时使用原动作图,而向左移动时,设置需要水平翻转,在更新、绘制GameObject部件时,就会调整部件坐标、当前帧动作图坐标,并将进行翻转后的动作图绘制到部件上。

接下来将添加跳跃、跳跃+A攻击、跳跃+B攻击、冲刺+跳跃+A攻击、冲刺+跳跃+B攻击。

2013-7-13

上述动作已经添加,但有许多细节需要完善,动作控制也需要改进。

整理一下需求:

需要显示游戏角色的影子,假定光照是与游戏角色垂直,在角色的脚底显示一个黑色椭圆形的影子就行了。

程序样品:游戏角色控制-04.zip,效果图:

刚刚测试游戏,感觉游戏刷新效率变慢了,在GameObject部件动画更新函数里,加了点代码,用于打印每次它响应定时器时的时间间隔,测试结果表明,时间间隔为37ms至47ms。怀疑是timer的问题,在定时器线程里加了同样的代码,每次处理定时器时的时间间隔也是如此。之后,我直接在定时器线程进行睡眠处的前后加上代码,统计睡眠时间是否准确;测试后发现,理论上是睡眠20ms,实际上却和上面的结果一样,难道是延时函数不准确?

修改了定时器的代码,在更新各个定时器的剩余等待时间时,改为减去实际流失的时间,而不是定时器记录的等待时间。

剩下的动作,部分是被动的,不能靠主动通过按键来触发,需要添加一个角色来当靶子打。

需要引入攻击框和被攻击框的概念,攻击框是什么?就是一个框框,框内的区域表示具有攻击性的,而被攻击框与之相反。当角色A的攻击框与角色B的被攻击框重叠时,则判定角色B受到了角色A的攻击。这两个框由每帧动作图记录。

还需要限制游戏角色所在的空间的范围。

2013-7-14

奔跑后进行跳跃,感觉跳跃到落地的过程太快了,运动轨迹曲度较大,需要调整计算一下公式。

添加第二个角色时,遇到点问题,一个角色动作动画正常,而另一个角色的动作动画只显示一帧。

角色动作动画播放的问题已经解决,问题原因是每次更新只更新一个GameObject的动作帧,而每次又间隔20ms,再加上排序不是按剩余时间从小到大的顺序排序,导致每次都是更新同一个GameObject的动作帧。

2013-7-15

攻击框、被攻击框的坐标是相对于谁呢?角色中心点?当前帧图像的中心点?还是当前帧图像的左上角点?

若角色的动作动画已标记为需水平翻转,那么在判断角色当前帧动作的攻击是否命中时,攻击框和被攻击框的数据在处理前也要做翻转处理,以保证正确的判断结果。

由于此游戏使用的是XYZ坐标系,因此,攻击框和被攻击框需要指定X、Z、Y轴的坐标,以及X、Y、Z轴的范围,当攻击框的Y轴范围设定为0时,那就使用默认的范围。

目前,游戏转换XYZ坐标为XY坐标时用的公式为:x = x',y = y' + z'。

决定让攻击框和被攻击框的XYZ轴坐标以游戏角色的底线中点为原点。

2013-7-16

攻击框和被攻击框以及相关处理代码已经添加,现在,能够检测出攻击者与受攻击者。

为避免同一次攻击动作让同一角色受到多次攻击,需要做些处理。

浏览了MiniGUI和GTK的主页,对项目主页做了些修改。

话说,怎么画架构图?GTK主页上的GTK架构图是描述用了哪些函数库,底层是Cairo和GLib,往上就是Pango、GDK、ATK和GIO,再往上就是GTK+。而MiniGUI的架构图却描述的比较详细,底层是操作系统、设备驱动和C标准库,往上是图形抽象层和输入抽象层,再往上是图形设备接口、消息模块、控件、窗口模块等。

为每个对象设置一个队列,用于存储受到它攻击的其它对象,当对象发起攻击时,就检测受攻击者,若受攻击者在队列中没有记录,则记录它,并向该受攻击者发送受攻击信号,否则不发送受攻击信号;当对象结束本次攻击时,清空队列中的受攻击者记录。

但是,如何得知当前攻击框是同一次攻击动作发出的?如果一个攻击动作要触发多次攻击怎么办?

搞个设定:一个动作最多只能有一次攻击,若想让一个动作能触发多次攻击,那就将动作分解掉。

何时清空受击者记录?考虑到攻击动作会被打断,如果让GameObject模块自动清空记录,比较难实现。那添加一个函数,用于清空受击者记录,由自己在切换攻击动作时手动调用,这样就好办了。

在响应受攻击信号时,需要知道攻击者和攻击类型,以方便计算自己受到的伤害值,那么就需要第二个参数,攻击类型可以通过获取攻击者当前的动作ID来确定,这样就可以省掉一个参数,但感觉有点不好,如果在响应攻击时,攻击者的攻击动作结束了,那怎么办?好吧,为GameObject再添加一个成员变量,用于记录攻击者及攻击类型,这样就不用添加第二个参数了。

已经完善游戏角色的攻击处理添加了攻击响应代码,接下来需要为每个动作的每一帧动作图添加攻击框和被攻击框,还需要添加新动作,以响应不同的攻击。

2013-7-17

为动作添加攻击框和被攻击框,测试时没触发攻击效果,后来才发现,是动作ID与动作动画不一致,载入时载入了错误的动作动画。

奔跑后进行跳跃,使用A攻击,用的不是ASJ_ATTACK动作,而是AJ_ATTACK动作,这是因为没有识别出跳跃动作是在奔跑后切换的,需要再加个STATE_SJUMP状态,以标识当前的跳跃动作是奔跑后切换的。

2013-7-18

加个成员变量,用于记录短时间内被攻击的次数,被攻击三次后,游戏角色切换为歇息动作,在该动作结束后,次数重置为0。若被攻击的次数小于三次,若一段时间后还未受到攻击,则次数重置为0。被攻击一次就启动定时器,一段时间后重置受攻击次数,若这段时间内又受到攻击,则重置定时器的等待时间。

LCUI的定时器模块需要修改,假设我先设定一个定时2秒的定时器,然后再设置一个定时20毫秒的定时器,结果,由于定时处理线程正处于2秒的睡眠中,无法处理后来的定时20毫秒定时器,导致后来的定时器也一起等了2秒。

我需要这样一个函数:用于阻塞等待接收信号,可设置最长等待时间,只有在收到信号时或者超过最长等待时间时才会停止阻塞,并让函数返回。这个函数和select函数类似。

加个全局变量,初始值为FALSE,用个循环,在循环里面判断这个全局变量,然后睡眠1毫秒,并更新剩余睡眠时间。当全局变量为TRUE时,或者剩余睡眠时间小于或等于0时,退出循环,函数返回。这样就实现了。

修改了定时器模块代码,上述功能已经实现。

部分动作的攻击框/受攻击框已经添加,目前游戏可以对基本的攻击与被攻击进行处理,程序样品:游戏角色的攻击与被攻击-01.zip,以下是测试程序的效果:

2013-7-19

终结一击必须要在对方处于歇息状态时才能发动,并且对方在自己的攻击范围内。处理的时候,先获取处于歇息状态、且受攻击框离自己最近的游戏角色,然后判断该角色的受攻击框是否与自己的攻击框相交,相交的话,当前的攻击就会变为终结一击。

需要有个定时器,让一个动作显示一段时间后,执行回调函数,切换成其它动作。

添加了击飞效果,共有三种:撞飞、重击飞和轻击飞。撞飞效果由 冲刺+攻击 触发;重击飞 由杀伤力较大的攻击动作(如:必杀技)触发,击飞距离最远;轻击飞则由 冲刺+跳跃+攻击 直接触发,也可以在对方处于歇息状态时,由 跳跃+攻击 触发。

程序样品:游戏角色的攻击与被攻击-02.zip,效果如下图所示:

若将第二个玩家打出至窗口范围外,可用方向键控制第二个玩家的移动。由于还未添加躺、趴的动作,游戏角色被击飞后,会立刻站起,而不是躺在地上。

FC原版 热血格斗 游戏,用的角色动作动画只有4种,因为游戏中有4种职业,同职业的人物动作都一样,不同的只是人脸。

目前,这游戏是通过给定角色ID来载入动作动画的,到后面还需要调整一下,让游戏根据给定的职业ID和肖像ID载入相应的动作动画。

2013-07-20

添加了游戏角色的动作时限设置功能,但定时器存在问题,没有针对一次性定时器做处理,不管是设置定时器为可重复使用还是一次性使用,最终都会被当做可重复使用的定时器来处理。

两个角色处于READY状态1秒后,理论上应该是两个都切换为STANCE状态,但只有一个角色切换了。经过一番调试,发现是两个定时器任务在添加时,前者被后者覆盖,导致程序在主循环里只处理到一个定时器任务,也就只有一个角色被切换成STANCE状态。

解决方法就是修改任务添加模式,只覆盖函数地址相同且第一个参数地址相同的任务。

定时器列表是使用选择排序法进行排序的,感觉效率还可以提升,只需要在添加和重置定时器时进行排序,根据定时器的剩余时间,确定它在列表中的位置,这样就只需要遍历定时器列表一次。以后再打算优化。

添加了翻滚动作、躺、趴动作,角色被撞飞后落地时会翻滚一段距离,然后躺/趴下。对躺/趴下的角色进行攻击,也会有相应动作来响应。测试时对躺下的角色进行攻击,结果,角色无法站起,一直躺着。。。

起初以为是动作被覆盖的原因,可是调试后发现,连实现站起动作的函数都没调用,那么就是定时器的问题。最后找到问题原因:对躺下的角色进行普通攻击,在处理该角色的受攻击时,会重设定时器,导致之前设置站起的定时器被撤销掉了。

又重新加入了FPS计算功能。

躺/趴在地上的角色,可以被另一个角色举起,并能够投掷出去。

2013-7-21

游戏画面刷新速度有时会变慢,其原因是有时延时不准确。针对这个问题,我修改了一下LCUI的图形输出模块,最大帧数设置为100,平均每帧画面更新耗时10ms;为了让每秒更新的画面帧数接近最大帧数,每帧画面更新都会进行计时;如果当前帧画面更新耗时低于10ms,则通过睡眠来补成10ms;由于并不一定会睡10ms,因此也需要对睡眠进行计时,若多睡了几毫秒,则在下帧画面更新时减少睡眠时间。

以上方法只适用于每帧画面更新的耗时都少于10ms的情况,如果每帧画面更新耗时都超过10ms,那么就没有睡眠了。

经过测试,正常的时候,游戏的FPS值会稳定在90;而在延时误差较高的时候,游戏的FPS值会稳定在64。

自己写了个函数,用于实现毫秒级延时,也就是循环调用clock()函数,并计算流失的时间,当大于或等于要延时的时间后,退出,但用clock()函数对这个函数进行计时后,得出的实际睡眠时间还是超出了预定时间,本来是要睡5ms,结果却睡了16ms。

需要调整GameObject部件的更新处理,之前是每隔20ms更新一次,有的时候并不一定会是20ms,可能会是37ms或47ms,针对这种情况,那就不用定时器来实现了,改用线程,针对睡眠时间的变化来调整更新频率。

改用线程实现后,出现问题,按两下J键,进行攻击,结果,一直是攻击动作,无法停止。

问题原因是:在攻击动作结束时,会撤销响应动作结束信号,用定时器的时候,游戏对象的动作动画及坐标的更新,是在主循环里进行的,当前更新任务执行完后,会立刻执行下个任务,即撤销响应动作结束信号。用线程实现时,若在动作结束时又重新播放该动作,那么,会在动作播放过程中 撤销响应动作结束信号,导致该动作结束时没有做处理,也没切换动作,就这样一直循环播放。

解决方法:由于动作结束信号可由任意动作在结束时发送,因此,每次响应完都要撤销响应,以避免该动作的信号响应函数在其它动作结束时调用,那么,让每个动作都能有各自的响应函数,就不用再撤销响应了。

修改了加速度和移动速度的单位,之前是以每帧为单位,现在改为每秒。

2013-7-22

修改了每帧耗时的调整代码,之前的代码存在问题,FPS值在延时误差较低的时候是90,而在延时误差高的时候是64,现在,都能稳定在100帧左右。

有时角色被撞飞后落地,没有翻滚动作就直接躺在地上,将翻滚时间调成1秒,结果,也是这情况,可鞥是定时器的问题。

完善了定时器的处理。理论上,定时器线程会先获取睡眠时间最短的定时器,并根据该定时器的剩余睡眠时间进行睡眠,睡眠结束后,该定时器就需要响应。实际上,没有考虑到睡眠被打断的情况,被打断后,当前定时器的剩余睡眠时间并没有耗尽,却将它视为已到响应时间的定时器进行处理,因此定时器还没到预定的时间就响应了。

经过测试,问题依然存在,定时器还有其它问题。

已经找到问题原因:定时器线程在睡眠的时候,添加新定时器后会打断睡眠,之后,定时器线程会根据实际睡眠时间,更新定时器列表中各个定时器的剩余停留时间,而在这之前,新定时器可能已经存在于列表中,更新时会把新定时器的剩余等待时间减少,导致新定时器刚添加没多久就消耗完了等待时间。

解决方法就是用互斥锁,在定时器睡眠前锁上互斥锁,睡眠结束或被打断后,等更新完列表中的定时器再解开互斥锁。而定时器在添加时会锁上互斥锁,让它等定时器线程更新完列表中的定时器后,再加入新的定时器。已经完成代码的修改

2013-7-23

正构思新动作的实现方法,以下是几个游戏需求:

  1. 若站在躺在地上的游戏角色的头、脚两个部分的位置上,进行A或B攻击时会触发另一种攻击动作。
  2. 若站在躺在地上的游戏角色的身体中间部分的位置上,进行A或B攻击时则会将其举起。
  3. 若在躺在地上的游戏角色的身体中间部分向上或向下移动,则会坐在他身上。
  4. 当一个游戏角色处于举起状态下,若进行A或B攻击,则会将举起的游戏角色向前摔或投掷出去。
  5. 当一个游戏角色处于站立状态,另一个游戏角色可以直接跳到他的头上,让他举起。
  6. 在游戏角色处于歇息状态(喘气状态)时,可以抓住他,从正面和从背面抓住,使用A攻击或B攻击所触发的动作是不一样的,B攻击是推对方向前跑,A攻击是直接就地使用特有技能对其进行攻击。

2013-7-24

加了两个动作。

添加空间边界设置功能。

有个想法:不用物理系统战斗场景的地形效果,加个设定:每个战斗场景,都需要有个函数,用于调整游戏角色在该场景的数据,比如在斜坡上的移动、在不同高度的地形上移动、是否触发了陷阱等,都由该函数处理。那么,这个所谓的“物理系统”,就成了只实现坐标变化的东西了。

2013-7-25

不能根据攻击者的动作类型来判断攻击类型,应该加个成员变量,用于标识攻击者在攻击时使用的是什么类型攻击,因为有些攻击是混合了多种类型的动作的。

添加了三个特殊技能:高跳旋转落踢、爆裂腿(BombKick)和自旋击(SpinHit),后两个技能的发动控制代码还未完成。

由于没有确切的 局域网对战 以及 人工智能(AI) 的解决方案,因此,现在游戏架构还处于“能用”的状态,等以后要实现这两项时再调整游戏架构。

2013-7-26

自旋击 过程中,会触发多次攻击,分解这个动作的话,又感觉麻烦,因此,为动作帧数据加了个成员变量,用于指示该帧动作是否为新的攻击。

自旋击 在命中对方时,若左/右键处于按住状态,则会触发二段自旋击。

添加了三个特殊技能:

已处于击飞状态的角色,对齐使用带有击飞效果的技能,不再二次将其击飞。

这是本次的程序样品:游戏角色的攻击与被攻击-03.zip,测试效果动画如下:

2013-7-27

对于处于喘气状态下的角色,玩家可以移动至他面前,并抓住它,实现方法大致如下:
在玩家移动时,检查自身附近是否有处于喘气状态的角色,若有,则检测他的受攻击框是否与自己的受攻击框相交,相交的话,改变两者的动作,并记录对方,以便在产生伤害时知道是谁产生的,由谁承受,并让两者有相应动作。
当玩家抓住一个角色后,按A或B,会用技能对对方造成伤害,由于此类技能没有攻击框,只对被抓住的角色有效,因此,需要能够直接向被抓者发送伤害消息。
在技能执行过程中,能够被其它角色打断,一旦攻击者被攻击,该技能被打断,需要考虑到此情况,并做出相应处理。

对于躺地角色,玩家能够举起他,实现方法大致如下:
玩家需要站在躺地角色的中间部分,按A/B才能触发举起动作。举起前,记录对方,以便知道谁是举起者,谁是被举起者,在投掷时也能够知道谁被投掷出去。举起时,躺地角色的休息时限被重置,当被举起一段时间后,若还未被投掷出去,则会站起来。

2013-7-28

受到攻击时,需要停止移动。

当角色被抓住一段时间后,没干任何事情,就解除抓住状态,让被抓者继续处于喘气状态,并让他无法被攻击到,等一段时间后,恢复正常状态。

在GitCafe上弄了个项目主页,并绑定了lcui.org域名,现在,用lcui.org域名访问的是gitcafe上的项目主页。

纠结了一段时间,最终完成了正面压制技能(肘压),在技能结束时,偶尔会有一瞬间显示动作的第一帧,估计是动作完成后,下个动作切换不及时导致的,需要为动作加个属性,用于标识该动作在播放完时是该重复播放,还是停留在最后一帧。

有个想法:将一个角色的动作图全部保存在一个文件里,文件里记录各个图像文件的名称、属性、像素数据;用专门的函数,载入这个文件,然后用一个函数,根据文件名,引用已载入至内存中的图像数据。在需要释放资源时,用一个函数,释放已经载入的图像资源。这样就不用将动作图直接暴露在外面。

2013-7-29

在Kubuntu下试着编译LCUI,解决掉警告/错误后运行测试程序,结果,画面更新速度太慢。调试时发现,睡眠10毫秒后,计算出来的实际睡眠时间为0,睡眠前后clock()函数的返回值相同,这相差也太大了吧?搞得定时器也不能正常工作了。

完成了背面压制技能,不知叫什么名字,就是把擒住的角色举起,然后拉下来,并抬起腿,让对方背面撞到腿上。

背面擒住对方时,可以改变自己面向的方向,而正面擒住对方时不行。

完成了正面拉推和背面直推的技能动作,面对着被擒角色使用B攻击,会将他拉到自己身后并推他向前移动,移动过程中,若与其他角色碰撞,则双方都会被击飞。背对着时,是直接推他,不带击飞效果。

碰撞检测是新加的功能,其实也就是检测受攻击框是否重叠。顺便修改了攻击检测,之前的方法,一个角色只能响应到一个角色的攻击,不能响应多个角色对他的攻击。

当对方处于喘气状态,并且自身已经与对方处于接触状态时,若背对着对方移动,也会擒住对方。这个细节问题已经解决,之前是将自身整个区域考虑在内,现在,只将正面一块区域考虑在内,忽略背面区域,也就是背面接触了对方不会擒住对方。

2013-7-30

LCUI的LOGO可以先不考虑,但游戏开发者的LOGO可以尝试做一个。EA的LOGO在不同的游戏里大都有不同的风格,可以拿来参考。

起初也想像EA一样设计成带圆圈的LOGO,像这样:

但感觉我这游戏不适合这种风格的LOGO,后来看中了这个LOGO:

貌似是EA旗下的一个工作室的LOGO,感觉不错,参考这种风格做了个自己的LOGO:

有待在游戏中测试实际显示效果,若不行的话,再改改,反正这个LOGO就是用几个字加描边做出来的。

已实现对躺地角色进行举起动作,待继续完善。

已添加举起状态下的跳跃与下落动作

在地上时应该隐藏阴影,离开地面时才显示阴影。

已添加在举起状态下的行走、奔跑和跳跃动作,在奔跑时,按一下相反的方向键即可停止奔跑,停止过程中会减速至0为止。

已添加 往前抛 和 往下砸 的动作

2013-7-31

完善抛投动作,被抛出的角色能够击飞其他角色。

处于举起状态下,跳起时将举起的角色投掷出去,落地时,若投掷动作还未完成,则继续维持该动作直到结束后切换动作。若投掷动作在完成时自己还未落地,则继续维持该动作,直到落地时切换动作。

角色的投掷技能动作及动作效果已经完成,目前被举起的角色还不能随举起者一起移动,待完善。

添加了代码,在跳跃过程中,检测自己的脚部区域是否与对方的头部区域相交,若相交,则会在对方头上着陆,并让对方举起自己。

已经实现让被举起者的与举起者一起移动,调整了抛投/砸的参数,在进行抛投时的速度会影响对方在被抛出时的初速度。

添加了游戏图像资源管理模块,可以将资源库中的图像写入至文件,也可以从文件中将图像数据载入至资源库,还未测试,有待完善。

2013-8-1

完善了游戏图像资源管理模块,功能已经能够正常使用,那么,现有的图像文件就可以用资源文件代替了。

添加了个测试程序,用于测试图像资源文件的读写。

角色动作图改为从游戏图像资源库中载入

修改游戏启动画面,没有用到的代码已经删除。

已经知道 攻击结束后无法控制的问题原因了,是动作动画处理的问题。在这里,假设动作动画处理线程为A线程,事件处理线程为B线程;A线程在攻击动作结束时,调用回调函数,解除了动作锁;与此同时,在B线程上,针对按键事件,调用了相应函数更改玩家角色的动作,并设置动作锁;B线程执行完回调函数后,A线程还在执行回调函数里的代码,更改了动作,结束回调函数的调用。

这个问题以后再解决。

2013-8-2

添加骑乘攻击,通过按上/下键控制角色移动,若自己移动至躺地角色的中间位置,则会骑到对方身上,按A/B可进行攻击。当对方到了站起的时间,骑乘状态会被打断。

准备发布游戏演示程序,测试过程中,出现了游戏角色动作被卡住的问题,就是上面说过的问题。

加了几段代码,为动作的切换设置了条件,也就是当游戏角色为A动作时,就不能切换为B动作。这种解决方法,感觉治标不治本,等以后再从根本上解决该问题。

这是本次的程序样品:游戏演示程序-01.zip

2013-8-4

多机对战

多机对战功能里,可以不用显示局域网内广播的游戏房间列表,这样也就不用写列表框部件了。

玩家选择“加入游戏”,游戏就会随机选择一个已经存在的游戏房间,当局域网中没有已创建或有空位的房间,则会提示建议玩家选择“创建游戏”。

当一个游戏房间满了4个人,则会进入准备界面,该界面可以做成类似于 LOL 的,给60秒等待时间,在此时间内,玩家们需要选择角色、分配属性点、选择4个必杀技,就像在 LOL 中选择英雄、英雄皮肤、符文页以及召唤师技能一样。对战方式是2v2,两人一组,由游戏预先分组,同一组的玩家可以使用合体技。

60秒结束后,就进入载入界面,载入界面也可以做成 LOL 那样,显示角色图片、角色名、玩家昵称、角色必杀技以及进度条。

由于LCUI没有中文输入法,在windows平台下,也不能获取windows输入法输入的内容,因此,玩家昵称就由游戏在预置的N个昵称中随机选择。

4个必杀技可以由玩家选的话,感觉选择角色没什么用,就是换脸而已。那么,用原版游戏那样的方式,想自定义必杀技,玩家的游戏角色就用系统默认的角色;想自定义角色,那必杀技就不能自定义,不同的角色有不同的必杀技分配。

实现4台计算机间的游戏数据同步,大致同步就行了,在玩家改变状态、切换动作时,发消息给其它计算机,让玩家在其他玩家的计算机里的游戏数据得到改变。

那些带坐标变化的动作,就不用在玩家自己计算机上时刻将更新后的坐标传给其他玩家的计算机了。在其它时间里,定时将玩家的游戏角色的坐标、当前动作等数据与其他玩家同步,毕竟,在不同的计算机里,游戏里的演算速度、演算结果会有点误差,定时器的精确度问题也需要考虑到,例如:游戏每隔10ms更新一次游戏角色的数据,实际上,有的计算机会是20ms,而有的可能会变成36ms,也可能会变成47ms。

为了方便测试,需要能够开4个游戏进程,并且这4个游戏进程间能够通过局域网进行通信,以模拟多机对战。

人工智能

需要调整相关接口,以方便AI调用,但现在可以推迟AI的开发,不用急着搞。AI需要根据自己以及其他玩家的角色的数据,计算自己控制的角色下一步该干什么。原版游戏中,各个角色都有自己的性格,这些角色的AI都有自己偏爱的对战方式。

代码调整

怎么调整呢?需要纠结,虽然堆代码容易,但设计框架、解决方案 和 数据结构 需要费脑力。

2013-8-5

按住按键控制游戏角色移动,并一直移动鼠标,在松开按键后,游戏角色还在移动,直到停止移动鼠标后,它才会停止移动。估计是鼠标坐标变化过程中,一直在按键记录中获取鼠标键的记录,而获取前会锁上互斥锁,导致键盘按键记录更新一直被延迟。解决方法:分开记录鼠标与键盘的按键记录。

已经完成修改,删除了无用函数,调整了部分函数的参数列表,其它调用了 mouse.c 里的函数的源文件也做了修改。

2013-8-6

看了一下SDL在win32平台下的定时器源码(src/timer/win32/SDL_systimer.c),其中用到了GetTickCount、QueryPerformanceFrequency,QueryPerformanceCounter这三个函数,但有预编译条件,在定义了USE_GETTICKCOUNT宏的情况下,就用GetTickCount函数,否则用后面那两个函数。

SDL 在unix平台下的定时器源码(src/timer/win32/SDL_systimer.c),里面用到的是clock_gettime和gettimeofday函数,也有预编译条件,当HAVE_CLOCK_GETTIME宏为真时,就用clock_gettime函数,否则用gettimeofday函数。

看了这篇文章:http://www.cnblogs.com/krythur/archive/2013/02/25/2932647.html,据说clock函数在linux系统上变得没有意义,怪不得我之前在linux系统上的几毫秒睡眠的计时结果是0。

修复一个BUG,该BUG会使角色在骑乘跳跃落地时,若目标躺地角色正站起,则会使自己卡住。就加了一行代码,把对方记录清除掉,因为对方在站起时,会根据这记录干其它事情。

上次修改 mouse.c 的代码,没改全,无法正常处理鼠标按键的释放,现已纠正

显示MessageBox时会出问题,按钮上的“确定”字体位图还未显示就出现异常了。

用VS2012调试,VS2012提示说程序访问了0x00000000地址,而VS2012给出的信息表明是在调用FT_Load_Char函数时出现了错误,如下图所示:

这函数调用有点奇怪,同一个函数连续调用了多次,在递归吗?FreeType的函数没有函数原型和行号显示,需要重新编译一下,用Debug版的FreeType库。

2013-8-7

这个是完整的函数调用堆栈信息:

Get_NewFontBMP和Get_ExistFontBMP这两个函数的名字需要改一下。

在TT_Load_Glyph_Header函数中,传给该函数的TT_Loader类型变量loader,其成员变量TT_Loader指针为NULL,函数内的指针p的值是loader->cursor的值,后面又没对其有效性进行判断,导致程序因访问NULL地址而异常终止。

问题可能不是FreeType的问题,一般情况下loader->cursor的值会是有效的,可能是LCUI做了什么,才使loader->cursor的值为NULL的。

这是线程信息:

能够查看每个线程的函数调用堆栈,因此,可以辨认出这些线程是干什么的,从上往下,这些线程分别是:主线程(5252)、事件线程(5272)、定时器线程(5276)、输入设备的处理线程(5280)、GUI线程(5284) 和 游戏线程(5288)。

有个可疑点,MessageBox是在游戏线程上调用的,主循环由游戏线程来执行,LCUI_MainLoop_Run函数说明游戏线程在执行主循环,但为啥主线程却在调用TT_Load_Glyph_Header函数?

之前测试时,是在点击按钮后显示MessageBox的,在点击按钮后,回调函数及参数会作为任务添加至任务队列,主线程在主循环里从队列里取出任务,并执行,这时,由于LCUI_MessageBox函数是阻塞调用的,主循环会阻塞在这里,LCUI_MessageBox函数会再开一个主循环,以解除主循环的阻塞状态,避免GUI处于未响应状态。这些都是在主线程中执行的,没考虑到其它线程调用LCUI_MessageBox函数的情况,主要就是多线程执行主循环的问题。

线程函数中,想中途退出线程的话,调用LCUIThread_Exit函数后不会立刻退出线程,还得在它后面加个return。

修改了Get_NewFontBMP和Get_ExistFontBMP这两个函数的名字,bitmapfont.c里的其它函数也做了修改,顺便调整了fontlibrary.c和相关头文件的内容。

MessageBox的问题先放一边。准备添加战斗场景以及游戏角色的状态栏。

2013-8-8

分辨率调整至800x600,为状态栏腾出一点屏幕区域。

physics_system.c更名为game_space.c,里面的数据类型名也做了相应修改。

按键状态的更新速度还是会受到鼠标移动的影响,之前以为是因为两个共用一个按键状态记录的问题,但改了后问题还是存在,需要通过调试来验证问题原因。

已经添加战斗场景,目标角色在移动时,会通过移动镜头来保持目标角色处于镜头内,但感觉在镜头移动时有点晃眼。

镜头移动时,角色的影子的位置更新速度也变得缓慢,需要改善。

帧速的限制方法需要调整,之前只是考虑到耗时问题,前面几帧画面更新耗时多,则用减少后面几帧画面的停留时间,但存在一个问题:若前1秒的帧数只有30,那后1秒内,为弥补多耗的时间,会提高每帧画面的更新速度,导致这1秒内的帧数超过100帧的限制。

有时,将对方推走后,自己去碰对方,被对方撞倒后,就再也起不来了。对方在被自己撞飞后,落地时的翻滚动作有时会很长,目测都超过1秒了,而游戏里设定的是只让翻滚动作维持200毫秒。

2013-8-9

准备添加状态栏区域,里面用于显示各个游戏角色的状态信息,包括角色头像、角色名称、血条,源代码存放至game_statusbar.c。

先用进度条当血条。

2013-8-10

血条的样式准备弄成DNF样式,颜色有五种:红、橙、绿、蓝、紫,据说DNF的BOSS的血条可以有几百条,而这几百条血条的颜色,是这5种颜色的循环,剩下的最后一条是红色血条。

先弄简单版本,扣血时血条不带特效,带特效的血条,在扣血时,会高亮扣除掉的血条,然后逐渐变暗并缩短长度至0。

血条已经完成,该部件的代码在 game_lifebar.c 里。

2013-8-11

准备为攻击添加扣血的功能,需要考虑伤害的计算方法,以及角色属性对伤害值的影响。

感觉可以弄个库,用于记录各类型攻击的伤害计算方法,添加新攻击类型时,只需要注册一下用于计算伤害的函数即可。

拳击和脚踢的伤害值上限为200,防御力上限为500,防御力影响自己受到的实际伤害,其计算公式为:减免比例 = 1 - 100/(防御力+100) * 100%;伤害减免比例最大为95%,这个公式是用LOL的。角色在躺着的情况下,会增加30%的伤害减免,减少”躺着也中枪“时造成的伤害。

游戏中全部带攻击动作/技能,现在都能够对目标造成扣血效果了。负责计算攻击所受伤害的代码在 game_attack.c 源文件里。

2013-8-12

想修改定时器的睡眠代码,之前那种方法,是用循环+判断+睡眠1ms实现的,但现在才发现WaitForSingleObject()函数满足我的需求,用它可以实现睡眠,发个信号就可以打断睡眠;搜索了相关内容,决定让该函数的第一个参数的对象句柄用Event。

加入了 sleeper.c 模块,先调用LCUISleeper_New函数创建一个睡眠者,然后调用LCUISleepr_StartSleep函数让睡眠者开始睡眠,之后在其它线程上调用LCUISleeper_BreakSleep函数,就可以打断目标睡眠者的睡眠。LCUISleepr_StartSleep函数的返回值是实际睡眠时间。本模块代码只适用于windows平台,等以后到GNU/linux平台下测试时,再添加该平台下的实现代码。

做了测试,发现计时不准,原本睡10ms,另一个线程会在5ms的时候打断睡眠,但打断后得出的睡眠耗时要么是0ms,要么是15ms。

用了之前在网上找的测试程序,用于测试clock()、TimeGetTime()、GetTickCount()、QueryPerformanceCounter()这几个函数的精度,发现GetTickCount函数计时不准,具体如下图所示:

改用了QueryPerformanceCounter函数,参考了SDL的定时器中的做法,若在初始化时,QueryPerformanceFrequency函数返回值为假,则说明高精度计时器不可用,改用timeGetTime函数。之前做了测试,结果,计时有问题,后来参考那个测试程序的源代码,问题原因是数据类型没搞对,需要用int64类型变量来保存计时器记录的滴答数。

FPS指示器有问题,到后面就不动了,测试时发现,是用于更新FPS显示的定时器出了问题。

准备添加血条数量显示功能,显示的文字需要描边,但是LCUI并不支持为字体描边,字体处理这块就不折腾了,直接用PS做几张已经描边的字体图像,0至9,外加一个”X“,再把它们写进资源文件里,供游戏直接载入并使用。字体图像资源文件是 font.data。

在生成字体位图时,需要将已载入的字体图像覆盖至背景图像上,测试时出现问题,中断点在Graph_RGBAReplaceRGB函数里,看了一下,发现两行代码有问题,怎么是++des->w和++src->w?居然对源图形和目标图像的宽度进行自加,应该是++des_n和++src_n,移动下标,以填充下个像素点的色彩值,代码已经纠正

血条数量的显示功能已经完成,之前在测试时发现部件不显示,打印的信息表示这个部件尺寸为2x2,无论怎么调整大小,总是2x2,后来仔细一看,部件类型居然是”label“,呃,复制粘贴代码时忘记修改要创建的部件类型了,改为NULL后,问题解决。

准备为血条添加扣血时的动态效果。

2013-8-13

查看打印的信息,发现后面创建的定时器的ID 与现有的定时器的ID相同,这会使现有的定时器出问题。

定时器的ID是用rand()生成的,LCUI_Init()函数中有srand()函数调用,但每次运行程序,生成的定时器ID都一样,难道是srand()的作用范围仅限于当前线程上,每个线程都需要调用srand()函数初始化该线程的随机种子?搜索了一番,的确如此。修改了ID的生成方式,ID从100开始,每次创建一个定时器时都会让ID自增,暂不用考虑超出上限的情况,就算1毫秒创建一个定时器,也得需要20多天才能达到ID上限。顺便移除了LCUI_Init()函数中的srand()函数调用。

之前那个一睡不起的BUG应该没了,因为倒地时会设置定时器,等多久让他起来,由于定时器ID生成的问题,因此这个定时器会在另一个同ID的定时器删除时被删除。

实现血条扣血的动态效果,需要再加两条部件,一个用于显示当前扣掉的血量,先高亮,然后迅速增加透明度;另一个用于从扣血前的血条长度缩短至当前血条长度,颜色是当前血条颜色的变暗版。需要考虑到一次性扣多条血的情况。

高亮效果需要用两个部件,假设有150点血,血槽容量为100点血,也就是有1.5条血,要扣80点血,那么,有两个需要高亮的地方,一个在第二条血上,一个在第一条血上。

已经实现血条在扣血时的闪光效果,还剩血条缩短效果,但在测试时感觉闪光效果已经了代替血条缩短效果。

看了DNF相关视频,需要用三个部件实现血条缩短效果,两个放血条前面,一个放血条后面;如果扣血后血条数量不变,则用血条后面的那个部件;如果扣血后血条少了一条,则用血条背面和前面的一个部件;当一次性扣血量达到2条及以上时,就需要同时用到这三个部件。

血条的这些效果已经完成了,为了测试血条扣血的动态效果,临时将角色的攻击属性提高了,扣得血多才便于观察。

接下来就是实现角色的“死亡”状态,血被打空了只要站着就不会死,一旦倒地,那就死了。

已经实现让游戏角色在血量为0时死亡,终于可以发布第二个演示程序了:游戏演示程序-02.zip,效果如下所示:

2013-8-14

准备搞AI了,需要时间酝酿一下。

Win8存在一些问题,有时程序弹出来的模态对话框是显示在程序主界面的后面,获得焦点的窗口理应显示在其它没有焦点的窗口之上。在回收站中删除大量文件时,windows资源管理器的全部进程都会变成未响应。

2013-8-15

一睡不起的现象还是有,这问题只发生在自己被对方撞倒的时候,就是先正面抱住对方,然后按K键将对方推向自己身后,之后跑过去碰对方,双方都会倒地。

SDL 的2.0版本已经发布了,之前读过1.2.15版本的图形输出的源代码,windows平台下它用的是DirectX5,据说SDL在2.0版本中改用了DirectX9,准备下载来看看,等需要改进LCUI的图形输出时再参考一下它的源代码。呃,SDL官网网页终于改版了。

2013-8-16

在某一时刻内,针对当前情况,AI会有若干个可行的想法,需要在这些想法中,根据各个想法的概率,随机选择一个想法。我的思路是,先给定要参与选择的“想法”及该“想法”的选中概率,概率取值范围为0(0.00%)至10000(100.00%),然后开辟一个能够容纳10000个int元素的内存空间,将这些想法的ID根据概率,均匀的填充至内存里,之后生成一个10000范围以内的随机数,取出处于该下标的“想法”的ID。

改进一下,不需要申请内存,直接计算。先计算出每个想法要每隔N次出现一次,然后用当前随机到的次数M除以N,得出浮点数A,对A取整赋值给B,之后用A减去B并将差值赋给浮点数C,越是接近0,则表示第M次出现的概率越高;通过比较各个想法的浮点数C的大小,筛选出被选中的概率最高的几个想法,并随机从中选择一个想法,作为最终选中的想法。

添加了AI的基本框架,待完善,目前只能够针对部分状态来决定AI的想法,AI想法的实施功能还未添加。

之前那个多线程调用MessageBox的问题可以用LCUISleeper来解决,改进一下LCUISleeper,为LCUISleeper_StartSleep函数和LCUISleeper_BreakSleep函数各自加一个参数,分别用于获取和发送自定义数据。再修改LCUI_MessageBox函数,加个判断,如果当前不在主线程上,就不会创建主循环,改为调用LCUISleeper_StartSleep函数,在MessageBox的按键点击事件的响应函数中,再加个LCUISleeper_BreakSleep函数调用,用于打断MessageBox线程的睡眠,并传递点击的按钮ID给该线程。

2013-8-17

原来之前那个动作锁死的问题是由游戏消息未添加而导致的。在改变动作时,会调用相应函数将动作改变消息加入至消息队列,但是,该函数会先判断改变的动作是否与当前动作相同,相同则不添加。在攻击结束时改变动作,这时消息能正常添加;在消息还未被处理前,再次使用攻击动作,需要改变为攻击动作,但由于之前的消息未处理,当前记录的动作ID还是攻击动作,因此,就没有添加消息了;结果,游戏角色卡在攻击结束后的动作上,而状态却是攻击状态,状态ID与动作ID不一致。

可以为AI添加“对战策略”的概念,这些策略由自己添加,每个策略都有前提条件,例如:与目标的距离范围、目标的状态、自己的状态等等,还要有优先级,用于调整被选中的概率;AI在控制游戏角色时,会筛选出适用于当前条件的策略,然后随机选择一个策略,之后执行这个策略,每当当前策略不适应当前条件时,会重新选择对战策略。

2013-8-18

选中概率与优先级的关系需要纠结一段时间,优先级高的策略,选中的概率要比优先级低的策略高。

有些动作被锁定后理应不能改变人物朝向的,而AI在符合条件时直接改变人物朝向,导致出现一些奇怪的效果。

AI 已经更改,还只有25个对战策略,目前AI已经能够按照对战策略进行基本的攻击了。游戏角色控制有待改进。

使用带击飞效果的技能攻击对方,若对方在移动,对方在被击飞时,只是原地飞起来,并未移动位置,这个问题有待解决。

2013-8-19

为AI添加了一些对战策略,解决部分 状态/技能/动作 在切换时出现的问题。

在此发布第三个演示程序:游戏演示程序-03.zip,效果如下图所示:

2013-8-20

下载了热血格斗PC版,进了目录后发现,原来游戏素材有现成的,只不过游戏为了节省空间占用,把动作图素材打乱后挤在一张图上了,提取出来有点麻烦,因为不知道哪块图和哪块图是一起的。

PC 版的热血格斗里面的技能/动作多了,可以使用武器,人物形象也能够自由组合。

2013-8-21

记录一些想法:

2013-8-22

装了个Fedora 19,用起来不习惯,感觉linux平台的文件管理器用起来没windows的资源管理器好。有些地方需要折腾。总感觉Fedora界面上的按钮之类的东西占的屏幕区域太大了,是我屏幕小的缘故?装上了eclipse,同样的分辨率,eclipse在这上面显示的东西比在windows平台上的要少。

顺便在Fedora上编译LCUI,更新一下Makefile.am中的文件列表。

运行configure脚本,检测不到FreeType头文件,将/usr/local/include/freetype2/添加至头文件搜索目录即可,export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/include/freetype2 ,之前我的做法是改FreeType的头文件内容,有些麻烦。

make第一个文件就报错,sleeper.c中没GNU/Linux平台上的实现代码,需要找与windows平台上的事件+WaitForSingleObject函数 类似的实现方法。

2013-8-23

用信号量实现了可打断式睡眠功能,主要用到sem_init()、sem_post()、sem_timedwait()这三个函数,调整了一下sleeper.c中的函数原型。

更新了 timer.c,为LCUI_GetTickCount函数添加linux环境下的实现代码,通过调用gettimeofday()函数获取时间。奇怪的是,在测试clock()函数时,clock()函数的返回值一直是0。

在eclipse里将制表符宽度设置成8了,怎么还是占4个空格的宽度?Consolas字体在这上面显示得有些粗,没有windows上的效果那么好。

更新了LCUI在GNU/Linux环境下的鼠标支持代码

纠正了游戏的问题代码后,开始测试游戏,在GNU/Linux环境下,不能设置LCUI的屏幕分辨率为800x600,以后看能不能添加分辨率设置功能。游戏启动时的淡入淡出效果还行,有点卡顿;进入游戏画面后,AI可以控制游戏角色对目标进行攻击,但画面卡顿现象严重,估计是定时器的问题,毕竟还没测试用信号量实现的可打断式的睡眠功能。

2013-8-24

已改用数据代替动作动画的设置代码,少了八百多行代码。一个动作动画最多包含10帧动作,每个动作帧信息包含了:是否启用、是否为新攻击、是否水平翻转、动作图名称、停留时间、XY轴坐标偏移量、攻击框和受攻击框。还未添加动作动画数据的处理功能。

2013-8-25

动作资源的载入功能已经完成,先找到指定类型动作的对应数据信息,然后根据这数据信息生成动作动画。在测试过程中,纠正了部分动作数据信息的错误。

接下来该干什么?没清晰的思路,再纠结一段时间,如果还是纠结不出,那就去做游戏菜单,减少时间浪费。

记录一些想法:

需要根据现有代码,抽象出一个“技能”模块,主要就是从现有代码中找出共同点,并根据这些共同点,设计出一套通用的接口。

先为“技能”这一对象设计一个数据结构,这个数据结构要记录几个回调函数,它们的用途分别为:判断是否符合发动条件、发动后更改自身动作、发动后更改目标动作。

游戏角色的各种动作,都可以看成是“技能”,它们都只在符合相应条件的情况下才会发动,发动时,发动者自身动作和状态会改变,有些技能还会改变对方的动作和状态。

技能发动的条件主要由角色的状态决定,例如:落地时、起身时、跳起时等状态,其次是技能私有的条件,以上面所说的破招技能为例,该技能的私有条件是:对方要处于冲撞攻击状态,面对自己,对方的受攻击框在技能的作用范围内。

若游戏角色是被按键控制的,还需要设置技能的按键控制方式;可以为每个技能添加一个标志变量,当游戏角色是被按键控制的,系统会检测按键情况,当按键情况符合某个技能的要求时,才设置该技能的标志变量,表示需要发动该能;AI控制的话,直接设置该技能的标志变量即可,剩下的就由回调函数来判断是否符合发动条件。

2013-8-26

随便看了一下LCUI的问题反馈页面,没想到会有人留言,由于评论工具使用的是“友言”而不是“多说”,因此看不到留言提示信息,需要修改。

更新了FAQ和问题反馈页面,增加了页面宽度。

在查看cnzz上的数据的时候,发现了一个陌生的来路域名:gurudigger.com,很好奇,难道这网站上有LCUI主页地址且恰好被人点了?进去看了一下,据说该网站是帮助靠谱的互联网产品找到志同道合的创业合作工程师,那我有个想法:等游戏的第一个完全版本完成时,就把它扔到这网站上去。

2013-8-27

弄个技能库,用于记录各个技能的信息和技能的回调函数,回调函数只需要两个,一个是检测是否符合发动条件,另一个是发动技能。为游戏角色添加一个成员变量,用于记录他能够使用的技能。

再弄个技能伤害库,用于记录不同技能攻击类型的伤害计算方式。

2013-8-28

技能分为通用技能和专用技能这两类,各个职业都可以有相应的专用技能。

貌似需要把game_control.c里的各种技能的代码分离至新的源文件内。

2013-8-29

技能的攻击伤害效果也需要记录,当游戏角色受到伤害时,根据伤害类型,调用相应回调函数,让受害者有相应的反应。

技能需要有优先级,有时会满足多个技能的发动条件,就需要根据优先级来决定发动哪个技能。

0为最高优先级,定义个枚举,枚举成员表示相应技能的优先级,枚举成员越靠近前排(0),则该枚举成员对应的技能的优先级就越高;这样,添加新技能或调整现有技能的优先级时,只需要调整对应的枚举成员的位置即可,省去了手动修改其它技能的优先级的麻烦。

但是,要添加新技能的话,还需要手动往头文件里的添加枚举成员,调整枚举成员的位置,也很麻烦。要么,根据技能所需的控制状态,将技能优先级分类,主要有4类:通过左/右控制发动的技能、通过上/下控制发动的技能、通过A攻击控制发动的技能、通过B攻击控制发动的技能。

2013-8-30

改进技能库的技能添加、技能获取功能,SkillLibrary_Init()函数改为手动调用,初始化技能库后会注册通用技能。初始化游戏角色的技能记录时,会默认启用部分技能。

添加部分通用技能,这些通用技能是:A/B攻击、高速A/B攻击、终结一击;之前实现这些技能的老代码已经移除。

2013-8-31

添加了 BombKick 技能,但测试时没法发动BombKick技能;玩家在跳跃后落地时,有200ms的时间处于下蹲状态,加上了打印角色状态信息的代码,在我按K键发动BombKick技能时,打印的状态信息表明玩家状态不符合要求,下蹲状态维持200ms都不满足发动条件?好吧,那就改为落地后一直蹲着,别站起来了,测试时BombKick技能能够发动。

纠结了一段时间,开始以为是多线程的问题,但感觉200ms的时间应该够几个线程轮换运行很多次了,总有一次是在玩家处于下蹲状态时检测技能发动条件的。后来看了处理B攻击的代码,找到问题原因了,游戏在获取符合发动条件的技能前,还会检测动作是否被锁定,如果被锁定,就不会发动技能,而BombKick技能的发动是不考虑动作锁的,只考虑是否处于下蹲状态,这是因为游戏角色的下蹲动作在结束前是锁定的;解决方法就是移除动作锁的检测代码

BombKick 和 SpinHit 技能已经添加

2013-9-1

添加投掷技能、冲刺跳跃攻击技能更新了动作数据

2013-9-2

添加 RideAttack 技能

2013-9-3

开学了,闲余时间减少,预计开发效率会降低30%至50%。

2013-9-4

添加擒获和骑乘技能

2013-9-5

有些称不上“技能”的动作,例如:被撞飞、被击飞、被扔出等不具主动攻击的动作,可以将它们的代码集中放在一个源文件里,主要供游戏在处理技能命中效果时使用。

测试了BombKick和SpinHit这两个技能,跳跃后一落地就能发动,太快了也不好,想增加落地时的下蹲动作的停留时间。改了代码后进行测试,第一次跳跃后落地,有停留,但发动SpinHit技能后落地时,下蹲动作就没停留了,可立刻发动第二次SpinHit技能。

2013-9-6

STATE_SQUAT和STATE_JUMP_DONE这两个状态使用的动作都是ACTION_SQUAT,在第一次跳跃后落地时,会设置在STATE_JUMP_DONE状态的ACTION_SQUAT动作完成时改变动作,等到第二次时,由于没有撤销之前的设定,在STATE_SQUAT状态时就改变了动作,导致并没有让ACTION_SQUAT动作停留指定的时长。

2013-9-7

移除受攻击时的动作响应代码

移除伤害计算代码,添加通用技能的攻击类型的宏定义

修改攻击类型的记录方式,之前是保存攻击类型ID,现在改为保存攻击类型名称。

还有个技能没加,在使用自旋击(SpinHit)命中对方那一刻,按左/右方向键可进行二段自旋击,也就是在撞到对方时弹起来,继续翻滚,但现有的功能无法实现这种技能,其它技能都是检测当前游戏角色状态和控制状态来判断是否满足发动条件的,不能在受到攻击的那一刻检测。

游戏在处理攻击记录时,会调用与该攻击类型对应的回调函数来实现相应效果,那么,在这里加点代码,检测攻击者的控制状态,满足发动二段自旋击时,就改变攻击者的动作。

2013-9-9

移除了 game_control.c 中各种击飞效果的代码,击飞效果属于技能效果,后面会在 skills_common.c 中加入实现该击飞效果的代码,供技能命中对方时调用。

添加拳击家技能的攻击伤害计算,拳击家的两个技能没技能效果,在发动技能时就包含了技能效果。

已经为大部分技能添加了伤害计算和技能效果

2013-9-10

添加了防御动作,防御动作需要在控制键处于按住状态时才会维持住,若处于释放状态,则取消防御动作,这个还未实现。

改进了指定控制键的玩家查找方法,之前是默认返回玩家1,不能正常控制玩家2。

修改了击飞效果代码,在攻击者使用带位移的技能攻击对方时,若对方处于防御、躺地或击飞状态,会向攻击者攻击方向移动一段距离,不会再次将对方击飞。

2013-9-11

添加用于解除防御状态的代码,如果没有可用技能,并且未控制玩家进行防御、玩家处于防御状态,则解除防御状态,并恢复至READY状态。

考虑到会被举起的情况,占在其它玩家头上使用防御动作后,会恢复至 STATE_BE_LIFT_STANCE 状态。

添加被撞击时的缓冲效果,在游戏角色受到带击飞效果的技能攻击时,若自己处于防御状态,则会向击飞方向位移一小段距离,以缓冲受攻击时冲击力,缓冲过程中,防御动作处于僵直状态,要解除的话,只能在缓冲完后解除。

2013-9-12

已实现破招技能,当对方使用冲撞攻击向自己袭来,只要在破招技能有效范围内发动A攻击,就会破他的技能,默认是使用拳击家的Elbow技能作为破招技能。

游戏存在一些问题:

攻击判定,这个词不错。

2013-9-14

再次单独测试定时器,总是有10ms至40ms的误差;重写了个测试程序,测试创建100个定时器,定时时间长度从100ms开始,每个定时器+100ms,测试结果显示,最后一个定时器理应是定时10000毫秒,结果却多出了500多毫秒,后来就将定时单位精确到微妙,改了下代码,再测试,误差少了,最后一个定时器实际定时10000±2毫秒,其它定时器的定时时长有些波动,有的较精确,有的误差较大。

修改了定时器模块的代码,之前是通过更新定时器的剩余睡眠时间来进行睡眠、排序,以及判断定是否超过定时时长,现在是记录定时器在启动时的时间、暂停时的时间、定时时长以及处于暂停状态的时长,而剩余睡眠时间可以根据当前时间计算出来。

修改了一下测试程序,再次测试创建100个定时器,测试结果显示,前几个定时器从创建到响应,时间间隔700多毫秒,这多出来的600ms哪来的?将定时器数量改为10个,测试结果正常,之后再改为50个,测试结果还是正常的。后来怀疑这多出来的700多毫秒是不是创建100个定时器时消耗掉的,于是加了代码,统计创建100个定时器所需耗时,结果表明的确是这里的问题。

问题原因已经找到:定时器列表排序代码的效率很低,数量越多,排序耗时越长。

优化了添加定时器时的处理代码,耗时缩减到2毫秒左右。之前的做法是先添加,后对整个列表进行排序,使用的是选择排序法,现在是在添加时遍历定时器列表,对比定时器的剩余定时时间,保证新定时器会添加在剩余定时时间不大于它的定时器的后面。每当有定时器需要重新定时时,会将它的调整至适当的位置。主要就是确保列表中的定时器按剩余定时时间由小到大的顺序排列。

想修改主循环,让它在任务队列中没有任务时进行阻塞等待,一旦有任务添加,就执行队列中的任务;这个可以用LCUI_Sleeper实现,在任务队列没有任务的时候,让主线程睡眠;在添加任务时就打断主线程的睡眠,让主线程中的主循环执行队列中的任务。这样,就能提高响应速度,之前的做法是在没有任务时睡眠几毫秒,无任务状态持续越久,睡眠时间越长,最大睡眠时间为50毫秒,这会对定时器的响应速度有影响,如果恰好在主线程开始睡眠时收到定时器的响应任务,那么定时器的响应就会被延迟。

可是windows的消息循环怎么办?现在是将它放在主循环里跑的,主循环里阻塞等待任务,那windows的消息就不能及时处理了,先试试开个线程跑消息循环。

新线程里的消息循环没有获取到消息,程序不久会变为未响应状态。据说窗口在哪个线程里创建的,就在哪个线程里获取消息,那么就在初始化windows图形输出时,创建个线程,让这个线程创建窗口,然后跑消息循环。

已经修改完毕,测试时发现唯一不正常的是在关闭窗口时,LCUI程序并未退出,加上代码,打印消息循环收到的消息,再次测试,结果WM_QUIT、WM_CLOSE和WM_DESTROY这三个消息都没收到。

测试游戏,定时器貌似没响应,本该维持1秒的动作,一直没有变化,后来发现是定时器线程对可重用的定时器的位置调整有问题,之前测试的都是一次性使用的定时器,定时器的位置调整功能未测试到,现在已经纠正此问题。

可以到这里查看本次定时器模块代码的修改情况,接下来需要加入睡眠,减少定时器线程的CPU使用率。

已经为定时器线程加入睡眠功能

游戏中有些动作还存在卡顿,可能定时器模块还存在问题。

2013-9-15

程序中用printf打印未初始化的变量的值,会弹出提示框,告知开发者该程序中存在的问题,之前进行除0运算时也会有这种提示框显示。

经测试发现当已存在正活动的定时器时,再添加定时器的话,这个定时器会等到活动中的定时器到了响应时间时才会处理,估计是添加定时器时没有选择好它的位置。

加上代码,用于打印定时器列表,最终发现在定时器位置调整的前后的列表内容有问题,调整前,列表内定时器信息正常,调整后,之前被删除的定时器又存在于列表中,而被调整的定时器却消失了。

已经纠正定时器模块中存在的问题,导致该问题的原因是:在复用定时器时没有调整好它的位置,添加定时器时也没将定时器添加至正确的位置,本来是拿现有定时器的剩余时长与新定时器的总定时时长进行比较的,结果却是用 现有定时器的剩余时长 与 现有定时器的总定时时长 进行比较。现在,定时器在使用定时器列表时会锁上互斥锁,避免在读取列表时被其它线程修改列表内容。

游戏中的角色动作的攻击/受攻击框可以保存至文件,让游戏在运行时从文件中载入这些数据,就不用写在.c源文件里了。以后可以考虑写个游戏角色编辑器,让它载入游戏资源文件,显示各个角色的动作图、攻击/受攻击框,这样就可以直观的修改游戏角色的动作数据了。

AI对战策略可以考虑也保存至文件,但在实现这个功能前需要调整游戏AI的代码;现在的AI策略的条件,主要是考虑到固定的距离范围,但没考虑到自身的移动速度,假设我将AI控制的游戏角色的速度调成50%,那么他的部分攻击就打不到目标;以冲撞攻击为例:发动该攻击前,游戏角色处于奔跑状态,发动该攻击时,游戏角色停止奔跑并开始减速,直至速度为0为止,从发动攻击到攻击结束的这段时间里所移动的距离,就是攻击范围,游戏角色的移动速度越低,攻击范围也就越小。

2013-9-16

修改了GameObject数据更新里的帧数控制代码,主要是确保每秒内要更新完指定量的数据帧,若本秒内更新的数据帧数没达到预定的总帧数,则会遗留至下一秒里的数据帧更新,影响这些帧的停留时间。一般情况下,一秒内应该能完成指定量的帧数更新,毕竟都是简单的运算。

顺便改了LCUI画面更新的帧数控制,主要就是把帧数控制代码整进函数里,直接调用函数来实现帧数控制。

当游戏角色仅在镜头内移动时,游戏中显示的FPS值更容易降到60左右,这次修改后,不知是FPS计算变得准确了点,还是误差变大了。

测试了几次游戏,发现游戏在1秒内更新100次游戏角色的位置和动作帧都无法完成,估计是游戏角色动画列表的排序问题,动作帧的更新方法是用以前的定时器的处理方法,需要修改成目前的定时器的处理方法。

2013-9-17

修改了动作帧更新处理,起初每秒更新的帧数是设置为100,之前测试时显示的效果有问题,位置变化不均匀;添加了代码,用于记录当前帧序号、理论停留时间、实际停留时间,然后将记录输出至文件;运行游戏进行测试,跑了几秒后再关闭游戏,然后打开记录文件,实际停留时间和理论睡眠时间相差很大,大多数是停留了20ms以上,导致后面更新的帧的停留时间减少,从而使游戏角色的位置变化由慢到快。

过了一段时间再来测试,效果较为正常,记录文件的内容表明,有时实际停留时间和理论睡眠时间一样,而有时相差很大(如:理论停留5ms,实际停留20ms),主要是调用windows的Sleep函数实现停留的,难道是由于windows系统的调度问题使得Sleep函数实际睡眠时间超出理论睡眠时间?

为缓解这个问题,决定将每秒更新的总帧数设置为50。

修改了游戏对象的坐标更新处理,之前那种计算方法有问题,除以的是每帧停留时间(10ms),而不是每秒更新的帧数(50),修改后,游戏中的坐标变化速度变慢了,需要重新调整参数。

2013-9-20

需要将玩家数添加至4个,先设定三个AI的攻击目标为玩家,玩家挂了后再切换目标,以后再实现AI目标的动态切换功能,AI的攻击目标会在一段时间后切换,若中途受到非目标角色的攻击,则会缩短等待时间,并且会优先将该角色作为攻击目标。

AI的对战策略需要修改,现在的AI是根据与目标的距离范围、目标状态和自身状态来选择策略的,部分技能的攻击范围受角色属性的影响,为了提高AI的攻击命中率,需要考虑到更多条件及情况。

目前是混战,以后会加入2v2对战,AI需要考虑到自己的对战策略是否会对搭档造成伤害,优先选择对搭档不造成伤害的策略,当然,并不会一直是选择这种策略,必要时还是会对搭档造成伤害。

2013-9-22

AI的改进方案还没什么头绪,暂时先放一边,找点不需要纠结的任务来做。

那就添加几个新角色吧,现成的动作图有两个,一个是格斗家的国夫君,一个是BOSS老虎,但动作都不是很全,缺少的动作还需要从原版游戏中截出来,PC版的虽然有完整的动作图,但却是零碎的,需要自己拼,感觉很费时,个人觉得从FC版里截取动作图轻松些。

模拟器中显示的人物肤色与现成动作图中的不一样,需要改颜色,还好动作图的颜色单一,改颜色不怎么费劲。

添加了老虎,还只有最基本的几个动作,动作帧的位置参数、攻击框和受攻击框没有设置。

2013-9-24

为老虎的“表莲华”技能添加了伤害计算

更新了老虎的动作参数

更新了阿力的动作参数

2013-9-25

更新了两个角色的动作图形资源文件,正补全老虎的剩余动作图形资源。

2013-9-26

老虎的肤色还是用橙色好些,模拟器中截取出来的是橙色肤色,而现有的动作图中的肤色都是粉色,于是写了个程序,根据现有的动作图文件列表,将各个动作图中的粉色肤色改成了橙色肤色。

修改了代码,设置玩家使用的角色时,也会自动设置玩家角色类型,不用自己再手动设置了。

每帧数据数据更新后的睡眠控制存在问题,目前的做法是每秒更新指定次数的数据,本秒内未更新完的次数,将留到下秒再更新,并且,每帧数据更新后都会进行睡眠,但存在误差、使实际睡眠时间超出预设时间时,游戏中的角色坐标变化速度会变慢,这状态可能会维持一小会,然后恢复正常,但感觉这样会影响游戏体验,于是修改了代码,在每次更新完数据后,会检测当前时间与上次更新完数据时的时间间隔,若超出每帧平均间隔时间(20ms),则本次就不进行睡眠,否则,计算剩余时间,进行睡眠,最后:上次更新完时的时间 += 20ms。这样,慢只是慢一瞬间,很快就会恢复正常速度,不会在一段时间内降低数据的更新频率。

修改部分技能的细节,在举起对方时,将锁定移动,按方向键不再会让角色继续移动;为RADE_A_ATTACK技能添加了发动条件,以确保该技能只能在骑在对方身上时发动;之前测试游戏时,偶然发现对方站起时,自己还处于骑乘状态,可以发动RADE_A_ATTACK技能,对方也会有反应。

调整老虎的 “里莲华”技能,落地后的反弹方向会在发动该技能时根据角色面向的方向来确定。

用老虎来测试破招技能,发现会有喜感的BUG:对方使用冲撞攻击向自己袭来,发动破招技能时,自己因受到冲撞攻击而被撞飞,但由于技能已经发动,对方依然会受到该技能的影响,继续实现该技能产生的效果(不会扣血)。下图所示的是情况较好的时候,自己没被撞飞,但在空中受到了对方的普通攻击。

2013-9-27

添加BE_PUSH的攻击效果,当对方背对着自己进行普通攻击时,对对方进行普通攻击,对方会因被推而导致失衡(站不稳),然后向前移动一段距离,之后倒地。

添加Guillotine技能,该技能和BigElbow技能的发动条件及效果基本一样。

添加START状态及动作,用于在战斗开始时使用。

玩家数量增加至4个,代码以后还需要再次调整。

修改AI的目标选定功能,不能选择自己为目标,目标挂掉后会更换目标,以后再改进该功能。

有些问题在2v2中出现了,需要改进。

2013-9-28

准备添加 国夫 这个角色,格斗家。

2013-10-01

国夫的动作图已经整理完毕。

2013-10-01

缩短虚弱奔跑的时间,增加虚弱奔跑的移动速度。

最多只能有一个角色骑在躺地者身上,当有人骑在躺地者身上时,不能举起该躺地者。

2013-10-02

在受到攻击时打断自身动作,并且撤销将要实现的技能效果。例如:A使用”高跳旋转落踢“技能,在跳起后旋转时受到了B的攻击,被击飞,但在Z轴速度接近0时会触发”落踢“动作,若不撤销这一技能效果,A会在被击飞后又会改变为”落踢“动作,继续”高跳旋转落踢“这一技能,到落地时才躺地。

玩家挂掉后,为方便观察剩余玩家的对战情况,需要能够用按键来移动镜头。

玩家A被玩家B擒住时,若玩家C对玩家A进行攻击,则会解除擒住状态。

玩家被击飞倒地后,有时会爬不起来,一直维持躺地状态,需要解决该问题。

需要改进碰撞后的击飞方向判定。

需要修改被举起的角色在受到攻击时的反应动作。

2013-10-03

受攻击者的击飞方向,由攻击者面向的方向决定,懒得纠结如何根据移动速度、位置和角色朝向来进行击飞方向判定了。

在修改击飞方向判定时,顺便把游戏角色在被抛出时的触碰攻击判定、击飞方向判定给改了。

短距离击飞效果还存在一点问题,有时会在持续到一半后,没了水平移动速度,直接垂直降落。

2013-10-04

为游戏做了个图标,拳头图标能够体现格斗游戏的“格斗”这一主题,这个图标可以应用在LC-Games的LOGO里,就像之前提到的EA的游戏LOGO一样。源图来自互联网,只是用PS抠出了个拳头,剩下的由自己编辑而成。

仔细看图片感觉更像是在体现革命战争之类主题,戴上拳击手套的话应该更能突出主题,还需要继续改进。

添加对被举起者的攻击效果处理,被举起者 能够被带击飞效果的攻击击飞,并会重置举起者的状态,例如:A举起了B,C冲过来把B打飞,那么A就不再处于举起状态了,重置为站立状态。

看了虚弱奔跑在碰撞时的处理代码,本来是想再加个攻击类型,设置攻击效果,可写了点代码后发觉到,这个处理代码自己实现了个攻击效果,由于需要计算攻击伤害,又添加了攻击记录,但是,该攻击记录中的攻击类型有相应攻击效果,难道是因为两个攻击效果重叠了才导致击飞效果出现问题?

修改了代码,添加了个攻击类型,没有为该攻击类型设置攻击效果。

偶尔会出现一个问题:AI在跳跃结束后进行移动,一直是READY动作,并未改变为步行动作,只有位置却在移动,继续测试,发现动作也被锁定了。

经过一番测试和分析,发现游戏角色在落地时的处理流程还未完成,动作就被改为READY了,理应是在该处理流程结束后才改变动作为READY的;再次添加了些代码以打印更多信息,进行测试,问题重现后查看打印的信息,原来这个动作修改是在游戏主循环中进行的;加了一项条件,在动作或移动被锁定的情况下,不能将动作设置为READY。

测试游戏过程中,程序偶尔会在刚运行时崩溃,调试器给出的BUG出现位置在LCUI的部件消息处理上;修改了代码,让WidgetMsg_Dispatch函数在部件消息无效时返回FALSE,修改前该函数会返回TRUE,GUI线程判断它的返回值为TRUE时就会删除队列中的部件消息,由于消息本来就不存在,删除会出现问题,使得下次获取到的消息所在地址是非法的。这只是个人猜测,BUG出现原因是否真的是因为这个,有待后续实验。

2013-10-05

为游戏角色对躺地的游戏角色的操作添加了限制,当两者高度相差大时,不能举起躺地者;当躺地者被已举起,或已有角色骑在躺地者上面,则不能举起对方,或者骑上去。

AI还是会卡在READY状态,之前加的限制条件没用,因为是改为READY状态后锁定动作的,又加了代码进行测试,根据打印的信息得知问题出现时,共设置了两次状态,都是修改状态为READY,再改了代码,打印线程ID,这两次修改是分别在两个线程上进行的;加了行abort(),以在游戏角色落地结束时终止程序;调试器给出的位置是在游戏主循环中的AI策略执行上,问题原因已经知道来了,加了个条件,只有在游戏角色的状态为站立、步行时才会调用函数将状态设置为READY;游戏角色的各种动作在完成后,都会还原为这几个基本状态,AI不应该打乱这些状态变化。

修改了GamePlayer_ChangeState函数的代码,舍弃了switch-case的方式来获取与状态对应的动作,改用一个数组,状态ID作为数组下标,数组中的各个元素的值就是与该状态ID对应的动作ID。

解决了部分技能效果的细节问题,例如:在游戏角色空中受到攻击后,可以在空中进行行走、跑步、跳跃等动作,修复后,则会改为下落状态,并且不可移动、也不可改变动作。

镜头移动与目标游戏角色移动不同步,有晃动,这是因为镜头位置和游戏角色位置的更新不是在同一帧下更新的,要么,想办法让这两个位置更新在同一帧下进行,要么,让镜头平滑移动,也就是让镜头以一定速度移动至目标位置,而不是瞬间移动过去。

2013-10-06

添加了功夫家 姬山 的动作资源。

添加一个功夫家专有技能,为实现这个技能,又添加了个动作,格斗家和柔道家的专有技能还需要些动作。

为这个技能添加技能伤害时,出了个BUG,技能结束后,自己打不到人,别人也打不到自己,后来改在其它地方记录伤害,又好了。

2013-10-07

为功夫家添加第二个专有技能

AI的目标改为每隔10秒更新一次

接下来为国夫添加两个专有技能,再添加一个柔道家的角色。

2013-10-08

内存占用一直在增长,需要用LCUI_Memory.c的代码来统计LCUI各种用途的内存的使用情况,以确定是哪里一直在增加内存占用量。

有人问过是否考虑过将LCUI应用在对内存占用和效率要求高的系统环境上,感觉减少内存占用、提升效率是件很麻烦的事情,减少内存占用的话,可以为LCUI添加个单缓冲模式,不为各个部件图层创建单独的内存空间,那么,LCUI_GraphLayer.c的代码就没用了。虽然现在不打算做这个,但可以先记录一下自己的想法,针对减少内存占用这一问题,我有两种解决方案:

话说,如果部件背景图是个拉伸的图,要重绘部件中的某个区域,该如何重绘?

字体数据库可以考虑进行修改,只记录字形,不记录各种字族、各种大小的字体位图,因为在绘制文本时,可以根据字形来生成字体位图。

只修改了LCUi_Queue.c里的代码,在游戏中设置一个定时器,每隔2秒打印各个线程上的队列的内存使用情况,以下是截图:

队列的内存占用量都上KB了,尤其是图中红框框住的3个;在后面的测试中,已经确定了这些占内存大的队列的分布,它们分别在GUI线程、定时器线程、游戏线程这三个线程上,其中,游戏线程上的184KB无明显变化,动作数据、图形资源、技能信息和攻击信息是从游戏线程上载入的,并都存放在各自的队列上,游戏过程中不会再对队列进行增删操作,因此,内存占用量较为稳定,但个人觉得这184KB占的太多了;而定时器线程、GUI线程上的队列的内存占用量处于增长状态,定时器线程上的队列居然占用了几十KB的内存,难以置信,不就是存了几个定时器的信息吗?怎么会占这么大的内存?

在检查GUI线程的时候,发现在添加矩形区域时跳过了相交矩形的分割处理,顺便删掉了continue以进行矩形分割;貌似之前是为了解决各个脏矩形更新显示不同步的问题,而没用这个功能,因为小区域的更新速度会比大区域的快,把重叠的大区域分割成小区域,再逐一进校重绘,画面更新效果就会像“碎片”一样。

又更新了一次帧数控制代码,现在FPS基本上不会超出100的限制。

修改了队列的处理代码,解决在添加队列成员时的内存泄露问题,但测试时,内存占用还是处于增长状态。

2013-10-9

游戏窗口关闭后,进程并未退出,记得之前是能够退出的,搜索了一下,找到了这个帖子:http://tieba.baidu.com/p/957000205,看了帖子中各个回复,难道说是因为WM_DESTROY消息不是发给窗口的,所以用GetMessage(&msg, hwnd, 0, 0)会收不到WM_DESTROY消息,导致LCUI程序无法退出主循环?改用GetMessage(&msg, NULL,0, 0)后,问题解决。

既然这样,之前把消息循环放子线程上跑时遇到的问题,也是这个原因了,那么,win32的消息循环就可以转移至单独的线程上跑了。

移除了main.c中的win32消息循环,顺便删减了一些代码。修改win32.c中的代码,在初始化图形输出模块时,将创建一个线程,用于跑win32的消息循环。

有时游戏一运行就会异常终止,连弹出的异常信息窗口都未响应,无法进行调试。

有时出现异常时,调试器给出的位置是在device.c里,在对设备列表进行操作时没有使用互斥锁,之前以为就那么个要添加的内容,很快就能添加完成,不会出现问题,既然现在出了问题,那就用互斥锁了,代码已经修改

在虚拟机里的XP系统中,用VS2010编译Debug版本的游戏,一运行就异常,弹出错误窗口,还好,这个窗口还可以操作,点击“重试”按钮后,使用VS2010的调试器进行调试,但是,“函数调用堆栈”窗口中的内容为空,“线程”窗口中也没线程的记录,没法调试。

2013-10-10

准备切换到 Fedora 系统下进行测试游戏,至少可以排除通用代码中存在的BUG。

在 Fedora 系统下复制LCUI项目文件,在make的时候才发现,windows上用的LCUI项目文件中,Makefile.am文件是过时的,部分文件没包含进来,又编辑了Makefile.am文件。

为 timer.c 添加了 linux 环境下的计时代码,记得之前是加过这样的代码的,可能是哪次用老版本的文件覆盖它。

测试游戏,屏幕无图形显示,加上代码,打印状况,最后发现是互斥锁的问题,在处理部件消息前,锁上了队列锁,而有些消息响应函数会添加消息,由于队列被锁,导致一直处于阻塞状态。这个在windows上没出现问题。

解除互斥锁的使用后,已经有图形输出了,但游戏会出现段错误而终止,根据gdb打印的信息,可知是游戏从非法的地址上获取了错误的游戏角色信息,经过一番代码审查,在AI目标选择、角色信息获取上存在BUG。

2013-10-11

调试时,点了下VS2010上的暂停按钮,函数调用栈 窗口里居然就有内容显示了,还不知道游戏在哪里终止的,查看各个线程的函数调用堆栈,有个线程停止在free函数处,再查看上级函数,该free函数是被Queue_Destroy函数调用的,仔细看了free函数调用处的附近代码,发现队列销毁处理存在BUG,重复释放了同一个队列成员,这个问题在 Fedora 下居然没有让游戏异常退出。此问题已经纠正,队列的使用中的内存在 0 ~ total_num-1 范围内,而空闲内存在 total_num ~ max_num-1 范围内,释放空闲内存时,应该从total_num开始,而不是total_num-1,因为total_num-1处的内存已经释放过一次,再次释放会使程序崩溃。

Debug 版的游戏在XP下,获取的屏幕尺寸为800x600,但Release版的获取到的却是600x600,后来发现是LCUIScreen_Init函数在调用LCUIScreen_SetMode函数设置窗口尺寸后,未重新获取屏幕尺寸,导致调整的根部件的尺寸不是800x600的,奇怪的是,为何Debug版本不会出现这问题?算了,懒得想。

窗口标题栏上的最大化按钮需要禁用掉;游戏运行时,窗口虽然显示了,但不是处于激活状态,用鼠标点一下游戏窗口才能控制游戏。

已经更新LCUI在win32下的图形输出支持

测试游戏时,发现游戏角色使用防御动作时,手臂没有黑色边框,难道我忘记为该动作图描边了?让游戏角色面朝右,再次使用防御动作,手臂又有黑色边框了,个人推测是图形的水平翻转存在问题,少绘制了一列像素。

LCUI 现有的问题已经解决,继续游戏开发。

更新镜头焦点的切换策略,从一号角色开始,选择一个有效的角色作为镜头焦点,当1号角色挂了,镜头焦点就会切换至2号角色,以此类推。

2013-10-12

已经为格斗家添加第一个专有技能

格斗家的第二个专有技能也已经添加

在测试技能的过程中,调整了部分技能和攻击效果。

准备加入柔道家的游戏角色。

2013-10-13

柔道家 御堂 这一角色已经加入,柔道家的两个专有技能有待添加。其它小BUG的处理过程在此就不提及了。

角色动作图终于要折腾完了,总是重复基本一样的操作:从模拟器里捕捉动作图-》用PS打开-》调整尺寸-》调整人物朝向-》去背景-》描边-》保存,封装至资源文件里后,还要写代码为每个动作设置参数(攻击框、受攻击框、位置偏移量等),之后还要测试,根据情况对动作参数进行微调。每个角色有65个左右的动作图,这样算来,我已经折腾过300多张动作图了,感觉比堆代码的枯燥程度还高。

看了个网盘广告,据说飞飞网盘能赚钱,于是就进了它的官网;官网的效果还不错,正好要为游戏设计个主菜单界面,可以参考它的效果来做。

保存了网站上实现动态效果所需的三张图,开始加入游戏主菜单界面。

原有的游戏主菜单界面的代码已经删除,重新写。

动态效果的实现代码已经完成,飞飞网盘官网的效果中,有前后两个波浪,都是向左移动,前面的波浪的移动速度比后面的波浪快;针对这个波浪的动态效果,我用了4个部件,每个波浪用2个部件,为什么一个波浪要两个部件?因为波浪的宽度是有限的,移动到左边后,右边就没有波浪显示,这时需要第二个部件显示的波浪来填补这块波浪,当前一个部件显示的波浪已经超出屏幕显示范围,则将它移动至第二个部件的后面,继续向左移动;就这样一直交替,波浪就会在屏幕上源源不断的向左移动。

波浪的移动使用定时器实现,每隔一段时间更改波浪的位置,时间间隔越短,移动速度越快。但遇到一个问题,当我为两个定时器分别设置不同的定时间隔时间后,两个波浪的移动速度是一样的,也就是说,两个不同定时间隔的定时器,实际定时间隔都一样,LCUI的定时器有BUG。

2013-10-14

修改了那两个定时器的定时时间,分别为2秒和10秒,继续测试,结果这两个定时器时10秒响应一次。

加了些代码,以打印定时器线程在处理定时器的状况信息,最后确定问题出现在定时器的位置更新上。

已经修复此BUG,当列表中所有定时器的剩余时间比目标定时器大时,des_i等于-1,该定时器的位置应该是最前面,而不是列表末尾。在定时器的位置更新和定时器的删除操作中,都会使用互斥锁,因此,不需要在操作前后对互斥锁操作,顺便删除了互斥锁操作代码。

添加了一个数据结构,用于保存各个角色的信息,如:角色名、角色ID、角色类型、属性、拥有的技能。在通过角色ID设置玩家使用的角色时,会从角色信息库中获取对应角色信息,以供相关功能利用。

添加了游戏角色选择界面,可以从五个角色中选择一个,游戏角色的属性信息显示功能还未添加。

测试时发现消息框在销毁后,若移动鼠标,程序会异常崩溃,原因是访问了无效的部件数据,这个有待解决。

部件透明度的设置功能也存在问题,有时实现的部件淡入效果,最后并没有变为不透明。

完成了角色属性信息显示功能

添加了一个函数,用于获取已选择的角色;调用它时,如果还未确定选择一个角色,则会阻塞等待,直到点击 角色选择界面 中的”选择该角色“按钮后,该函数才会结束等待,并返回已选择的角色的ID。

本想设置为无时限等待,但是LCUI还不支持,只能设置一个具体的时限,临时解决方法就是将时限设置为MAXINT32。

加了代码,在选择了游戏角色后,游戏会为其它3个CPU玩家随机选择一个角色,然后进入对战。

目前还未添加销毁函数(析构函数),游戏菜单、游戏场景、游戏对象等,自初始化之后,就不会释放掉,直到关闭游戏窗口时,才会由系统释放;因此,想要重新选择一个角色,进行新一场对战,只能重新运行游戏;以后再完善。

2013-10-15

部件透明效果存在问题,比如上面的窗口,透明化的效果如下图所示:

窗口透明了,窗口内的各个部件也会透明,本来白色客户区是遮挡窗口背景的,结果,客户区也透明了,能看到窗口背景色(蓝色);目前的重绘方法是逐层的进行叠加,无法将部件和子部件当成单个图层来实现透明,需要修改。

解决了消息框在关闭后移动鼠标会使程序崩溃的问题,widget_event.c 中保存着一个部件列表,用于处理鼠标移动/点击时的各个部件的状态变化,出问题的原因是在部件销毁后,并没有移除部件在这列表里的记录,使得鼠标移动时,LCUI对无效的部件进行了处理而导致异常崩溃。

柔道家的两个专有技能已经添加,为了实现技能效果,每个角色都添加了一个动作。

接下来就是检查并解决游戏中存在的小问题,无大碍后,准备发布游戏预览版。

2013-10-15

添加了游戏操作方法的图示。

血条的效果有时会不正常,经过仔细测试+观察,这种异常效果出现在跨血条扣血的时候。

增加了角色的攻击力,以更快的触发出跨血条扣血效果;经过 测试->收集信息->分析 后,找到了问题原因:当扣血后减少一个血条数,且生命值刚好充满血槽,会判定为扣了一条血,并显示对应的效果,实际上,生命值处于上条血槽与下条血槽的临界处,应该针对该情况进行单独的处理,效果要和本血槽内扣血的效果一样。已经修改血条的实现代码

接下来就是解决游戏角色有时会一躺不起的问题了。

2013-10-16

在主菜单界面上加了个游戏操作图示框。

在自己将对方推出去后,若自己跳到对方前面,让其正面撞到自己,结果会自己会一直躺着,不会起来。

经过一番调试后发现了问题原因:将对方推出去时,并未撤销双方的对方记录,在双方都倒地后,有一方会先起来,并切换至”下蹲“动作,而自己也准备起来,但检测到有对方记录,并将对方”下蹲“动作误判为对方要下蹲举起自己,于是就不站起了。代码已经修改,这个BUG只是导致一躺不起的问题原因之一,还需要继续找BUG代码。

2013-10-17

在游戏启动画面中添加了一行中文版权声明,顺便加了点代码注释。

添加一个函数,用于调整目标的状态,冲撞攻击的撞飞效果也做了修改,在对目标使用冲撞攻击时,若对方使用擒制技能,且技能目标正好是自己,则忽略冲撞攻击效果,此次修改主要是为了让破招技能更好的发动。

修改各个类型角色的技能,以让破招技能在发动前,目标有合适的状态/动作和位置,确保能够正常实现技能效果。

修改游戏消息循环,不再是固定的睡眠10毫秒了,改用LCUISleeper,在消息队列为空时,进入睡眠,有消息投递进队列时,会打断睡眠,让游戏消息循环及时处理消息。

CPU角色在步行状态下对对方使用了破招技能,但自身还在移动;已经修改代码,发动技能前,停止自身的移动。

柔道家的技能可以修改一下,在扔飞对方后,对方在飞行状态具备攻击性,若碰撞到其它游戏角色,则会击飞他们。

感觉功夫家 姬山 的技能有点少,为他加了 高跳旋转落踢 技能

为 按键提示框 实现了鼠标拖动响应,现在可以用鼠标拖动按键提示框了,点击它时,它会前置显示。

目前为止还没发现什么很大问题,游戏较为稳定,这是本次更新的预览版游戏程序:2D格斗游戏-预览版-01.zip

2013-10-18

对游戏进行了几次测试,有的技能会让目标处于击飞动作,这时被打断的话,击飞效果只能对目标造成位移,但不会改变动作,这样就会使目标一致卡在击飞动作上;修改了代码,在打断技能时,改变目标的动作,以正常触发击飞效果。上面给的预览版程序的下载地址已经更新。

2013-10-19

更新了项目主页内容,删减了LCUI概述页面中多余的内容,加入LCUI架构图,此页面有待继续完善。

在LCUI文档中加了许可协议链接。

需要更新英文版的主页。

2013-10-20

在网站的入口页面中加了javascript代码,如果当前系统语言不是中文的,则修改链接,以进入英文版的页面。

打算把游戏主界面、网络对战功能先完成,作为第一个正式版,游戏AI以后再修改。

正思考游戏主菜单的设计。

2013-10-21

添加了LOGO按钮部件类型,准备往标题栏左边添加一个LOGO按钮。

添加标题栏按钮部件类型,在光标覆盖时或点击它时,会将按钮上的文字缺省颜色设置为白色,但测试时发现文字不能变为白色,查看了textstyle.c和textlayer.c中的代码,TextStyle_FontColor函数中,设置了颜色,但并未将相应标志变量置为TRUE,代码已经修改;TextLayer_Draw函数中,文字的颜色判定代码也有些问题,修改了代码,问题已经解决。

现在的游戏主界面效果如下图所示,圆形LOGO由自己PS而成,可以将游戏启动时显示的LOGO改为这个圆形LOGO。

2013-10-22

想在主界面中显示演示对战,需要一个独立的对战场景,并且与实际游戏对战独立,互不影响。

思考了一段时间,决定添加GameBattle(游戏对战)对象概念,游戏可以有多个对战,每个对战中有各自独立的游戏场景、游戏空间、游戏对象等数据。

改动的地方比较多,不能很快看到效果,没具体思路,干起来很没意思。

折腾了几个小时,算是完成了,但测试时发现,游戏角色会有卡顿,经过几个小时的 加代码->打印信息->测试->收集数据->分析数据,最终发现卡顿是LCUiTimer_Set函数调用造成的,有时耗时1秒以上。

看了LCUI的timer.c里的代码,很快就找到了问题原因:以LCUiTimer_Set函数为例,该函数会使用定时器列表的互斥锁,原先的设计,是先调用LCUISleeper_BreakSleep函数打断定时器线程的睡眠,让定时器线程解开互斥锁,然后让本线程获得定时器列表的互斥锁;但有时定时器线程在中断睡眠、解开互斥锁之后,会立即进行下一循环,并锁上互斥锁,这时,LCUITimer_Set函数才刚结束LCUISleeper_BreakSleep函数调用,在调用Queue_Lock函数锁上定时器列表的互斥锁时,由于已经被定时器线程锁上,会使LCUITimer_Set函数进入阻塞等待,从而导致该函数花费了过多的时间在等待上。

timer.c 的代码已经修改,现在的设计是,统计等待获取互斥锁的线程数,当调用LCUITimer_Set函数时,它会先调用TimerList_GetLock函数,增加计数,然后打断定时器线程的睡眠,之后会获取互斥锁;而定时器线程在睡眠被打断后,会进入一个循环,直到计数不大于0时才退出循环,这样就能腾出足够的时间,让LCUITimer_Set函数得到互斥锁并进行上锁操作,在锁上互斥锁后,会减少计数。

主菜单界面需要改掉,感觉风格与游戏风格不符,用在软件界面上倒是可以。

2013-10-23

上面给的游戏标题栏效果是参考EA官网做的,这次改用 腾讯游戏 主页上的动态彩条效果。

效果不难实现,用两个部件,一个放前面,一个放后面,后者是前者的容器,两个部件中都有一个label部件,前者的文字是白色,后者的文字是黑色。要让彩条在鼠标游标停留在按钮上时迅速拉伸长度,可以让部件响应状态变化,当状态为WIDGET_STATE_HOVER时,让彩条变长,而当状态为WIDGET_STATE_HOVER时,让彩条缩短,可创建个定时器,用于实现彩条的长度变化。

已经添加菜单按钮部件的代码,还没添加被鼠标点击时的效果。

已经为按钮添加点击效果,准备添加GameMenu部件,用于实现游戏菜单,现在的代码虽然能够做出菜单,但个人想弄个独立的部件模块。

已经为GameMenu部件设计了函数接口,等待填补函数的实现代码。

GameMenu部件的基本功能代码已经完成游戏主菜单改用GameMenu部件实现

2013-10-24

为菜单按钮添加对获得/失去焦点时的响应

完善了菜单部件的功能,可以通过响应鼠标的点击来控制子菜单的显示/隐藏,在测试过程中,修改了LCUI的部件状态变化的触发条件,部分函数也名做了修改。

为菜单添加按键控制功能,现在可以用W、A、S、D、↑、←、↓、→、ENTER、ESC这几个键控制菜单中选定的按钮。

2013-10-25

为菜单部件添加了按键点击功能,可通过ENTER、D、J键点击菜单按钮,触发CLICKED事件,部件也会有相应的状态变化,接下来就是实现菜单特效了。

已经为菜单添加视觉特效,在显示/隐藏子菜单时,菜单会进行移动,并且菜单透明度会变化。由于菜单的淡入淡出效果实现起来有些复杂,因此就直接设置菜单透明度了。

在显示模态部件后,菜单还是会对按键做出反应,修改了代码,在处理菜单效果、响应按键控制前,检测主菜单的父级部件是否被允许响应事件,当有模态窗口显示时,该菜单及父级部件都是不允许响应事件的。

需要添加自动换行功能,如何实现?需要花时间构思。

2013-10-26

有些文字在绘制后,有的文字被抹去一部分,可能LCUI的字体坐标计算有点问题,让有些字与其它字出现相交部分,导致在绘制时前面的字体位图的一部分被后面的字体位图覆盖掉了。

想让label部件有背景色,但测试发现会有问题,毕竟文本位图是直接绘制在 label部件上的,若只为label部件设定了背景色,那倒好办,在重绘文本位图时先填充背景色就行了,但是,若部件有背景图像,那怎么办?

有了些想法:之前提到了单缓冲模式,对于有背景图像的部件的局部刷新问题,解决方法就是重绘整个部件区域,若仅仅是背景色,可以直接填充颜色。

修改了textlayer.c,TextLayer结构体中加入graph成员,用于保存TextLayer的图像数据;TextLayer_Draw函数更名为TextLayer_Update,不用给定一个目标图层去绘制了,调用该函数时会更新图像数据,要绘制的时候获取TextLayer的图像数据再进行绘制即可。

由于LCUI在linux和windows上的键盘支持不一样,因此文本框的输入效果也不一样;在linux下是获取输入流中的字符来判断按键状态,假设按住A键,文本框会连续获得字符 a 的输入;而在windows下是直接响应按键消息,文本框只会获得一次字符输入,除非再按一次A键。

修改了LCUI_InputMethod.c,在往文本框输入内容时,按一次键会产生两次字符输入,为解决此问题,添加了按键状态的判断,只在按下按键时产生字符输入。

修改了部件的EVENT_KEYBOARD事件,在事件数据结构中,添加按键状态记录,之前是判断部件事件类型是LCUI_KEYUP还是LCUI_KEYDOWN来判断按键状态的,但这是LCUI的事件,部件事件可不能这样。

TextBox部件和label部件的文本位图渲染操作不是在同一线程上进行的,前者在GUI线程,后者由于使用了自定义部件消息,是在主线程上进行的,有时会使程序异常终止,调试时给出的问题位置在FreeType的函数里。需要想办法将这两个部件的文本位图渲染操作放到同一线程上进行。

为文本框添加了只读模式,不能通过往文本框输入内容。

修改了textbox.c,文本框部件在将文本分割成文本块时,存在内存访问越界问题,现已解决;添加了TextBox_ShowCursor函数,用于设置是否显示文本框中的光标。

2013-10-27

文本框在尺寸变更后,里面的文本位图会出现不该有的图像,估计是在调整TextLayer的图像尺寸时,没有将alpha通道填充为0。修改了LCUI_Graph.c,在内存大小未变大、且图像尺寸有变化的时候,将现有的图像数据中的alpha通道填充为0。

文本框内的文本在滚动的时候,偶尔会有残余字体位图。

准备实现文本框的自动换行功能,准确来说应该是TextLayer的文本自动换行功能。

2013-10-28

思考了一段时间,决定将文本渲染操作统一放在主循环中进行。

label部件在重绘前会刷新文本位图,刷新时会重新载入各个字的位图,需要把这个操作转移至主循环中执行。

测试过程中发现label部件一直有更新、重绘操作,按理来说在绘制出来后就不会再有其它消息出现的。

修改了label.c,定义了几个部件消息,以将文本渲染操作转移至主循环中执行。

TextBox部件中的文本无法显示,后来发现是TextLayer_SetGraphSize函数中的判断有问题,本应是判断宽度和高度是否有改变,实际上由于我复制粘贴条件后忘记修改,使得高度被判断两次,导致文本图层未进行刷新。此问题已经解决

在插入/删减文本时,对当前行及后面所有内容进行自动换行处理。在启用自动换行功能时,对整个文本内容进行自动换行处理,也就是重新对文本进行排版。禁用自动换行功能时,再按默认方法对文本进行排版。当label部件启用了自动调整尺寸,并启用了自动换行功能,那么,若没指定部件尺寸,则将当前容器宽度作为最大宽度,并对文本进行自动换行处理。

经过几次修改,文本的自动换行功能算是能够实现基本效果了,中途出现的BUG,主要是因为没有更新每行的尺寸,导致绘制时,有些行的内容是重叠在一起的。添加了TextLayer_Redraw函数,用于标记每行的文字需要绘制,之前一直是用TextLayer_Refresh函数,该函数会更新每个字的字体位图,并更新每行的尺寸,耗时长。

这个自动换行功能只能用于显示静态文本,在编辑框中使用自动换行功能的话,估计会有问题,例如:光标位置异常。

2013-10-30

测试游戏时,发现Label部件显示的文本不能动态更新显示,后来发现是在更新文本内容后没有标记部件需要重绘,才导致label部件的图形内容未更新,现已解决

2013-10-31

修改了TextLayer_Refresh函数 ,之前测试,发现该函数耗时很长,注释掉用于记录刷新区域的代码,耗时明显减少,话说,添加刷新区域有那么耗时吗?TextLayer_Refresh函数是每更新一行文本就记录该行的区域,现在改为所有行文本更新后添加整个图层的区域。

LCUI的头文件中不应该包含FreeType2的头文件,返回值/参数的类型在FreeType2头文件里定义的函数 也不应该在LCUI头文件中声明。主要是为了避免在编译LCUI程序时因找不到FreeType2的头文件而导致编译失败,毕竟用到FreeType2的功能的是LCUI,而不是基于LCUI的程序。

在显示游戏子菜单后,用于显示“Powered by LCUI ”的label部件出现了问题,鼠标在上面滑过,文字会被抹去;加了些代码,打印部件列表,进行测试,发现该部件还在;再加入代码,打印子图层列表,发现该label部件的图层位置不对,具体如下图所示:

红框框住的是label部件的图层信息,蓝框框住的是子菜单的图层信息,子菜单在显示前是排在该label部件底层的,但在显示后,两者的图层位置被交换掉了。

需要修改图层排序方法,举个例子,有1、2、3、4、5这5个图层,它们的z-index值分别为:1(0),2(0),3(0), 4(0),5(1),如果使用选择排序法,那么排序后的结果为:5(1),2(0),3(0),4(0),1(0),虽然z-index顺序是正确的,但图层的顺序却不对,正确结果为:5(1),1(0),2(0),3(0),4(0),改用冒泡排序法可以得到正确结果。图层的排序算法已经修改

为游戏添加了暂停/继续的功能,测试时,感觉游戏有明显的卡顿,CPU占用率居然上80%了。

2013-11-1

游戏暂停不完整,各个角色自带的定时器并未暂停,游戏角色的动画播放也未暂停。

添加了数值显示功能,视觉效果有待改进。

修改了Queue_Add函数返回值类型,感觉 返回数据的地址 比直接返回位置序号 更好些,因为有时在添加数据后,要引用队列空间里的该数据进行其它操作,再调用Queue_Get函数根据位置序号获取地址的话,感觉有点麻烦。

已经实现游戏角色活动的暂停功能,主要就是暂停游戏角色数据中记录的各个定时器。

2013-11-2

修改了 event.c,添加解除事件连接的功能,由于要实现该功能,移除了其它函数,现在,要连接键盘、鼠标之类的事件,需要调用LCUISysEvent_Conncect函数,该函数会返回事件连接的ID,凭借该ID调用LCUISysEvent_Disconncect函数可以解除相应事件连接。为避免改数据结构的麻烦,就直接用了事件连接记录的内存地址作为事件连接的ID。

已经实现退出对战的功能,游戏线程中,对战退出后会释放相关资源

添加键盘按键事件的连接与解除代码,在开始对战时会连接键盘事件,以响应按键控制,而在退出对战时,会解除按键事件连接,避免进行下一场对战时重复连接键盘的按键事件。

更改了菜单的事件连接代码,此次修改是为了应用修改后的LCUI函数接口。

2013-11-3

在游戏配置数据中加入了控制键的数据,这是为了实现键位设置功能。

在初始化对战前,玩家控制键改为从游戏配置中获取并设置

添加了“键位设置”窗口

除了能读取键位配置,还需要能保存键位配置,功能已经添加

完善了键位设置功能,LCUI的部件堆叠顺序有些问题,第二个模态部件在显示后,不是堆叠在第一个模态部件的前面,而是后面,部件的响应也受到该问题的影响。

2013-11-4

调整了GraphLayer_Front函数的代码

修改了Widget_Front函数代码,已经解决模态部件的显示顺序问题。

由于忘记保存游戏角色挂掉时用的定时器的ID,使得在退出对战后该定时器还在访问无效内存,从而导致程序异常崩溃。该问题已经解决

解决多个模态部件的事件响应问题,当有多个模态部件时,这些模态部件都能响应鼠标光标,现已改为:只让显示在最前面的模态部件进行响应。

VisualStudio2012的Team Foundation Server原来支持Git,试用了一下,用git将项目push到微软的TFS服务器上,折腾了一段时间,装了些所需插件,感觉功能和Github客户端差不多,效果如下图所示:

2013-11-5

浏览了cphalcon项目的README.md,注意到了"build passing"图标,之前看其它项目时也出现过该图标,因好奇所致点了进去,发现这是Travis CI提供的服务,可以用Travis CI在云端对项目进行集成测试。根据Travis CI的文档以及cphalcon项目的.travis.yml文件,为LCUI写了个.travis.yml文件,折腾一番后已经完成了,可以到这里查看LCUI的构建状态:https://travis-ci.org/lc-soft/LCUI

为游戏的伤害值显示功能添加了 膨胀效果 和 数值累计功能,由于短时间内会造成多次伤害,如果把伤害值一个一个显示出来会感觉有些乱,因此就加了个数值累计功能,用于累计短时间内造成的伤害,每当有新伤害时,会更新显示的数值,并让显示的数值产生膨胀效果。

2013-11-6

更新数值提示的显示坐标判定,以确保显示在目标的头上。

调整处于被举起状态时的技能发动代码,修改可被擒获的目标的判定代码,现在,处于WEAK_RUN状态的角色也能被擒获。

添加几条AI策略,修改“远离”动作处理代码

添加了一个函数,用于向对战中添加更多的游戏角色,经过测试,本以为100个游戏玩家没问题,结果游戏只能在游戏角色数为30个左右时才能流畅运行,100个直接卡爆。暂时不用考虑优化。

2013-11-7

改进AI,添加“防御”策略,现在AI玩家在将要受到目标玩家攻击时进行防御。

2013-11-8

添加StatusBar_SetAvatar函数的实现代码。

添加一个头像资源,作为游戏角色的缺省头像。

2013-11-9

测试2D地图编辑器,本来是透明背景的地图对象却变成了白色背景。文本框也有问题,没显示文字。

是地图对象背景图的设置问题,地图对象的图像是引用自源图中的一块区域里的,在处理地图对象的图像时,调用了Graph_Copy函数将源图中的引用区域里的图像拷贝成独立图像,然后设置为背景图。问题就出在Graph_Copy函数,它拷贝出来的图像的色彩类型不和源图像一致,默认为RGB,使得图像的透明区域被白色替代了。修改了Graph_Copy函数的代码,问题已经解决。

载入以前做的地图,显示出来的地图有些异常,部分地图块不是固定的,被鼠标光标覆盖时是正常的地图快,拖动整张地图后,部分地图块就变了。算了,懒得搞。

测试login程序,文本框的占位符显示有些异常,有时会使程序异常崩溃。这个问题其实在之前就已经发现了。占位符的文本渲染操作是在GUI线程上进行的,TextBox部件Update函数会判断文本框内容是否为空,为空则设置显示占位符。

已经改用响应消息的形式更新占位符的显示,也就是把占位符的显示与隐藏的操作代码分离成两个函数,将这两个函数与自定义消息连接,然后在需要显示/隐藏占位符的时候,就投递相应的消息。

向密码框输入内容时,并未显示屏蔽符,文本光标也没显示。经过测试+收集信息+分析后,发现文本并未重绘,原因是该行文本不在可见范围内,可见范围是通过每行的尺寸计算出来的,但由于在同一行内进行文本删除/添加后并未更新该行的尺寸,导致可见范围计算错误。删除文本时,计算出来的刷新区域有问题,有残余的文本位图,后来发现,刷新区域是根据原始文本的位图计算出来的,而不是根据屏蔽符的尺寸。这些问题已经纠正,但文本光标的定位还存在问题,估计也是因为不是根据屏蔽符的尺寸来计算光标位置的,有待继续改进。

2013-11-10

花了一些时间折腾了一下,现在LCUI的源代码已经能够在travis-ci上构建通过了,即:./configure && make。

2013-11-11

看来还是解决文本框的文本块分割的问题,若文本框启用了样式标签,那么在对文本分割成文本块时,文本中包含的样式标签可能会被截断,在处理文本块时,就会将被截断的样式标签作为普通文本处理并显示。

2013-11-12

更新了文本块的分割处理,若文本框启用的样式标签,则在对文本进行分割时,会判断是否有样式标签,有则判断当前文本块的剩余空间是否能够容纳该标签,若不能,则调整文本块的大小,这样就能保证样式标签在文本块里是完整的。

2013-11-13

正在为LCUI的0.15.0的发布而做准备。

2013-11-14

使用互斥锁保护已记录的线程信息。

Widget_Event_Connect函数更名为Widget_ConnectEvent。

2013-11-15

Makefile.am已经更新完毕。

2013-11-16

需要为游戏确定一个正式的名字,以向开源社区正式公开本游戏项目。

2013-11-19

滑动解锁程序一运行就会异常崩溃,Debug版无此问题,后来发现是存储图片资源的LCUI_Graph结构体数组容量不够,使程序出现内存访问越界问题。

登录界面演示程序中的文本框有问题,不能按backspace键删除字符。此问题是因为之前修改事件机制时,相关代码没修改完全,在为事件响应而准备程序任务时,没有正确的程序ID,导致任务添加失败,因此,程序主循环中无法收到该任务并进部件事件的响应处理。代码已经修改

每逢新版本发布前,都需要检验各个演示程序是否能够正常编译、能够实现应有的效果,麻烦。

文本框还存在问题,进行多行文本编辑时,感觉反应有点慢,编辑文本时文本的插入位置也出了问题,直接断行时,有残余文本位图。

不能正常根据css文件中记录的样式来更改部件的样式。

这些问题以后再解决。

2013-11-22

准备发布 LCUI 0.15.0 和 游戏。

已经发布。

呃,二维码出了问题,去草料二维码网站上看,原来已经不提供文件上传功能了,需要改用网盘。