时间停止

简介

“时间停止”是晓美焰的标志性技能,其效果顾名思义,冻结时间,控制除自己以外的所有物体,暂停其一切行为。当这个技能发动的时候,怪物无法移动和攻击,花草树木不会生长,查理不会伤害黑暗中的玩家,飞行物将被定在原地,就连雨滴、雪和火山灰也会暂时悬浮在半空中。

时停技能可通过点按技能图标(在饥饿值图标左侧)来主动触发,持续8秒,技能结束后会进入冷却,时间为25秒(单机版)/30秒(联机版)。

在单机版中,世界时钟也会因为这个技能停摆。换言之,如果你频繁发动时间停止,那么一天的时间可能会延长到10分钟,而非默认的8分钟。这拖延了猎犬、蝙蝠和四季boss的来袭时间,但也意味着更多的饥饿值消耗和魔力的流失。

在联机版中,这个技能不会真正地停止游戏时间,并且它只会作用于晓美焰附近40单位距离内的物体,所以离晓美焰太远的玩家可能并不会察觉到这个技能被发动,但一旦玩家踏入魔法的控制范围,则ta也同样会被冻结。作为发动者,晓美焰自己显然不会受时间停止的影响,而其他玩家也可以在魔法栏位利用1个金块,1个齿轮和15点理智值制作一个精致的怀表,防止自己遭受时间魔法控制。


这些实体可以免疫时停:

  • 带有homuraTag_ignoretimemagic标签(如熔炉模式下的伤害数字)
  • 带有INLIMBO标签(放在物品栏和容器内的所有物体)
  • 携带精致的怀表(包括玩家、猪人、切斯特等)
  • 晓美焰
  • 本身不符合上述条件,但母体免疫时停(如玩家释放符咒时的光)

这些实体可以暂时免疫时停:

  • 刚刚被扔出的物体(如回力标、水球)
  • 刚刚被丢在地上的物体

代码实现

1.计时机制的暂停

暂停实体需要注意很多细节。比如,将一个物体的动画(AnimState)和运动(Physics)暂停以后,还需要阻止它内部的计时机制。以草的燃烧为例,一根草被点燃后,经过7.5秒的燃烧,熄灭变为灰烬,这个过程的计时是基于延时任务函数(DoTaskInTime)。对于这个计时器,官方提供了添加和取消它的方法,但没有提供暂停它的方法,所以得自己写一个。

--这个函数可以让延时任务的完成时间往后推1帧
local function PeriodicPostInit(self)
    function self:AddTick()
        local thislist = scheduler.attime[self.nexttick]
        self.nexttick = self.nexttick + 1
        if not thislist then
            return
        end
        if not scheduler.attime[self.nexttick] then
            scheduler.attime[self.nexttick] = {}
        end
        local nextlist = scheduler.attime[self.nexttick]
        thislist[self] = nil 
        nextlist[self] = true
        self.list = nextlist
    end
end
AddGlobalClassPostConstruct('scheduler', 'Periodic', PeriodicPostInit)

如果想要暂停一个实体的全部延时任务,可以用以下这段代码。

--遍历实体的所有计时器,并让它们的完成时间往后推1帧
--配合组件的OnUpdate函数就能实现计时器暂停
for k in pairs(inst.pendingtasks or {})do
    k:AddTick()
end

这样一来,物体内部的计时器就被控制住了。这有什么作用呢?举两个例子。一个干草已经燃烧了5秒,此时发动时停技能,则这个燃烧的草不会在技能持续期间突然变成灰烬,而是在技能结束后的2.5秒才熄灭;反之,先在地上摆5个干草,发动技能,趁技能结束前依次点燃这5个干草,那么这些干草会在技能结束后才会熄灭,并且是同时熄灭。(因为它们是被“同时”点燃)

2.阻止组件刷新

colourtweener(渐变),grue(查理攻击),projectile(子弹)等组件中存在着一个刷新机制,例如海象射出的吹箭,会以每秒30次的频率计算自己和目标的距离,如果距离小于1,就判定为击中目标,否则继续往前飞行。我们并不希望在撞上停止运动的飞行物后会受到伤害,所以要阻止组件的刷新活动。
有少部分组件的刷新是不应该被暂停的,如container(容器),如果暂停的话,会导致界面无法正常关闭。

local skip_components = {
    'container', --保证容器界面正常关闭
   --[[......]]--
}
local function CanHook(name)
    for _,v in pairs(skip_components)do
        if v == name then
            return false
        end
    end
    return true
end

local function ComponentPostInit(self)
    local old_add = self.AddComponent
    function self:AddComponent(name, ...)
        old_add(self, name, ...)
        if CanHook(name) and self.components[name].OnUpdate then 
            local cmp = self.components[name]
            local old_up = cmp.OnUpdate
            cmp.OnUpdate = function(self, ...)
                if self.inst:HasTag('homuraTag_pause') then 
                    return
                else
                    old_up(self, ...)
                end
            end
        end
    end
end

AddGlobalClassPostConstruct('entityscript','EntityScript', ComponentPostInit)
3.范围搜索

为了游戏的流畅性,时间停止技能显然不应该将饥荒世界里的所有物体都控制住,它只影响以玩家为中心,40半径范围内的物体——这个范围已经大大超出了玩家的视野范围。

在何时搜索附近实体呢?一般的思路是,在技能发动时搜索一次,把附近所有物体暂停,然后在技能结束时解除暂停状态。但略作思考就能发现问题:假如在技能持续期间,出现了一个新的物体(比如落下来一只鸟),则这个物体是无法受到控制的。因此,我们应该每隔一段时间就进行一次搜索。

--以每秒30次的频率搜索附近实体
local function OnUpdate(inst, dt) 

    --[[......]]--

    --再次遍历周围实体,加入新的,移除出范围的
    local ignoretags = {"INLIMBO", "homuraTag_ignoretimemagic"}
    local searchlocal = not TheNet:IsDedicated()
    for player in pairs(inst.centers)do
        local x,y,z = player:GetPosition():Get()
        local ents = TheSim:FindEntities(x,y,z, MAGIC_RADIUS, nil, ignoretags)
        for _, item in pairs(ents)do  
            if not IsMagicImmue(item) and (item.Network or searchlocal) then 
                if not inst.items[item] then
                    inst:AddItem(item)
                    inst.items[item] = 2
                else
                    inst.items[item] = 2
                end
            end

            --[[......]]--

        end
    end

    --如果对应值仍然为1,则移除
    --如果对应值为2,不移除,并重置为1,然后对这些实体进行更新
    for k,v in pairs(inst.items)do
        if v == 1 then 
            inst:RemoveItem(k)
        end
        if v == 2 then 
            inst.items[k] = 1
            ItemTimeMagicUpdate(k, dt)
        end
    end

    --[[......]]--

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

推荐阅读更多精彩内容