引擎工具开发的一些总结

参数编辑


可以说, 引擎工具在除了一些特定操作外, 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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

超越线程池:Java并发并没有你想的那么糟糕

很多人一直唠叨着并发中的新概念。然而,许多开发人员还没有机会把过多的注意力都放在上面。在这篇文章中,我们将带您了解Java 8 streams、 Hadoop、...

10120
来自专栏架构师之旅

3个面试中遇到的问题《JAVA面试题》

面试官:“一个http 请求,接受json数组,数组内容是id,返回用户信息,在测试上是ok的,到预生产就报错了,可能是什么问题?” 我想了想说:“代码一致吗?...

91750
来自专栏开发与安全

在腾讯实习的那段日子:不要在难受的时候选择 '逃避/离开'

时间过得很快,从2014.6.5入职实习到2015.1.5已经是7个月的时间了,在这边还是学到了很多东西,遇到的人大多数比较nice。中间拿到了留任offer,...

23700
来自专栏csxiaoyao

USTC高级软件工程课程学习心得

394100
来自专栏喔家ArchiSelf

回顾Bob大叔的简洁架构

Robert Martin 就是我们常说的Bob大叔,是码界的骨灰级人物了,在4年前提出了所谓的简洁架构,值得回顾反思一下,看看是否可以借鉴到微服务中呢?

10520
来自专栏编舟记

架构整洁之道导读(二)

我是《架构整洁之道》(Clean Architecture) 中文版的技术审校者,在审校的过程当中略有感悟,所以希望通过撰写导读的方式分享给大家。

13220
来自专栏HansBug's Lab

【作业2.0】HansBug的5-7次OO作业分析与小结,以及一些个人体会

25540
来自专栏机器学习算法与Python学习

Python:10篇不可错过的~热文~》》真的很热》》

以下是精选了“ Python开发者” 5月份的10篇 Python 热文。其中有基础知识,项目实战等。 《Python 爬虫建站入门手记(1):环境搭建》 本文...

32830
来自专栏温安适的blog

3个面试中遇到的问题

6月是个忙碌的月份,结完婚,处理完家事,也换了份工作。以至于6月都没有写blog,今天闲来无事,将之前面试的问题,以及一些感悟分享给大家。

18430
来自专栏养码场

一位资深Java的阿里系公司实战面试经验,套路还是面试官的多

占小狼:一位奋斗在魔都的资深Java开发。去年6月在简书上发第一篇技术文章,已坚持发表76篇技术文章,粉丝数突破4000。

24370

扫码关注云+社区

领取腾讯云代金券