Greetings devs!
I have recently figured out how Inverse Kinematics work and was surprised by how simple it actually is. I have tryed figuring them out before but it was always so difficult and unintuitive that I would quickly give up. I have seen modules like this before but they always confused me and where also unintuitive.
So I wrote EasyIK, a very simple inverse kinematics module that can let you write a system in minutes!
Here is an example of how to use it:
Code
-- Require the module
local EasyIK = require(game.ReplicatedStorage.Modules.EasyIK)
-- Create IK arm object
local Arm = EasyIK:CreateArm(workspace.A.Position,workspace.B.Position,3,25,workspace)
-- Make the segments look a bit better (not required)
local Segments = Arm.Segments
for Index,Value in pairs(Segments) do
Value.Material = Enum.Material.Glass
Value.Color = Color3.new(0.431373, 0.878431, 1)
end
-- Recalculate
while wait() do
Arm:Calculate(workspace.A.Position,workspace.B.Position, 2)
end
Result:
As you can see, in just 10 lines of code I have created a working inverse kinematics arm!
This is the first module I have released here, if you have any suggestions please leave them below.
I did not setup a github repo for this since I don’t think it is necessary and I am not sure how to do it, if this get’s enough feedback and support I may set that up.
Model:
Source code:
-- EasyIK | v1.0 writen by Ilikenoodles
local Module = {}
function Module:CreateArm(StartPosition: Vector3,EndPosition: Vector3,SegmentsAmount: number,ArmLength: number,Parent: Instance)
if not StartPosition or typeof(StartPosition) ~= "Vector3" then error("Invalid start position for IK") return end
if not EndPosition or typeof(EndPosition) ~= "Vector3" then error("Invalid end position for IK") return end
if not SegmentsAmount or typeof(SegmentsAmount) ~= "number" then error("You must pass a valid amount of segments for the IK arm") end
if not ArmLength or typeof(ArmLength) ~= "number" then error("You must pass a valid length for the IK arm") end
if not Parent then error("You must pass a valid parent for the IK segments") end
local Segments = {}
local function GetSegmentSurfacePosition(Segment: Part,Surface: string)
if Segment and Surface then
local HalfSize = (ArmLength / SegmentsAmount) / 2
if Surface == "Head" then
return (Segment.CFrame * CFrame.new(0,0,-HalfSize)).Position
elseif Surface == "Tail" then
return (Segment.CFrame * CFrame.new(0,0,HalfSize)).Position
end
end
end
-- Create Segments
for I = 1, SegmentsAmount do
local Segment = Instance.new("Part",Parent)
Segment.Name = "Segment_" .. I
Segment.Anchored = true
local Length = ArmLength / SegmentsAmount
Segment.Size = Vector3.new(1,1,Length)
Segment.CFrame = CFrame.new(StartPosition) * CFrame.new(0,0,-Length / 2 - Length * (I - 1))
Segments[SegmentsAmount - I + 1] = Segment
end
local IK_Arm = {}
IK_Arm.Configuration = {
["SegmentsAmount"] = SegmentsAmount,
["ArmLength"] = ArmLength,
["SegmentLength"] = ArmLength / SegmentsAmount,
["Parent"] = Parent;
}
IK_Arm.Segments = Segments
-- Calculation function
function IK_Arm:Calculate(PointA: Vector3,PointB: Vector3,Itterations: number)
if not PointA or typeof(PointA) ~= "Vector3" then PointA = StartPosition warn("Invalid start position for IK calculation") end
if not PointB or typeof(PointB) ~= "Vector3" then PointB = EndPosition warn("Invalid start position for IK calculation") end
if not Itterations then Itterations = 1 end
for Itteration = 1, Itterations do
-- Calculate
for I, Segment in ipairs(Segments) do
local TargetPosition = PointB
if I > 1 then
TargetPosition = GetSegmentSurfacePosition(Segments[I - 1],"Tail")
end
local Tail = GetSegmentSurfacePosition(Segment,"Tail")
Segment.CFrame = CFrame.lookAt(Tail,TargetPosition)
local Head = GetSegmentSurfacePosition(Segment,"Head")
Segment.CFrame *= CFrame.new(0,0,-(TargetPosition - Head).Magnitude)
end
-- Offset
local Offset = GetSegmentSurfacePosition(Segments[#Segments],"Tail") - PointA
for I, Segment in ipairs(Segments) do
Segment.Position -= Offset
end
end
end
IK_Arm:Calculate(StartPosition,EndPosition,5)
return IK_Arm
end
return Module
API
EasyIK:CreateArm(StartPos,EndPos,Segments,ArmLength,Parent)
Pretty self explanatory, the 2 positions, Amount of segments, The length of the whole arm and what to parent the segments to. Returns a IK arm object
IK_Arm:Calculate(StartPos,EndPos,Iterations)
Recalculates the IK arm object with a new set up points. Iterations is how many times it will recalculate within the function. Default: 1
IK_ArmObject.Segments -- {Part1, Part2 ...}
A table containing the segments (parts) that make up the arm.
IK_Arm.Configuration --[[ {
["SegmentsAmount"] = SegmentsAmount,
["ArmLength"] = ArmLength,
["SegmentLength"] = ArmLength / SegmentsAmount,
["Parent"] = Parent;
}]]
A table containing the information about the IK arm object
Feel free to reply with suggestions, bugs or opinions!