You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Roblox塔防游戏击退脚本问题:敌人击退后跳过路径点

问题分析
  • 核心矛盾:
    1. 修改PrimaryPart的CFrame会导致Humanoid:MoveTo()的WalkToPoint改变,触发moveTo函数里的终止条件,中断当前移动
    2. 原脚本用for循环静态遍历路径点,即使通过BindableEvent回退waypoints_hit,循环仍会按原顺序执行,导致敌人直接前往下一个路径点,出现"抄近路"现象
解决方案

1. 重构路径点移动逻辑:动态循环替代静态遍历

把固定顺序的for循环改成基于waypoints_hit的while循环,让敌人的移动目标始终跟随当前路径点进度,而非预先设定的遍历顺序。同时对路径点按名称排序,确保移动顺序正确。

2. 修复moveTo函数的终止条件

移除if humanoid.WalkToPoint ~= targetPoint then break end的判断,避免修改CFrame导致移动意外中断,改用手动标记的方式控制循环状态。

3. 调整击退逻辑与方向

修正击退方向为远离当前路径点(向后),同时确保先更新路径点进度再执行位置移动,让敌人的移动目标与新路径点同步。

修改后的代码

敌人移动脚本

-- In case the enemy dies before this script can run, pcall blocks errors.
pcall(function(...)
    -- Constants
    local humanoid = script.Parent.Humanoid
    local info = script.Parent.EnemyInfo
    local waypoints = game:GetService("Workspace").Waypoints
    local waypoints_hit = 0 -- Not constant, waypoint counter
    local knockback = script.Parent.Knockback
    local isMoving = false -- 标记是否正在执行移动任务

    -- 排序路径点,确保按编号顺序移动
    local sortedWaypoints = waypoints:GetChildren()
    table.sort(sortedWaypoints, function(a, b)
        local numA = tonumber(a.Name) or math.huge
        local numB = tonumber(b.Name) or math.huge
        return numA < numB
    end)

    -- MoveTo() without timeout,移除WalkToPoint判断避免CFrame修改导致中断
    local function moveTo(humanoid, targetPoint)
        isMoving = true
        local targetReached = false
        local connection
        connection = humanoid.MoveToFinished:Connect(function(reached)
            targetReached = true
            print("Reached target "..tostring(targetPoint))
            connection:Disconnect()
            connection = nil
        end)
        humanoid:MoveTo(targetPoint)
        while not targetReached do
            if not (humanoid and humanoid.Parent) then
                break
            end
            humanoid:MoveTo(targetPoint)
            task.wait(0.1)
        end
        isMoving = false
        if connection then
            connection:Disconnect()
            connection = nil
        end
    end
    
    knockback.Event:Connect(function(...: any)
        print("Fired")
        -- 回退路径点,确保不小于0
        waypoints_hit = math.max(0, waypoints_hit - 1)
        script.Parent.EnemyInfo.Waypoint.Value = tostring(waypoints_hit)
        
        -- 终止当前移动任务,重新设定目标
        if isMoving then
            humanoid:MoveTo(humanoid.RootPart.Position)
            task.wait()
        end
        
        -- 获取新的移动目标
        local targetPoint
        if waypoints_hit == 0 then
            targetPoint = game:GetService("Workspace").SpawnPoint.Position
        else
            targetPoint = sortedWaypoints[waypoints_hit].Position
        end
        moveTo(humanoid, targetPoint)
    end)

    -- Enemy configurations
    local damage: number = info.Damage.Value -- Damage to base
    local hp: number = info.HP.Value -- Hit points
    local speed: number = info.Speed.Value -- Speed
    
    -- Set HP and speed
    humanoid.MaxHealth = hp
    humanoid.Health = hp
    humanoid.WalkSpeed = speed
    
    -- 动态路径循环:根据waypoints_hit决定下一个目标
    while waypoints_hit < #sortedWaypoints do
        local currentWaypointIndex = waypoints_hit + 1
        local currentWaypoint = sortedWaypoints[currentWaypointIndex]
        if currentWaypoint then
            moveTo(humanoid, currentWaypoint.Position)
            waypoints_hit += 1
            script.Parent.EnemyInfo.Waypoint.Value = tostring(waypoints_hit)
        end
        
        -- 检查是否到达终点
        if waypoints_hit >= 8 then
            game:GetService("Workspace")["Match Config"].BaseHP.Value -= damage
            script.Parent:Destroy()
            break
        end
        task.wait()
    end
end)

击退脚本(修正方向与执行顺序)

task.wait(10)
local target = game:GetService("Workspace")["Live Enemies"]["Handcuff Arrest Officer"]

local waypoint: Part = nil
local currentWaypointValue = tonumber(target.EnemyInfo.Waypoint.Value) or 0
if currentWaypointValue == 0 then
    waypoint = game:GetService("Workspace").SpawnPoint
else
    waypoint = game:GetService("Workspace").Waypoints[currentWaypointValue]
end
local studsToMove = 5

-- Make sure the model has a PrimaryPart set
if target.PrimaryPart then
    -- 计算击退方向:远离当前路径点(向后)
    local direction = (target.PrimaryPart.Position - waypoint.Position).unit

    -- 计算新位置
    local newPosition = target.PrimaryPart.Position + direction * studsToMove

    -- 先触发事件更新路径点,再执行位置移动
    target.Knockback:Fire()
    target:SetPrimaryPartCFrame(target.PrimaryPart.CFrame + (direction * studsToMove))
end
关键修改说明
  • 动态路径循环:用while循环替代for,敌人每次完成移动后才更新路径点索引,中途路径点回退会直接影响下一次的移动目标
  • 路径点排序:确保敌人按预设的路径顺序移动,避免因GetChildren()返回顺序不稳定导致的路径错乱
  • 移除WalkToPoint判断:避免修改CFrame导致moveTo函数意外终止,确保移动任务能正常完成
  • 击退方向修正:原脚本方向为朝向路径点(向前),修正为反向(向后),符合击退逻辑
  • 状态同步:击退时先更新路径点进度再移动位置,确保敌人的移动目标与新路径点一致

内容的提问来源于stack exchange,提问作者Rumi-SE Must Follow the CC-SA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:05:59