为了能更好的完成实体的交互与新玩法的开发,我们经常需要用到下面的技巧:
发送事件:inst:PushEvent("event_name",data)
监听事件:inst:ListenForEvent("event_name",fn(inst,data)
延时执行:inst:DoTaskInTime(time,fn(),data)
循环执行:inst:DoPeriodicTask(time,fn(),data)
一、事件的发送与监听
1.1 推送事件:PushEvent
事件的推送与监听分为实体内推送与世界内推送。
1.1.1 实体内推送事件:inst:PushEvent
在实体内推送的时间只有本实体能接收到,同一个实体可以同时推送多个不同名字的事件,此时如果有其他实体推送的事件与本实体推送的事件的名字相同,也不会有任何影响。
也就是说,各个实体间的事件相互独立,同一实体内的不同事件相互独立。
inst:PushEvent("event_name",data1,data2,...)
-- 第一个位置的参数为事件名称
-- 之后所有位置均为向“ListenForEvent("event_name",fn(inst,data))”中表“data”推送的参数,存放在data.data1、data.data2...
1.1.2 世界中推送事件:
在世界内推送的事件所有实体都可以接收到,且同一名字的事件会发生覆盖。具体用法与实体内推送事件相同:
TheWorld:PushEvent("event_name",data1,data2,...)
1.2 监听事件:ListenForEvent
监听事件需要与推送事件配套使用,相当于远程传递参数的便捷方法。
inst:ListenForEvent("event_name",fn(inst,data)
-- 第一个位置为监听的事件名字
-- 第二个位置为监听到事件后需要执行的函数,向内传递的两个参数是inst(监听者)与data(PushEvent传来的表)
1.3 事件的进阶运用:timer组件
timer组件是用来计时的组件,通过下面语句即可添加到实体:
inst:Addcomponent("timer")
timer通过发送事件和监听事件来完成“时间到”的回调:
1.3.1 开始计时
开始计时的代码为:
inst.components.timer:StartTimer("timer_name",time,paused,initialtime_override)
-- 第一个参数为计时器名称,后面在监听计时器结束时会用到
-- 第二个参数为计时时间
-- 第三个参数为延迟计时时间
-- 第四个参数为覆盖计时时间time
后两个参数为可选参数。
initialtime_override相当于一个判断语句,如果传入则覆盖,如果没传入则继续使用time。
StartTimer的定义如下:
function Timer:StartTimer(name, time, paused, initialtime_override)
if self:TimerExists(name) then
print("A timer with the name ", name, " already exists on ", self.inst, "!")
return
end
self.timers[name] =
{
timer = self.inst:DoTaskInTime(time, OnTimerDone, self, name),
timeleft = time,
end_time = GetTime() + time,
initial_time = initialtime_override or time,
paused = false,
}
if paused then
self:PauseTimer(name)
end
end
1.3.2 暂停计时
暂停计时的代码为:
inst.components.timer:PauseTimer("timer_name")
-- 暂停计时器“timer_name”
1.3.3 恢复计时
恢复计时的代码为:
inst.components.timer:ResumeTimer("timer_name")
-- 恢复计时器“timer_name”
1.3.4 计时时间到
在timer组件的定义中,计时时间到是通过推送事件来通知的,其代码如下:
local function OnTimerDone(inst, self, name)
self:StopTimer(name)
inst:PushEvent("timerdone", { name = name })
end
其中调用的StopTimer()如下:
function Timer:StopTimer(name)
if not self:TimerExists(name) then
return
end
if self.timers[name].timer ~= nil then
self.timers[name].timer:Cancel()
self.timers[name].timer = nil
end
self.timers[name] = nil
end
通过监听事件,我们可以获知计时时间到的消息,再通过推送事件中传递的参数判断是哪一个计时器结束,我们就可以利用计时器完成一些工作。
监听计时结束的代码如下:
inst:ListenForEvent("timerdone", function(inst, data)
-- 你的代码
end)
-- 注意区分这里function传递下来的inst与原inst的区别。当这是在实体内推送的事件(比如现在使用的timer组件)时,二者没有区别;当这是世界事件是,二者可能会不同
二、执行事务
2.1 延时执行事务
通过延时执行事务,我们可以完成:
多样化游戏内容(随机推迟任务执行时间)
有序化任务(设置不同的推迟时间)
等。
推迟执行事务的 代码如下:
inst:DoTaskInTime(time, fn, data1,data2,...),
-- 第一个位置为延迟时间
-- 第二个位置为推迟时间后执行的函数
-- 其余位置为传递的参数
以前面提到的timer组件中计时时间到函数为例:
local function OnTimerDone(inst, self, name) -- 注意这里接收的参数
self:StopTimer(name)
inst:PushEvent("timerdone", { name = name })
end
function Timer:StartTimer(name, time, paused, initialtime_override)
if self:TimerExists(name) then
print("A timer with the name ", name, " already exists on ", self.inst, "!")
return
end
self.timers[name] =
{
timer = self.inst:DoTaskInTime(time, OnTimerDone, self, name), -- 延时执行
timeleft = time,
end_time = GetTime() + time,
initial_time = initialtime_override or time,
paused = false,
}
if paused then
self:PauseTimer(name)
end
end
这里inst:DoTaskInTime(time, OnTimerDone, self, name)向函数OnTimerDone中传递了两个参数,但OnTimerDone(inst, self, name)中接收了三个参数,这是因为DoTaskInTime默认吧inst传进函数内。
2.2 重复执行事务
物品的实体预制件函数fn()只会在每个实体初始化时执行之后若无调用则不会执行我们写在其中的逻辑的。这时我们可以利用重复执行事务语句,通过不断重复执行我们安置在fn()中的代码,加上一些条件语句,即可在我们需要他们的时候发挥作用。这就是我们所说的“刷帧”。
重复执行事务的代码如下:
inst:DoPeriodicTask(time, function()
-- 你的代码,一般不会长时间重复执行某一动作,所以常常与条件语句结合,判断时机恰当时再执行
end)
这里直接将执行的事务函数写在了DoPeriodicTask语句内,为了使fn()代码简洁,我们可以将其转移至fn()外:
local function on_doing_task(inst,...)
{
-- 代码
}
inst:DoPeriodicTask(time,on_doing_task(inst,...))
其中的time为每隔time秒执行一次任务,故time=1时,每1s执行一次任务;也可以用宏定义帧:FRAMES