Roblox LocalScript 多次触发代码

问题描述 投票:0回答:1

我为名为 Police Raid 的故事型 Roblox 游戏编写了 LocalScript (MessageController)。该脚本通过更改 UI 消息和触发事件来控制故事的流程。

-- This script controls the progress of the messages! (It's the GOAT)
local message = script.Parent.Message

-- Wait for character to load
local player = game:GetService("Players").LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()

-- Change message
task.wait(7)
message.Text = "Back there is the panic room. If there's any trouble, click the big red button and get inside. We're just going to hole up in here until someone comes to help us."

-- Change message
task.wait(7)
message.Text = "[static] This is the Robloxian Police Department, Barricaded Supsects Division. We have a warrant for your arrest on counts of [static]..."
message.TextColor3 = BrickColor.Red().Color
script["Heli Sound"]:Play()

-- Change message
task.wait(5)
message.Text = "Uh oh..."
message.TextColor3 = BrickColor.Black().Color

-- Change message
task.wait(2)
message.Text = "If you don't come out and surrender now, we will raid the premises!"
message.TextColor3 = BrickColor.Red().Color

-- Change message
task.wait(3)
message.Text = "Get to the panic room! NOW!!!"
-- Enable panic room
game:GetService("Workspace").Button.Part.ClickDetector.MaxActivationDistance = 32
message.TextColor3 = BrickColor.Black().Color

game:GetService("Workspace").TouchDetector.Touched:Connect(function(otherPart: BasePart)
    -- In the panic room
    task.wait(1)
    script["Explosion"]:Play()
    
    -- Change message
    task.wait(3)
    message.Text = "They're raiding the house... we should be safe in here."
    script["Loud Explosion"]:Play()
    
    -- Change message
    task.wait(2)
    message.Text = "They're in the panic room!"
    message.TextColor3 = BrickColor.Red().Color
    script["Extra Loud Explosion"]:Play()
    
    -- Change message
    task.wait(2)
    message.Text = "I don't think we're as safe as I thought. Grab that rifle from the floor and get to the basement, QUICK!"
    message.TextColor3 = BrickColor.Black().Color
    game:GetService("Workspace")["Panic Door"].Fire.Enabled = true
    game:GetService("Workspace")["Panic Panel"].CanCollide = false
    -- Change message
    game:GetService("Workspace")["Basement Detector"].Touched:Connect(function(otherPart: BasePart)
        task.wait(2)
        message.Text = "Okay... this basement was designed to survive a raid, so we should be okay."

        -- Change message
        task.wait(7)
        script["Extra Loud Explosion"]:Play()
        game:GetService("Workspace")["Basement Wall"].Transparency = 1
        message.Text = "Put your hands up!"
        message.TextColor3 = BrickColor.Red().Color

        -- Change message
        task.wait(2)
        message.Text = "Get down, part of the wall is still up!"
        message.TextColor3 = BrickColor.Black().Color
        local counter = 10
        task.wait(1)

        for i = counter, 0, -1 do
            task.wait(1)
            script.Gunfire:Play()
            -- Damage player

            -- Check if they are in a safe area
            local safezone = game:GetService("Workspace")["Wall Parts"].Safezone
            safezone.Touched:Connect(function() end) -- TouchInterest

            local function CheckIfPlayerIsInArea(Part,Character)
                local touching = Part:GetTouchingParts()
                for i=1,#touching do
                    if touching[i] == Character.HumanoidRootPart then
                        return true
                    end
                end
                return false
            end
            if not CheckIfPlayerIsInArea(safezone, game:GetService("Players").LocalPlayer.Character) then
                --game:GetService("ReplicatedStorage").DamagePlayer:FireServer(10)
                game:GetService("Players").LocalPlayer.Character.Humanoid.Health -= 1
            end
        end

        -- Change message
        message.Text = "The Robloxian Freedom Fighters are here! Get into their helicopter and escape to headquarters!"
        game:GetService("Workspace")["Basement Wall"].CanCollide = false
        local fighters = game:GetService("Workspace")["Freedom Fighters"]
        fighters["RFF-Beta-31"]:MoveTo(Vector3.new(-100.242, 3.003, 39.393))
        fighters["RFF-Alpha-0"]:MoveTo(Vector3.new(-103.259, 3.003, 31.674))
        fighters["RFF-Epsilon-97"]:PivotTo(CFrame.new(-90.501, 9.305, 40.968))
        fighters["RFF-Sigma-69"]:MoveTo(Vector3.new(-98.693, 3.003, 24.884))
        fighters["RFF-Gamma-3"]:MoveTo(Vector3.new(-122.102, 3.003, 25.833))
        fighters["RFF-Foxtrot-11"]:MoveTo(Vector3.new(-96.868, 3.003, 48.219))
        fighters["RFF-Delta-4"]:MoveTo(Vector3.new(-113.647, 3.003, 32.763))
        game:GetService("Workspace")["RFF Heli"]:PivotTo(CFrame.new(-105.629, 3.5, 49.35))
        local ChatService = game:GetService("Chat")
        local talkpart = game:GetService("Workspace")["BSD Commander Parker"].Handle
        do
            ChatService:Chat(talkpart, "It's an ambush!")
        end
        print("Triggered")
        task.wait(1)
        script["Machine Gun Fire"]:Play()
    end)
end)

问题是,“Triggered”似乎被打印到控制台 528 次,并且“Gunfire”声音非常快速地播放。 NPC 帕克说:“这是一场伏击!”一遍又一遍地在他头顶的聊天气泡中。自由战士和 RFF 直升机移动得太多以至于最终消失了,而且由于代码执行太快,游戏也出现了滞后。我不知道为什么代码会执行这么多。有问题的部分似乎是:

local counter = 10
        task.wait(1)

        for i = counter, 0, -1 do
            task.wait(1)
            script.Gunfire:Play()
            -- Damage player

            -- Check if they are in a safe area
            local safezone = game:GetService("Workspace")["Wall Parts"].Safezone
            safezone.Touched:Connect(function() end) -- TouchInterest

            local function CheckIfPlayerIsInArea(Part,Character)
                local touching = Part:GetTouchingParts()
                for i=1,#touching do
                    if touching[i] == Character.HumanoidRootPart then
                        return true
                    end
                end
                return false
            end
            if not CheckIfPlayerIsInArea(safezone, game:GetService("Players").LocalPlayer.Character) then
                --game:GetService("ReplicatedStorage").DamagePlayer:FireServer(10)
                game:GetService("Players").LocalPlayer.Character.Humanoid.Health -= 1
            end
        end

        -- Change message
        message.Text = "The Robloxian Freedom Fighters are here! Get into their helicopter and escape to headquarters!"
        game:GetService("Workspace")["Basement Wall"].CanCollide = false
        local fighters = game:GetService("Workspace")["Freedom Fighters"]
        fighters["RFF-Beta-31"]:MoveTo(Vector3.new(-100.242, 3.003, 39.393))
        fighters["RFF-Alpha-0"]:MoveTo(Vector3.new(-103.259, 3.003, 31.674))
        fighters["RFF-Epsilon-97"]:PivotTo(CFrame.new(-90.501, 9.305, 40.968))
        fighters["RFF-Sigma-69"]:MoveTo(Vector3.new(-98.693, 3.003, 24.884))
        fighters["RFF-Gamma-3"]:MoveTo(Vector3.new(-122.102, 3.003, 25.833))
        fighters["RFF-Foxtrot-11"]:MoveTo(Vector3.new(-96.868, 3.003, 48.219))
        fighters["RFF-Delta-4"]:MoveTo(Vector3.new(-113.647, 3.003, 32.763))
        game:GetService("Workspace")["RFF Heli"]:PivotTo(CFrame.new(-105.629, 3.5, 49.35))
        local ChatService = game:GetService("Chat")
        local talkpart = game:GetService("Workspace")["BSD Commander Parker"].Handle
        do
            ChatService:Chat(talkpart, "It's an ambush!")
        end
        print("Triggered")
        task.wait(1)
        script["Machine Gun Fire"]:Play()

如果代码太多或者格式错误,抱歉。

我尝试通过打印文本“已触发”来调试代码,并查看脚本以查找可能导致无限循环的任何内容。我还尝试在脚本末尾添加

task.wait(math.huge)
以防止其多次执行(在
end
语句上方),但这并不影响循环。有趣的是,
Machine Gun Fire
(循环声音)似乎不会播放多次。如果有帮助的话,LocalScript 的父级是 ScreenGui,而 ScreenGui 的父级是 StarterGui。

由于缺乏声誉,我无法在 Roblox 开发论坛上发布此问题,因为我已经很长时间没有登录而浏览该网站了。

events lua roblox luau roblox-studio
1个回答
0
投票

您面临的问题是您将侦听器连接到事件并且从不断开它们的连接,或者消除多次触发的事件。一个常见的问题,尤其是

Touched
事件,是当工作区中的任何东西接触它时它会触发,并且还会针对同一角色模型多次触发。如果您的手和脚都接触到某个部件,则这两个部件都会触发该事件。

除此之外,许多游戏逻辑应该在脚本中发生,这样,当播放声音或移动、隐藏或销毁对象时,它们应该同时发生在每个人身上。

LocalScript 中唯一需要发生的事情是显示消息的位置。那么让我们设置一个简单的方法让服务器将消息推送到 UI。

因此请按照以下步骤操作:

  1. 在 ReplicatedStorage 中创建一个名为
    DisplayMessage
  2. 的 RemoteEvent
  3. 在您的 LocalScript 中,删除除此之外的所有内容(不用担心,您的代码正在移至脚本):
-- import services
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- find the UI elements
local message = script.Parent.Message

-- find the RemoteEvent telling us that the server has a message for us
local DisplayMessage = ReplicatedStorage.DisplayMessage

-- display the messages when the server sends them to us
DisplayMessage.OnClientEvent:Connect(function(message : string, color : Color3)
    message.Text = message
    message.Color3 = color
end)
  1. 在 ReplicatedStorage 中创建一个 ModuleScript 来处理玩家触摸某个部件时的去抖逻辑。将其命名为
    debounceTouch
    。我们将使用它来防止用户在角色模型触摸某个部件时多次触发触摸事件。
local Players = game:GetService("Players")

-- Debounce a function so that it only fires once per player touch event
local function debounceTouch(part : BasePart, onPlayerTouched : (Player)->(), onPlayerTouchEnded : (Player)->()?)
    -- keep track of which players are touching the part
    local playersTouching : { [Player] : number } = {}
    
    local touchedConnection = part.Touched:Connect(function(otherPart : BasePart)
        local player = Players:GetPlayerFromCharacter(otherPart.Parent)
        if not player then
            return
        end

        if not playersTouching[player] then
            -- a player has begun touching!
            onPlayerTouched(player)
            playersTouching[player] = 1
        else
            playersTouching[player] += 1
        end
    end)
    local touchEndedConnection = part.TouchEnded:Connect(function(otherPart : BasePart)
        local player = Players:GetPlayerFromCharacter(otherPart.Parent)
        if not player then
            return
        end
        -- when a player stops touching, decrement the counter so that we can clear the debounce flag
        if playersTouching[player] then
           playersTouching[player] -= 1
           if (playersTouching[player] <= 0) then
               playersTouching[player] = nil

               -- the player has stopped touching, fire the callback if provided
               if onPlayerTouchEnded then
                   onPlayerTouchEnded(player)
               end
           end
        end
    end)

    -- return an object that functions like an RbxScriptConnection token so that we can disconnect it when it is no longer relevant
    return {
        Connected = (touchedConnection.Connected or touchEndedConnection.Connected),
        Disconnect = function()
            touchedConnection:Disconnect()
            touchEndedConnection:Disconnect()
        end,
    }
end

return debounceTouch
  1. 在ServerScriptService中创建一个Script,并将其放入内容中。这将处理您的游戏逻辑,并向玩家发送消息。
-- import services
local ChatService = game:GetService("Chat")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")

-- import helper functions
local debounceTouch = require(ReplicatedStorage.debounceTouch)


-- create a helper function to send the messages to all players
local DisplayMessage = ReplicatedStorage.DisplayMessage
local function displayMessageWithDelay(delay : number, message : string, color : Color3?)
    task.wait(delay)
    DisplayMessage:FireAllClients(message, color)
end

-- Wait for any player to load into the game
if #Players.GetPlayers() == 0 then
    Players.PlayerAdded:Wait()
end

-- define some values
local SAFEZONE_DAMAGE_PER_SECOND = 10


-- write down all the messages at the top
local messages = {
    "Back there is the panic room. If there's any trouble, click the big red button and get inside. We're just going to hole up in here until someone comes to help us.",
    "[static] This is the Robloxian Police Department, Barricaded Suspects Division. We have a warrant for your arrest on counts of [static]...",
    "Uh oh...",
    "If you don't come out and surrender now, we will raid the premises!",
    "Get to the panic room! NOW!!!",
    "They're raiding the house... we should be safe in here.",
    "They're in the panic room!",
    "I don't think we're as safe as I thought. Grab that rifle from the floor and get to the basement, QUICK!",
    "Okay... this basement was designed to survive a raid, so we should be okay.",
    "Put your hands up!",
    "Get down, part of the wall is still up!",
    "The Robloxian Freedom Fighters are here! Get into their helicopter and escape to headquarters!",
}

local colors = {
    black = BrickColor.Black().Color,
    red = BrickColor.Red().Color,
}

-- load some workspace elements
local ButtonClickDetector = Workspace.Button.Part.ClickDetector
local TouchDetector = Workspace.TouchDetector
local PanicDoor = Workspace["Panic Door"]
local BasementDetector = Workspace["Basement Detector"]
local BasementWall = Workspace["Basement Wall"]
local safezone = Workspace["Wall Parts"].Safezone
local fighters = Workspace["Freedom Fighters"]
local helicopter = Workspace["RFF Heli"]
local talkPart = Workspace["BSD Commander Parker"].Handle




-- start playing the game
displayMessageWithDelay(7, messages[1])
displayMessageWithDelay(7, messages[2], colors.red)
script["Heli Sound"]:Play()
displayMessageWithDelay(5, messages[3], colors.black)
displayMessageWithDelay(2, messages[4], colors.red)
displayMessageWithDelay(3, messages[5])

-- Enable panic room
ClickDetector.MaxActivationDistance = 32

local touchedConnection
touchedConnection = TouchDetector.Touched:Connect(function(otherPart: BasePart)
    -- make sure that this only fires if a player touches it
    if not Players:GetPlayerFromCharacter(otherPart.Parent) then
        return
    end

    -- disconnect the connection so that it doesn't fire again
    touchedConnection:Disconnect()

    -- In the panic room
    task.wait(1)
    script["Explosion"]:Play()
    displayMessageWithDelay(3, messages[6], colors.black)
    script["Loud Explosion"]:Play()
    displayMessageWithDelay(2, messages[7], colors.red)
    script["Extra Loud Explosion"]:Play()
    displayMessageWithDelay(2, messages[8], colors.black)

    -- set the door on fire
    PanicDoor.Fire.Enabled = true
    PanicDoor.CanCollide = false

    
    local basementTouchedConnection
    basementTouchedConnection = BasementDetector.Touched:Connect(function(otherPart: BasePart)
        -- make sure that this only fires if a player touches it
        if not Players:GetPlayerFromCharacter(otherPart.Parent) then
            return
        end

        -- disconnect the connection so it only fires once
        basementTouchedConnection:Disconnect()
 
        -- start playing the next set of messages
        displayMessageWithDelay(2, messages[9])
        displayMessageWithDelay(7, messages[10], colors.red)

        -- explode the basement walls
        script["Extra Loud Explosion"]:Play()
        BasementWall.Transparency = 1
        displayMessageWithDelay(2, messages[11], colors.black)

        -- create a list of players currently touching the safezone
        local playersInSafezone = {}
        local playerList = Players:GetPlayers()
        for _, player in ipairs(playerList) do
            playersInSafezone[player] = false
        end
        local safezoneTouchConnection = debounceTouch(safezone, function(player)
            -- when they touch the safezone, mark them as safe
            playersInSafezone[player] = true
        end, function(player)
            -- when they stop touching the safezone, mark them as unsafe
            playersInSafezone[player] = false
        end)

        task.wait(1)
        for i = 10, 0, -1 do
            task.wait(1)
            script.Gunfire:Play()

            -- Check if they are in a safe area
            for player, isSafe in pairs(playersInSafezone) do
                if not isSafe then
                    -- Damage player
                    player.Character.Humanoid:TakeDamage(SAFEZONE_DAMAGE_PER_SECOND)
                    --ReplicatedStorage.DamagePlayer:FireServer(10)
                end
            end
        end

        -- clean up the safezone events
        safezoneTouchConnection:Disconnect()
        playersInSafezone = nil

        -- Change message
        displayMessageWithDelay(0, messages[12])
        BasementWall.CanCollide = false

        -- move the fighters into place
        fighters["RFF-Beta-31"]:MoveTo(Vector3.new(-100.242, 3.003, 39.393))
        fighters["RFF-Alpha-0"]:MoveTo(Vector3.new(-103.259, 3.003, 31.674))
        fighters["RFF-Epsilon-97"]:PivotTo(CFrame.new(-90.501, 9.305, 40.968))
        fighters["RFF-Sigma-69"]:MoveTo(Vector3.new(-98.693, 3.003, 24.884))
        fighters["RFF-Gamma-3"]:MoveTo(Vector3.new(-122.102, 3.003, 25.833))
        fighters["RFF-Foxtrot-11"]:MoveTo(Vector3.new(-96.868, 3.003, 48.219))
        fighters["RFF-Delta-4"]:MoveTo(Vector3.new(-113.647, 3.003, 32.763))
        helicopter:PivotTo(CFrame.new(-105.629, 3.5, 49.35))
        
        ChatService:Chat(talkPart, "It's an ambush!")
        print("Triggered")
        task.wait(1)
        script["Machine Gun Fire"]:Play()
    end)
end)
  1. 最后,将 LocalScript 子级的所有声音移至脚本中。这将确保保留声音的路径。

你会注意到我改变了你伤害玩家的方式,以及安全区的工作方式。这样我们就可以快速检查每次谁在接触安全区,而不需要让引擎向玩家查找零件。

我真正做的唯一一件事是将所有游戏消息移动到顶部的列表中,这并不是真正必要的,但我认为这可以更轻松地查看游戏逻辑,而不是阅读游戏中的消息脚本的主体。这是我的偏好,对于代码的运行来说确实不是必需的。

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.