JIT
Improved Roblox Character Balancing Script
--[[ Improved Roblox Character Balancing Script Inspired by Euphoria physics for more human-like reactions. Features: - Active ragdoll state toggled with 'R' key. - Dynamic torso stabilization using AlignOrientation. - Predictive stepping based on Center of Mass (COM) offset and velocity. - Active arm balancing using AngularVelocity. - Foot planting to keep feet grounded when not stepping. - Smoother motion using Lerp and dynamic parameter adjustments. ]] local character = script.Parent local humanoid = character:WaitForChild("Humanoid") local rootPart = character:WaitForChild("HumanoidRootPart") local upperTorso = character:FindFirstChild("UpperTorso") -- Store for frequent access local lowerTorso = character:FindFirstChild("LowerTorso") -- Optional, but good for COM local userInputService = game:GetService("UserInputService") local runService = game:GetService("RunService") local physicsService = game:GetService("PhysicsService") humanoid.BreakJointsOnDeath = false humanoid.RequiresNeck = false -- Often disabled for custom ragdolls -- ============================================================ -- STATE VARIABLES & CONSTANTS -- ============================================================ local isRagdolled = false local jointConstraints = {} -- Stores BallSocketConstraints replacing Motor6Ds local physicsConstraints = {} -- Stores AlignPosition, AlignOrientation, VectorForce etc. local originalCollisions = {} -- Stores original collision settings of parts local ragdollAttachments = {} -- Stores attachments created for constraints local stepConnection = nil -- Connection for the RunService.Stepped event -- Active Balancing State local steppingFootName = nil -- "LeftFoot", "RightFoot", or nil local stepTargetPosition = nil -- Where the stepping foot is aiming local stepCooldown = 0 -- Prevents rapid, jittery stepping local stepProgress = 0 -- [0,1] normalized progress of the current step local lastSteppedFoot = "RightFoot" -- Start by assuming the right foot was last planted local isStumbling = false -- Flag for quick follow-up steps -- Timing & Distance Constants (Adjust these for feel) local STEP_COOLDOWN_TIME = 0.4 -- Base time between steps local STUMBLE_COOLDOWN_TIME = 0.15 -- Faster cooldown if stumbling local STEP_DURATION = 0.35 -- Time to complete a step animation local STEP_COMPLETION_DISTANCE = 0.3 -- How close foot needs to be to target to finish step local STEP_MAX_REACH = 3.5 -- Maximum distance a step will target -- Balancing Thresholds & Parameters (Crucial for tuning the feel) local BALANCE_LeanThreshold_FB = 0.25 -- Forward/backward lean distance before stepping local BALANCE_LeanThreshold_LR = 0.20 -- Left/right lean distance before stepping local BALANCE_VelocityPredictionFactor = 0.2 -- How much velocity influences predicted lean (higher = more predictive) local BALANCE_StepDistanceFactor = 1.1 -- Multiplier for how far to step based on lean local BALANCE_StepForce = 8000 -- Force applied to move the stepping foot local BALANCE_StepResponsiveness = 80 -- How quickly the foot moves to the target (lower = softer) local BALANCE_StanceWidth = 1.0 -- Desired distance between feet laterally local BALANCE_LiftHeight = 0.4 -- How high the foot lifts during a step arc -- Smoothing & Damping Factors (Lower = smoother/slower response) local TORSO_LERP_BASE = 0.08 -- Base interpolation factor for torso upright correction local TORSO_LERP_AGGRESSION = 0.3 -- How much the Lerp factor increases when heavily leaned local ARM_LERP_FACTOR = 0.05 -- Interpolation for arm balancing movements local ARM_SWING_INTENSITY = 15 -- How strongly arms swing out to counterbalance lean local FOOT_PLANT_RESPONSIVENESS = 25 -- How quickly planted feet stick to the ground local FOOT_PLANT_MAX_FORCE = 5000 -- Force keeping planted feet down local COM_SMOOTHING = 0.1 -- Damping factor for Center of Mass calculation (reduces jitter) -- Anti-Gravity Force Multiplier (Slightly less than 1 to keep feet on ground) local ANTI_GRAVITY_MULTIPLIER = 0.985 -- ============================================================ -- SAVE ORIGINAL COLLISION SETTINGS -- ============================================================ local function saveOriginalCollisions() originalCollisions = {} for _, part in pairs(character:GetDescendants()) do if part:IsA("BasePart") then originalCollisions[part] = { CanCollide = part.CanCollide, CollisionGroupId = part.CollisionGroupId, Massless = part.Massless, CustomPhysicalProperties = part.CustomPhysicalProperties } part.Massless = false -- Ensure parts have mass for physics end end end saveOriginalCollisions() -- Save initially -- ============================================================ -- UTILITY FUNCTIONS -- ============================================================ -- Cleans up all created constraints and attachments local function clearRagdollPhysics() if stepConnection then stepConnection:Disconnect() stepConnection = nil end for _, constraint in pairs(physicsConstraints) do if constraint and 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 isStumbling = false end -- Creates an attachment safely and stores it for cleanup 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 -- Calculates a smoothed Center of Mass (COM) local lastCOM = rootPart.Position local function calculateSmoothedCenterOfMass() local totalMass = 0 local weightedSum = Vector3.new(0, 0, 0) -- Include major body parts for COM calculation local partsToConsider = { upperTorso, lowerTorso, character:FindFirstChild("Head"), rootPart } for _, part in pairs(partsToConsider) 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 local currentCOM = rootPart.Position -- Fallback if totalMass > 0 then currentCOM = weightedSum / totalMass end -- Smooth the COM using Lerp to reduce jitter lastCOM = lastCOM:Lerp(currentCOM, COM_SMOOTHING) return lastCOM end -- Raycast downwards to find ground position, ignoring the character local groundRaycastParams = RaycastParams.new() groundRaycastParams.FilterType = Enum.RaycastFilterType.Exclude groundRaycastParams.FilterDescendantsInstances = {character} local function findGroundY(position) local result = workspace:Raycast(position + Vector3.new(0, 1, 0), Vector3.new(0, -10, 0), groundRaycastParams) return result and result.Position.Y end -- ============================================================ -- FOOT PLANTING (Keeps non-stepping foot grounded) -- ============================================================ local function updateFootPlanting() for _, footName in pairs({"LeftFoot", "RightFoot"}) do local footPart = character:FindFirstChild(footName) if footPart then local plantConstraintName = footName .. "_Plant" local alignPos = physicsConstraints[plantConstraintName] if not alignPos then alignPos = Instance.new("AlignPosition") alignPos.Name = plantConstraintName alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment alignPos.Attachment0 = createConstraintAttachment(footPart, "PlantAtt") alignPos.MaxForce = FOOT_PLANT_MAX_FORCE alignPos.Responsiveness = FOOT_PLANT_RESPONSIVENESS alignPos.ApplyAtCenterOfMass = true -- Apply force smoothly alignPos.Parent = footPart physicsConstraints[plantConstraintName] = alignPos end -- Enable planting only if the foot is NOT the one currently stepping if steppingFootName == footName then alignPos.Enabled = false else local groundY = findGroundY(footPart.Position) if groundY then -- Target slightly above ground to prevent sinking local targetPos = Vector3.new(footPart.Position.X, groundY + 0.1, footPart.Position.Z) -- Smoothly move towards the target position alignPos.Position = alignPos.Position:Lerp(targetPos, 0.5) -- Lerp for smoother planting alignPos.Enabled = true else -- No ground detected, disable planting alignPos.Enabled = false end end end end end -- ============================================================ -- DYNAMIC TORSO CORRECTION (More adaptive upright force) -- ============================================================ local function updateTorsoCorrection(dt) if not upperTorso then return end local torsoAlign = physicsConstraints["UpperTorso_Align"] if torsoAlign then -- Calculate lean angle (dot product with world up vector) local currentUp = upperTorso.CFrame.UpVector local uprightDot = currentUp:Dot(Vector3.new(0, 1, 0)) -- 1 = perfectly upright, -1 = upside down local leanAmount = 1.0 - math.clamp(uprightDot, 0, 1) -- 0 = upright, 1 = horizontal or more -- Dynamically adjust responsiveness and interpolation based on lean local aggressionFactor = leanAmount * leanAmount -- Square it to make correction stronger when heavily leaned local currentLerpFactor = math.clamp(TORSO_LERP_BASE + aggressionFactor * TORSO_LERP_AGGRESSION, TORSO_LERP_BASE, 0.6) local currentResponsiveness = math.clamp(30 + aggressionFactor * 100, 30, 130) -- Increase responsiveness when needed -- Target orientation: Upright, facing the same direction as the root part local targetLook = rootPart.CFrame.LookVector local targetUp = Vector3.new(0, 1, 0) local targetRight = targetLook:Cross(targetUp).Unit targetLook = targetUp:Cross(targetRight).Unit -- Ensure orthogonality -- Use CFrame.new(pos, lookAt) for potentially better stability if needed -- local targetCFrame = CFrame.new(upperTorso.Position, upperTorso.Position + targetLook) local targetCFrame = CFrame.fromMatrix(upperTorso.Position, targetRight, targetUp, -targetLook) -- More explicit construction -- Apply smoothed orientation target torsoAlign.CFrame = torsoAlign.CFrame:Lerp(targetCFrame, currentLerpFactor) torsoAlign.Responsiveness = currentResponsiveness -- Optional: Slightly increase MaxTorque when heavily leaned too torsoAlign.MaxTorque = math.clamp(20000 + aggressionFactor * 30000, 20000, 50000) end end -- ============================================================ -- ACTIVE ARM BALANCING (Swing arms to counter lean) -- ============================================================ local function updateArmBalancing(leanVectorRight, leanVectorForward, dt) local rightArm = character:FindFirstChild("RightUpperArm") local leftArm = character:FindFirstChild("LeftUpperArm") if not rightArm or not leftArm then return end local rightAV = physicsConstraints["RightUpperArm_Swing"] local leftAV = physicsConstraints["LeftUpperArm_Swing"] if not rightAV or not leftAV then return end -- Calculate target angular velocity for arms based on lean -- Swing arms outwards/upwards relative to the torso's current orientation local torsoRight = upperTorso.CFrame.RightVector local torsoForward = upperTorso.CFrame.LookVector local torsoUp = upperTorso.CFrame.UpVector -- Target swing: proportional to lean, opposing the lean direction -- Lean right -> swing left arm out/up, right arm slightly in/down (or less out) -- Lean forward -> swing arms slightly back/down local targetRightArmVel = (torsoForward * leanVectorRight * -ARM_SWING_INTENSITY) + (torsoRight * leanVectorForward * -ARM_SWING_INTENSITY * 0.5) local targetLeftArmVel = (torsoForward * leanVectorRight * ARM_SWING_INTENSITY) + (torsoRight * leanVectorForward * -ARM_SWING_INTENSITY * 0.5) -- If stepping, add a swing component related to the step if steppingFootName then local swingDir = (steppingFootName == "LeftFoot") and 1 or -1 -- Swing opposite arm forward targetRightArmVel = targetRightArmVel + torsoRight * swingDir * -ARM_SWING_INTENSITY * 0.7 targetLeftArmVel = targetLeftArmVel + torsoRight * swingDir * ARM_SWING_INTENSITY * 0.7 end -- Smoothly interpolate towards the target velocity rightAV.AngularVelocity = rightAV.AngularVelocity:Lerp(targetRightArmVel, ARM_LERP_FACTOR) leftAV.AngularVelocity = leftAV.AngularVelocity:Lerp(targetLeftArmVel, ARM_LERP_FACTOR) end -- ============================================================ -- ADVANCED ACTIVE BALANCING LOGIC (Predictive 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 or not upperTorso then return end -- Calculate key vectors and positions local com = calculateSmoothedCenterOfMass() local leftPos = leftFoot.Position local rightPos = rightFoot.Position local avgFootPos = (leftPos + rightPos) / 2 local groundY = math.min(leftPos.Y, rightPos.Y) -- Use lowest foot as ground reference -- Character's local XZ plane directions local rootCF = rootPart.CFrame local forwardXZ = (rootCF.LookVector * Vector3.new(1, 0, 1)).Unit local rightXZ = (rootCF.RightVector * Vector3.new(1, 0, 1)).Unit -- Calculate COM offset relative to average foot position (support base center) local comXZ = Vector3.new(com.X, 0, com.Z) local avgFootXZ = Vector3.new(avgFootPos.X, 0, avgFootPos.Z) local offsetVector = comXZ - avgFootXZ -- Predict future offset based on current velocity local velocityXZ = rootPart.AssemblyLinearVelocity * Vector3.new(1, 0, 1) local predictedOffset = offsetVector + velocityXZ * BALANCE_VelocityPredictionFactor -- Project predicted offset onto character's forward/right axes local leanForward = predictedOffset:Dot(forwardXZ) local leanRight = predictedOffset:Dot(rightXZ) -- Update active arm balancing based on lean updateArmBalancing(leanRight, leanForward, dt) -- === STEP EXECUTION (If currently stepping) === if steppingFootName then local footPart = character:FindFirstChild(steppingFootName) if footPart and stepTargetPosition then stepProgress = math.min(stepProgress + dt / STEP_DURATION, 1) -- Use SmoothStep for easing (starts slow, accelerates, ends slow) local easeAlpha = stepProgress * stepProgress * (3 - 2 * stepProgress) -- Calculate foot position along the step arc local startPos = physicsConstraints[steppingFootName .. "_BalanceStep"].Attachment0.WorldPosition -- Where the step started local currentTargetPos = startPos:Lerp(stepTargetPosition, easeAlpha) local arcHeight = math.sin(stepProgress * math.pi) * BALANCE_LiftHeight -- Sine curve for lift currentTargetPos = currentTargetPos + Vector3.new(0, arcHeight, 0) -- Update the AlignPosition constraint target local stepConstraint = physicsConstraints[steppingFootName .. "_BalanceStep"] if stepConstraint then stepConstraint.Position = currentTargetPos -- Optional: Reduce responsiveness towards the end of the step for softer landing stepConstraint.Responsiveness = BALANCE_StepResponsiveness * (1 - easeAlpha * 0.5) end -- Check for step completion local distToTarget = (footPart.Position * Vector3.new(1, 0, 1) - stepTargetPosition * Vector3.new(1, 0, 1)).Magnitude local isBalancedEnough = math.abs(leanForward) < (BALANCE_LeanThreshold_FB * 0.7) and math.abs(leanRight) < (BALANCE_LeanThreshold_LR * 0.7) if stepProgress >= 1 or distToTarget < STEP_COMPLETION_DISTANCE then -- Step finished if stepConstraint then stepConstraint.Enabled = false end lastSteppedFoot = steppingFootName steppingFootName = nil stepTargetPosition = nil stepProgress = 0 stepCooldown = isStumbling and STUMBLE_COOLDOWN_TIME or STEP_COOLDOWN_TIME -- Use normal cooldown after successful step isStumbling = false -- Reset stumble flag -- Gradually reset arm swing after step completion local rightArmAV = physicsConstraints["RightUpperArm_Swing"] local leftArmAV = physicsConstraints["LeftUpperArm_Swing"] if rightArmAV then rightArmAV.AngularVelocity = rightArmAV.AngularVelocity * 0.5 end -- Dampen quickly if leftArmAV then leftArmAV.AngularVelocity = leftArmAV.AngularVelocity * 0.5 end end else -- Something went wrong (foot missing?), cancel step steppingFootName = nil stepTargetPosition = nil stepProgress = 0 isStumbling = false end updateTorsoCorrection(dt) -- Continue torso correction during step return -- Don't evaluate new step while executing one end -- === STEP DECISION (If not currently stepping and cooldown is over) === if stepCooldown > 0 then updateTorsoCorrection(dt) return end local needsStepFB = math.abs(leanForward) > BALANCE_LeanThreshold_FB local needsStepLR = math.abs(leanRight) > BALANCE_LeanThreshold_LR if needsStepFB or needsStepLR then -- Determine which foot to step with (usually the one opposite the last step) local footToStepName = (lastSteppedFoot == "LeftFoot") and "RightFoot" or "LeftFoot" local footToStepPart = character:FindFirstChild(footToStepName) local stationaryFootPart = (footToStepName == "LeftFoot") and rightFoot or leftFoot if not footToStepPart or not stationaryFootPart then return end -- Safety check steppingFootName = footToStepName stepProgress = 0 -- Calculate step direction and distance based on predicted lean local stepDir = (forwardXZ * leanForward + rightXZ * leanRight).Unit local stepDist = math.sqrt(leanForward^2 + leanRight^2) -- Magnitude of lean local requiredStepLength = math.clamp((stepDist - math.min(BALANCE_LeanThreshold_FB, BALANCE_LeanThreshold_LR)) * BALANCE_StepDistanceFactor, 0.1, STEP_MAX_REACH) -- Calculate base target position relative to the stationary foot local targetXZ = (Vector3.new(stationaryFootPart.Position.X, 0, stationaryFootPart.Position.Z) + stepDir * requiredStepLength + rightXZ * ((footToStepName == "LeftFoot") and -1 or 1) * (BALANCE_StanceWidth / 2)) -- Adjust for stance width -- Find ground height at target location local targetGroundY = findGroundY(Vector3.new(targetXZ.X, com.Y, targetXZ.Z)) or groundY -- Use current ground if ray fails stepTargetPosition = Vector3.new(targetXZ.X, targetGroundY + 0.1, targetXZ.Z) -- Target slightly above ground -- Activate/Update the AlignPosition constraint for the step local constraintName = steppingFootName .. "_BalanceStep" local alignPos = physicsConstraints[constraintName] if not alignPos then alignPos = Instance.new("AlignPosition") alignPos.Name = constraintName alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment -- Create attachment at the foot's center for applying force local stepAtt = createConstraintAttachment(footToStepPart, "BalanceStepAtt") stepAtt.Position = Vector3.new(0, -footToStepPart.Size.Y/2, 0) -- Apply force lower? Experiment needed. alignPos.Attachment0 = stepAtt alignPos.MaxForce = BALANCE_StepForce alignPos.ApplyAtCenterOfMass = false -- Apply force at attachment point alignPos.Parent = footToStepPart physicsConstraints[constraintName] = alignPos end alignPos.Responsiveness = BALANCE_StepResponsiveness -- Reset responsiveness at step start alignPos.Position = footToStepPart.Position -- Start lerping from current pos alignPos.Enabled = true -- Set cooldown and potentially mark as stumbling if the lean was large local leanMagnitude = predictedOffset.Magnitude if leanMagnitude > (BALANCE_LeanThreshold_FB + BALANCE_LeanThreshold_LR) * 0.8 then -- Heuristic for large lean isStumbling = true stepCooldown = STUMBLE_COOLDOWN_TIME -- Shorter cooldown for potential follow-up step else isStumbling = false stepCooldown = STEP_COOLDOWN_TIME end end -- Always update torso correction, even when stable updateTorsoCorrection(dt) end -- ============================================================ -- INITIALIZE RAGDOLL & BALANCING SYSTEM -- ============================================================ local function makeRagdoll() if isRagdolled then return end isRagdolled = true clearRagdollPhysics() -- Clear any previous physics state saveOriginalCollisions() -- Re-save in case properties changed local totalMass = 0 for _, part in pairs(character:GetDescendants()) do if part:IsA("BasePart") and not originalCollisions[part].Massless then totalMass = totalMass + part:GetMass() -- Apply slightly higher density for stability? (Optional) -- part.CustomPhysicalProperties = PhysicalProperties.new(1.2, 0.5, 0.5, 1, 1) -- Density, Friction, Elasticity, FrictionWeight, ElasticityWeight end end totalMass = math.max(totalMass, 1) -- Avoid division by zero -- Replace Motor6Ds with BallSocketConstraints for _, joint in pairs(character:GetDescendants()) do if joint:IsA("Motor6D") then local part0 = joint.Part0 local part1 = joint.Part1 if part0 and part1 and part0.Parent and part1.Parent then -- Ensure parts exist local a1 = createConstraintAttachment(part0, part0.Name .. "_" .. part1.Name .. "_A1") a1.CFrame = joint.C0 local a2 = createConstraintAttachment(part1, part0.Name .. "_" .. part1.Name .. "_A2") a2.CFrame = joint.C1 local constraint = Instance.new("BallSocketConstraint") constraint.Name = joint.Name .. "_BSC" constraint.Attachment0 = a1 constraint.Attachment1 = a2 constraint.LimitsEnabled = true constraint.TwistLimitsEnabled = true -- === Refined Joint Limits (Example - Adjust as needed!) === local partName = part1.Name:lower() if partName:find("foot") or partName:find("hand") then constraint.UpperAngle = 45 constraint.TwistLowerAngle = -15 constraint.TwistUpperAngle = 15 elseif partName:find("lower") and partName:find("leg") then -- Lower Leg (Knee) constraint.UpperAngle = 80 -- More bend backward constraint.TwistLowerAngle = -5 constraint.TwistUpperAngle = 5 elseif partName:find("upper") and partName:find("leg") then -- Upper Leg (Hip) constraint.UpperAngle = 90 -- Wide range constraint.TwistLowerAngle = -35 constraint.TwistUpperAngle = 35 elseif partName:find("lower") and partName:find("arm") then -- Lower Arm (Elbow) constraint.UpperAngle = 85 constraint.TwistLowerAngle = -10 constraint.TwistUpperAngle = 10 elseif partName:find("upper") and partName:find("arm") then -- Upper Arm (Shoulder) constraint.UpperAngle = 110 -- Very flexible constraint.TwistLowerAngle = -80 constraint.TwistUpperAngle = 80 elseif partName:find("head") then constraint.UpperAngle = 40 constraint.TwistLowerAngle = -30 constraint.TwistUpperAngle = 30 elseif partName:find("torso") then -- Connection between lower/upper torso constraint.UpperAngle = 25 constraint.TwistLowerAngle = -20 constraint.TwistUpperAngle = 20 else -- Default fallback constraint.UpperAngle = 45 constraint.TwistLowerAngle = -30 constraint.TwistUpperAngle = 30 end constraint.Parent = part0 -- Parent constraint to one of the parts table.insert(jointConstraints, { constraint = constraint, a1 = a1, a2 = a2 }) joint.Enabled = false -- Disable Motor6D instead of destroying, allows easier reset end end end -- Set physical properties for ragdoll parts for part, data in pairs(originalCollisions) do if part and part.Parent then part.CanCollide = true part.Massless = false -- Ensure all parts have mass -- Set collision group to avoid self-collision if needed -- physicsService:SetPartCollisionGroup(part, "Ragdoll") if part == rootPart then part.CanCollide = false -- HRP usually doesn't collide end end end -- physicsService:CreateCollisionGroup("Ragdoll") -- physicsService:CollisionGroupSetCollidable("Ragdoll", "Ragdoll", false) humanoid:ChangeState(Enum.HumanoidStateType.Physics) -- Enable physics control -- === Create Physics Constraints for Balancing === -- Anti-Gravity Force (applied at COM for stability) local antiGravAtt = createConstraintAttachment(rootPart, "AntiGravityAtt") antiGravAtt.WorldPosition = calculateSmoothedCenterOfMass() -- Apply at COM local standForce = Instance.new("VectorForce") standForce.Name = "AntiGravityForce" standForce.Attachment0 = antiGravAtt standForce.RelativeTo = Enum.ActuatorRelativeTo.World standForce.ApplyAtCenterOfMass = false -- Apply at the attachment point (COM) standForce.Force = Vector3.new(0, totalMass * workspace.Gravity * ANTI_GRAVITY_MULTIPLIER, 0) standForce.Parent = rootPart physicsConstraints[standForce.Name] = standForce -- Torso Alignment if upperTorso then local torsoAlignAtt = createConstraintAttachment(upperTorso, "TorsoAlignAtt") local align = Instance.new("AlignOrientation") align.Name = "UpperTorso_Align" align.Attachment0 = torsoAlignAtt align.Mode = Enum.OrientationAlignmentMode.OneAttachment align.MaxTorque = 30000 -- Start with a reasonable torque align.Responsiveness = 50 -- Start responsiveness align.CFrame = CFrame.lookAt(Vector3.zero, rootPart.CFrame.LookVector, Vector3.yAxis) -- Initial target align.Parent = upperTorso physicsConstraints[align.Name] = align end -- Arm Swing Angular Velocity constraints for _, armName in pairs({"LeftUpperArm", "RightUpperArm"}) do local arm = character:FindFirstChild(armName) if arm then local swingAtt = createConstraintAttachment(arm, "SwingAtt") local av = Instance.new("AngularVelocity") av.Name = armName .. "_Swing" av.Attachment0 = swingAtt av.MaxTorque = 1500 -- Increased torque for more effective swings av.RelativeTo = Enum.ActuatorRelativeTo.World -- Swing relative to world space based on lean av.AngularVelocity = Vector3.new(0, 0, 0) -- Start still av.Parent = arm physicsConstraints[av.Name] = av end end -- Initialize foot planting constraints (they will be enabled/disabled in the update loop) updateFootPlanting() -- Connect the main update loop stepConnection = runService.Stepped:Connect(function(_, dt) -- Use the second argument (delta time) if not isRagdolled then return end -- Exit if ragdoll was disabled mid-frame -- Update attachment positions if needed (e.g., anti-gravity at COM) antiGravAtt.WorldPosition = calculateSmoothedCenterOfMass() standForce.Force = Vector3.new(0, totalMass * workspace.Gravity * ANTI_GRAVITY_MULTIPLIER, 0) -- Update force if mass changes (unlikely but safe) updateFootPlanting() updateActiveBalance(dt) -- Pass delta time end) end -- ============================================================ -- RESET CHARACTER (Restore original state) -- ============================================================ local function resetCharacter() if not isRagdolled then return end isRagdolled = false clearRagdollPhysics() -- Destroy all constraints and attachments -- Restore original Motor6D state for _, joint in pairs(character:GetDescendants()) do if joint:IsA("Motor6D") then joint.Enabled = true end end -- Restore original collision properties for part, data in pairs(originalCollisions) do if part and part.Parent then part.CanCollide = data.CanCollide part.Massless = data.Massless part.CustomPhysicalProperties = data.CustomPhysicalProperties -- physicsService:SetPartCollisionGroup(part, "Default") -- Reset collision group -- Reset velocity/angular velocity part.AssemblyLinearVelocity = Vector3.zero part.AssemblyAngularVelocity = Vector3.zero end end humanoid:ChangeState(Enum.HumanoidStateType.GettingUp) -- Trigger Roblox's standard recovery state task.wait(0.1) -- Short delay to allow GettingUp state if humanoid:GetState() ~= Enum.HumanoidStateType.Dead then humanoid:ChangeState(Enum.HumanoidStateType.Running) -- Or Idle, depending on context end end -- ============================================================ -- EVENT HANDLERS -- ============================================================ humanoid.Died:Connect(function() if not isRagdolled then task.wait(0.05) -- Short delay before ragdolling on death makeRagdoll() end -- Optionally disable balancing updates on death -- if stepConnection then stepConnection:Disconnect() stepConnection = nil end end) -- Toggle Ragdoll/Balancing with 'R' key 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) -- Clean up when the script is removed script.Destroying:Connect(function() resetCharacter() -- Attempt to reset state on removal end) -- Initial state (optional - start ragdolled or standing) -- makeRagdoll() -- Uncomment to start in ragdoll mode woah dude, thats really good!, if you fix so it doesnt step on its other leg then it would be really good

Okay, I can adjust the step target calculation to prevent the character from stepping on its own stationary foot.

The core idea is to ensure the calculated stepTargetPosition maintains a minimum lateral distance (related to the BALANCE_StanceWidth) from the stationary foot.

I've added a "FOOT COLLISION AVOIDANCE" section within the updateActiveBalance function, right after the initial targetXZ is calculated. This section checks the lateral distance between the calculated step target and the stationary foot. If the distance is less than a defined minimum (BALANCE_StanceWidth * MIN_FOOT_SEPARATION_FACTOR), it pushes the target position laterally outwards to maintain the minimum separation. This should help prevent the stepping foot from landing directly on or too close to the other foot.*_

Updated files
v1
remove the camera shake, and make it more realistic, and make it react more when leaning ,make it better at saving itself

Okay, I've adjusted the script to address your points. I've focused on making the character's reactions to leaning more pronounced and improving its ability to recover from imbalances. This involves tuning several parameters related to step triggering, step execution, torso correction, and arm swinging.

Regarding "camera shake," the script itself doesn't control the camera. If you're experiencing excessive character jitter, these tuning changes aim to make the balancing movements more decisive and hopefully smoother overall, which might reduce the appearance of shakiness.

Here are the key changes:

  1. Faster Step Triggering: Reduced the lean thresholds (BALANCE_LeanThreshold_FB, BALANCE_LeanThreshold_LR) so steps initiate sooner.
  2. More Predictive & Longer Steps: Increased the velocity prediction factor (BALANCE_VelocityPredictionFactor) and step distance factor (BALANCE_StepDistanceFactor) for more proactive and potentially larger recovery steps.
  3. Faster & Stronger Steps: Increased step force (BALANCE_StepForce) and responsiveness (BALANCE_StepResponsiveness), and slightly decreased step duration (STEP_DURATION) for quicker foot movement. Reduced stumble cooldown (STUMBLE_COOLDOWN_TIME) for faster follow-up steps when needed.
  4. Stronger Torso Correction: Increased the aggression factor (TORSO_LERP_AGGRESSION) and the dynamic scaling of responsiveness/torque in updateTorsoCorrection to make the torso fight harder to stay upright during significant leans.
  5. More Active Arm Balancing: Increased the arm swing intensity (ARM_SWING_INTENSITY) for more pronounced counter-movements.
  6. Slightly Stronger Anti-Gravity: Increased ANTI_GRAVITY_MULTIPLIER marginally to help keep the character slightly lighter on its feet, potentially aiding recovery.

Remember that tuning physics-based balancing is an iterative process. These values should provide a more reactive and stable character, but you might need to fine-tune them further based on the specific feel you're aiming for and how the character interacts with your game's environment._

Updated files
v2
make it more advanced, make the arms be more realistic, and try to catch themselves like styretch out

Okay, this is getting into more complex, nuanced behavior! To make the arms react more realistically, especially in "catching" or bracing motions when significantly unstable or falling, we need to add another layer to the arm control.

Instead of only using AngularVelocity for subtle balancing swings, we'll introduce AlignOrientation constraints for the arms as well. These will activate during high instability to drive the arms towards specific "outstretched" poses.

Key Changes:

  1. Dual Arm Control: We'll now have both AngularVelocity (for balancing swings) and AlignOrientation (for catching/bracing poses) constraints for each upper arm.
  2. Instability Detection: A new function isHighlyUnstable() will determine if the character is falling rapidly or leaning excessively, triggering the catching behavior.
  3. Catching Pose Logic: When isHighlyUnstable() is true:
    • The arm AlignOrientation constraints will be enabled and given high torque.
    • Their target CFrame will be calculated to position the arms outwards and slightly forwards/downwards relative to the torso.
    • The arm AngularVelocity constraints will be disabled or given zero torque.
  4. Normal Balancing Logic: When not highly unstable:
    • The arm AlignOrientation constraints will be disabled or given zero torque.
    • The arm AngularVelocity constraints will be enabled for the subtle balancing swings, as calculated previously.
  5. Parameter Tuning: Added new constants for catching thresholds, arm angles, and constraint strengths.

Now, when the character falls quickly (rootPart.AssemblyLinearVelocity.Y is very negative) or its predicted lean (predictedLeanMagnitude) becomes large, the AlignOrientation constraints on the arms will take over, driving them to the outstretched "catching" pose defined by ARM_CATCH_ANGLE_OUTWARD and ARM_CATCH_ANGLE_FORWARD. In less extreme situations, the AngularVelocity constraints will handle the subtle balancing swings.*

Updated files
v3

Alright, let's push the complexity further. We can enhance the realism and responsiveness by incorporating reactions to the environment and adding more nuanced control layers.

Advanced Features Added in V5:

  1. Uneven Terrain Adaptation:
    • The script now detects the ground normal (slope) under each foot.
    • Foot planting targets are adjusted to align slightly with the ground slope.
    • The torso's target upright orientation is subtly tilted based on the average slope under the feet, making the character lean into slopes naturally.
    • Step targets also consider the ground slope at the landing location.
  2. Look Direction Influence:
    • The torso now subtly orients itself towards the Humanoid.MoveDirection when the character is moving, adding a sense of intention to the balancing. When idle, it faces the HumanoidRootPart's direction.
  3. Stumble Detection (Basic):
    • During a step, if the stepping foot gets too close to the other foot (potential collision/trip), it triggers a stumble state (isStumbling = true), forcing a faster recovery cooldown. (A more robust version would use raycasts or .Touched, but this adds complexity).
  4. Dynamic Step Duration:
    • The duration of a step (STEP_DURATION) can now be slightly adjusted based on the required step length – longer steps take marginally longer.
  5. Refined Catching Trigger:
    • The trigger for the arm "catching" pose now also considers the rate of change of lean (how fast the character is becoming unstable) in addition to absolute lean and fall velocity.

This version introduces more environmental awareness (slopes) and internal state awareness (rate of change of lean, foot proximity). The character should now feel more grounded on slopes, look more intentionally where it's going, and have slightly more nuanced triggers for its recovery actions (stepping and arm bracing).

Further advancements could involve more complex environmental interactions (wall bracing, obstacle avoidance/tripping using raycasts), reactions to external forces, or even more sophisticated gait planning beyond single predictive steps._

Updated files
v4