Haha, finally made mech have that big stompy feel. Just a little sin and cos to make sway and go up and down really make a difference from my very first attempt.
https://i.imgur.com/dstM2Ma.mp4
I’ve decided to share the module of the procedural animation which tbh is the same mechanism as @iGottic R15 foot planting resource but now is more OOPed and a bit more robust. It’s robustness because this module now uses a for loop to control all the legs in the form of a dictionary so now I can make a mech with 4 or more legs .
It could be better, more polished, and more robust which is why I didn’t put it in #resources:community-resources for now maybe. Moreover, this could potentially work with any IK method, but for now, it uses my LimbChain module to do it.
Procedural animator class script
-- Procedural Animator Class
-- Dthecoolest
-- November 20, 2020
local RunService = game:GetService("RunService")
local ProceduralAnimatorClass = {}
ProceduralAnimatorClass.__index = ProceduralAnimatorClass
--Module to handle the procedural animation the hip and legs
--remnants from iGottics Code
local CF =CFrame.new
local ANGLES =CFrame.Angles
local x_and_y = Vector3.new(1, 0, 1)
local TAU = 2*math.pi
local DOWN = 10*Vector3.new(0,-1,0)
--template for legs
local mechLegs = {
["rightLeg"] = {
["CurrentCycle"] = 0,
["LimbChain"] = rightLegChain,
["HipAttachment"]= rightHipAttachment,
["FootAttachment"] = rightStepAttachment,
},
["leftLeg"] = {
["CurrentCycle"] = math.pi,
["LimbChain"] =leftLegChain,
["HipAttachment"]= leftHipAttachment,
["FootAttachment"] = leftStepAttachment,
}
}
function ProceduralAnimatorClass.new(RootPart,Legs,RootMotor)
local self = setmetatable({}, ProceduralAnimatorClass)
--Constants
self.RootPart = RootPart
self.RaycastParams = nil --manual input it
if RootMotor then
self.RootMotor = RootMotor
self.RootMotorC1Store = RootMotor.C1
self.WaistCycle = 0
end
self.Legs = Legs
--Default settings for legs
self.DefaultStride = 12 -- Changes how far the legs move
self.CycleSpeed = 4 -- How fast the leg-movement cycle is. Change this to suit your needs!
self.DefaultStrideOffset = 2
-- Radius of the circle at CFrame front of the player
self.DefaultStrideCF = CFrame.new(0, 0, -self.DefaultStride / 2) -- Turn that stride number into a CFrame we can use
--Variables that will change
self.MovementDirectionXZ = Vector3.new(1, 0, 1) -- This will be changed
self.rootvelm = 0
return self
end
function ProceduralAnimatorClass:MoveLegs(stepCycle)
--if moving
if self.rootVelocityMagnitude > 0.1 then
for i, Leg in pairs(self.Legs) do
local strideCF = Leg.StrideCF or self.DefaultStrideCF
local strideOffset = Leg.StrideOffset or self.DefaultStrideOffset
local raycastParams = self.RaycastParams
Leg.CurrentCycle = (Leg.CurrentCycle+stepCycle)%360
local cycle = Leg.CurrentCycle
local hip =Leg.HipAttachment.WorldPosition
--Position of where the lower leg should be, spread out
local ground =Leg.FootAttachment.WorldPosition
local desiredPos =(CF(ground, ground+self.MovementDirectionXZ)*ANGLES(-cycle, 0, 0)*strideCF).p
local offset =(desiredPos-hip)--vector from hip to the circle
local raycastResult = workspace:Raycast(hip,offset.unit*(offset.magnitude+strideOffset),raycastParams)
local footPos = raycastResult and raycastResult.Position or (hip + offset.unit*(offset.magnitude+strideOffset))
--Do IK towards foot pos
Leg.LimbChain:IterateOnce(footPos,0.1)
Leg.LimbChain:UpdateMotors()
end
else--stand still
for i, Leg in pairs(self.Legs) do
local strideCF = Leg.StrideCF or self.DefaultStrideCF
local strideOffset = Leg.StrideOffset or self.DefaultStrideOffset
local raycastParams = self.RaycastParams
local hip =Leg.HipAttachment.WorldPosition
--Position of where the lower leg should be, spread out
local desiredPos =Leg.FootAttachment.WorldPosition+DOWN
local offset =(desiredPos-hip)--vector from hip to the circle
local raycastResult = workspace:Raycast(hip,offset.unit*(offset.magnitude+strideOffset),raycastParams)
local footPos = raycastResult and raycastResult.Position or (hip + offset.unit*(offset.magnitude+strideOffset))
--Do IK towards foot pos
Leg.LimbChain:IterateOnce(footPos,0.1)
Leg.LimbChain:UpdateMotors()
end
end
end
function ProceduralAnimatorClass:MoveTorso(stepCycle,dt10,rootVelocity)
local lowercf = self.RootPart.CFrame
local waistjoint = self.RootMotor
local waist1 = self.RootMotorC1Store
local rootvel = rootVelocity
if self.rootVelocityMagnitude > 0.1 then
self.WaistCycle = (self.WaistCycle+stepCycle)%360
local relv0 =lowercf:vectorToObjectSpace(rootvel)
local relv1 =relv0*0.2
do -- Upper Torso
local sway = math.rad(-relv1.X)+0.08*math.cos(self.WaistCycle+90)
local goalCF = waist1*ANGLES(math.rad(relv1.Z),0.1*math.cos(self.WaistCycle)-2*math.rad(relv1.X),sway):inverse()
goalCF *= CFrame.new(0,math.cos((self.WaistCycle+90+45)*2),0)
--local rotationOnly = goalCF-goalCF.Position
waistjoint.C1 = waistjoint.C1:Lerp(goalCF,dt10)
end
else
--when not moving go back to original position
local goalCF = waistjoint.C1:Lerp(waist1, dt10)
--local rotationOnly = goalCF-goalCF.Position
waistjoint.C1 = goalCF
end
end
function ProceduralAnimatorClass:Animate(dt,onScreen)
-- Begin the step-------
local dt10 = math.min(dt*10, 1) -- Normalize dt for our needs
local rootpart = self.RootPart
local rootvel0 = rootpart.Velocity -- Our movement velocity
local rootVelocity = rootvel0 * x_and_y --XY plane velocity only
local rootVelocityMagnitude = rootVelocity.Magnitude --root velocity magnitude
self.rootVelocityMagnitude = rootVelocityMagnitude
--if moving then lerp current direction
if rootVelocityMagnitude > 0.1 then
--lerp current direction towards curren velocity
self.MovementDirectionXZ = self.MovementDirectionXZ:Lerp(rootVelocity.unit, dt10)
end
local relativizeToHumanoidSpeed = rootVelocityMagnitude/16 --default walk speed is 16
local stepCycle = relativizeToHumanoidSpeed*dt*self.CycleSpeed
self:MoveLegs(stepCycle)
if self.RootMotor then
self:MoveTorso(stepCycle,dt10,rootVelocity)
end
end
return ProceduralAnimatorClass
But yeah I have been doing a lot of research on structuring my game through Aero framework which tbh felt strict while Selene keeps yelling at me, I’m still learning so haven’t had much chances for these rewarding tasks.
Anyways, any feedback on how the walk looks? The mech model could definitely be better that’s for sure, perhaps I will learn blender one day for its advantages with creating superior details.
Edit: Also the new first-person vehicle camera is really cool and simplifies my life creating a first-person view, too bad the sway and the way it resets can get unbearable hopefully it’s modifiable haven’t looked into it.