【问题标题】:How do you fire a projectile that follows along a curved angle你如何发射沿弯曲角度跟随的射弹
【发布时间】:2021-10-25 18:27:27
【问题描述】:

我希望我的一个射弹在击中目标之前遵循一个拱形路径。不管我怎么想,我就是无法绕开它。任何帮助将不胜感激

【问题讨论】:

    标签: lua angle roblox projectile


    【解决方案1】:

    Bezier Curves 是制作有趣且平滑的弧线和路径的好方法。

    贝塞尔曲线计算本质上只是花哨的加权平均函数:传入 0 时得到起点,传入 1 时得到终点。介于两者之间的所有值都是您的弹丸将要经过的路径。

    因此,知道了弹丸的速度,我们就可以测量它在一段时间内行进了多远。如果我们将该距离与贝塞尔曲线的长度进行比较,我们可以得出一个介于 0 和 1 之间的比率。使用该比率,我们可以将其代入贝塞尔曲线计算并获得弹丸的位置。

    但是三次贝塞尔曲线需要额外的信息。您需要起点和终点,还需要选择两个额外的控制点来定义曲线。起点和终点很容易,可以是玩家的位置以及他们瞄准的任何地方。并且使用CFrame 数学,您可以将控制点设置为相对于起点和终点的直角。

    作为一个小的工作示例,我制作了一个简单的工具,只要你点击它就会产生一个射弹。

    在工具的 LocalScript 中:

    script.Parent.Activated:Connect(function()
        -- choose a spot in front of the player
        local player = game.Players.LocalPlayer
        local playerCFrame = player.Character:GetPrimaryPartCFrame()
        playerCFrame += playerCFrame.LookVector * 2.0
    
        -- choose the target based on where they clicked
        local target = player:GetMouse().Hit
        
        -- choose how much to rotate the control points around the player and target.
        -- set this to zero to always fire to the right
        local rotation = math.random(0, 180)
        
        -- tell the server to spawn a projectile
        game.ReplicatedStorage.RemoteEvent:FireServer(playerCFrame.Position, target.Position, rotation) 
    end)
    

    然后,在 ServerScriptService 的脚本中,监听 RemoteEvent 的触发:

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local RunService = game:GetService("RunService")
    
    local BULLET_VELOCITY = 50.0 -- studs / second
    local BULLET_MAX_LIFETIME = 60.0 -- seconds
    local BZ_NUM_SAMPLE_POINTS = 100
    local BZ_ARC_RADIUS = 10
    
    -- define some helper functions...
    local function spawnBullet(startingPos)
        local bullet = Instance.new("Part")
        bullet.Position = startingPos
        bullet.Shape = Enum.PartType.Ball
        bullet.Size = Vector3.new(0.5, 0.5, 0.5)
        bullet.BrickColor = BrickColor.Blue()
        bullet.CanCollide = true
        bullet.Anchored = true
        bullet.Parent = game.Workspace
        
        return bullet
    end
    
    -- Bezier Curve functions adapted from :
    -- https://developer.roblox.com/en-us/articles/Bezier-curves
    local function lerp(a, b, c)
        return a + (b - a) * c
    end
    
    function cubicBezier(t, p0, p1, p2, p3)
        -- t = % complete between [0, 1]
        -- p0 = starting point
        -- p1 = first control point
        -- p2 = second control point
        -- p3 = ending point
        local l1 = lerp(p0, p1, t)
        local l2 = lerp(p1, p2, t)
        local l3 = lerp(p2, p3, t)
        local a = lerp(l1, l2, t)
        local b = lerp(l2, l3, t)
        local cubic = lerp(a, b, t)
        return cubic
    end
    
    function cubicBzLength(p0, p1, p2, p3)
        local calcLength = function(n, func, ...)
            local sum, ranges, sums = 0, {}, {}
            for i = 0, n-1 do
                -- calculate the current point and the next point
                local p1, p2 = func(i/n, ...), func((i+1)/n, ...)
                -- get the distance between them
                local dist = (p2 - p1).magnitude
                -- store the information we gathered in a table that's indexed by the current distance
                ranges[sum] = {dist, p1, p2}
                -- store the current sum so we can easily sort through it later
                table.insert(sums, sum)
                -- update the sum
                sum = sum + dist
            end
            -- return values
            return sum, ranges, sums
        end
        
        local sum = calcLength(BZ_NUM_SAMPLE_POINTS, cubicBezier, p0, p1, p2, p3)
        return sum
    end
    
    
    ReplicatedStorage.RemoteEvent.OnServerEvent:Connect(function(player, spawnPos, targetPos, rotation)
        local bullet = spawnBullet(spawnPos)
        
        -- calculate the path
        local startingCFrame = CFrame.new(spawnPos, targetPos)
        local targetCFrame = CFrame.new(targetPos, spawnPos)
        
        -- calculate the control points as Vector3s
        -- p1 and p2 will be right angles to the starting and ending positions, but rotated based on input
        local p0 = (startingCFrame).Position
        local p1 = (startingCFrame + (startingCFrame:ToWorldSpace(CFrame.Angles(0, 0, math.rad(rotation))).RightVector * BZ_ARC_RADIUS)).Position
        local p2 = (targetCFrame + (targetCFrame:ToWorldSpace(CFrame.Angles(0, 0, math.rad(-rotation))).RightVector * -BZ_ARC_RADIUS)).Position
        local p3 = (targetCFrame).Position
        
        -- calculate the length of the curve
        local distance = cubicBzLength(p0, p1, p2, p3) -- studs
        
        -- calculate the time to travel the entire length
        local totalTime = distance / BULLET_VELOCITY -- seconds
        local startingTime = tick()
        
        -- start moving it towards the target
        local connection
        connection = RunService.Heartbeat:Connect(function(step)
            -- calculate the percentage complete based on how much time has passed
            local passedTime = tick() - startingTime
            local alpha = passedTime / totalTime
            
            -- move the bullet
            local updatedPos = cubicBezier(alpha, p0, p1, p2, p3)
            bullet.Position = updatedPos
            
            -- once we've arrived, disconnect the event and clean up the bullet
            if alpha > 1 or totalTime > BULLET_MAX_LIFETIME then
                bullet:Destroy()
                connection:Disconnect()
            end
        end)
    end)
    

    这使您可以发射沿有趣路径弯曲的射弹。

    【讨论】:

      猜你喜欢
      • 2018-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-19
      • 2022-06-28
      • 1970-01-01
      • 1970-01-01
      • 2019-01-28
      相关资源
      最近更新 更多