前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >引擎工具开发的一些总结

引擎工具开发的一些总结

作者头像
逍遥剑客
发布2018-05-21 15:59:30
8260
发布2018-05-21 15:59:30
举报

参数编辑


可以说, 引擎工具在除了一些特定操作外, 80%的事情都是在进行参数的编辑与保存等. 从我接触工具开发开始, 就一直在学习如何简化这么部分的工作. 因为我见过很多业余的编辑器, 大多都是每加一个参数就在UI层写一些代码, 在IO层加一些版本兼容代码等. 而这些代码常常都是大同小异的, 很多都是Ctrl+C, Ctrl+V出来的. 说起来, 这个探索过程中我也走了不少的弯路, 顺便写出来当教训吧

最早是从java/.net转来写C++, 所以对于C++的UI开发十分的不满. 但是对于3D引擎来说, 目前来说语言也没有更好的的选择. 所以, 也有很多引擎是多语言的架构, 如底层C++, 工具C#, 逻辑lua. 这是比较常见的一种选择. .net在语言层次对于反射和序列化提供了非常好的支持, 可以参考我早期的文章: 强大的PropertyGrid. 为此, 我自学了C++/CLI, 把C++与.net的interop全部搞定了, 并且使用WPF试着做了一个工具. 结果呢? 没有人能维护的了, 因为.net对于那些只写过C/C++的人来说, 太复杂了. 更何况, 又加上一个毁三观的WPF. 所以说, 新技术并不一定就好, 就算它们吹的多么玄乎, 也不要忘了自己团队的实际情况. 虽然这次尝试最终放弃了, 不过在此过程中积累的经验让我山寨成功了Unity3D的脚本系统. 这条路不是说走不通, 因为很多国外的中小引擎就是这么干的. 只不过对于开发人员的要求会高一些. 做过脚本系统的人都知道, 在两种语言之间转来转去的要多恶心就有多恶心.

当然, 上面这些尝试, 多数是业余的技术玩具. 工作中, 很多工具还是MFC的. 所以现实一点的话, 一般还是会在原有基础上做. 所以那时也参考了一下同样是MFC写的Ogitor(后来改Qt了). 于是乎就有了这么一篇: 基于属性的编辑器框架. 这个思路经过验证还是不错的, 对于当时的我来说, 在一条没有人走过的路上把东西做出来了, 算是一种自我突破. 一些编辑器常见的问题: Undo/Redo, 版本格式兼容等也做了考虑. 不过有两个问题没有解决: 一是属性的访问效率, 二是代码冗余(手工重复添加的代码太多).

中间还试过把WPF的控件放到MFC的工具里, 虽说技术上的问题也都解决了, 但是只要出了问题别人都搞不定. 算是一条邪路, 哈哈

目前阶段, 在接触了一些大牛和商业引擎后, 最终的选择是: Qt + C++反射 + C++序列化. 这个虽然有对语言进行改造的嫌疑, 但是实际项目验证下来, 是相对比较完美的解决方案. 因为一旦把底层搞定了, 后续开发可以节省程序至少三分之二的开发量, 一劳永逸. 参见: 关于游戏引擎结构上的思考C++的反射和序列化

Undo/Redo(撤消/重做)


我们一大牛说过: "判断一个工具是不是成熟, 就看它有没有Undo/Redo的功能". 的确是这样的, 因为在我维护过的编辑器里, 只要没有做到这一点的, 编辑器都是拿代码堆出来的, 没有一个整体上的设计, 然后换个人来维护就是死去活来的感觉. 其实看过设计模式的人都知道, Undo/Redo就是使用Command模式来解决. 但是, 从我面试过的人来看, 大多数都是知道这个模式, 真正做了的很少. 因为这个模式有一个弊端: 编码量大. 因为很多操作只是改变一个变量的值而已. 所以呢, 一些偷懒的程序员, 就把这个功能给省了, 反正工具的用户通常最低的要求是"先有这个功能, 再考虑易用性". 在基于属性的编辑器框架里, 我第一次尝试了基于属性的Undo/Redo. "编辑器"其实本质上来说, 就是"编辑数据". 所以呢, Undo/Redo本质上来说, 就是"数据"的备份/还原的过程. 按照这个思路来设计, 肯定是没有错的. 最近结合C++的反射序列化做了Undo/Redo, 其实就是通用的Undo/Redo操作. 这样就解决了Undo/Redo需要每个操作都定义一个Command的问题, 因为数据抽象了, Command也简化了. 

文件格式版本兼容


这也是一个很多项目面临的问题. 对于二进制文件来说, 低水平的人会直接把结构体写进去, 加个版本号; 中水平的人会使用ChunkData, 让格式可以扩充. 高水平的呢? 格式中保存的参数可以改变类型, 增加/删除属性, 不但向下兼容, 还向上兼容. 所以说, 很多人会选择XML/JSON来做开发时的数据保存格式. 相对于二进制格式来说, XML/JSON为什么可以实现新老版本之间的兼容呢? 我觉得本质上来说, 还是因为它的属性访问是基于"名字"的. 也就是说, 把文件格式设计成类似于map的方式, 通过key去查找对应的值, 就可以实现版本之间的兼容. 形象点说, 文件里保存的是pair<name, value>的集合. 那么, 二进制格式也要把"名字"字符串保存进去吗? 虽说有这么干的, 更好的办法是保存字符串的CRC值, 也就是pair<nameCRC, value>.

稳定性


不会犯错的程序员, 基本上是不存在的. 所以呢, 工具中新开发的功能, 99%是有BUG的. 严重一点, 就是崩溃了. 然后程序就会不停打喷嚏了. 如何改善工具的稳定性呢? 这个需要分三个方面来说. 预防, 容错, 查错, 三管齐下.

把错误扼杀的摇篮里是最理想的. 这个就需要引入自动测试. 但是呢, 我之前都没有重视这个, 然后就不停地做善后工作. 做烦了回头来想想, 原来是这个东西没做到位...所以呢, 算是一个教训, 还没有经验, 提醒自己改进

容错的话, 通常就是做异常处理了. 这个只是补救的办法, 让用户体验好一点而已. 如果之前使用了Command模式, 那就更好办了, 只需要在Command执行的时候统一处理就行了. 虽说容错可能会导致更加严重的错误, 但是多数是可以补救的, 利大于弊

查错. 如果经常在做这件事, 那就要从头想想自己哪里没做到位. 这里说的, 只是方便查错的一些措施. 一, 多写log, 代码中多写assert. 二, 生成dump, 让用户可以反馈崩溃. 顺便推荐一个库: CrashRpt

以上就是工作以来的一些经验教训了. 下一步还有什么可以尝试的? 或许是多人协作编辑, 多进程通信, 插件机制......

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2013年07月20日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档