你如何发射沿弯曲角度跟随的射弹
Posted
技术标签:
【中文标题】你如何发射沿弯曲角度跟随的射弹【英文标题】:How do you fire a projectile that follows along a curved angle 【发布时间】:2021-10-25 18:27:27 【问题描述】:我希望我的一个射弹在击中目标之前遵循一个拱形路径。不管我怎么想,我就是无法绕开它。任何帮助将不胜感激
【问题讨论】:
【参考方案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)
这使您可以发射沿有趣路径弯曲的射弹。
【讨论】:
以上是关于你如何发射沿弯曲角度跟随的射弹的主要内容,如果未能解决你的问题,请参考以下文章