实现 Blaster 行为

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

实现 blaster 行为 是编程爆炸机械人在第一人称射击体验中的过程。虽然玩家可以用单击或按钮的单击来爆炸,但创建令人满意和准确的爆炸行为是重要的,因为它会增强玩家的总体游戏体验。

使用示例激光标记体验作为参考,这个教程的第二部分教你了解两种不同类型的激光器的实现方法,包括有关:

  • 检测玩家是否按下爆炸按钮。
  • 检查玩家是否可以使用他们的冲击枪,如果他们刚刚按下了爆炸按钮。
  • 生成告诉服务器谁启动了爆炸、它从哪里来的和每个激光束的最终目的地的爆炸数据。
  • 通知服务器,以便它可以在爆炸与另一名玩家碰撞时执行适当的行动。
  • 在每次爆炸之间重置激光,让激光有足够的时间来冷却,以便它可以再次爆炸。

完成此部分后,您将了解允许激光与其他玩家碰撞时检测的脚本,然后根据每个激光输入型 deduct 相应的生命值。

检测玩家输入

实现 Blaster 行为的第一个步骤是听到玩家按下爆炸按钮的时候。 输入类型由玩家使用来按下爆炸按钮依赖于他们使用的设备。 例如,复制存储中的示例激光标签体验支持鼠标和键盘控控制、游戏手柄和触摸控控制。 您可以在 ReplicatedStorage > 用户输入处理器 此客户端脚本使用 ContextActionService 来将 MouseButton1ButtonR2 绑定到爆炸操动作。这意味着每次玩家按下左键或游戏手柄的 R2 按钮时,都会触发激光从 blaster 中发射。注意,

用户输入处理器

ContextActionService:BindAction("_", onBlasterActivated, false,
Enum.UserInputType.MouseButton1,
Enum.KeyCode.ButtonR2
)

另一个重要的注意事项是在 Enum.UserInputState.Begin 定义中使用 Enu

要示例,您可以将 Enum.UserInputState.Begin 变更为 Enum.UserInputState.End ,然后玩测试以确定爆炸对体验的游戏玩法有何影响。例如,如果玩家可以按住按钮 without triggering the blast,如何这可能会改变他们的体验标记其他玩家时?

用户输入处理器

local function onBlasterActivated(_actionName: string,
inputState: Enum.UserInputState, _inputObject: InputObject)
if inputState == Enum.UserInputState.End then -- 更新了行,请确保返回
attemptBlastClient()
end
end

检查玩家是否可以爆炸

UserInputHandler 检测到一个按钮或屏服务器触摸后,它会调用 ReplicatedStorage > Blaster</

可以本地玩家爆炸

local function canLocalPlayerBlast(): boolean
return localPlayer:GetAttribute(PlayerAttribute.blasterStateClient) == BlasterState.Ready
end

如果您

此轻度暂停会阻止您尽可能快地单击。例如,如果您将函数更改为 always return true,您可以快速爆炸您的冲击枪,无视激光标签游戏的延迟,这是对于激光标签游戏的不真实。

可以本地玩家爆炸

local function canLocalPlayerBlast(): boolean
return true -- 更新了行,请确保返回
end

生成爆炸数据

在验证玩家的 blaster 在 Ready 状态后,attemptBlastClient 调用 ReplicatedStorage > 1> attemptBlastClient1> > 4> blastClient4> 。 第一步,

下一步是生成爆炸数据。如果您查看 ReplicatedStorage > Blaster > BlastData ,您可以看到每个爆炸包含三个信息块:

  • 发生爆炸的玩家。
  • 一个 DataType.CFrame 代表爆炸的起始点。
  • 一个 RayResult 表,包含每个激光波的最终目的地和命中玩家,如果命中另一个玩家。

要生成此数据,blastClient 调用ReplicatedStorage > attemptBlastClient > 2> blastClient2> > 5> generateBlastData5>,您可以在下面查看。

生成爆炸数据

local function generateBlastData(): BlastData.Type
local blasterConfig = getBlasterConfig()
local rayDirections = getDirectionsForBlast(
currentCamera.CFrame, blasterConfig)
local rayResults = castLaserRay(
localPlayer, currentCamera.CFrame.Position, rayDirections)
local blastData: BlastData.Type = {
player = localPlayer,
originCFrame = currentCamera.CFrame,
rayResults = rayResults,
}
return blastData
end

此函数使用 getBlasterConfig 来检索玩家的激光输入。样例提供两种类型的激光:一个生成多个光束,覆盖横向布局,另一个生成单个光束。您可以在 ReplicatedStorage > Instances > 1> LaserBlastersFolder1> 中找到其配置。

然后,该函数使用 currentCamera.CFrame 作为爆炸的起始点,将其交给 getDirectionsForBlast 。在此时,代码不再是关于 Blaster,它是关于

向服务器发送通知

一旦 blastClient 有完整数据为爆炸,它会触发两个事件:

爆炸客户

local laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEvent
local laserBlastedEvent = ReplicatedStorage.Instances.LaserBlastedEvent
laserBlastedBindableEvent:Fire(blastData)
laserBlastedEvent:FireServer(blastData)

Class.BindableEvent 向其他客户端脚本发布爆炸。例如, ReplicatedStorage > FirstPersonBlasterVisuals 使用此事件来知道显示视觉效果的时间,例如爆炸动画和冷却条。同样,1>Class.

激光冲击处理器

local function onLaserBlastedEvent(playerBlasted: Player, blastData: BlastData.Type)
local validatedBlastData = getValidatedBlastData(playerBlasted, blastData)
if not validatedBlastData then
return
end
if not canPlayerBlast(playerBlasted) then
return
end
blastServer(playerBlasted)
processTaggedPlayers(playerBlasted, blastData)
for _, replicateToPlayer in Players:GetPlayers() do
if playerBlasted == replicateToPlayer then
continue
end
replicateBlastEvent:FireClient(replicateToPlayer, playerBlasted, blastData)
end
end

为了帮助防止作弊,服务器必须验证每个客户端发送的所有数据。这些检查包括:

  1. 是否为BlastData表?它包含一个Class.CFrame和另一个名为rayResults的表?
  2. 玩家有没有装备了冲击波?
  3. 玩家有一个角色和一个地点在世界内吗?
  4. 发送爆炸数据后,玩家是否已经从发射激光柱的地方移动过度?

最后一步检查涉及判断调用,根据服务器的延迟和玩家移动速度,您可能会决定这些不同的值对于您自己的体验过于。要示例这个判断调用,您可以在 getValidatedBlastData 中添加打印声明,并且测试体验。

获取验证爆炸数据

local distanceFromCharacterToOrigin = blastData.originCFrame.Position - rootPartCFrame.Position
print(distanceFromCharacterToOrigin.Magnitude) -- 更新了线,请确保移除
if distanceFromCharacterToOrigin.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then
warn(`Player {player.Name} failed an origin sanity check while blasting`)
return
end

当你移动和爆炸时,请注意输出。它可能看起来像这样:


1.9019629955291748
3.1549558639526367
2.5742883682250977
4.8044586181640625
2.6434271335601807

如果您在 ReplicatedStorage > PlayerStateHandler > togglePlayerMovement 测试玩家移动速度,您将可能遇到许多因过度移动而导致的故障。

切换玩家移动

local ENABLED_WALK_SPEED = 60 -- updated line, be sure to change back

服务器然后会做到以关注中/正在关注:

  • 验证 rayResults .
  • 检查玩家是否可以爆炸。
  • 重置激光状态。
  • 降低任何受标签玩家的生命值。
  • 复制爆炸到所有其他玩家,以便他们可以看到第三人称视觉。

了解有关此服务器操作的更多信息,请参阅检测命中部分的教程。

重置冲击波

在示例激光标记体验中,激光使用热机械。 而不是在一系列激光爆炸后重新加载,它们需要在每次激光爆炸之间“冷却”。 这种冷却延迟在客户端(blastClient)和服务器(blastServer)之间发生,服务器作为真实的源头。

爆炸服务器

local blasterConfig = getBlasterConfig(player)
local secondsBetweenBlasts = blasterConfig:GetAttribute("secondsBetweenBlasts")
task.delay(secondsBetweenBlasts, function()
local currentState = player:GetAttribute(PlayerAttribute.blasterStateServer)
if currentState == BlasterState.Blasting then
player:SetAttribute(PlayerAttribute.blasterStateServer, BlasterState.Ready)
end
end)

secondsBetweenBlasts 属性是 ReplicatedStorage > Instances > 1> LaserBlastersFolder1> 中的一部分。 在 4>secondsBetweenBlasts4> 延迟通过后,玩家可以再次爆炸,整个过程都会重复。 为了帮助玩家了解当他们可

在此时,玩家可以生成和重生,瞄准并爆炸,但体验仍然必须确定每次爆炸的结果。 在教程的下一部分,您将学习如何程序 blaster 检测到另一个玩家时击中时,然后根据 blaster 设置减少适当的玩家健康。