EZ Fabrik IK [Deprecated] - Inverse Kinematics Intended for any Motor6d Rig

Hello there, I’m @dthecoolest and this is my first open-source project which I made to contribute back to the community.

Credit


One of the main reason this project is open-source, couldn’t have done it without them:

@EgoMoose The basis: FABRIK Algorithm explanation video and also for the RotatedRegion3 Module for primary and secondary constraints.

@TeamSwordphin Experimental IK Creature: inspiration to use the FABRIK method

@LMH_Hutch and @iGottic example projects for IK Motor6d Manipulation

@Kironte OOP Libary to implement OOP simplification and methods

@Arch_Mage RigEdit lite 100% helped me understand and visualize Motor6ds

Showcase


Version 1.2 , Added lerping to motors, 100% makes it look a lot smoother even in the glitchy areas.

https://i.imgur.com/pNGisc3.mp4

Version 1.1, Improved the foot placement system, rough around corners but it’ll do:

https://i.imgur.com/pELmHt7.mp4

This is done by the below code:

Code to implement foot placement system
--Foot placement system

local footParams = RaycastParams.new()

footParams.CollisionGroup = "MovementSys"

leftLegChain.FootPlacementRaycastParams = footParams

leftLegChain.LengthToFloor = 20

--Get all the attachments in the left foot

leftLegChain.FootBottomAttachment = lowerBody.LeftLeg.LFeet.FootBottom

leftLegChain.FootBottomRightAttachment = lowerBody.LeftLeg.LFeet.FootBottomRight

Version 1.0, Primary and Secondary constraints! this will help tell the algorithm to try not to calculate the impossible by making the secondary constraints less strict.

https://i.imgur.com/bD3qcm7.mp4

Without secondary constraints:

The leg goes sicko mode

https://i.imgur.com/HaMwf28.mp4

Version 0.9, Multiple limbs support including the spine/torso, +R15 support

Watch them all vibe :grinning:

https://i.imgur.com/Smq2A0S.mp4

Old Versions Showcase and the features they introduced

Version 0.8, Iterate until the goal, and swapped parameters
(bugged with constraints)
(Fixed in version 1.0, now it update motors and hence updates the constraint parts as well)

leftLegChain:IterateOnce(goalPosition,toleranceInStuds)
leftLegChain:IterateUntilGoal(goalPosition,toleranceInStuds,maxIterationUntilBreakCount)

https://i.imgur.com/1Ct1NS6.mp4

Version 0.7, Now featuring Constraints! :

https://i.imgur.com/QWFD2C8.mp4

Unconstrained version:

https://i.imgur.com/czsX5ms.mp4

Assets


Download the latest release place file with the code in my Github
(Download the zip file of the latest release version )

v1.3 where I consolidated it into a single module script for easy installation.

The IK Solver only module.
(Not recommended as it doesn’t include all the objects and features of the system)
(Can be good to study how I Implemented the FABRIK algorithm)

Introduction


While there are many inverse kinematics tutorials and resources on Roblox the ones I found use relatively complex trigonometry methods intended for use for R15 characters. While they helped me understand how motor6d’s work I believe the trigonometry math will get more complex for my purpose of making a mech game where mech legs often have more than 2 joints and 2 limbs cuz thats what makes mech’s look cool and functional.

Further searching led to this amazing project by @TeamSwordphin using the FABRIK algorithm showing the possibilities of the algorithm with the downside that it doesn’t work for motor6d rigs and the code has been automatically generating the limbs making it hard to edit for new scripters like me.

Consequently, I created this resource to share my findings and hopefully gain the benefits of open-sourcing a project especially code review. Also, I aim to learn how GitHub works via this project.

How it works


Open if you are interested

The algorithm uses the FABRIK algorithm seen below in EgoMoose’s video which greatly helped me out.

EgoMoose's FABRIK explanation Video

FABRIK (Inverse kinematics) - YouTube

However, to translate the FABRIK algorithm into Roblox’s Motor6D rigs I found out I could just obtain the vector from joint to joint via c0 and c1 manipulation seen in the thread below via this function:

--find length of the joints
local function lenOneToTwo(partOne,partTwo) 
	--Check if its a motor6d
	if  partOne:IsA("Motor6D") and partTwo:IsA("Motor6D") then
		local vecOne = partOne.C1.p
		local vecTwo =  partTwo.C0.p
		local combinedVector = vecTwo-vecOne
		return combinedVector
	end
	return "Error: motor 6ds are not inserted in the function"
end

Transforming positional points to Motor6D joints - #2 by dthecoolest

Usage, Instructions, and Explanation


  • Disclaimer: This IK method uses an odd CFrame method that causes problems if your model is not oriented (0,0,0) and not all your parts are facing front like the R15/R6 dummy. To solve this view the newer CCDIK method which doesn’t have this issue:

The instructions for using the system have been transferred to the GitHub wiki below:

It’s incomplete so if you have a question please reply in the thread

--[[

This is the LimbChain class which manages the Motor6Ds within a rig using the FabrikSolver class to do inverse kinematics with.

API:

Constructors:
	LimbChain.new(Table Motor6DTable, Bool includeFoot, Bool spineMotor)
        > Creates the inverse kinematics handler for a set of Motor6D's inserted in the table
        >Motor6ds are conected sequentially in increasing order from the origin towards the endpoint 
        >Motor1, Motor2, Motor3
        >UpperLeg, LowerLeg, Foot for R15, makesure these motors are like a tree branch
        > Spine motor is an odd workaround don't use it, please only works for Mr floop man
        
Methods:
	LimbChain:IterateOnce(Vector3 targetPosition, Number tolerance)
        > Performs one forwards or backwards iteration of the fabrik iteration on the IteratedLimbVectorTable
        > Tolerance indicates the distance when the iterations stop if the goal is already reached
    LimbChain:IterateUntilGoal(Vector3 targetPosition, Number tolerance, Number breakcount)
        > Performs forwards or backwards iteration of the fabrik iteration on the IteratedLimbVectorTable
        > Until goal is reached (including tolerance) or maximum iterations in breakcount is reached
	LimbChain:UpdateMotors()
        > Rotates the Motor6D's until the they match the directions of the curent IteratedLimbVectorTable vectors
	LimbChain:DebugModeOn(Bool freezeLimbs, Bool primaryDebug,Bool secondaryDebug)
        > Turns on the debug mode for the FabrikSolver, primary and secondary constraints depending on the bool.
        > the freeze limbs will tell the fabriksolver to not perform iteration and instead update the constraints debug
        > Essentially freezing them in place so you can change the orientation of the constraints

Properties:
	LimbChain.LerpMotors
        > Bool that enables or disables the lerp methods for the IK
    LimbChain.LerpAlpha
        > Number that represents the alpha in CFrame:Lerp() from the motors current C0 towards the IK target goal CFrame
    LimbChain.IncludeFoot
        > Bool that enables or disables the foot placement method which rotated the foot independently
    LimbChain.FootBottomAttachment
        > Attachment required for the foot placement system
    LimbChain.FootBottomRightAttachment
        > Attachment required for the foot placement system place to the right of the initial footbottom
    LimbChain.FootPlacementRaycastParams
        > The raycast params object for the foot to detect the floor
    LimbChain.DebugMode
        > Bool to turn on debug mode which visualizes the limb vectors and the constraints
        > Currently does nothing as this object has debugging properties turned off
    LimbChain.FootBottomAttachment
        > Attachment required for the foot placement system
    LimbChain.PrimaryLimbConstraintTable
        > Table of FabrikConstraint objects which do the constraining for each limb
    LimbChain.SecondaryLimbConstraintTable
        > Table of FabrikConstraint objects which do the constraining for each limb if the target position is
        > out of the primary constraint region
    LimbChain.PrimaryConstraintRegionFromParts
        > Table of BaseParts that the primary constraint region will activate in accordance to the TargetPosition

Enjoy!
- dthecoolest

Thanks EgoMoose for the FABRIK explanation and the explanation on how to do constraints.

--]]

For further exploration, I recommend downloading the place file I use for testing in my GitHub called “MechTest.rbxlx” and play around with it by dragging the LTarget.

Attribution and Credit

If you do use any of the resources provided within a game please credit me in the description or an in-game credits screen. Otherwise, the project uses an MIT License.

Edit: Disclaimer for the mech legs model included in the project file which demonstrates the constraints mechanism.

Yeah to be honest I made it in the image of the Touro from Brigador which is a mech game that rekindled my interest in making mech games on Roblox and started my scripting journey.

Brigador Touro Speen gif

BrigadorTouro

Luckily and wisely I emailed the developers to confirm permissions and they have been really supportive of my project, and the good news is that it should be fine if it’s free for the community.

But yeah don’t try to monetize and use it in a real game due to how the American copyright law works and how the devs are working hard on a new Brigador game. The blocky industrial design of the current legs is way too similar I suggest just making something totally original for the best.

I will try making my own mech designs like I once did with Roblox games like build your mech/cybersuit and Plane Crazy by @rickje139 and @madattak but sadly I have less time than I once did before.

Skinny Halo Scarab on Plane Crazy

Please help I can’t build lol.

In the meanwhile please support the Brigador devs by buying Brigador in you are interested in the retro game genre, but be aware the game has quite some mature themes as it deals with war themes like most works in the mech genre.

The Future of the Project

As of 19 January 2021 this project is deprecated in favor of my new IK method CCDIK.

The reason being this newer module is better scripted and doesn’t have the (0,0,0) orientation requirement of the current method. Moreover, it’s easier to set the constraints using a dictionary and attachments rather than welding the parts onto the model than creating objects for it. Currently, planning to create one massive IKController with both FABRIK and CCDIK combined stay tuned I guess.

Past Project Goals: 4 September 2020

The constraint method is done and the OOP has been implemented however:

  1. The math for using constraints can get intensive increasing script activity from 3% to 10%. Moreover, setting the axis of the conical constraint can get pretty complicated so I’m searching for a better method.

  2. Using OOP code to execute the methods has been simplified but could use more functionality and methods for stuff like debugging the position of the joints.

  3. Maybe even functionality for multiple targets

  4. Better control of how many iterations to perform to reach the target goal maybe?

Past Project Goals: Aug 26, 2020

Currently, the IK Solver is “functional” but complicated to use so I’m planning to make it more accessible similar to the fast cast module via OOP methods which I’m severely lacking in skill to do having just finished the PeasFactory advanced tutorials on youtube.

However, before that I’m trying to work out the conical constraint method as described by EgoMoose in order to further flesh out the functionality for my own game.

Thanks for reading. Please message me if someone has made a giant enemy spider with inverse kinematics or have used this project.

77 Likes

Nice I might use this in a game im making.

1 Like

I have wanted to create a IK Rig for a bit, but it seemed complicated. This makes it a whole lot easier. Thank you so much for your contribution!

3 Likes

August 30, 2020 Changelog:

  1. Newer showcase with better quality that doesn’t use Gyazo

  2. Easier functionality to use through OOP with better instructions for usage

2 Likes

September 4, 2020 Changelog:

  1. Simple? Constraint system using parts and welds to set the axis of stuff like conical constraints.
    Includes:
    Rigid Constraint, Ball Socket constraint, and Hinge Constraint.

  2. Foot placement.
    (Experimental feature, disable it by putting false in the limb chain’s second parameter like so:)

LimbChain.new(motorTable,false)
  1. More bugs especially with hinge constraint which makes it look jittery if constantly iterated :frowning:
2 Likes

Really nice module, people don’t know how useful this is.
I’ve tried implementing IK for rigs in my game, but in the end I gave up due to the fact that I suck at math :sad:.
This still seems confusing to me as I’m not good with these terms, but I’ll try setting something up with this nevertheless. Thank you for this module!

1 Like

September 6, 2020 Changelog:

  1. More distinct iteration methods with swapped parameters
leftLegChain:IterateOnce(goalPosition,toleranceInStuds)
leftLegChain:IterateUntilGoal(goalPosition,toleranceInStuds,maxIterationUntilBreakCount)
  1. Ability to store motor c0 CFrame data
--Returns a table of motorC0 position extensive testing hasn't been done yet
leftLegChain:StoreMotorsC0(floorNormal)

Edit: Whoops forgot to mention to set constraints now you have to use the SetConstraints function for the limb chain object

--Set the constraints of the object
leftLegChain:SetConstraints(limbConstraintTable)
3 Likes

Should make procedural animation really easy. Thank you!

1 Like

September 9, 2020 Changelog, Version 0.9:

  1. Motor6d Parent dependencies are gone, since it uses the Part0 property. Placement of motor6ds in rig no longer mattes :partying_face:

  2. Methods found to use the already made LimbChain object to create multiple chains bringing Mr. Floopy Man to life:

  1. Debug mode: creates and puts parts where the FABRIK algorithm thinks the motors should be during UpdateMotors() like in the unconstrained version video with random colors and stuff.
leftLegChain:DebugModeOn()
  1. New third parameter in LimbChain allowing the movement of the spine or body whatever you call it :partying_face:,
local spineChain = LimbChain.new(spineMotorTable,false,lowerTorsoMotor)
4 Likes

September 12,2020 Changelog, Version 1.0:

  1. Primary and secondary constraints! Allow the algorithm to not glitch out with a work flow like this:
    My tip is to use the ball socket constraints as I read somewhere online that FABRIK works better with a conical constraint method rather than an angle based hinge constraint.
Sample Code
--Set up two constraint tables to allow
local leftLegConstraintsPrimary = {upperLegBallSocketConstraint,lKneeHinge,lLegHinge,rigidFeet}
local leftLegConstraintsSecondary = {upperLegBallSocketConstraintAlternative,lKneeBallSocket,lLegBallSocket,rigidFeet}

--Set the constraints of the object
leftLegChain:SetPrimaryConstraints(leftLegConstraintsPrimary)
leftLegChain:SetSecondaryConstraints(leftLegConstraintsSecondary)

--Set the region for the primary constraints
local leftLegRegionPart1 = lowerBody.ConstraintZones.LeftLegPart1
local leftLegRegionPart2 = lowerBody.ConstraintZones.LeftLegPart2
local leftLegRegion = {leftLegRegionPart1,leftLegRegionPart2}
leftLegChain:SetPrimaryConstraintRegion(leftLegRegion)
  1. LimbChain:IterateUntilGoal() function is less glitchy now by updating the motors after every iteration to update the parts which are constraining the Model.
1 Like

finnaly i can make dani’s inverse kinematics as he said they are kinametics but inversed

September 22, 2020, Version 1.2 and 1.1:

  1. Version 1.1, Introduces a rudimentary foot placing system that places a foot on a surface at a point. Just add two attachments to the LimbChain object like in the place file and enable the bool option for IncludeFoot and it’s good to go! Or you could also this system to change the target position from the joint (by default) to an attachment like going from the wrist to the fingertip.

  2. Version 1.2, Finally worked out how to lerp the motors without them detaching like a ray man. Now by default it’s on just because of how good it looks however there are options to disable it or adjust by setting the Object’s properties like below:

    --Bool to turn on lerp mode and by default it's true because it just looks nicer
    LimbChain.LerpMotors = true
    --Default LerpAlpha is 0.25 or 1/4 to make it look nicer like it has momentum,
    LimbChain.LerpAlpha = 1/4
3 Likes

13 October 2020, Version 1.2.1:

This time the update is very small as I added a very small nil check to the LimbChain:UpdateMotors() function in order for all the parts that are being updated exist before trying to index their CFrames.

Edit: Whoops forgot to mention I deleted the store motor C0 method, feels unnecessary and could be way better implemented so it gets updated alongside LimbChain:UpdateMotors() method which maybe is the problem since it’s a do-all method.

However, the most important change is the attribution and credits section of the community resource where I noted some very important copyright concerns in the disclaimer in order to hopefully avoid future conflicts with the project.

Moreover, I also further listed some of my inspiration towards the project stemming from Mech games on Roblox which Roblox is severely lacking rn I’d be happy if anyone were to take up that task I’m too busy with Uni rn.

Anyways, I’m curious if people are using this resource? If not then, please vote in the poll below to see what could be improved of this resource:

  • Documentation
  • Better Foot Placement
  • Other Features pls describe

0 voters

But yeah so far I’m able to make it work for my purpose for procedural animations thanks to @iGottic amazing procedural animation method of doing it client-sided since well yeah the computer doesn’t like too much IK math. It’s the same script but modified to work with my IK method, a few attachments to denote where I want it to step to, and in a module script.

Hope you guys enjoy the resource though and are able to make it work!

8 Likes

Thank you so much for your contribution, this should help alot of people who are confused on using motor6D, the last picture left me laughing can’t lie :joy:

1 Like

November 1, 2020 Changelog, Version 1.3:

  1. So yeah I did some comment based documentation, and generally tidied up the module scripts by rewriting my entire Git history and squashed it from 154 commits to 72 commits kinda messed up the tags previously but it works. Also, I consolidated it into one LimbChain module script after discovering what init.lua does in Rojo so it follows a similar format to RotatedRegion3.

  2. Also added a debugging mode using an enhanced cone mesh to visualize the range of ball socket constraints. It’s not exactly accurate especially for the hinge constraints but it’s better than having to visualize mentally.

And I’m kinda burnt out after trying a lot of things to fix the constraints and mixing and matching CFrames it just fared a lot worse and didn’t work…

Hopefully, to expand on the project a different algorithm should be used and I expect Cyclic Coordinate Descent Inverse Kinematics (CCDIK) to solve my problems especially with hinge constraints.

This GitHub resource thanks to Johnathon Selstad which I found online has a really cool interactive IK arm with some PsuedoCode and a real code example in javascript to help understand CCDIK. Perhaps someone can import it to Lua? It would be cool but would also require some Quanternion knowledge to clamp the constraints of the rotation expressed within EulerAngles. However yeah the way it mentions the possibility of CCDIK to implement a working hinge constraint got me interested.

For now, the FABRIK works I guess, I wanna make my mech shoot a gun first so yeah.

7 Likes

Thank you for this. I’ve been looking for a Inverse Kinematics on roblox. I can now start my very long and painful progress for my War Of the Worlds Tripod for my game.

1 Like

Do you have the File for the Floppy Man?
I would like it as I need it for a reference for my tripod legs.

1 Like

I believe it’s already there in the place file in the github I placed him in replicated storage, and the server script controlling is a disabled script called MultiChain which you will need to re-enable.

1 Like

I reenabled MultiChain. Also Found Floppy Man and r15 inside lighting.
I tested them out and it seems there incomplete as they do not move but are rigged

Here is the rbxlx file, sorry the server script wasn’t updated with the new location of the LimbChain module which is fixed in this place file.

MechTest.rbxlx (754.6 KB)

3 Likes