Feb 1

重构伪春菜Nana整理

Lrdcq , 2017/02/01 23:12 , 日誌 , 閱讀(5059) , Via 本站原創
对,就是博客右下角的Nana酱,现在已经是重构之后的版本了。部分新功能的代码已经写入,但是并没有启用,现在功能基本和之前的版本保持着一致,等待稍后的更新和补充。另外再确认一下定位,伪春菜不是人工智能(比如小娜),只是一个桌面辅助程序(就像office的大眼夹一样),因此主功能完全由前端提供完全没问题,交互也单纯通过点击选项完成,不包含输入框等。当然,显然如果要提供更复杂的服务,肯定还是需要网络通讯,因此也准备了网络通讯的方法技术预留。

1.立绘重绘

其实我首先做的是重新绘制了一次立绘。重绘立绘一大原因当然是之前用的图是网上随便扒的,盗图当然不好,而去其实那张图并不适合做伪春菜,而且还真的找不到更加合适的图了。因此这次重绘了更加适合的图:

1.伪春菜使用的重绘应该是Q版的绘图,就算不是,头也应该明显大很多,并且身体侧向文本框侧更好。因此这次基本按照这个需要来绘制,即4头身+左侧身。应该差不多了。

2.伪春菜的实际使用往往会有多个立绘或者表情。之前那张图由于是盗图,也懒得并很难ps出其他表情,而这次不一样,因为自己是分层绘制的,所以可以方便的导出不同表情的立绘并组合使用。最终导出的表情和素材如下:

點擊在新視窗中瀏覽此圖片

2.功能重新设计

之前的伪春菜的设计是按照 页面-反馈 模式进行的,也就是特定的页面,或者特点的操作触发特定的对话,然后进行一条对话链条最终结束。虽然伪春菜基本确实是这样用的,总有一种在玩galgame的感觉。当时后来觉得缺了点什么,加入了空闲随机对话的功能,但并没有什么卵用。

这次重构的时候才意识到,缺少的是一个集合功能的中心菜单。无论是在何处的乱七八糟的功能,只要不是针对页面的功能,都集合在中心菜单中,这样也可以保障外层菜单的干净。最后在页面-反馈模式的基础上,硬塞入了中心菜单,页面对话的选项包括[页面功能,前往菜单,关闭],而中心菜单就可以随便加入像加入的各种乱七八糟的功能。

另外还有一个是对话随机性。伪春菜一大问题就是不想人工智能一样回复对话是逻辑合成的,伪春菜的对话都是事先预订好的,包括部分伪春菜的伪人工智能(实质是正则识别关键字和句式)作出的回复都是预订死的(顶多塞入正则匹配的结果)。因此我们可以在细节上开刀让对话稍显丰富。因此设计了一个同义词库,里面可以塞入可以随意替换的词或者语句,这样生成对话的时候会更加丰富一些。代码看起来像是这样:
m.dic(["你好", "Hello", "Hi", "欢迎", "Konnichiwa"]);
m.dic(["我是Nana", "Nana在此"]);
m.dic(["欢迎来到Lrdcq的个人博客", "这里是Lrdcq的个人博客", "Lrdcq的个人博客就是这里啦"]);
    
data[NANA.PAGE_INDEX] = [
        new m("##你好##,##我是Nana##,##欢迎来到Lrdcq的个人博客##.<br>这里。。。", NANA.FACE_NORMAL)];

这样可以保证编写剧本的可理解性,并且也方便解析咯。

同时,这一套东西可以完成更加复杂的功能,dic方法的参数有三种:
void : dic(key : string ,data : array);
//声明名叫key的替换词,从data中随机取

void : dic(data : array);
//声明名叫data[0]的替换词,从data中随机取,这是上一个方法的简化版本

void : dic(key : string ,handler : function);
//声明名叫key的替换词,从handler中运行返回

通过第三种重载方法,我们可以动态(同步)的塞入很多东西。至于异步的塞入数据(比如需要从接口访问的数据),现在设计的策略是此处返回一个新建的空element,然后在这个闭包中完成异步操作再把数据塞回来,由于是在闭包中完成的,所以可以避免很多多异步的冲突问题,可谓是一举多得。

除了对话部分,包括表情,也增加了随机选择和回调方法选择,可以方便的进行特殊功能拓展。

同时,由于对话和表情可以通过回调方法进行特定中断选择,因此,依赖这些方法完成了一个简易的好感度系统,提供方法增减好感度,并且通过定制好的回调选择对应好感度的对话。现在还没启用(因为剧本编写量翻倍啦),但是用起来的话应该是一个不错的锦上添花的功能。

3.代码重构

最后回到代码层面上。原本的代码是我4年前初学js面向对象的时候写的,当时的方法还是全局建立命名域,然后在里面通过prototype写方法之类的。然后因为功能单一,也没有使用什么第三方库,就用原生(并且尽量低端的)api完成的。(就是漫天getElementById的感觉)。由于不是完成重构,因此这次在这基础上进行的现代化修改。

1. module化。将每一个类通过立即运行函数的方法包起来并且导出。比如原本直接裸声明在外面的Nana类现在的样子是:
var NANA = (function () {
  var NANA = function () {...}
  NANA.prototype.set = function (d) {...};
  ...
  return NANA;
})();

同样还是导出的类型NANA,但是由于环境在立即运行的函数的闭包内部了,所以需要的话可以很方便而且随意的加入闭包类变量作为私有变量,而不用全部绑在当前实例化的对象里(那样还并不安全)。

2. 常量化。之前代码中的很多数据都用常量重新进行了声明。比如预订好会调用的对话id,表情id等。部分参数也用常量声明来方便配置。反正在闭包里面的无所谓。

3. 工具集合。除了最核心的几个类型是面向对象的,其他小工具,特别是需要在对话嵌入的html中调用的工具还是一个一个的函数,为了防止module化后导出爆炸,因此导出的小函数还是通过一个对象进行了集合,html中调用虽然麻烦了一点,但js上下文还是干净了很多。

其他重构的内容就是根据上面的功能设计,该加类型加类型,该加逻辑加逻辑,总之,向面向对象和面向现代的js开发有进了一步。不过,和之前一样,为了用原生js保持最低限度的浏览器可用性,还是没有大量使用比较高端的api,当然这种小玩意儿引入babel之类的也没考虑啦。



最后,这样开发完成之后,基本功能的重构就算告一段落,总之现在的伪春菜Nana功能拓展性又达到了一个新的高度,接下来我会继续往里面塞入各种各样的功能,敬请期待。另外,也在调研是否可以和第三方人工智能工具结合使用来完成更自然的交互体验。
logo