local humanoid = script.Parent:WaitForChild("Humanoid") local character = script.Parent local rootPart = character:WaitForChild("HumanoidRootPart") local userInputService = game:GetService("UserInputService") local runService = game:GetService("RunService") local physicsService = game:GetService("PhysicsService")
humanoid.BreakJointsOnDeath = false humanoid.RequiresNeck = false
-- Variables to track state local isRagdolled = false local jointConstraints = {} local physicsConstraints = {} local originalCollisions = {} local ragdollAttachments = {} local stepConnection = nil
-- Active Balancing State local steppingFootName = nil -- "LeftFoot" or "RightFoot" or nil local stepTargetPosition = nil local stepCooldown = 0 -- Timer to prevent rapid steps local stepProgress = 0 -- For smooth step interpolation local lastSteppedFoot = "RightFoot" -- Track last foot for alternating local STEP_COOLDOWN_TIME = 0.25 -- Faster steps local STEP_COMPLETION_DISTANCE = 0.4 -- Closer completion
-- Tuning Parameters for Active Balancing local BALANCE_LeanThreshold_FB = 0.4 -- Forward/Backward lean sensitivity local BALANCE_LeanThreshold_LR = 0.35 -- Left/Right lean sensitivity local BALANCE_StepDistanceFactor = 6 -- Step length multiplier local BALANCE_StepForce = 12000 -- Force for stepping local BALANCE_StepResponsiveness = 100 -- Step speed local BALANCE_StanceWidth = 2 -- Width between feet local BALANCE_LiftHeight = 0.4 -- Height of foot lift
-- Store original collision settings for _, part in pairs(character:GetDescendants()) do if part:IsA("BasePart") then originalCollisions[part] = { CanCollide = part.CanCollide, CollisionGroupId = part.CollisionGroupId, Massless = part.Massless } part.Massless = false end end
-- Function to clear physics constraints local function clearRagdollPhysics() if stepConnection then stepConnection:Disconnect(); stepConnection = nil end for _, constraint in pairs(physicsConstraints) do if constraint.Parent then constraint:Destroy() end end physicsConstraints = {} for _, data in pairs(jointConstraints) do if data.constraint and data.constraint.Parent then data.constraint:Destroy() end if data.a1 and data.a1.Parent then data.a1:Destroy() end if data.a2 and data.a2.Parent then data.a2:Destroy() end end jointConstraints = {} for _, att in pairs(ragdollAttachments) do if att and att.Parent then att:Destroy() end end ragdollAttachments = {} steppingFootName = nil stepTargetPosition = nil stepCooldown = 0 stepProgress = 0 end
-- Function to create an attachment local function createConstraintAttachment(parentPart, nameSuffix) local att = Instance.new("Attachment"); att.Name = "RagdollAttachment_" .. (nameSuffix or parentPart.Name); att.Parent = parentPart table.insert(ragdollAttachments, att); return att end
-- Estimate Center of Mass local function calculateCenterOfMass() local totalMass = 0; local weightedSum = Vector3.zero local parts = { character:FindFirstChild("UpperTorso"), character:FindFirstChild("LowerTorso"), character:FindFirstChild("Head"), rootPart } for _, part in pairs(parts) do if part and part:IsA("BasePart") and not part.Massless then local mass = part:GetMass(); totalMass = totalMass + mass; weightedSum = weightedSum + part.Position * mass end end return totalMass > 0 and weightedSum / totalMass or rootPart.Position end
-- Foot planting logic with ground detection local function updateFootPlanting() local down = Vector3.new(0, -1, 0) local raycastParams = RaycastParams.new(); raycastParams.FilterDescendantsInstances = {character}; raycastParams.FilterType = Enum.RaycastFilterType.Exclude local totalMass = 0; for _, child in pairs(character:GetChildren()) do if child:IsA("BasePart") and not child.Massless then totalMass = totalMass + child:GetMass() end end totalMass = totalMass > 0 and totalMass or 20 local footPlantForceMagnitude = totalMass * workspace.Gravity * 0.8
for _, footName in pairs({"LeftFoot", "RightFoot"}) do local footPart = character:FindFirstChild(footName) if footPart then local forceName = footName .. "_PlantForce" local existingForce = physicsConstraints[forceName]
if steppingFootName == footName then if existingForce then existingForce.Enabled = false end continue end
local origin = footPart.Position local result = workspace:Raycast(origin, down * 3.5, raycastParams)
if result then local groundY = result.Position.Y local currentPos = footPart.Position if currentPos.Y > groundY + 0.1 then footPart.Position = Vector3.new(currentPos.X, groundY + 0.05, currentPos.Z) end
if not existingForce then local force = Instance.new("VectorForce"); force.Name = forceName; force.Attachment0 = createConstraintAttachment(footPart, "PlantForceAtt") force.RelativeTo = Enum.ActuatorRelativeTo.World; force.Force = Vector3.new(0, -footPlantForceMagnitude, 0); force.Parent = footPart physicsConstraints[forceName] = force else existingForce.Enabled = true; existingForce.Force = Vector3.new(0, -footPlantForceMagnitude, 0) end else if existingForce then existingForce.Enabled = false end end end end end
-- Active balancing with realistic stepping local function updateActiveBalance(dt) stepCooldown = math.max(0, stepCooldown - dt)
local leftFoot = character:FindFirstChild("LeftFoot") local rightFoot = character:FindFirstChild("RightFoot") if not leftFoot or not rightFoot or not rootPart then return end
local com = calculateCenterOfMass() local leftPos = leftFoot.Position local rightPos = rightFoot.Position local avgFootPos = (leftPos + rightPos) / 2 local groundY = math.min(leftPos.Y, rightPos.Y)
local forwardXZ = Vector3.new(rootPart.CFrame.LookVector.X, 0, rootPart.CFrame.LookVector.Z).Unit local rightXZ = Vector3.new(rootPart.CFrame.RightVector.X, 0, rootPart.CFrame.RightVector.Z).Unit
local comXZ = Vector3.new(com.X, 0, com.Z) local avgFootXZ = Vector3.new(avgFootPos.X, 0, avgFootPos.Z) local offsetVector = comXZ - avgFootXZ
local backwardOffset = -offsetVector:Dot(forwardXZ) local forwardOffset = offsetVector:Dot(forwardXZ) local rightwardOffset = offsetVector:Dot(rightXZ) local leftwardOffset = -offsetVector:Dot(rightXZ)
-- Handle current step if steppingFootName then local footPart = character:FindFirstChild(steppingFootName) if footPart and stepTargetPosition then stepProgress = math.min(stepProgress + dt * 4, 1) local distToTarget = (footPart.Position - stepTargetPosition).Magnitude local leanCorrected = math.abs(backwardOffset) < BALANCE_LeanThreshold_FB * 0.25 and math.abs(rightwardOffset) < BALANCE_LeanThreshold_LR * 0.25 if distToTarget < STEP_COMPLETION_DISTANCE or leanCorrected then local constraintName = steppingFootName .. "_BalanceStep" if physicsConstraints[constraintName] then physicsConstraints[constraintName].Enabled = false end lastSteppedFoot = steppingFootName steppingFootName = nil stepTargetPosition = nil stepProgress = 0 local rayResult = workspace:Raycast(footPart.Position, Vector3.new(0, -5, 0), raycastParams) if rayResult then footPart.Position = Vector3.new(footPart.Position.X, rayResult.Position.Y + 0.05, footPart.Position.Z) end else local arcHeight = BALANCE_LiftHeight * math.sin(stepProgress * math.pi) local targetXZ = stepTargetPosition local rayResult = workspace:Raycast(targetXZ, Vector3.new(0, -5, 0), raycastParams) local targetY = rayResult and rayResult.Position.Y or groundY stepTargetPosition = Vector3.new(targetXZ.X, targetY + arcHeight, targetXZ.Z) end else steppingFootName = nil stepTargetPosition = nil stepProgress = 0 end return end
if stepCooldown > 0 then return end
-- Decide to step local stepUrgency = 0 local stepDirFB = 0 local stepDirLR = 0 if backwardOffset > BALANCE_LeanThreshold_FB then stepDirFB = -1; stepUrgency = stepUrgency + (backwardOffset - BALANCE_LeanThreshold_FB) elseif forwardOffset > BALANCE_LeanThreshold_FB then stepDirFB = 1; stepUrgency = stepUrgency + (forwardOffset - BALANCE_LeanThreshold_FB) end if rightwardOffset > BALANCE_LeanThreshold_LR then stepDirLR = 1; stepUrgency = stepUrgency + (rightwardOffset - BALANCE_LeanThreshold_LR) elseif leftwardOffset > BALANCE_LeanThreshold_LR then stepDirLR = -1; stepUrgency = stepUrgency + (leftwardOffset - BALANCE_LeanThreshold_LR) end
if stepUrgency < 0.08 then return end
local stepDistFB = math.max(0, math.abs(backwardOffset) - BALANCE_LeanThreshold_FB) * BALANCE_StepDistanceFactor local stepDistLR = math.max(0, math.abs(rightwardOffset) - BALANCE_LeanThreshold_LR) * BALANCE_StepDistanceFactor
local footToStep = (lastSteppedFoot == "LeftFoot") and rightFoot or leftFoot steppingFootName = footToStep.Name stepProgress = 0
local rayOrigin = com + forwardXZ * stepDirFB * stepDistFB + rightXZ * stepDirLR * stepDistLR local raycastParams = RaycastParams.new(); raycastParams.FilterDescendantsInstances = {character}; raycastParams.FilterType = Enum.RaycastFilterType.Exclude local rayResult = workspace:Raycast(rayOrigin, Vector3.new(0, -5, 0), raycastParams) local targetY = rayResult and rayResult.Position.Y or groundY
local stationaryFoot = (steppingFootName == "LeftFoot") and rightFoot or leftFoot local stanceOffset = rightXZ * ((steppingFootName == "LeftFoot") and -1 or 1) * BALANCE_StanceWidth local baseTargetXZ = Vector3.new(stationaryFoot.Position.X, 0, stationaryFoot.Position.Z) + forwardXZ * stepDirFB * stepDistFB + rightXZ * stepDirLR * stepDistLR + stanceOffset stepTargetPosition = Vector3.new(baseTargetXZ.X, targetY + BALANCE_LiftHeight, baseTargetXZ.Z)
local constraintName = steppingFootName .. "_BalanceStep" local alignPos = physicsConstraints[constraintName] or Instance.new("AlignPosition") alignPos.Name = constraintName; alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment alignPos.Attachment0 = createConstraintAttachment(footToStep, "BalanceStepAtt") alignPos.MaxForce = BALANCE_StepForce; alignPos.Responsiveness = BALANCE_StepResponsiveness alignPos.Position = stepTargetPosition; alignPos.Parent = footToStep; alignPos.Enabled = true physicsConstraints[constraintName] = alignPos stepCooldown = STEP_COOLDOWN_TIME
local torso = character:FindFirstChild("UpperTorso") if torso then local torqueName = "BalanceTorque" local torque = physicsConstraints[torqueName] or Instance.new("Torque") torque.Name = torqueName; torque.Attachment0 = createConstraintAttachment(torso, "BalanceTorqueAtt") torque.RelativeTo = Enum.ActuatorRelativeTo.World; torque.Parent = torso local leanTorque = Vector3.new(rightwardOffset * 600, 0, backwardOffset * 600) torque.Torque = leanTorque.Magnitude > 2000 and leanTorque * (2000 / leanTorque.Magnitude) or leanTorque physicsConstraints[torqueName] = torque end end
-- Ragdoll function local function makeRagdoll() if isRagdolled then return end; isRagdolled = true clearRagdollPhysics() local totalMass = 0; for _, child in pairs(character:GetChildren()) do if child:IsA("BasePart") and not child.Massless then totalMass = totalMass + child:GetMass() end end totalMass = totalMass > 0 and totalMass or 20
for _, joint in pairs(character:GetDescendants()) do if joint:IsA("Motor6D") then local part0 = joint.Part0; local part1 = joint.Part1 if part0 and part1 then local a1 = Instance.new("Attachment"); a1.Name = "RagdollJointAttachment"; a1.Parent = part0; a1.CFrame = joint.C0 local a2 = Instance.new("Attachment"); a2.Name = "RagdollJointAttachment"; a2.Parent = part1; a2.CFrame = joint.C1 local constraint = Instance.new("BallSocketConstraint"); constraint.Parent = part0; constraint.Attachment0 = a1; constraint.Attachment1 = a2 constraint.LimitsEnabled = true; constraint.UpperAngle = 45; constraint.TwistLimitsEnabled = true; constraint.TwistLowerAngle = -30; constraint.TwistUpperAngle = 30 table.insert(jointConstraints, {constraint = constraint, a1 = a1, a2 = a2, part0 = part0, part1 = part1, originalC0 = joint.C0, originalC1 = joint.C1, jointName = joint.Name, jointParent = joint.Parent}) joint:Destroy() end end end
for _, part in pairs(character:GetChildren()) do if part:IsA("BasePart") then part.CanCollide = true; part.Massless = false; if part.Name == "HumanoidRootPart" then part.CanCollide = false end end end humanoid:ChangeState(Enum.HumanoidStateType.Physics)
local standForce = Instance.new("VectorForce"); standForce.Name = "AntiGravityForce"; standForce.Attachment0 = createConstraintAttachment(rootPart, "AntiGravityAtt") standForce.RelativeTo = Enum.ActuatorRelativeTo.World; standForce.Force = Vector3.new(0, totalMass * workspace.Gravity * 0.98, 0); standForce.Parent = rootPart physicsConstraints[standForce.Name] = standForce
stepConnection = runService.Stepped:Connect(function(dt) updateFootPlanting(); updateActiveBalance(dt) end) end
-- Reset function local function resetCharacter() if not isRagdolled then return end; isRagdolled = false if stepConnection then stepConnection:Disconnect(); stepConnection = nil end clearRagdollPhysics() for part, data in pairs(originalCollisions) do if part.Parent then part.CanCollide = data.CanCollide; part.Massless = data.Massless end end humanoid:ChangeState(Enum.HumanoidStateType.GettingUp) end
-- Event connections humanoid.Died:Connect(function() if not isRagdolled then task.wait(0.05); makeRagdoll() end end) userInputService.InputBegan:Connect(function(input, gameProcessed) if not gameProcessed and input.UserInputType == Enum.UserInputType.Keyboard then if input.KeyCode == Enum.KeyCode.R and humanoid.Health > 0 then if isRagdolled then resetCharacter() else makeRagdoll() end end end end) make this balancing code way more realistic and humanlike, like euphoria, make no errors