今天,孤陋寡闻数学不好的我,才知道n阶bezier 曲线是有一个通用公式的。
我先把这个公式截图放在这里,留作备忘
感觉就这么一个公式,说的就比较明确了,用代码实现起来也比较简单。
稍微解释一下:
上面的公式,说的是 2d 场景下, 每个点的插值坐标,是怎么算的。
首先得有一个所有点集合的数组。数组里包括 起点、控制点x N,终点。
比如有 3个控制点, 加上 起点终点,这个 数组长度就是5, 这个bezier 曲线就叫做 4阶贝塞尔曲线。
上面公司描述的是,在 整条曲线区间里 [0,1] ,的 其中某一个时刻, 曲线上的点的位置,是怎么计算得来的。
网上找的那些实现的博客,写的烂七八糟,就是不把公式帖明白。代码写的可读性差的可以。找来找去还是就这么2张图片说的最明白。
放在这里备忘
补充
每次自己想写 的时候 ,看到自己的博客 也晕头转向,都得 重新梳理一遍 .中间用 C++ , C# 甚至是 bolo 各种语言都实现过一次. 这次用 Lua 重新实现的,把代码贴在这里 ,方便自己将来做参考
local Bezier2D = {}
Bezier2D.__index = Bezier2D
local function factorial(n)
local result = 1
for i = 1,n do
result = result * i
end
return result
end
local function pow(num,powerNum)
local r = 1
for i = 1,powerNum do
r = r * num
end
return r
end
--[[
ctrlPoints:[{x = ?,y = ?},...]
[1] is start point
[n] is end point
]]
function Bezier2D.New(ctrlPoints)
local o = setmetatable({},Bezier2D)
assert(#ctrlPoints >= 2)
o:Init(ctrlPoints)
return o
end
function Bezier2D:Init(ctrlPoints)
self.ctrlPoints = ctrlPoints
self.from = self.ctrlPoints[1]
self.to = self.ctrlPoints[#self.ctrlPoints]
self.n = #self.ctrlPoints - 1
end
--[[
t: [0,1]
]]
function Bezier2D:GetPoint(t)
local x = self:GetX(t)
local y = self:GetY(t)
return {x = x,y = y}
end
function Bezier2D:GetX(t)
local r = 0
for i = 0,self.n do
local kbd = self:GetKBD(i,self.n)
local pow1minusT = pow(1 - t,self.n - i)
local powT = pow(t,i)
local tempPoint = self.ctrlPoints[i + 1]
local value = kbd * pow1minusT * powT * tempPoint.x
r = r + value
end
return r
end
function Bezier2D:GetY(t)
local r = 0
for i = 0,self.n do
local kbd = self:GetKBD(i,self.n)
local pow1minusT = pow(1 - t,self.n - i)
local powT = pow(t,i)
local tempPoint = self.ctrlPoints[i + 1]
local value = kbd * pow1minusT * powT * tempPoint.y
r = r + value
end
return r
end
function Bezier2D:GetKBD(i,n)
return factorial(n) / (factorial(i) * factorial(n - i))
end
_G.Bezier2D = Bezier2D
_G.factorial = factorial
_G.pow = pow
测试用例:
local points = {
{x = 0,y = 0},
{x = 0.5,y = 1},
{x = 1,y = 0},
}
local bezier = Bezier2D.New(points)
local duration = 1
local function GetObj()
local charId = 2
local char = _G.CharacterManagerIns.characters[charId]
return char.HeroModel.Prefab
end
VeAsync(function()
px.warning("done.")
end,function()
local elapsed = 0
while elapsed < duration do
local point = bezier:GetPoint(elapsed / duration)
local go = GetObj()
GameObjectSetPosition(go,point.x , point.y, 0)
VeSleep(0.02)
elapsed = elapsed + 0.02
end
end)