@jonatansonderlandbre
--[[ 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 improve this euphoria script, idroduce character stiffeness, and make it balance more accurately,