为游戏中的统计变量加入标签

睿智的唯物主义者+40

1. 一个现实的需求

在某个游戏中,会根据每一局玩家遇到的内容决定游戏结束后的提示(tips)。例如:

  • 当玩家玩得很差时(这一般说明他是个新手),显示最基础的提示
  • 当玩家遇到了特定敌人时,显示这类敌人的提示
  • 当玩家获得了某些道具时,显示这些道具的提示
  • 当玩家使用了某些技能时,显示这些技能的提示
  • 当玩家因为某种原因死亡时,显示避免这种死亡的提示
  • 等等……

玩家结束一局时可能会满足多个提示的显示条件,这时就在可以显示的提示里随机选择一个。

选择的实现方式是给各种种类的提示做一个权重表。根据玩家行为的不同调整不同类型提示的权重,然后选择一个结果。比如说:

local tipsWeightList = {
    [TIP_GROUP.TYPE_1] = 10,
    [TIP_GROUP.TYPE_2] = 0,     -- 不可能出现
    [TIP_GROUP.TYPE_3] = 50,
    [TIP_GROUP.TYPE_4] = 1000,  -- 出现概率最高
    [TIP_GROUP.TYPE_5] = 10,
}

睿智的策划为权重的调整加入了很多条件,希望能给玩家更好的体验,例如:

  • 当玩家还处在新手阶段时,大幅提升基础类提示的权重
  • 当一局游戏中出现了很多类型的道具时,之前出现提示较少的道具类型获得更大的权重
  • 某些死亡方式和某些敌人的搭配会大幅影响提示的权重
  • 随着一部分提示的出现,还没有出现过的提示的权重会有所变化(例如一些提示只有当一定数量的提示解锁后才可能出现)
  • 等等……

我们需要根据各种规则反复修改之前的权重表。当规则多起来之后,我们在测试中遇到了问题:这个权重表的每一项到底对不对?如果不对是哪里不对了?

回想一下P社游戏会把大量影响因素罗列出来,非常便于分析:

《欧陆风云IV》中的数值细节

我们希望自己的游戏一局游戏结束后能够将权重表罗列成这样:

基础组 10
    基础  +10
敌人1组 160
    发现了敌人  +10
    敌人1组出现率<10% (0/6)  +150
敌人2组 0
道具1组 10
    发现道具  +10
道具2组 0
道具3组 110
    发现道具  +10
    内容组出现率<10% (0/6)  +100
后期组 0
    全体出现率 (10/100)  +0

这样我们可以容易地看出权重计算得对不对,以及以此估算可能出现的提示。

为了适应睿智的策划的需求,这个记录肯定要是动态的。

2. 先说一个简单的例子

游戏中玩家拥有的金币的数量,最简单的实现是直接放在一个全局的单例里:

PlayerData:getInstance().coin = 10

如果再讲究一点,可以稍微封装一下:

PlayerData:getInstance():addCoin(20)  -- 收入
PlayerData:getInstance():addCoin(-5)  -- 支出

这样在赋值的时候可以干更多的事,比如做个安全检查、发个事件什么的。

在某个时刻,我们发现玩家拥有的金币和我们期望的不一致——肯定是哪里出现bug了。然后我们失望地发现游戏中赚钱和花钱的途径有十几种。为了找到问题出在哪里,我们要么在每处打log,要么加断点然后反复尝试,即便这样也不一定能很快定位到问题所在。

如果我们能像查询银行账单一样查询这个值的修改历史该有多好啊!

所以我们真的来做一个类似日志的系统。实现非常简单:

coin_history = {}
function addCoin(value, tag)
    coin_history[#coin_history+1] = {value = value, tag = tag}
end
function getCoin()
    local coin = 0
    for k, v in pairs(coin_history) do
        coin = coin + v.value
    end
    return coin
end

用起来并没有更麻烦,但是我们现在可以很容易地获得操作历史了:

function listCoinLog()
    print("------ Coin ------")
    local coin = 0
    for k, v in pairs(coin_history) do
        coin = coin + v.value
        if v.value >= 0 then
            print(coin .. "(+" .. v.value .. ") Comment: " .. v.tag)
        else
            print(coin .. "(" .. v.value .. ")  Comment: " .. v.tag)
        end
    end
end

测试一下:

addCoin(10, "income 1")
addCoin(20, "income 2")
addCoin(-5, "expend 1")
listCoinLog()

输出:

------ Coin ------  
10(+10) Comment: income 1   
30(+20) Comment: income 2   
25(-5)  Comment: expend 1

就像查询银行账单。

这样做的一大好处是,我们可能在游戏开发的某一时刻突然有了对数据的统计需求,比如:

  • 今天有10个工人在工作,每个工人贡献了多少?
  • 今天生产了20种产品,每种产品创造了多少收入?

为了这种需求加记录和统计的代码太可怕了,但是我们可以把这些信息全都变成tag,然后在需要的时候统计这些tag。

3. 扩展这个系统

我们有很多事可以做。

  1. 我们可以把这个标记系统单独抽出来做一个类,然后每个数值型的用户数据都是它的实例。
  2. 我们在持久化数据时可以把这些标记一起序列化。这种做法:
    1. 简单
    2. 可以保持全部的原始数据(可以在下次运行时de上次的bug了)
    3. 可以让篡改数据变得稍微更困难些。
  3. 我们可以直接在原始数据的基础上分析数据,例如按照tag分类排序,或者统计单一tag的结果。
  4. 虽然现在只有一个tag字段,但可以扩充更多的信息,比如来自谁、时间戳等等。
  5. 当记录过多时,可以加一个merge方法,归并所有的记录,只保留一条归并后的结果。
  6. 如果比较讲究的话,可以在归并数据时把记录全都转移到另一个archived列表里。但我们毕竟是做游戏不是记账软件,这个有点过于讲究了。

4. 回到最初的问题

有了这个系统之后最开始的问题变得很平凡了。

比如有两个条件都会增加『敌人1组』tips的出现权重:

  • 发现这种敌人
  • 敌人1组总体出现率极低

那么当条件1触发时我们就记录:

{group = 1, value = 10, tag = "发现了敌人"}

条件2触发时记录:

{group = 1, value = 150, tag = "敌人1组出现率低"}

在游戏结束时我们就可以获得类似这样的输出:

敌人1组 160
    发现了敌人  +10
    敌人1组出现率低  +150

当睿智的策划增加新的条件时,我们就加入一小段条件判断,然后把权重的改变打上tag。例如一个降低『敌人1组』出现率的条件是敌人2出现,我们就可以在敌人2出现时加入记录:

{group = 1, value = -100, tag = "敌人2出现"}

在最后获得的结果是:

敌人1组 60
    发现了敌人  +10
    敌人1组出现率低  +150
    敌人2出现  -100
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容