事件指南
关于事件你需要了解的一切。
类事件
在 nanos world 中,玩家或实体执行的所有操作都可以通过事件来获取。
最基本的事件例子是 Spawn(生成)。每当实体生成时,都会触发 Spawn 事件。
订阅类事件
使用 :Subscribe() 方法注册事件非常简单直接。例如,我们想在有玩家加入服务器时获得通知:
Player.Subscribe("Spawn", function(player)
Console.Log(player:GetName() .. " has joined!")
end)
大多数事件都会在客户端和服务器端同时触发,只有少数例外情况,这些例外会在每个事件中的可用性图标中标出。
阻止操作
某些事件会在某事发生之前触发,并允许你通过返回 false 来取消该操作。它们通常以 Attempt 开头,例如 AttemptEnterVehicle:
-- 仅允许队伍 1 的 Character 驾驶载具
Character.Subscribe("AttemptEnterVehicle", function(character, vehicle, seat_index)
if (seat_index == 0 and character:GetTeam() ~= 1) then
return false
end
end)
退订类事件
目前有两种退订事件的方法:
注意: 退订事件将始终只会注销在你当前 Package 中注册的事件。
退订所有事件
-- 这将注销在此 Package 中注册的所有 Player 的 "Spawn" 事件
Player.Unsubscribe("Spawn")
退订特定的事件回调
-- 提前声明回调函数
function OnSpawnPlayer(player)
Console.Log(player:GetName() .. " has joined!")
end
-- 订阅事件并传入回调
Player.Subscribe("Spawn", OnSpawnPlayer)
-- 再次订阅同一个事件
Player.Subscribe("Spawn", function(player)
Console.Log(player:GetName() .. " has joined again!")
end)
-- 仅退订第一个回调
Player.Unsubscribe("Spawn", OnSpawnPlayer)
-- 这将继续触发 "player has joined again",但不再触发
-- "player has joined"
Subscribe 会返回你传递的回调,因此你稍后也可以退订匿名函数:
local my_callback = Player.Subscribe("Spawn", function(player) end)
Player.Unsubscribe("Spawn", my_callback)
实体事件
在 nanos world 中,你也可以为特定实体注册事件。这样一来,只有当该特定实体成为事件触发原因时,才会触发回调。
订阅实体事件
-- 生成一个 Character
local my_character = Character(Vector(0, 0, 100), Rotator(), "nanos-world::SK_Male")
my_character:Subscribe("EnterVehicle", function(self, vehicle)
-- my_character 进入了载具
end)
请注意,一旦实体被销毁,为该实体注册的所有事件都会自动注销!
退订实体事件
目前有两种退订事件的方法:
注意: 退订事件将始终只会注销在你当前 Package 中注册的事件。
退订所有实体事件
-- 这将注销在此 Package 中注册的该特定 Character 的所有 "EnterVehicle" 事件
my_character:Unsubscribe("EnterVehicle")
退订特定实体事件回调
-- 提前声明回调函数
function OnCharacterEnteredVehicle(character, vehicle)
-- 进入了载具!
end
-- 订阅事件并传入回调
my_character:Subscribe("EnterVehicle", OnCharacterEnteredVehicle)
-- 再次订阅同一个事件
my_character:Subscribe("EnterVehicle", function(character, vehicle)
-- 再次进入了载具!
end)
-- 仅退订第一个回调
my_character:Unsubscribe("EnterVehicle", OnCharacterEnteredVehicle)
自定义事件
在 nanos world 中,可以定义和调用自定义事件。技术文档请参考 Events 静态类。
自定义事件是由用户创建的事件,你可以向所有其他 Package 订阅或调用这些事件。它们有两种类型:
本地事件
本地事件保持在同一端(服务器或客户端),并传达到该端上的每一个包。使用 Events.Call() 来触发它们,使用 Events.Subscribe() 来监听:
-- 在任何包中
Events.Subscribe("RoundStarted", function(round_number)
Console.Log("Round %d started!", round_number)
end)
-- 在任何其他包中(或同一个包中)
Events.Call("RoundStarted", 1)
远程事件
远程事件通过网络传输,从客户端发送到服务器,或从服务器发送到客户端。使用 Events.SubscribeRemote() 监听它们:
-- 在服务器,第一个参数始终是发送它的 Player
Events.SubscribeRemote("BuyItem", function(player, item_name)
Console.Log("%s wants to buy %s", player:GetName(), item_name)
-- 仅回复该 Player
Events.CallRemote("ItemBought", player, Reliability.Reliable, item_name)
end)
Events.SubscribeRemote("ItemBought", function(item_name)
Console.Log("I bought %s!", item_name)
end)
-- 发送到服务器
Events.CallRemote("BuyItem", Reliability.Reliable, "medkit")
服务器还可以使用 CallRemotePlayers 向 Player 列表发送事件,使用 BroadcastRemote 向所有人发送事件,或使用 BroadcastRemoteInRadius 和 BroadcastRemoteDimension 仅向指定半径或维度内的 Player 发送事件。
需要牢记的几个重要事项:
- 可靠性:
Reliability.Reliable保证事件按顺序送达。Reliability.Unreliable开销更小但可能会丢失,适用于发送非常频繁(例如每个 Tick)且只有最新事件重要的场景。 - Entity 可以作为参数传递,它们在另一端会作为相同的实体到达(只要它在另一端存在)。
- 本地和远程是分开的:
Events.Subscribe不会接收远程事件,Events.SubscribeRemote也不会接收本地事件。 - 永远不要信任客户端:玩家可以发送带有任何参数的任何远程事件,请务必在服务器对其进行校验。参阅永远不要信任客户端指南。
Entity 也拥有自己的远程事件,这些事件会在特定实体上触发。参见 :CallRemoteEvent() 和 :BroadcastRemoteEvent()。