又到年底了，年更博主不忘初心还是坚持一年写一篇博客，这是一种什么样的精神！ 两年前就开始捣鼓3D打印，当时买了台创想三维的Ender 3 Max。用了两年，但其实真正打的东西不多，并且打出来的效果很多也不太令人满意。究其原因还是因为Ender 3 Max用的是i3架构，打印时整个热床都得前后移动以保证Y轴填料。这个黑五趁着打折入了个拓竹P1S，因为是CoreXY架构，热床可以保持相对静止，再加上一些软件层面的调教做得不错，已经算是一台很成功的3D打印机了。也就顺势把我雪藏多年的两个项目又掏出来做了。 第一个项目是哆啦A梦的道具。 想法是把哆啦A梦最常见或者最被读者喜欢的道具建模、打印出来。因为任意门算是其中比较好做的，就先拿任意门开刀了。建模方面因为还在学习（摸鱼）Blender中，就先用TinkerCAD这种傻瓜平台了！ 上面这个是最终效果，其实在建模的过程中也打印了好几次试手感。有些细小的零件感觉留公差什么的还是挺麻烦的，得多打印几次或者积累经验才能做得更流畅一些。 打印用的是白色PLA，看起来还不错，稍微磨了一下就上色了。但是因为实在是调不出任意门那种紫红/粉红的颜色，最终凑合出了个朱红色…… 任意门这边就告一段落了，接下来还打算做竹蜻蜓和放大灯、缩小灯。不过这些都需要涉及到电路板。试着做了一些竹蜻蜓的设计，不过底座部分所需要的空间还是过于大了，不知道能不能找到小一些的电路板+电池组合，否则实在是很难做下去……至于两个手电筒，感觉会稍微简单一些，材料也都买好了，可能最近就会陆陆续续开始做。但这个博客是年更制，所以下次更新是什么时候那谁也说不好好.jpg 另一个一直想做的项目就是都市妖奇谈的纪念币了，参看：都市妖奇談紀念幣。我都忘了这是什么时候立的项了，反正要比这篇博客要早几年。当时的想法是设计出来之后直接丢给淘宝去开模制作，但因为开模一次的费用也不少，并且纪念币不走量制作的话确实很亏，而我也没都市妖的版权，不能卖做好的纪念币，想来想去还是算了…… 前些年有Ender 3 Max的时候也做过一些3D打印的尝试，但主要有两个问题：1）打印精度不够，虽然喷嘴我也换成了0.2的了，但一些震纹层纹之类的东西还是显得纪念币很廉价；2）3D打印的归根结底还是塑料，拿在手上很轻，会感觉很廉价。我也想过往里塞铁片，但这个方案虽然会增加重量，外层还是塑料的手感，也不是特别靠谱。基于以上这两点问题，最后也没能推进项目的进展。 但今年换拓竹之后试过用0.2喷嘴打印，效果好像比之前要好很多了，所以又花了点时间研究这个项目。 首先想到的方案是打印出塑料模具，然后用火漆（封蜡）填充，再用石膏包裹住火漆硬币做一个石膏模具，然后在烤箱裡融化掉火漆，最后得到一个空心的石膏模具用来浇筑金属。想得很好，实际执行结果却并不如人意…… 火漆的粘稠程度远超我的想象，并且可能因为是冬天的缘故，刚浇筑没多久火漆就开始冷却凝固，以至于很多地方并没有被火漆完美填充。最后脱模也是惨不忍睹…… 然后我调整战略，买了一些做硅胶的AB胶，想着先3D打印一个做硅胶模具的模具，然后用硅胶模具做出火漆硬币，这样应该就会更方便脱模了。 再一次，想法很好……Well，其实这次做出来的成品火漆硬币在细节方面也还说得过去吧，但因为硅胶过于柔软，在成型的时候硬币很容易收到外力扭曲，最终的效果还是不够理想（这次甚至没有拍照片，一定是太失望了！）。 没办法，只能接着改良方案。聪明伶俐的我灵机一动，我好像也不是一定要火漆硬币吧？我的目的是最后做出一个中空的石膏模具用来浇筑金属，那如果我能直接解决石膏模具不就能跳过做出火漆硬币这一步了？ 所以我又变换思路，打印了制作硅胶模具的边框，而做出来的硅胶模具又可以做出石膏模具…… 至于结果如何，现在还不好说……反正到石膏这一步我觉得还算OK。有一些小瑕疵，可能是因为空气没有完全抖出来，如果接下来的测试进行顺利的话，后面可能会稍微扩大硬币的尺寸，也就会更方便一些了。至于为什么现在还没继续……是因为我的金属被卡在海关了……因为我不想冒险用高熔点金属，家里也没那个条件，所以从淘宝上买了一些低熔点合金（无铅无镉），熔点好像不到两百度。如果一切顺利，不被海关扣下来的话，这几个月也能继续进行项目了……
转眼就到年屁股了，再不写点什么今年就没有任何博客发表了！那就来推荐一下个人觉得今年最好玩的游戏《巴别塔圣歌》吧。（不知道为什么，最近的游戏是越来越玩不下去了，今年能让我觉得好玩的游戏其实不太多。也许《博德之门3》算是另一个我觉得特别上瘾的游戏了，但对于《博3》这种游戏类型，我觉得它属于成功，但也理应如此的范畴。而《巴别塔圣歌》是一款真的让我玩了以后觉得特别惊艳的独立游戏——也许和我喜欢捣鼓语言也有关系。） 《巴别塔圣歌》的英文名是Chants of Sennaar，并没有出现巴别塔这个词，但根据维基百科对Sennaar/Shinar一词的描述： In the Book of Genesis 10:10, the beginning of Nimrod's kingdom is said to have been "Babel [Babylon], and Erech [Uruk], and Akkad, and Calneh, in the land of Shinar." Verse 11:2 states that Shinar enclosed the plain that became the site of the Tower of Babel after the Great Flood. 《创世记》第10章第10节中说，尼姆罗德王国的起点是「示拿地的巴别（巴比伦）、厄瑞克（乌鲁克）、阿卡德和迦勒尼」。第11章第2节指出，示拿包围着在大洪水后建立起巴别塔的平原。 可以理解为Sennaar就是广义上的巴别塔地区，结合游戏中确实以高塔楼层的形式分隔各个族群/语言，开发者的灵感之一应该的确是来源于巴别塔。游戏的中文化做得还是比较成功的，可能也是出于巴别塔更容易被玩家理解的考量将其翻译为了《巴别塔圣歌》。 游戏背景是一座塔内五层人民使用着不同的语言，互相无法理解，产生分歧。而你这个不知道从哪蹦出来的人需要一一破译他们的语言/文字，并帮助他们交流、重新建立关系。 在探索过程中，你可以对他们使用的文字逐一记录想法，记录之后对方再说话时鼠标放上去就能显示出你对这个句子的理解。同时，你还会有一个小本本，每当经历过一段满足条件的剧情后都会有一页新的破译页面出现。你要做的就是根据你所掌握的词汇，将你认为合适的字符填进去验证。之后，如果遇上一段文字包含的所有词汇都是你所验证过的，就能完全理解其意思——也就是会转换成游戏玩家所能看懂的文字和语序。 千万不要以为它就只有字符对应单词这点能耐了！实际上每一层文字的字符结构、语序等都很不相同，就跟现实中的语言似的。不同的词性会有不同的造型、相同的事物会有相近的结构等等，甚至在第三层还出现了类似南亚/东南亚结构的文字，有着类似西班牙语感叹句/问句类似的结构（¡!/¿?）。 第二层相对来说会更考验操作一些，因为这一章大概前一半的时间你都需要潜行绕开NPC。不过这种程度的操作连我这种手残都能完全胜任——最多也就是多死几次，反正原地复活嘛。 当你理解了多种语言之后，还能回到传送装置帮助使用不同语言的人民互相理解、化解矛盾。这也是我觉得游戏做得很有意思的地方：一层的人是凡人、神的追随者，给人感觉就是偏底层一些的人，他们使用的文字也偏图形，语法结构也不复杂；二层的是士兵，他们的文字更有棱有角一些，不过语法结构和一层大差不差；三层的是诗人，他们的文字比较有艺术性，也多了很多抽象概念，语法结构也和前两层的有很大区别了。感觉就像是回到了古代，上流社会使用的语言/文字和底层人民的完全不一样，或者是底层人民几乎不懂写字。 另外一个我觉得很现实的小细节是，不同人民对相同事物会有完全不一样的表述。这个表述指的不是字符不一样，而是当你破解了他们的文字之后会发现虽然他们指的是同一件事物，但想法却完全不一样。比如一层人口中的「信徒」，在二层人看来却是「不洁之人」；二层人眼里的「神选之人」是三层人的「诗人」。语言的隔阂真的很有意思www 目前我还在第三层——据说是最难的一层，抽象结构特别多，不过就目前的体验而言，我想给《巴别塔圣歌》打10/10的满分。也许唯一的缺点就是重玩的概率不大……
《勇者斗恶龙建造者2》：谁说沙盒建造游戏不能有剧情？
很早很早很早以前就一直想买《勇者斗恶龙建造者2》来玩了，一直没买主要是考虑到两点：一是想买个卡带玩，二是我很确定这游戏对我而言是个巨大的时间黑洞……一直没敢对自己下狠手。最近港区打折，看了看居高不下的卡带价格，没经受起群友的诱惑，还是对时间痛下杀手了。 很明显，《勇者斗恶龙建造者》（下简称dqb）系列是受到Minecraft的启发立项的，我没玩过dqb一代，但二代给我的感觉已经基本脱离Minecraft发展出了自己的风格：各种方块、家具、道具十分齐全；融入了乘骑系统、种田培育系统；用设计图简化了建造流程等等，就像是在玩一个Mod丰富的Minecraft懒人包一样……【。 整体感受 以前和朋友联机玩Minecraft的时候就发现，每个人喜欢的类型不一样，有些人就不能接受完全没有目标的沙盒游戏（虽然Minecraft有终极目标打倒末影龙，但中途所有的过程都没有明确指向）。而dqb2实际上就很好地结合了沙盒游戏和任务剧情。通过剧情玩家一步一步解锁更多物品和建材，也慢慢了解到各种不同的游戏机制。在大约60小时的剧情之后，当玩家彻底了解这款游戏的机制时，真正的沙盒探索、建造才刚刚开始。 优点 剧情 玩之前我很确定自己会因为dqb2的沙盒建造而废寝忘食，而玩了试玩版之后我才发现原来这游戏能吸引人的不光是它的自由度。可能是因为冠了勇者斗恶龙的名，dqb2的剧情真的十分引人入胜，而且每个任务都环环相扣，玩的时候我经常对自己说「好了，再多做一个任务就关游戏……」 很难在不剧透的情况下把这部分说清楚，不过大体上就是dqb2的剧情分散在几个小岛上，而每一个岛都有自己的特色，让玩家对每个岛的剧情都很好奇，从而停不下来地想探索更多东西。它不像有些游戏一样剧情拖沓、玩家需要做好多任务才能推进一点点剧情，dqb2给我的感受是，你做一个任务就能立刻获得一个任务带来的回报。这个回报可以是推进剧情，也可以是给玩家成就感与满足感的其他东西，总之它一定会给玩家一些甜头，所以才有了之前我说的「完全停不下来」的感觉。穿插在剧情之中，每个岛也都能让玩家加深对游戏机制的理解，也能体验到游戏里不同道具、不同建材的乐趣。我觉得是十分精巧的设计。 数值设计 在玩主线的过程中我不止一次地和多个朋友提及dqb2的设计，说游戏课程应该拿它来做范例，很多东西都是教科书级别的存在。除了上面说的剧情之外，数值设计也是一个很有意思的东西。在dqb2里玩家几乎不会感觉到打剧情很累，因为数值设计得都恰到好处。只要跟着任务走，偶尔搜刮点东西、打打怪，你完全不需要为等级、素材发愁（就算素材有短缺也很容易在附近取得，也是正面设计的体现之一）。而为了让玩家又能体验到紧张刺激的感觉，在不同阶段游戏又给玩家设置了不同的「上限」，所以玩起来既不会觉得过分难，也不会觉得毫无体验。 除了战斗方面，在所需素材上的设计也很不错。游戏开头十小时，几乎所有素材都需要玩家手撸，而正当玩家开始觉得素材获取有些无聊，开始产生疲惫感的时候，dqb2用素材岛给玩家眼前一亮的感觉。所谓素材岛，和《动物森友会》里的素材岛又有区别，动森里的素材岛是每次玩家有需求就得去岛上获取限量的素材，而dqb2里玩家只需要去素材岛上记录陌生的物品、资源或动植物，完成所有项目时系统会奖励玩家无限使用某一种相对应的资源。这样循序渐进，玩家只需要自己采集相对有新鲜感的资源即可继续建造各种东西，大大推迟了「感受到无聊」的出现时间。 游戏难度 基于以上两点，dqb2实在是说不上是一款硬核游戏，甚至可以说是轻松娱乐向（毕竟是沙盒建造、种田，轻松才是对的吧喂！）除了上面提到的打怪完全不用担心等级之外，就算是BOSS战，只要掌握了上手很快的战斗机制，甚至可以比打小兵还轻松（😥是的，我没在BOSS手下死过，倒是被小兵打趴过） 剧情部分我用了大约60小时，不过期间我也偷偷做了一些其他事。如果认真先打剧情的话，应该还能少个10小时吧。 自由玩法 dqb2的剧情很棒，特别棒，以至于有朋友说打完剧情都没有动力玩沙盒模式了。我曾经也一度这么担心过。但当我真正打完剧情进入自由模式之后才发现我的担心完全是多余的。实际上通关之后玩家能做很多在剧情里没来得及多体验或者是压根没有体验过的东西，还能解锁很多新奇的、帮助玩家更方便建设岛屿的工具（还能钓鱼！） 如果觉得素材收集太麻烦，还可以去各个素材岛上探索、完成清单，大部分素材都可通过这种方式变成无限使用。光是探索这些素材岛就能花掉一大把时间，更别提你还需要在上面抓小动物和收集种子…… 等你完成了上面这些，就可以专心建设自己的小岛了。除了建造各种房屋、设施满足岛民的日常需求之外，你还需要开垦农田和培育小动物，活脱脱一个买RPG送牧场物语……相信喜欢Minecraft的玩家一定会继续沉迷好久…… 优化 玩的时候我还有点怀疑，毕竟岛上那么多人、那么多植物，还有一些动物、魔物，计算每个个体已经很吃资源了，还得渲染满地掉落物（比如小心心），在资源如此有限的Switch（高情商表达.jpg）上，dqb2应该会挺吃力的吧……但实际体验下来，将近60个岛民和17片种满的田地，几乎没有什么严重的卡顿。经历过几次卡顿都是因为NPC太蠢，卡在死角陷入了找路循环……看来这部分倒是需要加强一些。 不过有优化就会有限制，dqb2在限制方面的方案就是对各种东西都设置上限：人口上限、进餐点上限、烹饪点上限、储物箱和衣柜上限、相框上限、作物上限……等等等等。最主要的还是人口上限，光这一点就决定了后面那些基本上不会触碰到上限点。目前来说对我没有什么影响，不过如果你打算养个几百人遍布全岛什么的，那确实是办不到了…… 不足 本来想写「缺点」，但想了想，其实这些也不算很致命的缺点，还是叫「不足」更为贴切一些。希望未来如果有机会出第三代的话能在这些方面多费些心思。 配方提示 主要是食谱和房间配方吧。 食谱很好理解，在dqb2里你需要往烹饪器材里丢1~3种不同的食材，可以做出不同的料理。不光食材的范围很大，从作物、鱼肉到魔物掉落、花花草草都可以成为食材，就连牛奶、水、甚至一次加工后的食物也可以放进去，而烹饪器皿也分火上操作和酿造……但是吧，能不能做出料理主要还看制作组的脑洞，所以为什么番茄和鸡蛋不能做出西红柿炒蛋啦！！！(╯‵□′)╯︵┻━┻ 房间配方算是dqb2的一个很有意思的机制。以往在Minecraft里，主要是玩家建什么就是什么，房间有床可能就是卧室。而dqb2对房间的作成有一个配方列表，比如放个擦手巾和马桶就能判定为厕所。这样的设计还是挺惊艳的，而且为NPC的AI系统带来了很大的便利，NPC可以很清楚地知道去什么地方可以干什么。但就如同食谱一样，玩家还挺难和制作组的脑洞对上的，不去看攻略基本上不太可能把所有房间配方都试出来…… 有一些NPC会在对话里提到一些食谱和房间配方，但玩家需要记住它，因为它并不会自动被系统收录到记录列表里去……所以当这些NPC在其他岛上或者是触发对话的条件没对上的时候，想记录下这个配方还是有一些麻烦的…… 联机体验 说实话我还没体验联机部分。主要是因为怂恿我玩的群友虽然口口声声说大概会一口气打完dqb2，但实际上还一直卡在最终章之前没怎么动。而dqb2联机部分主要还是得通关之后打才比较有意思…… 不过这都不算是不足，真正让我觉得联机体验比较可惜的是「访客玩家」不能带东西来、不能带东西走，真正做到来时空空，走时也是空空……某种意义上降低了大家联机的欲望吧，只能是当个工具人给房主造房子……如果能学习一下动森的联机方式就完美了。 总结 写了这么多，我真的是太爱这款游戏了。强烈推荐给喜欢动森或者Minecraft类型游戏的玩家们，也希望自己有朝一日能玩到dqb的续作！
休假在家，时间比较充裕了。除了没日没夜地打一打哈利波特之外，还是干点事儿好了…… 国内一直因为众所周知的缘故不能使用Firebase的服务，导致我手游半成品也一直没有什么心思继续完善。而切换到Playfab又有其他问题（比如没办法很好地实现玩家交易板功能），索性自己捣鼓用Parse手撸服务器好了…… 环境需求 安装Parse需要有Node、MongoDB，虽然这俩我都已经有了，但各种版本号、用途不一的问题，让我觉得似乎直接拿来调戏Parse不是个明智之举……所以打算搞个Docker，在Docker里装Parse。反正现在Windows可以装Sub-Linux了，无缝衔接应该会很方便。 安装Docker 安装WSL2 因为这台电脑是年初才配的，装的Windows也够新，所以安装子Linux系统还是很方便的。只需要用管理员模式打开控制台，运行：wsl --install就可以了。默认会安装Ubuntu，不过反正我也挺喜欢Ubuntu的，就不改了。 下载好所需资源之后会提示你在重启WIndows之前不会有任何更改生效。重启之后Ubuntu便会自己蹦出来安装。 装好之后你需要给Ubuntu创建一个初始用户，输入用户名和密码即可。最后再运行一次sudo apt update && sudo apt upgrade就算是搞定了——可以直接在Windows里打开Terminal，在下三角里选择Ubuntu运行想在Linux下运行的命令（不过暂时用不到了）。 安装Docker 在装好WSL（目前默认版本号是2，所以是WSL2），就应该装Docker了。 嘛……实际上这也不是这篇文章的重点，装Docker感觉和装其他软件没啥区别。只要去Docker for Windows的下载页面下载下来安装即可……安装之前会有个小勾勾选上了WSL2，确保它是勾着的就好。 安装完成之后会需要登出/重新登入一次Windows，做好准备【。 安装Parse 安装MongoDB Parse支持NoSQL，也支持Relational Database（默认是MongoDB和Postgres）。但既然都用了这么方便的后端了，谁还用关系数据库啊！果断选择MongoDB。 在Docker上安装MongoDB也很方便，只要Docker for Windows是开着的，你就可以打开任何终端运行Docker的命令——我用的是Windows Terminal。 docker run --name rin-fishing-db -v /data/rin-fishing-mongo:/data -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=admin -p 27017:27017 -d mongo 输入以上命令，就会在Docker里新建一个叫rin-fishing-db的Container。名字随意，自己明白意思就行。这里用户名密码我都设置成admin了，反正本地开发，方便…… 顺带一提，数据储存位置在\\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes\。 安装Parse 安装好MongoDB，就可以装Parse本体了。运行： docker run --name rin-fishing-server -e PARSE_SERVER_APPLICATION_ID=rin-fishing -e PARSE_SERVER_MASTER_KEY=MASTER_KEY -e PARSE_SERVER_DATABASE_URI=mongodb://admin:admin@mongo/parse_server?authSource=admin --link rin-fishing-db:mongo -p 13579:1337 -d parseplatform/parse-server 相同地，设置的名字随意。Application ID和Master Key在开发环境也可以随意，产品上线的话就请自己掂量掂量了【。 [scode type="yellow"]这里还有个坑，就是设置端口的时候，因为我的1337端口被占用了，就用了13579。一开始我用的是-p 13579:13579也就是外部访问和内部都是13579，结果访问不到……所以如果各位要修改端口号，也请务必保留内部端口为1337不变。[/scode] Parse Dashboard 装完Parse，我们还可以装一个网页版的控制台： docker run --name rin-fishing-dashboard -e PARSE_DASHBOARD_APP_ID=rin-fishing -e PARSE_DASHBOARD_MASTER_KEY=MASTER_KEY -e PARSE_DASHBOARD_USER_ID=wiseclock -e PARSE_DASHBOARD_USER_PASSWORD=123456 -e PARSE_DASHBOARD_ALLOW_INSECURE_HTTP=true -e PARSE_DASHBOARD_SERVER_URL=http://localhost:13579/parse -p 4040:4040 -d parseplatform/parse-dashboard 其中PARSE_DASHBOARD_USER_ID和PARSE_DASHBOARD_USER_PASSWORD是你登入这个网页控制台的用户名和密码，请牢记。相同地，如果要修改访问端口，只需要修改-p 4040:4040的前半部分。 安装完成 装完后，Docker里就会出现三个Container了。只要Docker程序运行，这三个Container也就会持续工作。 打开http://localhost:4040/并登入，也可以看到Parse Dashboard的操作界面——大功告成！
今天才發現的大坑，大概是我玩Java太少了。 有個函數： BufferedReader reader = new BufferedReader(new FileReader(stopWords)); String line = reader.readLine(); while (line != null) { STOP_WORDS.add(Pattern.compile(line, Pattern.CASE_INSENSITIVE)); line = reader.readLine(); } reader.close(); 在IntelliJ IDEA測試環境下完全沒問題，可以順利載入所有文本。 但到了生產環境發現只有ASCII字符有效。 一開始還以為是UTF-8 BOM的問題，改成帶BOM的存了一次，還是不行。 後來才發現，原來FileReader並不會強制默認編碼到UTF-8。Java 11倒是可以手動設置new FileReader(fileName, StandardCharsets.UTF_8)，但Java 8就無緣了。 無奈，只好轉用Files.newBufferedReader，這個函數在Java 8默認使用UTF-8，但Java 7裡還是需要手動設置的。 所以最後改成了： BufferedReader reader = new BufferedReader(Files.newBufferedReader(Paths.get(path))); 運行……發現其他行都正常了，但首行還是不行。想到了之前添加的BOM…… 好嘛，到頭來還得把BOM刪回去…… 行吧。 EOF
开年第一篇博客！ 前言 用OBS录像已经有一段时间了，但有个问题一直困扰着我。比如录Switch游戏，我可以设置Switch的声音单独录到一条音轨上，但因为同时也需要在电脑上听到Switch的声音，所以又不得不打开监听——把Switch声音再播放回耳机/音箱。这时候问题就来了……多人游戏时其他人的语音也是通过默认播放设备播放的，所以就算把它单独录到一条音轨上，音轨也是语音聊天和Switch声音混合的。这给后期剪辑带来了一丢丢的不便利…… 今天用虚拟声卡（以及一些其他工具）解决了这个问题！ 工具列表 虚拟声卡（最上面的下载免费，额外的A+B和C+D都需要捐赠……或者看文末） RTX Voice（链接是安装说明。RTX显卡直接下载NVIDIA Broadcast，其他显卡下载RTX Voice） 楔子 之前用FaceRig给纯字幕视频做动画角色的时候用到过虚拟声卡，用来lip sync对口型，这样我就能一边闭着嘴摇头晃脑一边播放录好的素材合并成摇头晃脑说话的角色了（后来FaceRig好像直接支持播放文件了(:з」∠)）。反正虚拟声卡的原理就是给电脑添加一个虚拟播放设备（P）、一个虚拟录音设备（R），通过P播放的音频会传递到R，就好像这个音源是由R采集到的一样。这样再通过Windows自带的设置我们就能监听R，并且在耳机/音箱裡再播放出来。在FaceRig的例子里，我只要设置播放器用P播放音频文件，设置FaceRig把R当做我的话筒，就能完美实现内录了。 设置虚拟声卡 回到标题，怎么样才能实现区分不同音源到音轨呢。我们可以设置两对虚拟声卡，分别是播放设备A（Voice Chat Play）、播放设备B（Game Play）、录音设备A（Voice Chat Rec）、录音设备B（Game Rec）。Voice Chat给语音，Game给游戏音效。再设置监听两个录音设备到默认播放设备即可。我图里有三个虚拟声卡，因为……装都装了，说不定以后要用来播放BGM呢…… 设置聊天软件 接下来就要去各个聊天软件中设置播放设备了。举例TIM，其他大部分软件都有播放设备的设置选项。 到这里，一般已经能满足大部分人的需求了，但……很多朋友现在用微信语音交流，而PC微信又没有设置播放设备的选项……没办法，只能让Windows壁咚微信，强迫其用我们的设备播放。Windows 10的具体设置可右键单击任务栏的音量图标→打开声音设置→拉到最下面→应用音量和设备首选项。把微信的输出设备改成Voice Chat即可。 到这里！！应该！！可以！！满足！！绝——大部分人的需求了吧！！但我就是事儿多……我电脑配备了音箱和耳机，一般白天就用音箱看视频之类的，夜间，或者是和人语音的时候就会换成耳机模式。多一句嘴，如果你主机连着音箱，耳机插在主机的耳机孔里，你只需要在插上耳机的时候把耳机设为默认设备即可——这样做当你拔掉耳机默认设备会自动设置为音箱，插上又能复原。我之所以不能这么做是因为我麦克风用的是Razer Seiren X独立USB麦克风，而耳机是接在麦克风上的，又不愿意经常拔插（否则不就没意义了！）——所以只能被迫地、每次需要切换的时候手动点击任务栏的音量图标切换： RTX Voice 这样就导致了一个问题……当我手动切换默认播放设备的时候，程序是无法识别到的（不知道是我用的虚拟声卡驱动的问题还是Windows的问题，反正问题应该在侦听那一块儿，不能自动识别默认设备的变更）。然后我灵机一动……嘿！我不是装了RTX Voice嘛！那玩意可以无缝识别切换！所以我就改了一下，把侦听设备改成了RTX Voice： 这样RTX Voice知道我默认设备有改动就可以了，我们只要保证它随系统启动就万事OK。 OBS Switch游戏 语音设置好了，接下来是OBS的设置。以Switch为例，我用的是GC513（Live Gamer Portable 2 PLUS）采集卡，在设置来源时勾选自定义音频设备即可： 然后再添加一个音频输出捕获，选择配置好的Voice Chat设备，它将会被用来采集语音聊天。现在在混音器里应该会有Switch、Voice Chat、桌面音频和麦克风四个来源（或者三个，如果你没有麦克风）。右键任意一个，选择高级音频属性可以设置音轨。 因为Switch的音频不是来自电脑本身的，所以我们需要监听（在耳机/音箱内播放），并且输出到最终的视频，所以音频监听选择监听并输出。Voice Chat我们已经在Windows里监听过了，所以不用在此处设置，关闭监听即可。麦克风嘛……除非你想在耳朵里听到自己说的内容，否则也关闭吧。 轨道方面，考虑到有直播需求，上述三条音源我都设置输出到了轨道1，而轨道2、3、4则是分别配给了Switch、麦克风、语音（Voice Chat）。桌面音频是其他一切声音（比如Windows突然报错啊、某个程序的下载完成提醒啊之类的），我不想录进视频内，所以没有勾选任何音轨。 电脑游戏 因为Switch是使用采集卡录制，本身其实已经是一个独立音源了，所以这就是对Switch的全部配置。但如果你要录制电脑游戏/程序，最开始我们设置的另一对虚拟声卡就有用处了。部分游戏/软件可以设置声音播放设备，把它改成Game（第二对虚拟声卡的播放设备）即可。对于不能在游戏/软件内设置播放设备的，操作和微信一样，在Windows内强行逼迫就范……最后在OBS内多添加一个声音输出捕获并设置单独的音轨即可。 输出 最后，别忘了在OBS的设置→输出里选择输出音轨。根据以上设置，串流选择音轨1；录像选择mkv作为录像格式，音轨勾选2、3、4就好了。 附件 VB-CABLE可以直接在官网下载，免费版本带一对虚拟声卡。这里提供A+B两对附加声卡的资源： 点我访问下载链接（提取码: peg7）
前几个月发现今年还没写博客……再不更新这年更博客岂不得变成双年刊了……嘿！您看这可不巧了嘛——赛博朋克2077（下简称2077）终于不跳票啦~ 写这篇评测的时候Steam记录我游戏时间是14.4小时，游戏内显示是12小时……虽然我有过几次读档吧，但那都是在路边打帮派啊前后也没几分钟的啊！！我也不知道怎么会差这么大 (:з」∠) 无严重剧透，请放心食用。 整体感受 反正呢，我进度也不快，主线打到救艾芙琳，然后打了些支线。鉴于这游戏序章特别特别长——说是六小时，实际上我打了七八个小时——虽然我没打通整个游戏，但能分享的感受也算是挺多了。 先说第一印象。我自己本身就是一个开放世界游戏爱好者……喜欢可以在游戏里到处探索、调戏路人、看各种隐藏剧情/文献记录的感觉。2077在这方面比较突出的惊喜就是全程汉语语音（有普通话也有粤语）——不光是主线对白，所有路人NPC、甚至是游戏内的电视新闻、广播都是配音了的。甚至嘴唇的闭合也是根据语音语言做了调整的。 诚然，加入母语语音多多少少会因为语音不贴近现实生活的问题让玩家出戏，但在2077里我觉得声音演员已经尽全力把这个出戏的问题减弱到了极致——至少我玩起来是觉得很有代入感的。 母语语音能带来的好处可就太多了：以往，英语不好的玩家需要很专注地低头看主线对白的字幕了解剧情，这个问题屏幕尺寸越大就越为明显；即便是有一定英语基础的玩家在主线剧情可以不太看字幕也能了解剧情，但如果想体验真实的开放世界、了解清楚每一个路人NPC/每一个路边播放的音频视频说的什么内容，恐怕都不是一件容易的事。但现在在2077里有了母语配音，玩家可以轻松地追任务，也不会错过身边的各种信息源——毕竟母语就是即便不去认真听也能自然而然进入脑袋里的东西…… 优点 第一印象说完了，先谈谈我认为本作的一些优点。 剧情 比较直观的体验就是2077的剧情是真的给人一种哇塞的感觉。_ (:з」∠)_不过也可能是我玩的剧情类游戏不多吧，朋友说荒野大镖客2的剧情更好（买了没玩就是我了）。序章最后你要到绀碧大厦去，而整个序章剧情的高潮就在这里。我整个人当时都看呆了……（哎呀这玩意不剧透真的好难描述，反正就是很哇塞！） 而有些选项真的是……嗯，很有意思了。有个支线任务某NPC会想给你辆摩托，选项1是感谢并收下，选项2是婉拒。我发誓我当时就是想客套一下，选了2：「这个我不能要……」谁知道她就真的把钥匙收起来不给我了！！还夸我！！！(╯‵□′)╯︵┻━┻一般这种游戏这么选不是会再确认一次的吗！！这究竟是制作组的恶意还是白人不懂的东亚客套……就很伤心。 本地化/口音 在第一印象里说过了，语音本地化是一个很加分的优势。但随着深入游戏，我发现本地化的内容不仅止步于语音。因为游戏里的角色都来自五湖四海，最终在夜之城相聚，所以他们说着不同的语言是很正常的。虽然如果强行让他们说同样的语言也说得通，但就显得比较枯燥了——GTA IV在这方面做得也很好，Niko就是操着一口俄式英语，让玩家很有代入感。 而在2077里，不同角色也会说不同语言。说不同语言又有多种体现方式。 第一种是小段文本（phrase）说他们的母语，而字幕上会使用母语+括号内翻译的形式让玩家理解。比如杰克来自拉美，他偶尔就会蹦出西班牙语的词汇：「太棒了，Chica！」，这时字幕上就会打出「太棒了，Chica（妹子）！」。 第二种是长短文本全是母语，这种情况的体现方式十分特别，字幕先是会把母语文本全部打出，然后一个词一个词马赛克掉，马赛克再溶解成玩家设置的字幕语言文本。很奇特的体验，就像未来科技已经在人体内植入了自动翻译一样（等下，2077年就这么先进了，那2112年的翻译魔芋咋办啊？？？）。 第三种方式是带口音的语音。这种情况我的理解是如果一个角色不是在夜之城长大，但在夜之城生活得足够久了，他就能流利使用夜之城的语言，但伴有他母语的口音。游戏里有几个日本人就是操着一口经典的日式汉语和玩家交流，配音团队真的很用心。 人口密集度 不得不说，2077的城市是所有我玩过的开放世界游戏里最能让我体验到真实世界的游戏了。虽然可能和科技的发展有关系吧——现代CPU、GPU早已不是十年前的技术可相提并论的了——但还是得说，我玩老滚5、GTA4、5的时候就特别想让城市人口暴增，看着也会更亲切一些（特别是老滚5，一个城没几个人，就算装了MOD也还是一种中世纪乡村的感觉）。在2077里，如果把人口密集度设置高，那你在市中心地区真的能体验到现实世界中的人来人往，很有生活气息。 缺点 主要的几大优点说完了。当然我不是说没有其他优点，其实比如技能点啊、耐力条长啊等等比较小的东西还是有很多优点的，但给我的惊艳程度确实不如上述的几个，在此就不赘述，让我们说说缺点吧…… 所谓爱之深恨之切，我有多喜欢2077就有多遗憾它的一些缺陷导致玩家体验并不完美。 BUG 一般来说……游戏总归是有BUG的，体量越大BUG越多，并且是程指数级上升的。但专门把BUG拎出来作为一款游戏的缺点谈……我只能说，育碧点了32个赞。 2077的BUG实在是太多了……真的很让人困惑这游戏是怎么通过QA的，还是说压根没完成制作，是被投资方逼迫赶在圣诞前发售好吃下这段时期的大肥肉？ 我遇到的第一个BUG是光头。当时剧情进展到一定程度，我回到自己家公寓了。一番整理之后我和镜子互动，惊奇地发现我居然是个光头？！等等，我可是个美丽的女孩子啊……我那一头温软的秀发哪去了？？？惊讶之余我还特地截了个图发到群里表示了疑惑。后来才知道，戴上帽子照镜子就不会显示头发，也不会显示帽子……这种明显的视觉BUG真是不知道QA是怎么能做到熟视无睹的…… 第二个比较明显的BUG是几乎第一时间开玩的玩家一定会遇到的。序章最后你会体验强尼的一小段剧情，剧情中你的义眼（机械眼睛）被打坏，画面进入黑暗，但很快你就被送入医院修好了这个问题。大概是某个Shader变量忘了关吧……反正游戏中的体现是，即便义眼被修好，你也还是看不到东西。当强尼的剧情结束后，你又回到主角剧情，这时候BUG还在，玩家看到的还是黑屏——但它又不是完全的黑屏，是真的义眼坏了的黑屏，有时候闪两下正常画面，时不时来点红光，再结合之前玩家体验的剧情你真的丝毫察觉不到有任何毛病……况且，这个任务的名字就叫「黑梦」…… 中途玩家还得经历抹黑开枪，抹黑移动等一系列骚操作……直到十多二十分钟后时间来到了第二天，玩家一觉醒来，画面才恢复正常。是不是听起来也还挺合情合理的？？？我觉得奇怪是发现任务居然提示我开枪打人……一片黑对我这种选简单难度的手残玩家怎么可能嘛……直接读档。嘿，您猜怎么着？从义眼修复之后读档那可是一切顺利，啥都能看得见了！(╯‵□′)╯︵┻━┻很多玩家玩到后面都没察觉到这是个BUG……真有你的啊CDPR。 还有诸如穿模、瞬移、动画缺失、T-Pose、悬空筷子/香烟等各种不太影响游戏（但略微影响体验）的BUG我就不多说了，加油修BUG吧…… AI 个人而言，感觉AI是个很大的缺陷。作为一款开放世界游戏，路人、车辆、警察、帮派等元素的躯壳都具备了，但没有灵魂，这是很尴尬的。 路人都是按照既定路线行走，不会绕路，没有自己的行为习惯，活像一具行走的僵尸。和路人对话也是固定的一些台词，不会对你的一些行为做出反应。 车辆没有自动驾驶（除了任务内的既定路线），没有真实的交通系统AI。在GTA里，特别是GTA SA和GTA IV，甚至是在看门狗里，我最喜欢做的事情之一就是造成交通事故和大堵车。GTA SA里你只要在高速公路旁边走，突然跑向车道中心，向你迎面驶来的车辆会一个急刹车或是紧急打方向盘。有些车会撞到你，有些车会刹住，但结局基本都会导致后面的车辆一辆撞一辆形成连环车祸。在看门狗里，你可以黑掉交通信号灯，让四面的车辆相撞或是让汽车和火车相撞。这些在2077里都是想也别想的事情。 警察就更别说了。做违法的事情会直接导致被通缉，不管有没有被警察看见（这一点倒是可以理解，毕竟科技发达，应该遍布摄像头的）。但是警察直接刷在玩家身后，而不是通过定位玩家就近调用警力……这种做法怕是在开放世界游戏里已经算是很古董、也很破坏体验的做法了。 开放世界 优点缺点都写了几段，其实这一段也应该算是缺点吧，但我想把它专门拎出来谈谈——因为我对开放游戏的爱。 在我看来，开放世界一个核心要素就是让玩家融入进这个世界，是沉浸感。这一点需要在很多地方体现，其中就包括2077做得很好的语音本地化。但2077也忽视了很多其他的东西。比如上文提到的路人NPC对玩家行为的反应——老滚5发售初期一个病毒式传播的视频就是当玩家将一把武器丢在酒馆里，其他NPC会为了争夺这把武器斗殴。而在GTA里玩家如果对路人NPC辱骂、霸凌，取决于NPC的性格，他们也会作出逃跑或正面硬刚等的不同反应。这多多少少让玩家更能感受到自己就是这个世界的一份子，玩家能做的事情越多，沉浸感就越强。 如果说这些细节方面的内容没有交付到玩家手中是因为CDPR不具备Rockstar或Bethesda等大厂的技术力以及对开放世界游戏的经验积攒，那么一些诸如整容功能（修改角色外貌）、身体塑形（或者身体肥胖程度动态调整）、装饰（包括身体纹身、车辆装饰以及公寓装潢）等系统机制就是CDPR有技术实力却还是没能选择加入进游戏的失败决策了。 还有一些在开放世界游戏中有一定历史了的功能，比如在GTA SA中的各种小游戏和健身项目，或是GTA 5和黑道圣徒中遍布整个地图的迷你游戏……CDPR有实力，且在巫师3中已经向玩家展示过了，但在2077中它们没能得到很好的体现——如果2077中的街机能玩一些怀旧的像素游戏，会立刻将代入感提升一个层次。 总结 赛博朋克2077是一款合格，或者说近乎合格的3A游戏。特别是等后期通过更新修复BUG之后，它一定是一款值得一玩的有水平的大作。目前来看完成度还是不及巫师3刚出时候的程度，但我相信它会和巫师3一样通过更新和扩展包把自己打磨完善。但如果你是从2017、2014甚至是2012年就开始期待，心心念念等到能玩到它的那一天，那我只能说，你的期待值太高了，很有可能是得落空一大截。 将它归类于开放世界游戏我还是有点心有不甘——它缺少的开放世界内容太多了，与其将它和GTA、老滚做类比，不如说它更偏向无主之地类型的传统RPG多一些……又或者说它是两者的混合？ 总之，希望CDPR能吸取教训，积累经验，做出一款真正能和Rockstar媲美的开放世界大作。 2020年12月13日於溫哥華 撰文共計2小時
《AI：梦境档案》：一部剧本绝佳的视觉小说
前几天发到SteamCN论坛了，感觉自己家也应该发一篇才对…… 太长不看：剧情很赞、游戏性不够。喜欢视觉小说/galgame的买买买（应该基本都已经买了吧）；有点兴趣还在观望的可以等打折；只对剧情好奇的，本作适合视频通关（如果喜欢看冷笑话、沙雕剧情，就找个什么选项都会选、什么物品都会调查的up就好了）。 刚刚打完了AI梦境档案，总用时25小时20分钟。不过我喜欢所有能调查的物品都点一遍，无意义对话也看了很多，所以如果直追剧情的话应该会更快。 总体来说，剧情真没的说，很精彩。特别是真结局路线，很喜欢。虽然免不了一点点套路（玩着玩着能猜到剧情走向），不过还是觉得很有意思。 但是——（铺垫了这么多 转折开始了） 推理成分是真的没有，不光是游戏性上没体现本格推理要素（哪怕只是单纯推理）就连剧情的真结局把所有线索串起来，也是突然天降一个【玩家、玩家控制的角色都不知道的设定】才把零散的剧情拼起来。虽然剧情汇总很精彩，也很严谨，所有事情都能说通，也没什么bug，但还是不得不说，这种空降设定的做法不太适合游戏。还是动画、小说比较好一些— —虽然AI梦境档案本质上也确实是视觉小说：90%看剧情，10%玩游戏。 然后是这游戏的两个系统：一个是QTE，一个是梦境。我都……很不喜欢。QTE怎么说呢，我认为是在看小说的时候强行给玩家加入游戏性，完全不管是否和游戏类型契合。我是真的讨厌在看剧情看到一半的时候，突然要我狂按B键，或者是移动摇杆抓时机开枪（还不支持体感）。不过不管怎么说，虽然我很讨厌QTE，但至少这游戏的QTE还算简单，也还凑合忍受吧。 相比起QTE，我觉得梦境更加不舒服一些。梦境是本作核心玩法之一，开篇几个梦境还行，至少有一些逻辑性，想要过关也很轻松。但越接近尾声，梦境就越发缺少逻辑，全凭玩家能不能跟制作团队的脑电波对上。推理成分（几乎）为0。还时不时多出一大堆扰乱玩家思路的无意义选项，导致后期基本没可能一次过关……当然制作组很狡猾地把这个设置为梦境，也就是在告诉玩家这都是梦里发生的东西，所以所有的不合理都是有原因的（实际上剧情里还真的这么说了）。不过用这种方式加强游戏性，我觉得反而不如设置一些简单的谜题，至少让玩家好好看剧情。 总体来说，因为我是在玩完逆转裁判之后玩的，而玩的时候看介绍说是本格推理游戏¹，以为是和逆转裁判差不多的游戏。期待值是偏向推理游戏的，所以在这方面减分不少，但如果单纯当做视觉小说玩，只在乎剧本是否引人入胜，又可以接受GALGAME里各种分支存档读档的话，AI梦境档案可以是一个高分游戏（因为跳章节、跳分支或是梦境里的存档中继点都非常的方便，你甚至可以自由选择重玩某一天的某一个环节）。 最后夸一下游戏的幽默，我个人是特别喜欢的，比逆转裁判还有趣。各种谐音梗、冷笑话、沙雕剧情挺符合我胃口，游戏里的百科系统和人物资料档案也设计得很用心。 1：游戏介绍中文文案是「本格派推理冒险游戏」，和基友讨论了一下，在中文语境下很难判断本格是修饰推理还是冒险游戏，但在日文里很可能是本格派Mystery game——正统冒险游戏。但不得不说冲着推理去玩的话会很减分，毕竟涉及推理的情景少之又少，强行套的话，梦境里有一些类似3D解谜游戏一样的情景——比如冰冻的门旁边有冒着蒸汽的水管，需要踢一脚。但同时也会给你无数看上去也能解谜的假选项，主要还是看脑电波能否对上…… 优点 剧情很赞 跳分支十分方便 全程配音（日英任选，但觉得英配缺少日常生活的感觉） 小萝莉很可爱 沙雕梗很有趣 缺点 缺少推理要素 游戏性不佳 QTE很突兀 在Switch上优化不好（画中画都是纯3D 加载时间过长 有时候说一句话等半分钟加载资源 其实完全可以用图片替代）
這些年在開發App上也是遇到過不少坑了……從native到hybrid，從UI到authentication，遇到的坑是接連不斷，踩完一個掉另一個、從老坑爬到新坑一陣子才發現還是老坑好…… 現在基本沉澱好了，來總結一下，為以後失憶的自己以及正在辛苦挖掘的新入行的農民們留下點蛛絲馬跡。 為什麼選擇Hybrid App 一開始嘗試的時候也試圖開發native的iOS和Android App，不過很多東西來來回回改，兩邊都要顧及到，一個是開發時候耗時耗力，再就是維護成本也是雙倍的。像我這種懶人……自然更傾向於code once, run everywhere的Hybrid App了！ Hybrid App架構選擇 嘗試過幾種不同的Hybrid App架構，比如Xamarin、Ionic/PhoneGap (Cordova)以及React Native。最後認定的還是React Native——不得不說，真的是好用。 Ionic/PhoneGap 對於一些小型App來說其實Ionic/PhoneGap是一個很好的起點，因為你基本上只需要了解HTML和Javascript就能很好地上手了。 其中Ionic自帶的UI設計甚至會根據手機系統切換（保持和原生App一直的設計，比如說Android用的就是Material Design）。其自帶的豐富多彩的Components也能滿足大部分開發需求——比如按鈕、彈窗、ActionSheet等等。就算這些無法滿足你的App需求，你也還能使用Cordova Native插件來幫助開發——比如很受歡迎的一個就是AdMob，用來在App中插入廣告。 但如果你的項目比較複雜，或者需要展示的東西比較多（大型List），用Ionic/PhoneGap可能會嚴重拖慢App的性能。因為歸根結底Ionic/PhoneGap都是使用內嵌瀏覽器渲染的。雖然有辦法消除原生瀏覽器的300ms延遲，但面對龐大的UI渲染，Ionic/PhoneGap還是很吃力的。曾經我用Ionic做了幾十個卡片，每個卡片上有一些文本信息和一個內嵌的List，整個App滑動起來就已經不是特別順暢了。 Xamarin Xamarin和Ionic不同，完完全全是基於Native架構的。要說它是個Hybrid開發框架可能不太確切，但它確實是一個非常不錯的跨平台開發框架。和Ionic不一樣，Xamarin是直接和最新的SDK溝通，確保你能用到最新的API，然後把C#寫的代碼編譯成Native App。其實我挺愛Xamarin的。（讀到這裡你們應該能猜到後面會跟一個「但是」了……） 不過（我就不寫「但是」！），讓我棄了Xamarin這個坑的主要原因是它的用戶界面開發。誠然，Xamarin自帶的Xamarin.Forms能夠解決可能90%以上的需求——一次設計，跨平台分發。但同時你也需要注意到剩下10%的use cases會是極其痛苦的，因為一旦你有什麼UI上的需求在自帶庫中無法實現的話，你就需要手擼代碼實現。別理解錯，我說的手擼代碼是你真的需要對所有你想要分發到的平台做一個小插件。比如我當時的需求是在卡片裡內嵌一個可改變順序的List，我就需要在iOS和Android包裡分別寫一個Custom Renderer來實現——他們的代碼還很不一樣，因為底層SDK不同嘛。另外Xamarin的UI設計和Android也比較類似，用的是XAML，也有各種Layout，其實我個人覺得還是比較痛苦的。 最後就是Xamarin提供的插件真心不多——至少在我當時用的時候是吧，還有很多已經屬於比較過時的API了。考慮到需求裡可能會有蠻多沒有插件支持的功能，如果都要自己手擼可就太痛苦了，我還不如切回Native開發呢……所以最終放棄了可愛的Xamarin。 React Native React Native是我最後接觸到的Hybrid框架了。但是這東西一用就愛上了啊。相較於Ionic和Xamarin，React Native是真的取其精華、去其糟粕了。它擁有Ionic編寫UI的便捷，也擁有Xamarin執行的效率。 它不存在Ionic的性能問題，因為它本來就可以算是個半原生App了。React Native是一個用ES6編寫的框架，在你的App編譯後會將所有代碼打包成一大塊的js文件，然後React Native核心會用Objective-C或Java API將這個js文件渲染成Native View——感謝現代Javascript引擎，讓我們完全感覺不出用React Native開發的App和原生App的區別。 同時你也不需要為UI編寫擔心，因為React Native的UI編寫起來和HTML相差無幾。比如這個就是一個簡單的React Native頁面： import React, { Component } from 'react'; import { Text, View } from 'react-native'; class App extends Component { render() { return ( <View> <Text> View基本上就是Div。 </Text> <Text> Text可以理解為Span吧，但文字必須裝在Text標籤內。 </Text> </View> ); } } Expo還是原生React Native 很多剛入門React Native的人會選擇Expo，因為Expo確實更簡單一些，並且有一些Expo官方魔改過的插件。使用Expo，你甚至不需要用數據線將移動設備連接到電腦上即可測試你的App——只需要下載一個Expo App，然後將你的代碼通過WiFi傳輸給手機就可以在手機上測試了。使用Expo，你甚至不需要一台Mac就能編譯iOS版本的App——你可以直接將代碼提交給Expo雲平台進行編譯。 聽起來很方便很酷炫，然而我還是不太推薦剛玩React Native的小夥伴直接使用Expo。因為Expo也是挺多坑的…… 首先一個很大的不同就是原生React Native項目裡分別有iOS和Android文件夾，他們存儲的是原生iOS和Android工程文件，如果你需要對其中一個或多個平台做任何修改，只需要打開這些項目，像修改原生工程一樣進行操作即可。有很多很方便的React Native插件也確實需要你對這些文件進行修改。而使用Expo，這倆文件夾會徹底消失，也就是說你沒辦法針對平台做出任何調整，所有代碼都只能以Javascript的形式存在。 其次是在一些小功能上Expo和原生React Native可能會有些許細微的差別。比如我當初用Expo的時候，Android版App上面的Status Bar一直有問題，要麼不能設置成translucent，要麼會自動隱藏，要麼是設置成了translucent卻不佔Header的位置導致Header的文字和Status Bar黏在一起。而這些問題使用原生React Native是沒有的。 最後是插件數量的問題，GitHub/NPM上有很多React Native的插件，但只有很少一部分支持Expo。所以當你覺得你的工程需要很多插件來完善的時候，Expo並不是一個很好的選擇。 希望初入React Native的你不要踏入Expo這個大坑了，還是老老實實用react-native init開始吧。但如果你已經用了Expo——蟹蟹Expo，很貼心的準備了一個功能幫助大家脫離它：npm run eject。 版本控制 為什麼版本控制會是個坑呢？其實我也不知道最新版的React Native有沒有解決這個問題，但我當時給我的React Native項目進行Git init的時候莫名其妙就一直在報錯……後來我發現我必須在一個父文件夾內創建repo，然後新建一個子文件夾放React Native項目，這樣就再也沒出現過錯誤了…… UI 因為React Native只是一個代碼級別的框架，在UI層面其實它給與的幫助不是特別多。這時候我們就需要用到第三方插件/庫來美化我們的App，讓它在各個平台看起來都更像原生App——這一點Ionic做得不錯，框架自帶，而React Native除了手擼StyleSheets之外就只能選擇第三方支持了。 我個人試過React Native Elements和NativeBase兩個比較有名的UI框架。這兩個框架都很不錯，前者在GitHub上（目前）擁有13k的star，後者也有10k，都是很流行的項目。但因為Elements自定義樣式還是複雜了點，我更傾向於使用NativeBase。NativeBase在更換基礎樣式上比較容易，但類似ActionSheets或Picker這種頁面級的原件還是比較難自定義的（他們有主題功能，需要自己定義一套主題，但因為我的App需要有夜間配色，切換主題略微繁瑣了所以沒有體驗）。 數據結構 在Javascript驅動的React Native裡做數據結構自然得是JSON了。但這裡又有一個坑了。假設你的數據是一個JSON Array，Array裡每個Object都是一個用戶，每個用戶有用戶名、性別、年齡、喜好、簡介、朋友列表等等信息。你要做一個列表來顯示這個Array就會需要設計一個自定義Component——我們姑且叫他卡片。這個卡片裡可能會有十多個View、十多個Text，並且從父Component獲取用戶信息（props）。 那麼問題來了，當你用ListView或ScrollView顯示一百個用戶卡片的時候，執行效率就會明顯降低，甚至刪除一張卡片都會需要幾秒鐘。如果你打開資源查看器甚至會驚人地發現你的RAM正在以肉眼可見速度被吃光…… 遇到這個問題，我首先把裝載容器換成了FlatList，但收效甚微——不是說完全沒用吧，但該卡頓還是卡頓。在用戶體驗方面，按下刪除按鈕卡五秒鐘和卡兩秒鐘我覺得差別並沒有太大…… React Native的工作原理是監視一切可變動的數據，當你把整個JSON Array放進State裡就表明這些數據隨時都有可能變動。所以不管是你滑動頁面也好，按下刪除按鈕也好，還是刪掉卡片也好，React Native時時刻刻都在觀察你的數據並在必要的時候（幾乎每時每刻）重新渲染你的頁面。那麼就好理解了，我有100個用戶卡片，時時刻刻重繪100×10個View/Text，能不卡嗎？ 這時候我們的救兵來了，它就是Immutable。Immutable可以將JSON Object封裝成immutable數據，標明它們是永遠不會改變的。這時候React Native就不會每時每刻重新渲染你的頁面了，自然效率就提高了不少。但是使用Immutable你將沒有辦法像正常JSON數據一樣使用delete arr[0];、let x = arr[0];或arr.splice(1, 1);因為這些數據是不可以被改變的。在Immutable結構裡，你想獲得數組內的一個Object或者是獲得一個key的value你必須用let x = arr.get(0);和let x = obj.get('key');。splice雖然也存在於Immutable中，但返回值和原本的splice不太一樣，它返回的是一個新的、去除掉你想刪除項目的數組。 光有Immutable還不夠，你還是需要FlatList來助陣。FlatList能幫助展示龐大的數據列表。它一次只渲染x個屏幕的數據，若用戶再往下拉，則再渲染更多數據，最大程度地提升了運行效率。但FlatList不支持Immutable，因為FlatList是React Native自帶控件而Immutable是第三方庫。如果你想在FlatList內使用Immutable，要麼你可以用VirtualList（FlatList的父級Component）自行編寫引入Immutable的功能，要麼就使用一個很好用的第三方庫：react-native-immutable-list-view。 Navigation 像很多現代App一樣，我們都想要自己的App有一個底部的標籤切換以及一個頁面切換、後退的Navigation系統。這些東西react-navigation都包含在內。那為什麼他坑呢？ 我沒有研究過React Navigation底層是怎麼運作的，但它呈現出來的方式是標籤系統是一個TabNavigator，頁面切換系統叫StackNavigator。你需要在這些Navigator中定義頁面以及Navigation Options。而每個頁面的Header，也就是App頂部的標題和按鈕都是儲存在Navigation Options內的。你可以將這個東西寫在每個頁面自己的js文件內，但很坑的是它是一個static field。所以如果你需要修改Header標題，只能先在Navigation Options裡將標題定義為變量，再在頁面中修改Navigation的變量達到修改標題的目的。 乍一看還好是吧，因為這只是修改文字。當你在Header內有按鈕要call頁面內的函數的時候，一切就十分頭大了……比如我在Header上有個按鈕可以切換頁面內List的複選框，我是這麼做的： 首先在頁面的componentDidUpdate(prevProps, prevState)函數裡檢查this.props.navigation.state.params.toggleSelectMode是否存在，如果不存在則代表沒有設置標題按鈕的函數： 設置Navigation的param：this.props.navigation.setParams({ toggleSelectMode: this.toggleSelectMode.bind(this) });，將頁面內的函數設置給Navigation，然後： 在static navigationOptions裡給按鈕加上onPress函數：if (navigation.state.params.toggleSelectMode) { navigation.state.params.toggleSelectMode(); }，注意一定要先檢查是否存在，因為天知道建立Header和componentDidUpdate誰先發生…… 是不是很繁瑣？StackNavigator裡切換頁面也會需要用到同樣的hack……比如你想用戶在提交完數據之後回到上一頁並觸發上一頁的一個函數，就得在從上一頁打開本頁的時候就把那個函數以param的形式傳遞過來…… React Navigation還有一個我不知道算不算坑的地方吧，就是老版本內一個StackNavigator包含一個TabNavigator的時候，每個Tab頁面都是可以單獨設置Header標題的，而在新出的2.x版本裡不行了。在2.x版本裡如果你想要每個Tab都有不同的標題，得在TabNavigator本身的Navigation Options裡自己判定當前是什麼頁面，應該給什麼樣的標題。但這樣呢，就將我剛剛提到的Header裡按鈕的問題又提高了幾個幾何級的難度了……所以我一直在用1.0.0-beta.27，也就是最後一個支持每個Tab擁有自己標題的版本。 數據庫 這是個大坑，但取決於你的App Design。如果你在編寫一個Offline First App要求用戶可以使用email/密碼以及其他OAuth登入，當聯網時自動同步兩端的數據，那數據庫的選擇真的會是個大坑…… 最早的時候我們選擇的是Firebase，因為這種同步要自己寫一套系統來判斷每個action的發生時間、本地和服務器誰覆蓋誰、哪個設備覆蓋哪個設備真的太繁瑣了，還是直接用已有的BaaS/PaaS服務比較方便。但Firebase遇到的問題是它的filter功能十分局限，比如你只能filter當前object和一級子object的key/value，不能下拉x級子元素filter，也無法count。如果要做複雜一些的filter只能把所有信息都從服務器上下載下來用lodash篩一遍。這樣下載量就太大了，故棄之。 後來發現了Realm。講真，Realm真是一套很好的專為Offline First App提供的數據庫。我們想要的本地/遠程同步和OAuth它都有，filter起來也很方便——雖然還是用到了lodash，但工作量小了很多。可一切完善之後我們發現了問題——OAuth很方便，但email/密碼登入有問題。首先是發送email驗證，這個坑還好，不大，我們先在服務器上寫了個發送email的api，在App內註冊的時候先POST到api，然後在下一步用戶填寫的驗證碼和發送過去的一致的話則通過註冊。 大坑在於重設密碼。因為Realm的用戶名密碼數據都儲存在它自己的系統內，而又不提供任何api修改未登入用戶的密碼，就算我們有用戶的email並且可以發送郵件，也無法在外部更改Realm內用戶的密碼。慌亂之中只好又去看他們的文檔，發現從2.x開始，Realm支持CLI重設密碼，從3.x開始支持發送註冊驗證和重設密碼的email。然而我們的項目構建得比較早，用的還是Realm 1.x。 怎麼辦呢？升級唄，看看是升級到2.x方便還是3.x方便……一看官網，傻眼了，免費版沒了。就連1.x的文檔都找不到了。現在要用Realm每年得花2000美刀。對於一個非盈利App來說確實是有點多了，故而放棄。 在掙扎一番之後，還是瞄準了Firebase，這時候Firebase已經推出了一個新的數據結構Cloud Firestore，宣稱擁有更好更方便的sorting和filtering。再加上Firebase本身就擁有絕佳的Authentication，不光能用email/密碼和Google賬號登入，還支持Twitter、Facebook、GitHub等各種常用的方式，我們決定再給Firebase一次機會xD。 希望這次不會坑了吧_(:з」∠)_ 2018年9月15日23點15於溫哥華 撰文共計4小時