分类
未分类

游戏常用英文词典(第1版)

为了方便游戏开发中为各种变量、配置等等内容起名字,我整理了这个简单的词典,目前只是第一版,比较简单,后面有机会的话会不断迭代更新的。

职业篇

  • 角色:character
  • 英雄:hero
  • 种族:race
  • 生物:creature、critter
  • 怪物:monster、mob
  • 职业:class
    • 格斗家:fighter
    • 武僧:monk
    • 武术家:martial artist
    • 督军/军阀:warlord
    • 剑士:swordsman
    • 日本武士/侍:samurai
    • 骑兵/骑士:rider、knight、horseman
    • 龙骑兵:dragoon、dragon rider
    • 狼骑兵:wolf rider
    • 武士:man-at-arms
    • 步兵:infantry
    • 勇士/冠军:champion
    • 弓手:archer
    • 弩手:crosbowman
    • 长枪兵:lancer
    • 戟兵:halberdier
    • 治疗者、奶妈:healer
    • 牧师:priest、cleric
    • 圣骑士:paladin
    • 魔术师、法师:magician、mage
    • 术士、巫师、男巫:wizard、sorcerer、warlock
    • 女巫:witch、sorceress
    • 德鲁伊:druid
    • 附魔师、巫师:enchanter、enchantress(女)
    • 死灵法师:necromancer
  • 转职:transfer

技能篇

  • 技能:skill、ability
  • 主动技能:active skill
  • 被动技能:passive skill
  • 技能树:skill tree
  • 天赋:talent
  • 魔法:magic
  • 巫术:witchcraft
  • 能力:
  • 状态:status
  • 特性:trait、perk

物品篇

通用词汇

  • 道具:item
  • 货币:currency
  • 代币:token

装备类型(RPG风格):

  • 装备:equpment、gear
  • 头盔:helmet
  • 护目镜:goggles
  • 头冠:
  • 盔甲:armor
  • 板甲:plate armor
  • 锁子甲、链甲:chain mail
  • 棉甲:gambeson
  • 手套:glove
  • 手甲:gauntlet
  • 武器:weapon
  • 钝器:blunt weapon
  • 腰带:belt
  • 披风:cape
  • 斗篷:cloak
  • 靴子:boot
  • 胫甲:greave
  • 鞋:show
  • 凉鞋:sandal
  • 近战武器:melee weapon
  • 远程武器:ranged weapon
  • 单手武器:one-handed weapon
  • 双手武器:two-handed weapon
  • 长柄武器:polearms
  • 配饰、首饰:accessory
  • 戒指:ring
  • 项链:necklace
  • 护符:amulet、talisman、charm
  • 宝石:gem
  • 珠宝:jwelry
  • 宝珠:orb

材料(模拟经营风格):

  • 矿物:mineral
  • 矿脉:mine
  • 矿井:mineshaft
  • 矿石:ore
  • 锭:ingot
  • 铜矿:copper ore
  • 铜锭:copper ingot、copper bar
  • 秘银、米斯里、米斯里鲁:mithril
  • 精金:adamantite
  • 原木:log
  • 黏土:clay
  • 砖:brick

冷兵器:

  • 弓:bow
  • 长弓:long bow
  • 复合弓:composite bow
  • 十字弩:cross-bow
  • 弓箭:arrow
  • 弩箭:bolt
  • 投射物、抛射物:projectile
  • 指虎:knuckles
  • 拳刃:katar、katar dagger
  • 剑:sword
  • 短剑:short sword
  • 长剑:long sword
  • 双手剑:two-handed sword
  • 大剑:claymore、great sword
  • 阔剑:claymore、broadsword
  • 焰形剑:flamberge
  • 双头剑:twinblade
  • 罗马短剑:gladius
  • 劈刺剑:thrust sword
  • 迅捷剑、西洋剑、刺剑、护手刺剑、护手礼剑:rapier
  • 匕首:dagger
  • 波刃剑:kris
  • 五指剑:cinquedea
  • 破刃剑:sword breaker
  • 爪:claw
  • 军刀、马刀:sabre、saber
  • 弯刀、砍刀:machatte
  • 武士刀:katana、samurai sword
  • 太刀:notachi
  • 斧:axe、hatchet
  • 印第安战斧:tomahawk
  • 魔杖:wand(哈利波特那种)、staff(甘道夫那种)
  • 法典:codex
  • 棍、杖:staff
  • 双节棍:nunchakus
  • 棒:club
  • 狼牙棒:spiked club
  • 晨星锤:morningstar
  • 锤:mace
  • 战锤:war hammer
  • 凸缘钉头锤:flanged mace
  • 权杖、节杖:sceptre
  • 连枷:flail
  • 镰刀:sickle
  • 玛拉卡拉镰刀:makraka
  • 长矛:spear
  • 投矛、投枪、标枪:throwing spear、javelin
  • 长枪:lance
  • 长柄刀、长刀、朴刀、偃月刀、薙刀:glaive
  • 斧枪、戟:halberd、halbard、halbert
  • 盾:shield
  • 塔盾:tower shield
  • 筝型盾、鸢形盾:kite shield
  • 小圆盾:buckler
  • 刺盾:spiked shield

攻城器械:

  • 巨石(抛石机抛出的那种):boulder
  • 抛石车:catapult
  • 抛石机:trebuchet
  • 牵引式抛石机(靠人力发射弹丸):traction trebuchet、mangonel
  • 配重抛石机:counterweight trebuchet
  • 弩车:ballista
  • 冲车/攻城车:battering ram
  • 云梯:escalade
  • 攻城塔:siege tower

现代武器:

  • 子弹:bullet
  • 弹药:ammo、ammunition
  • 弹匣:magazine
  • 弹夹:clip
  • 弹巢(类似左轮手枪用的那种):cylinder
  • 弹鼓:drum magazine
  • 弹链:belt、ammunition belt
  • 填装:load
  • 再填装:reload
  • 火焰喷射器:flame thrower
  • 手枪:pistol
  • 转轮手枪/左轮手枪:revolver
  • 步枪/来复枪:rifle
  • 卡宾枪:carbine
  • 狙击步枪:sniper rifle
  • 霰弹枪:shotgun
  • 无人机:drone

建筑篇:

  • 城镇中心:towncenter
  • 马厩:
  • 靶场:
  • 兵营:
  • 伐木场:
  • 谷仓:
  • 武器库、军械库:armory
  • 军火库、兵工厂:arsenal

游戏机制篇:

  • 注册:register、sign up
  • 注销:unregister
  • 登录:login、sign in
  • 登出:log out、sign out
  • 退出:exit
  • 单人:single-player、solo
  • 多人:multi-player
  • 玩家:player
  • 玩家对抗玩家/PVP:player vs player
  • 玩家对抗环境/PVE:player vs enviroment
  • 在线:online
  • 离线:offline
  • 掉线、断线:disconnect
  • 网络延迟、卡了:lag
  • 暂离:away from keyboard(缩写afk)
  • 削弱(平衡性方面的):nerf
  • 过强(平衡性方面的):overpowered(缩写op)
  • 任务:quest、task
  • 主线任务:main quest
  • 支线任务:side quest
  • 副本/地下城/地牢:dungeon
  • 首杀:first down(fd)
  • 成就:achievement
  • 天梯、排行榜:ladder
  • 赛季:season
  • 世界纪录:world record(缩写WR)
  • 个人纪录:personal record(缩写PR)
  • 公会:guild
  • 回合:turn
  • 回合制:turn-based
  • 锦标赛、淘汰赛:tournament、championship
  • 双败淘汰赛:double-elimination tournament
  • 三局两胜:best of three(bo3)
  • 五局三胜:best of five(bo5)
  • 速通:speedrun
  • 冠军:champion
  • 联赛:league
  • PK:player killing
  • 竞技场:arena
  • 排位:ranking
  • 排位赛:ranked match
  • 排行榜:leaderboard
  • 一对一:one-on-one
  • 比赛、对局:match
  • 大乱斗、死斗:free for all(缩写ffa)、deathmatch
  • 大逃杀:battle royale
  • 合作:cooperation(缩写coop)
  • 休闲:casual
  • 刷(反复从事某个行为以获取收益):grinding、farming
  • 刷装备:grinding gears
  • 掉落:drop、loot
  • 刷怪:spawn
  • 刷新点:spawn point、spawner
  • 刷怪点:mob spawner、monster spawner
  • 玩家重生(特指FPS游戏那种被打死后过一阵子会自动重生的机制):respawn
  • 重生/复活:reincarnation
  • 掉率:drop rate
  • 宝箱:chest
  • 稀有度:rarity
  • 稀有:rare
  • 传说:legendary
  • 史诗:epic
  • 等级:level(缩写lv)
  • 阶级:tier
  • 品质:quality
  • 增益:buff、boon
  • 减益:debuff
  • 范围效果:area of effect(缩写:aoe)
  • 伤害:damage
  • 溅射伤害:splash damage
  • 暴击伤害:critical damage
  • 持续伤害:damage over time(缩写dot)
  • 持续治疗:healing over time(缩写hot)
  • 触发:programmed random occurrence(缩写proc)
  • 暴击:critical(缩写crit)
  • 招架:parry
  • 格挡、阻挡:block
  • 未命中:miss
  • 精准、命中率:accuracy
  • 闪避、躲避:dodge
  • 避免:avoid
  • 生命偷取/吸血:leech、vamp
  • 音效:sound effects(缩写sfx)
  • 音乐特效:musical effects(缩写mfx)
  • 视觉特效:visual effects(缩写vfx)
  • 背景音乐:background music(缩写bgm)

机制篇——卡组:

  • 卡牌:card
  • 骰子:dice
  • 卡组:deck
  • 抽卡:draw
  • 洗牌:shuffle
  • 赢:win
  • 输:lose
  • 平局:draw-game、fair
  • 赢家:winner
  • 输家:loser

商业篇:

  • 氪金、充值、课金、应用内付费:microtransition、In-App Purchase(IAP)、In-App Billing
  • 抽卡:gacha
  • 保底:guarantee
  • 礼包:bundle
  • 礼物:gift
  • 彩票、乐透:lottery
  • 折扣:sale
  • 八折:20% off
  • 买一赠一:buy one get one free
  • 免费:free、free of charge
  • 价格:price
  • 收费:charge
  • 费用:cost
  • 战令:battle pass
  • 季票:season pass
  • 会员:VIP、membership

怪物/种族篇:

  • 人类:human
  • 半身人:halfling
  • 侏儒:gnome
  • 巨人:giant
  • 矮人:dwarf
  • 精灵:elf
  • 兽人:orc
  • 地精:goblin
  • 狗头人:kobold
  • 豺狼人:gnoll
  • 鱼人:murloc
  • 魔像、魔偶:golem
  • 小精灵:pixy(皮克西)、gremlin、gnome(侏儒)、fairy(彼得潘风格的小仙子)、wisp
  • 无头骑士/杜拉罕:dullahan
  • 巴风特/巴弗灭:baphomet
  • 伊芙利特:ifrit
  • 沙罗曼蛇:salamander
  • 希瓦/湿婆:shiva
  • 天使:angel
  • 大天使/天使长:arch angel
  • 堕天使:fallen angel
  • 路西法:lucifer
  • 炽天使/六翼天使:seraph、seraphim(复数)
  • 恶魔:demon、devil、diablo
  • 大恶魔:arch demon、arch devil
  • 小恶魔/小鬼:imp
  • 地狱火:inferno
  • 利维坦:leviathan
  • 九头蛇/海德拉:hydra
  • 衔尾蛇:Ouroboros
  • 耶梦加得:Jormungand(Jörmungandr)
  • 龙:dragon
  • 龙兽:drake
  • 双足飞龙:wyvern
  • 蛇:snake
  • 眼镜蛇:cobra
  • 蝰蛇:viper
  • 宝箱怪:mimic
  • 魅魔:succubus
  • 男妖/梦魇(魅魔的男性版本):incubus
  • 女妖:banshee
  • 幽灵/幽魂:ghost、wraith、spirit
  • 亡灵/不死:undead
  • 僵尸:zombie
  • 骷髅:skeleton
  • 吸血鬼:vampire、nosferatu
  • 狼人:werewolf
  • 恐狼:direwolf
  • 恐熊:direbear
  • 枭熊:owlbear
  • 枭兽:moonkin
  • 渡鸦:raven

控件

  • widget
  • tooltip
分类
未分类

游戏开发中的多语言文本管理2

前一阵子在做新项目的时候想了一下目前的项目在本地化文本管理方面遇到的诸多问题,正好又碰到了一个新朋友来问这个问题,翻出了7年前我写的一篇博客,现在回头看的话感觉当时写的太简陋了,因此决定重新分享我的个人经验,在多语言文本管理中遇到的问题,以及我建议的解决方法。

中心化管理:看起来很美

市面上最常见的多语言文本管理的手段是中心化的管理手段,即多语言文本完全脱离其所被使用的场合,作为一个单独的表单或文件存在,任何需要显示文本的地方,都以key为唯一的索引从这个文本中来获取对应的本地化语言的文本。

在举一个具体的例子之前,我们要明确一个观点:一个项目中的文本主要分两种:UI文本和数据文本。UI文本指的是界面上各种控件显示的文本,数据文本则是需要策划管理的游戏数据的文本。除此之外还有第三种文本:服务器文本,这个属于比较特殊的情况,不在下面的例子中讨论,文章结尾的地方会专门说明。

假如我们在做一款RPG游戏,有英雄和技能两个表,最终导出了三个文件,那么一个典型的中心化管理的多语言文本可能是这样设计的文件结构:

  • hero.json
  • skill.json
  • text.json

我们只看hero.json和text.json来说明问题。

假设一个英雄非常简单,只有两个属性:名字、技能,那么英雄的数据可能长这样:

  • hero_id = 1
  • hero_name = “text_hero_name_001”
  • hero_skill = [1,2,3]

本文重点关注的即是hero_name的部分,这是需要多语言管理的部分。显然“text_hero_name_001”是一个key,其指向的真实文本储存于text.json中。

那么text.json长什么样?如果我们用同一个text.json来存储全部多语言的文本,那么可能长得类似这样(Dictionary/Object风格):

  • text_hero_name_001 =
    • {
      • “zh_CN” : “王老王”,
      • “en_US” : “King Old King”,
    • }

也可能是这样(CSV风格):

  • key,”zh_CN”,”en_US”,
  • text_hero_name_001,”王老王”,”King Old King”,

也可能是每个语言对应一个text.json文件,这种就不再举例赘述,但不管哪种,本质都是一样的:文本脱离于其所被使用的环境,进行中心化管理,并且在被使用的地方通过Key来索引。

这看起来没有任何问题,也是业界的主流做法,但在我这些年游戏开发的经验中却觉得这样做的问题非常大,甚至可以说这都是程序思维的产物,能实现需求但却基本没考虑策划和UI的维护成本,下面就开始详细的阐述这种方式的弊端。

问题一:脱离使用环境

假设你是一个UI同学,现在做了一个简单的弹出层,有标题、正文和确定按钮,你需要三个文本来描述这个界面:

  • 标题:确认充值?
  • 正文:做游戏不赚钱,就是交个朋友。确认充值648钻?
  • 按钮:确认

那么按照上述的管理方式,界面上可能是这样的:

  • 标题:text_dialogue_title_iap_001
  • 正文:text_dialogue_content_iap_001
  • 按钮:text_dialogue_button_iap_001

一个UI同学在UI编辑器或游戏引擎中长久的面对这种不可读、不可排版的文本,长此以往对心灵会产生什么样的打击不言而喻——当然,这跟程序没关系,程序也不在乎。

假如UI抱怨的非常凶,要求程序在编辑环境中也要默认把这堆看不懂的key给显示成简体中文,而程序勉为其难的也给做了,那么至少解决了UI同学的问题,但前文提过,UI文本只是一个项目中的一部分,还有另外一个大头:数据文本

还是刚才的hero.json的例子,假如我们这个RPG项目有100个英雄,那么我们在hero.json中能看到的是什么呢?是任何一个英雄你都不知道他的名字是什么,只能看到从text_hero_name_001到text_hero_name_100这种意义不明的东西。

作为一个策划,你不太可能记得住一个英雄的ID,反而记住他的名字要容易得多。那么现在你要去找一个英雄的数据,你要怎么办?你可能要去text.json中找到这个英雄的名字,再看对应的key来识别他的ID,再去hero.json中找到这个英雄的数据。

这时候假如有个策划不那么讲究,没按照规范去好好的给hero_name对应的文本建key,并且当时他偷摸就提交了,谁也不知道,本来应该叫做”text_hero_name_100″,但这个策划给起成了”text_hero_name_wanglaowang”,那你找起来就会想杀人了。

这时候还有更大的问题,如果一个策划想要知道一个英雄有什么技能,那怎么办?因为技能表里也是不带名字的,他就只能先想办法找到英雄的ID,然后再找到技能的ID,然后再去看技能的名字

这种套娃操作每多一层,策划的心理都会多一层崩溃。

除此之外,脱离使用环境还使得你的项目需要翻译外包的时候看似容易,只要把text.json丢过去就行了,但因为翻译的人员不知道这个文本是用在哪里的,因此很容易出现翻译完了结合上下文意义错误或UI的显示出现异常的情况。这部分的沟通成本也是相当巨大的。

问题二:单一职能原则

假设我们还是上面那个做充值对话框的UI同学。这个项目肯定不止这样一个对话框,所以我们还会做很多很多其他的对话框,比如购买各种东西的确认对话框、各种危险操作的确认对话框、需要选择个数或者填写内容再提交的对话框等等。

做多了之后我们就会发现,这些对话框都有一个确定按钮,而且按钮的文本内容可能都一样,都是“确定”,但却每次都要起一个新的text的key。当这个UI同学打开了text.json后,发现有几十上百个不同key的文本都叫“确定”的时候,他一定会怀疑这种做法是不是有问题。

最后UI们商量了一下,决定所有的确定按钮都用一个key,比如这个key叫做“text_dialogue_button_confirm”。大家觉得工作简单多了,所有的确定按钮都用这一个文本,不用再弄大量臃肿的文本了。

直到再过了一段时间,策划提了一个需求,充值界面的确认按钮不能只写“确认”,要写“确认,我家有矿”。策划认为简单的改文本就行,于是就把“text_dialogue_button_confirm”的内容给直接改了。

改完了之后发现项目出了大问题,所有的对话框的确认按钮的文本都变成了“确认,我家有矿”。然后QA爸爸就提着刀过来了。

QA爸爸把这个问题捅到了项目主管那里,等到项目主管发现这个问题之后,决定检查一下所有的text.json里面的文本,要确认下到底有哪条key是不止被用了一次的,但却发现很难做到这样,因为项目进展到这个阶段,text.json里面可能已经有上万条数据了,你既没法简单的知道里面有没有完全没被引用实际上已经冗余了的条目(比如删了一个技能,但技能的名字没删),也没法确认里面的某一个条目有没有被多次引用(类似上面的确认的问题,策划和美术都会有这个情况),从而导致改动的时候引发意料之外的问题。

当然,你可以硬性的规定,凡是要复用的text都放到同一个文件里(比如它叫general_text.json),凡是应该被用且应该只被引用一次的text都放在text.json里,但毕竟只要key是人为的手写的,就无法完全避免这个问题,很快两个文件里面可能又会出现混乱——text.json里面出现了被引用多次的key,而general_text.json里面出现了冗余的key。

假设这时候有一个程序同学觉得可以从工具上来入手解决这个问题,于是他写了一个工具,从此text.json的key都是自动管理的了,策划同学可以方便的在各个数据表中添加、修改、删除文本,key的关联都是自动完成的,或者说text.json的key完全都由这个工具自动管理了,key甚至对策划来说是不可见的,从而从根本上避免了策划手误的问题。

这听起来很美好是吧?但其实并不是的,因为这个方案依然有问题。

从原则上来说我个人是非常不建议项目在常规的开发工作流中加入一些自己开发的工具的,除非是在这方面有相当丰富经验的开发团队,或者这个工具本身足够简单、不进入工作流(比如用了一次就不用了的“日抛型”工具)。原因有三点。

首先是这个工具的可维护性问题。这些工具在开发之时只是为了尽快的解决某些小问题,并没有严格的当做一款可以交付使用的产品来对待,因此缺乏文档,并且开发的也相对随意(用户是内部开发人员而不是游戏玩家)。这个工具本身需要测试但缺乏测试,它的完整性和可靠性需要支付相当大的成本,而这部分成本本来是可以去开发玩家可以体验到的功能的。这还只是短期的问题,如果我们从长计议的话,短期少做点功能,把工具做好,也没什么问题,但长期问题实际上更大。当这个工具被用了几个月甚至几年之后,不出bug的概率几乎等于0,而当初写这个工具的人可能早已不在这个项目组甚至离职了,而由于缺乏文档且开发随意,接手维护这个工具的人可能完全无法上手,这种情况下项目就陷入了一个两难的境地。还有很多情况下,随着工作流的调整或人员的变化,慢慢的这个工具也会被不断的迭代,但其迭代的速度往往是滞后于团队的变化的,甚至最终成为拖后腿的卡点——你不得不用它,而你又明知道它已经不好用了。

其次是这个工具的可靠性问题。这些工具所依赖的开发环境过于复杂,公司的一次停电、游戏引擎的一次升级、一个脏数据的写入、一个策划不小心的误操作(比如把ID填重复了),都有可能产生大量的问题,这些问题是最早做工具的同学所意料不到的(大家都知道,程序员是乐观的),而为了解决这些问题所有的策划都不得不停工,并且问题解决的代价和效果也不得而知(比如可能导致json中的所有排序都变了,虽然实际上没变化因为json里面排序不重要,但QA爸爸能看到的就是一个上万行的json文件的每一行都变了,而导致QA爸爸提着刀过来)。

最后一旦你开始依赖这种小工具后,往往会接二连三的做一大堆工具,前面的两个问题很快会变成多个问题,很快就会陷入按下葫芦起了瓢的状态,甚至当出了问题之后你都不知道究竟是哪个小工具的锅,开发工具的人抱怨使用工具的人提的问题模糊而不确定(甚至怀疑是使用者自己的问题),使用工具的人抱怨开发工具的人做的破玩意不靠谱,最终所有人都难受。

因此,如果你必须要把某个工具加入工作流中,那么如果有外部的、成熟的、有长期维护的解决方案,尽量不要自己造轮子,宁可花点钱买解决方案,也比自己花精力去做这些事情要强,否则当你过了几个月甚至几年后,一定会为当初自己的决定后悔——假如你还在这个项目组的话。

要相信你的问题别人都早就遇到过了,用别人造好的轮子总是比自己造轮子强,人类社会就是这么进步的。

问题三:依赖性问题

假如你是一个策划,已经设计好了一个新的英雄,以及他的技能,现在要开始配表了。

既然要加英雄,那么理所当然的你打开了hero.json,但是当你加到一半的时候发现加不下去了,因为hero_name需要一个key,你必须先去text.json里面配置好这个文本,再填回到hero.json中来才能完成配置。提交的时候你必须同时提交这两个文件,否则英雄的名字就会显示错误,显然这是会被QA爸爸暴揍的。

等你搞完了之后又发现这个英雄要加技能,那必须又先去配技能表。而配技能表的时候又发现了必须要去先去text.json里面写好技能的名字。

而现实情况中这个套娃的情况往往更严重,以我目前的项目为例,我需要新加一个宝箱,这个宝箱有【名字】有【描述】,这个宝箱有一个对应的道具ID,道具有【名字】有【描述】,这个宝箱里面是一件新时装,这个时装有【名字】有【描述】,这个新时装有对应的道具ID, 道具有【名字】有【描述】,这个新时装有对应的时装碎片,这个碎片有【名字】有【描述】,这个碎片有对应的道具 ID , 道具有【名字】有【描述】。等这一套折腾完,你会发现最耗费精力的不是宝箱->时装->碎片的套娃,而是不管你干啥都需要去text.json里面加名字,而这对于策划来说也是非常痛苦的。

当然,在删除配置的时候策划也会面对同样的噩梦,要删一个东西就要删大量对应的text,而这种删除操作的危险性在上面的一个问题中已经提到过了,因此最终往往会演变成“冗余就冗余,只加不减就好了, 这样至少不会出错”的情况。这会导致外包成本急剧增加,因为你也不知道哪些文本有用,哪些文本没用的。

更严重的是这会让你的项目很屎,而这屎不是喂给玩家的,是喂给策划的。大家都知道这里面充满了屎,但谁也没法认出来究竟哪些是屎。一个敢喂自己屎的策划,对玩家能做出多么丧心病狂的决策都是有可能的。

当一个东西变得反直觉的时候,大概率有更好的方案可以去替代它。

问题四:冲突问题

极端的情况下,不管你改客户端的什么表,都需要同时的去改text.json。而text.json只有一个,所有策划共用,因此冲突的几率极高,同时类似SVN的版本管理工具对json文件的比对支持的也不是特别好(你可能需要专门的json语意比对工具),于是策划的作业变成了一场噩梦,你只是想把自己改的东西提交上去而已,但却发现这竟然如此困难。

而更大的问题是,你不只是提交就完事了,你还需要合并呢,合并的时候更是一场噩梦。

虽然靠培训每个人都要学会提交、合并、解决冲突可以来克服这个困难,但毕竟这个困难实际上……有可能压根就不存在,而只是因为一开始设计的偷懒导致的没必要的困难。你让程序把所有代码都写在一个文件里他们自然不干,那为什么策划和UI的所有文本都在一个文件里他们就干了呢?——因为不用他们维护,这是个屁股决定脑袋的问题。

问题五:唯一性问题

由于大量的文本都堆在一个文件里,为了保持key的可读性和唯一性,命名就成了一大难题,其困难程度甚至比美术同学考虑美术资源的命名还困难,因为美术资源可以分文件夹,但key都堆在一起。

想找到一套完美的可以描述所有东西的key的方法不是不行,但这种结果大概率会让key变得非常冗长。比如美术可能考虑把界面的名字或者prefab的名字加进去,策划则要把是哪个表的哪个id的哪个字段用到的加进去。很快你的项目就会出现一个神奇的现象:大部分的字符串的key甚至比其本身的内容还要长的多的多,整个text.json文件奇大无比,但“三斤鸭子两斤嘴”,肉没多少。这种情况的体验就像是大家上班的时候互相不叫昵称也不叫姓名,而是互相喊身份证号的完整号码来互相沟通一样,显得非常蠢。

那怎么办?

解决方法我觉得很简单,去中心化,直接对症下药,需要完成以下几个需求:

  • 文本不再中心化全放在一起,而是分散开来,根据各个模块分散到各个文件中,并且把程序设计成只能访问自己相关模块的文本。这样即能解决一个大文件策划互相冲突的问题,也能解决滥用key的引用导致无法追踪每个key都被哪里用到了问题。一言以蔽之,把text从全局的改成本地的。
  • 文本尽量贴近其所被使用的场合,甚至可以完全免掉key是最好的。这样既不用费劲去给key取名字,也不用去琢磨key的冗余或被多次引用的问题了,也不用去开发劳什子自动关联key的工具了。

于是hero.json会变成什么样子?大概会变成这样:

  • hero_id = 1
  • hero_name =
    • {
      • “zh_CN” : “王老王”
      • “en_US” : “King Old King”
    • }
  • hero_skill = [1,2,3]

回头一看,这其实就是我七年前贴的文章中《炉石传说》的做法。暴雪在《星际争霸2》中采用的还是中心化的管理办法,而《炉石传说》他们选择了另外的做法,我猜是他们吃屎吃够了。

如果策划用Excel来管理多语言的话,需要写插件,否则一个格子里面写Dictonary/Object风格的东西会很蛋疼;或者策划可以在多列中配置语言,但最终把多列导出成Dictionary/Object风格的内容。但是毕竟长痛不如短痛,总比搞好几个表来回贴key要舒服得多。

有人可能说了,这样策划要每次都去写”zh_CN”和”en_US”这种文本,不很容易错吗?但你想想,是写这样固定的文本容易错,还是每次都要依照一个规则去想一个新的key更容易错呢?

还有人可能会说,这样客户端不管当前语言是什么,都会加载全部的语言文本,有点浪费资源。但根据我个人的理解,占内存大头的永远不会是文本文本,而是二进制文件,因此这方面的性能问题也可以忽略不计。反而这可能成为一个好处,即切换语言不需要重启客户端更容易实现了,但毕竟我不是专业的程序,这里可能还涉及到加载字体等问题,这里要视项目最初的需求是否需要不重启客户端就能切换语言了。

好了,策划的同学解决完了,那么UI同学怎么办?

比较简单的做法是扩展一个支持多语言的控件,比如原本系统的文本控件只能输入一套文本,让程序扩展一下可以写多套文本即可,同时默认显示中文。比如一个确定按钮,本来UI拉上来一个button后直接在text里面写“确定”就行了,现在的话会有多行的text,其中第一行是”zh_CN”的,UI同学要在这里写”确定”,然后在第二行”en_US”里面写”Confirm”。同时还可以支持切换预览多语言效果。实际上很多多语言插件都是用类似的思路去做的,比如Unity的I2 Language(这里不是打广告,我没用过,但看demo感觉挺好用的,还支持图片和音频的多语言,而且这也是Unity Asset Store中最受欢迎的本地化插件)。

UI文本本地化之后的一个问题就是外包的时候我们还是需要导出文本,因此在一开始设计结构的时候需要尽量设计一个不依赖于Key同时还可以方便导出导入的结构,单独把每个界面prefab的文本导出,翻译完成后再导回来即可。上面提过的I2 Language貌似还支持谷歌翻译以及导入导出功能(这插件本质上还是中心化管理文本的,只是界面上隐藏了),感兴趣的同学可以自己试试。

但是以上毕竟只是脑洞,我决定在新项目中试一下用这种方式来管理文本的实际效果如何。因为都是我一个人做的,所以UI的多语言文本我决定不写在控件上,而写在代码里,一个prefab的代码把需要用到的文本统一在一个地方以Dictonary/Object的风格来声明好,方便后面调用。而策划配表的部分,我决定在Excel中每个文本一列,再导出的时候把同一个key的文本合并成Dictonary/Object风格的变量。

这里实际也引申出了另外两个关于UI实现问题。

第一个问题是Prefab这个东西往往是客户端程序和UI同学都要打交道的,这里非常容易出现问题,比如UI同学一不小心把某个节点隐藏了,QA同学提了BUG,程序同学绞尽脑汁的去调查问题最终发现居然不是自己的锅。解决方案业内也有一些,有的比较笨的办法是由程序同学来拼界面,这样避免UI同学染指Prefab,但拼完了难免坐标不对,UI同学要再调整一下;有的方法则是参考策划的工作流,把UI的编辑过程独立于游戏引擎之外,把界面当做是资源文件导入到项目中,从而实现了UI同学和程序同学作业对象的分离。也有其他的做法,比如对UI设计师和UI程序员的要求更高,双方必须紧密的结对作业,而不是各自分属不同的部门。还有的做法是UI在PhotoShop里面做完了工作后策划来拼界面,把和程序对接的工作交给了策划,从而UI不用考虑上传SVN的问题。还有的做法是逼着UI同学去学写代码,比如至少会用个蓝图啥的,从而避免程序同学染指Prefab。反正各种奇奇怪怪的做法都有,哪种是最好的不好说,要视团队和项目的具体情况而定。以上的种种方法中我个人最倾向的是哪种?如果是UI表现十分重要的游戏,那么提高对UI和程序人才素质的要求,强制其结对作业共同对UI的结果负责,是比较好的解决手段,能够实现质量和性能都比较出色的UI界面,但这样对人才的要求很高(相当于半个TA),大部分的团队不一定能做到,这种情况下另外一个选择是退而求其次,将UI的设计工作交给策划,只保证功能性但不保证美观度,在人力捉襟见肘的小公司/独立团队或功能无限庞杂的大项目中这也是个不错的方法。不管哪种,其本质都是要适应UI工作的特殊性,这不是一个可以简单的“美术vs程序”二元割裂的工作,而是必须有机的结合在一起才能完成的综合性工作,一切想着切割UI和程序的工作流的方案都注定不是最优的。

第二个问题是界面的很多状态和内容应该是以资源驱动为主导还是以代码驱动为主导?比如一个界面默认状态是应该隐藏的,那么是UI同学把隐藏给勾上比较好,还是程序同学在代码里初始化的部分写上强制隐藏好?按照我的倾向性的话,如果不影响性能的前提下,我倾向于用代码来控制。UI资源本身毕竟是静态的,因此凡是动态的东西都应该由代码来控制,这也是为什么多语言文本我觉得写在代码中比挂在界面上要好的原因,因为涉及到多语言,本身就已经不是静态的文本了。

祝我好运吧emmmm,前途未卜,不知道还会踩到多少坑,但总比现在的情况舒服。

(另外别问我为什么我不用I2 Language,因为Godot天下第一)

写在最后:关于服务器文本

所谓的服务器文本,即并列于UI文本和数据文本之外的第三类文本,这种文本常见的是停服公告、发给玩家的邮件、紧急维护通知跑马灯等种种由于具体写什么文本完全无法预估而只能在服务器端设置文本,客户端收到什么就显示什么的文本。这类文本大部分属于运营工具的范畴。

那么这类文本如何实现本地化?

这首先要看游戏服务器的结构,是各个语言的用户都混在一个服务器里面玩?还是每个语言的用户自己有自己的服务器?如果是后者的话那么很简单,每个语言的服务器发对应语言的公告就好了(甚至可能是完全不同的运营商),如果是前者那么这涉及到另外一个问题:客户端能否切换多语言?如果能的话,比如我用英文客户端登录之后,系统发给我的邮件可能是英语写的,这时候我切换成中文客户端重登后,看到的这封邮件是英文的还是中文的呢?确定了这个需求之后,才能设计服务器端文本的多语言是应该如何处理的。

只要提前考虑好,程序做起来都是很容易的。难的都是已经在线上跑了一阵子了又不得不改,这才是最难受的。

分类
未分类

堕落与背叛:暴雪曾经辉煌的叙事主题

首先一个开门见山的问题:

最能代表暴雪IP的是哪几张脸?

我觉得是上面这三张脸,分别代表了《魔兽争霸》《暗黑破坏神》和《星际争霸》三个系列。

那么这几张脸有什么共同点?

我觉得就是他们的故事主题都是堕落与背叛

阿尔萨斯的故事,是一个内心深处有着弱点的年轻王子被巫妖王利用,不断做出亲者痛仇者快的事情,最终彻底堕落,完全背叛了自己所本应坚持的道路与职责,并成为真正的巫妖王的故事。

因此阿尔萨斯堕落的原因有一半是其固有人格缺陷的内因,另外一半则是被诅咒教派拿来当枪使,沦落为其摆脱燃烧军团的道具,一半是外因。

暗黑破坏神的故事则更为黑暗,整个李奥瑞克一家全都不得善终。一条线是李奥瑞克王如何堕落为骷髅王,背叛了自己的王后与人民的故事,整个故事给人一种结合了《指环王》中刚铎摄政王迪耐瑟的疯狂与洛汗国王希优顿被奸臣支配的堕落,并且下场比两个人更惨。另外一条线是李奥瑞克王的儿子,年轻的王子艾丹铲除了暗黑破坏神后,将其灵魂石封印在自己的额头中与其在精神上对抗,但最终不敌,堕落为黑暗流浪者,直至完全变为下一代暗黑破坏神的故事。

在《暗黑破坏神》的语境中,李奥瑞克王和艾丹都是好人,但由于外在的污染所堕落,因此暗黑破坏神的故事更为黑暗,如果说阿尔萨斯还是自作自受罪有应得的话,暗黑破坏神的调性则基本是好人没好报

刀锋女王的故事与前两个有所不同,其之所以成为刀锋女王是由于被自己人背叛,沦为了牺牲品后又崛起成为刀锋女王的故事。在阵营转换之后,她也和阿尔萨斯类似,对原本所在的阵营或者说种族再没什么怜悯之心了,狡诈狠辣,击败种种强敌,最终称霸了科普卢星区。

刀锋女王为人类带来的威胁犹如魔兽中的诅咒神教或暗黑中的几大恶魔,但在星际争霸中,这一恶果是由人类所自己培养出来的,因此其结果颇有讽刺意味——在星际争霸中负责“伟大光荣正确”的种族是星灵,而魔兽争霸中负责“伟大光荣正确”的种族是人类。

因此我们可以概括出来暴雪塑造大BOSS的成功配方了,就是由于各种原因让他们堕落,背叛原本的阵营,并最终成为原本阵营的心头大患的故事。听起来好像没什么技术含量,但暴雪正是在这里下的功夫足够扎实,以其中最为出彩的阿尔萨斯堕落史为例:

  • 阿尔萨斯不听师父乌瑟尔的命令,屠杀了黑石兽人
  • 阿尔萨斯发现吃了被污染的谷物会感染瘟疫,并最终感染者会变成亡灵
  • 阿尔萨斯杀掉了污染谷物的来源,死灵法师克尔苏加德,但克尔苏加德说上面还有大佬,恐惧魔王玛尔甘尼斯
  • 阿尔萨斯遇到了麦迪文的预言,但拒绝前往卡里姆多
  • 阿尔萨斯发现被污染的谷物已经发给斯坦索姆的百姓吃掉了,因此进行了屠城,与玛尔甘尼斯展开屠杀竞赛。师父乌瑟尔拒绝服从,被阿尔萨斯剥夺了骑士资格,两人决裂
  • 阿尔萨斯击败了玛尔甘尼斯,并追随他来到了诺森德,吉安娜劝他不要去当他不听,两人决裂
  • 阿尔萨斯遇到了穆拉丁·铜须,为了让手下听命于自己,偷偷烧了返航用的船,并把罪名嫁祸给雇佣兵身上
  • 阿尔萨斯为了击败玛尔甘尼斯,去寻找霜之哀伤,并且不顾穆拉丁·铜须的劝阻获得了霜之哀伤,代价则是穆拉丁的死亡
  • 阿尔萨斯击败了玛尔甘尼斯,丢下了自己的部队任凭他们自生自灭
  • 一段时间之后,阿尔萨斯回到了洛丹伦,亲手杀了自己的父亲,继承了王位
  • 阿尔萨斯杀了自己的师父乌瑟尔,并从其手中夺取了父亲的骨灰盒,用来复活克尔苏加德成为巫妖

我们会发现阿尔萨斯的堕落史中存在这三个友方角色乌瑟尔、穆拉丁和吉安娜,以及两个敌方角色克尔苏加德和玛尔甘尼斯。乌瑟尔是老师,穆拉丁是亦师亦友,吉安娜则是爱人。阿尔萨斯最终背叛了所有的友方角色,直接害死了乌瑟尔和穆拉丁(后面还有吉安娜的师父大法师安东尼达斯、希尔瓦娜斯风行者等等),杀了被巫妖王当枪使的玛尔甘尼斯,并且最终把克尔苏加德从当年你的敌人变成了自己的随从。通过这一系列故事,我们可以清晰的看到阿尔萨斯是如何不断做出亲者恨仇者快的事情,最终完全倒戈,以及后面继续的一系列暴行的。

在《魔兽争霸1》和《魔兽争霸2》中,故事都是简单的善恶二元对抗,人类就是好的,兽人就是坏的。而《魔兽争霸3》通过刚才说的阿尔萨斯所代表的人物故事,把整个系列的叙事水平提高到了一个前所未有的高度。

类似的,李奥瑞克一家的堕落史与刀锋女王的遭遇背叛也都是非常详实的,情节跌宕起伏而令人信服。暴雪通过这些高超的故事,奠定了这几个角色成为各自系列中的C位角色。

很多非C位的角色,也有着类似的叙事主题,如魔兽的麦迪文与古尔丹、伊利丹、玛维·影歌、迦罗娜、凯尔萨斯,暗黑的衣卒尔、爱德莉亚,星际的萨米尔·杜兰等等。因此可以说很大程度上这种故事构成了暴雪的叙事特色。

然而,暴雪已经很多年没有再讲述类似主题的故事了,甚至可以说是完全背叛了原本的叙事基调,这无疑是令人失望的,甚至让很多老粉丝感到遭到背叛的,比如我,这是我成为暴黑的很大一个原因。

不得不说,暴雪目前自己对于老粉丝所做的事情,也是堕落和背叛。

在细数暴雪的罪名之前,要先提一下,暴雪除了堕落与背叛之外另外一个擅长的主题——牺牲与荣耀。

三个系列的作品中也都有很多类似的角色,《魔兽争霸》中的格罗姆·地狱咆哮,提里奥·弗丁,安度因·洛萨,《星际争霸》中的塔萨达,泽拉图,以及《暗黑破坏神》中的塔·拉夏,刚刚提过的艾丹王子,亚瑞特圣山的三蛮子等等。

在《暗黑破坏神》的黑暗幻想语境中,牺牲与荣耀是没意义的,最终无罪之人依然会堕落,这是是整个暗黑破坏神的一贯基调。在《星际争霸》与《魔兽争霸》中则没有那么黑暗,牺牲与荣耀成为了塑造悲情英雄的主要手段。塔萨达与迪里奥·弗丁都是面对族人的错误做法勇于进行挑战的典范,格罗姆·地狱咆哮更是通过牺牲完成了自我救赎,这些故事都非常感人,与前面说的堕落与背叛一同构成了《星际争霸1》与《魔兽争霸3》的主要故事,这也是这两部作品之所以伟大的原因:英雄的堕落令人惋惜,英雄的牺牲令人感动

回头看看《魔兽世界》、《暗黑破坏神3》以及《星际争霸2》的剧情,失望之处比比皆是了。

《暗黑破坏神3》完全没有了堕落,也完全没有了牺牲。死亡天使马瑟尔的动机苍白而又无力,智力水平和灭霸差不多,完全没有深刻的剖析他为什么要与泰瑞尔对着干。莉亚的死亡和迪卡·凯恩的死亡都是单纯的黑恶势力的牺牲者,而不是堕落者。反而是铁匠海德格老婆变成了僵尸不得不杀掉,以及寇马克发现自己整个人生都是谎言的故事更加丰满,但这些角色完全都不在主线中,有没有他们都不耽误。爱德莉亚的背叛倒是有些应该有的味道,无奈她本人也并不是什么关键角色。泰瑞尔的下凡也非常的无聊,离开天使议会的原因非常的小孩子气,并且从头到尾没什么关键作为,令人觉得这连牺牲都算不上。玩家作为涅法雷姆很快就明白了所有的恶魔都不是自己的对手,自己的主角光环如此之强,完全背叛了当年艾丹王子好人没好报的叙事基调。

《星际争霸2》也基本是一样的水平。曾经大量描写刀锋女王是多么凶恶狡诈的篇幅,都被2代苍白无力的洗白剧情所抛弃,给你一种棺材板都压不住了的感觉。刀锋女王从原本的荼毒生灵的大反派摇身一变成为宇宙的拯救者,就好像你跟我说一个十恶不赦的罪犯也可以被原谅,并且你必须原谅一样强奸民意。星灵的战役同样是主角光环感十足,你知道你最终能争取到并团结所有人,显得塔萨达当年的牺牲是如此没有必要。萨米尔·杜兰当年的种种阴谋诡计背后所透露出的可怕真相沦为不断刷怪为玩家送经验的手段而已,玩家都心疼艾蒙了。

《魔兽世界》就更扯淡了,现在各个角色充满了莫名其妙的背叛,由于动机是如此苍白,你甚至会以为他们疯掉了——脑残吼可不是浪得虚名的,希尔瓦娜斯烧树的动机更是莫名其妙,玛里苟斯更是脑子有病,诺兹多姆也是脑子有病,你会发现这些角色偏执的程度跟吉安娜她爸海军上将普罗摩尔有一拼。那些被强行写死的角色也是变着花样的作死,给人的感觉不是牺牲而是愚蠢——比如瓦里安·乌瑞恩,另一个时间线的先知维纶,大法师罗宁,等等。原本好好荡气回肠的史诗,变成了被疯子和傻子所充斥的无聊世界观。

不过暴雪的用户群体现在分化的也十分严重了,就在《暗黑破坏神:不朽》在youtube上遭遇差评风波的时候,《守望先锋》的动画空前好评冲上了youtube的时下流行榜。 这说明新游戏的群体基本不吃老三样那一套了,所以也许暴雪很清楚自己在干什么吧。