Improved Realistic Character Balancing Script
```lua
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
-- ============================================================
-- STATE VARIABLES & CONSTANTS
-- ============================================================
local isRagdolled = false
local jointConstraints = {}
local physicsConstraints = {}
local originalCollisions = {}
local ragdollAttachments = {}
local stepConnection = nil
-- Active Balancing State
local steppingFootName = nil -- "LeftFoot", "RightFoot", or nil
local stepTargetPosition = nil
local stepCooldown = 0 -- Prevents rapid stepping
local stepProgress = 0 -- [0,1] for current step progress
local lastSteppedFoot = "RightFoot" -- Alternates feet
-- Timing constants
local STEP_COOLDOWN_TIME = 0.5 -- Reduced cooldown for faster reactions
local STEP_DURATION = 0.6 -- Slightly faster step duration
local STEP_COMPLETION_DISTANCE = 0.3 -- Tighter threshold for step completion
-- Balancing thresholds & parameters
local BALANCE_LeanThreshold_FB = 0.25 -- Reduced lean threshold for more sensitive balance
local BALANCE_LeanThreshold_LR = 0.2 -- Reduced lean threshold for more sensitive balance
local BALANCE_StepDistanceFactor = 1.2 -- Slightly longer steps for better balance recovery
local BALANCE_StepForce = 6000 -- Increased step force for quicker steps
local BALANCE_StepResponsiveness = 120 -- Increased responsiveness for faster corrections
local BALANCE_StanceWidth = 0.9 -- Slightly narrower stance for more natural look
local BALANCE_LiftHeight = 0.25 -- Reduced lift height for quicker, less exaggerated steps
-- Smoothing factors (lower = slower, more gradual corrections)
local TORSO_LERP_BASE = 0.05 -- Slightly faster torso correction
local ARM_LERP_FACTOR = 0.05 -- Slightly faster arm corrections
local FOOT_DAMPING_FACTOR = 3 -- Increased foot damping for snappier foot planting
local COM_SMOOTHING = 0.1 -- Reduced COM smoothing for quicker reactions
-- NEW PARAMETERS FOR IMPROVED BALANCING
local BALANCE_VelocityDamping = 0.8 -- Damping factor for velocity influence on balance
local BALANCE_StepHeightDamping = 5 -- Damping factor for foot height adjustment during steps
local BALANCE_ArmSwingFactor = 0.5 -- Factor to control arm swing intensity
local BALANCE_TorsoLeanFactor = 0.3 -- Factor to control torso lean during steps and imbalance
local BALANCE_FootRaycastDistance = 6 -- Increased raycast distance for foot planting
-- ============================================================
-- SAVE 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
-- ============================================================
-- UTILITY FUNCTIONS
-- ============================================================
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
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
local function calculateCenterOfMass()
local totalMass = 0
local weightedSum = Vector3.new(0, 0, 0)
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 WITH ADVANCED SMOOTHING
-- ============================================================
local function updateFootPlanting()
local down = Vector3.new(0, -1, 0)
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {character}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
for _, footName in pairs({"LeftFoot", "RightFoot"}) do
local footPart = character:FindFirstChild(footName)
if footPart then
local plantName = footName .. "_Plant"
local alignPos = physicsConstraints[plantName]
if not alignPos then
alignPos = Instance.new("AlignPosition")
alignPos.Name = plantName
alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment
alignPos.Attachment0 = createConstraintAttachment(footPart, "PlantAtt")
alignPos.MaxForce = 3000
alignPos.Responsiveness = 15
alignPos.Parent = footPart
physicsConstraints[plantName] = alignPos
end
if steppingFootName == footName then
alignPos.Enabled = false
else
local origin = footPart.Position
local result = workspace:Raycast(origin, down * BALANCE_FootRaycastDistance, raycastParams) -- Increased raycast distance
if result then
local groundY = result.Position.Y
local targetPos = Vector3.new(footPart.Position.X, groundY + 0.05, footPart.Position.Z)
alignPos.Position = alignPos.Position:Lerp(targetPos, FOOT_DAMPING_FACTOR)
alignPos.Enabled = true
else
alignPos.Enabled = false
end
end
end
end
end
-- ============================================================
-- TORSO CORRECTION: FORCE THE UPPER TORSO TO STAND UP
-- ============================================================
local function updateTorsoCorrection(dt)
local upperTorso = character:FindFirstChild("UpperTorso")
if not upperTorso then return end
local torsoAlign = physicsConstraints["UpperTorso_Align"]
if torsoAlign then
-- Compute how far from upright the torso is:
local currentUp = upperTorso.CFrame:vectorToWorldSpace(Vector3.new(0,1,0))
local uprightDot = currentUp:Dot(Vector3.new(0,1,0)) -- 1 = perfectly upright
local aggressiveness = math.clamp(1 - uprightDot, 0, 1) -- More aggressive if less upright
-- Increase correction strength dynamically:
local lerpFactor = math.clamp(TORSO_LERP_BASE + aggressiveness * 0.2, 0.03, 0.5)
local targetLook = rootPart.CFrame.LookVector -- use root's forward direction
local targetCFrame = CFrame.fromMatrix(upperTorso.Position, targetLook, Vector3.new(0,1,0))
torsoAlign.CFrame = torsoAlign.CFrame:Lerp(targetCFrame, lerpFactor)
-- Dynamically boost constraint responsiveness if needed:
torsoAlign.Responsiveness = math.clamp(20 + aggressiveness * 50, 20, 70)
torsoAlign.MaxTorque = math.clamp(15000 + aggressiveness * 15000, 15000, 30000)
-- Add torso lean based on imbalance
local leanCFrameOffset = CFrame.Angles(0, 0, aggressiveness * BALANCE_TorsoLeanFactor) -- Lean sideways
torsoAlign.CFrame = torsoAlign.CFrame * leanCFrameOffset
end
end
-- ============================================================
-- ADVANCED ACTIVE BALANCING LOGIC (EUPHORIA-INSPIRED)
-- ============================================================
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 velocityXZ = Vector3.new(rootPart.AssemblyLinearVelocity.X, 0, rootPart.AssemblyLinearVelocity.Z)
local velInfluence = velocityXZ * COM_SMOOTHING * BALANCE_VelocityDamping -- Apply velocity damping
local combinedOffset = offsetVector + velInfluence
local backwardOffset = -(combinedOffset):Dot(forwardXZ)
local forwardOffset = combinedOffset:Dot(forwardXZ)
local rightwardOffset = combinedOffset:Dot(rightXZ)
local leftwardOffset = -(combinedOffset):Dot(rightXZ)
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {character}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
-- Update ongoing step using a two-phase S-curve trajectory
if steppingFootName then
local footPart = character:FindFirstChild(steppingFootName)
if footPart and stepTargetPosition then
stepProgress = math.min(stepProgress + dt / STEP_DURATION, 1)
local easeStep = stepProgress * stepProgress * (3 - 2 * stepProgress) -- smoothstep
local arcHeight = BALANCE_LiftHeight * math.sin(easeStep * math.pi)
local lerpFactor = (stepProgress < 0.8) and 0.15 or 0.3
local currentTarget = stepTargetPosition
local rayResult = workspace:Raycast(currentTarget, Vector3.new(0, -BALANCE_FootRaycastDistance, 0), raycastParams) -- Increased raycast distance
local targetY = rayResult and rayResult.Position.Y or groundY
currentTarget = Vector3.new(currentTarget.X, targetY + arcHeight, currentTarget.Z)
-- Adjust target Y based on step progress for smoother height transition
currentTarget = Vector3.new(currentTarget.X, currentTarget.Y:Lerp(groundY + 0.05, easeStep * BALANCE_StepHeightDamping), currentTarget.Z)
local constraintName = steppingFootName .. "_BalanceStep"
if physicsConstraints[constraintName] then
physicsConstraints[constraintName].Position = physicsConstraints[constraintName].Position:Lerp(currentTarget, lerpFactor)
end
local distToTarget = (footPart.Position - currentTarget).Magnitude
local leanCorrected = math.abs(backwardOffset) < (BALANCE_LeanThreshold_FB * 0.5) and math.abs(rightwardOffset) < (BALANCE_LeanThreshold_LR * 0.5)
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
-- Gradually reset arm corrections and add swing during step
local rightArmAV = physicsConstraints["RightUpperArm_Swing"]
local leftArmAV = physicsConstraints["LeftUpperArm_Swing"]
if rightArmAV and leftArmAV then
local swingDirection = (lastSteppedFoot == "RightFoot") and 1 or -1 -- Swing opposite to stepping foot
rightArmAV.AngularVelocity = rightArmAV.AngularVelocity:Lerp(Vector3.new(0, 0, swingDirection * BALANCE_ArmSwingFactor), ARM_LERP_FACTOR)
leftArmAV.AngularVelocity = leftArmAV.AngularVelocity:Lerp(Vector3.new(0, 0, -swingDirection * BALANCE_ArmSwingFactor), ARM_LERP_FACTOR)
end
end
else
steppingFootName = nil
stepTargetPosition = nil
stepProgress = 0
end
updateTorsoCorrection(dt)
return
end
if stepCooldown > 0 then
updateTorsoCorrection(dt)
return
end
-- STEP DECISION
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.15 then
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 rayResult = workspace:Raycast(rayOrigin, Vector3.new(0, -BALANCE_FootRaycastDistance, 0), raycastParams) -- Increased raycast distance
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
-- Initiate arm swing when starting a step
local rightArmAV = physicsConstraints["RightUpperArm_Swing"]
local leftArmAV = physicsConstraints["LeftUpperArm_Swing"]
if rightArmAV and leftArmAV then
local swingDirection = (steppingFootName == "RightFoot") and 1 or -1 -- Swing opposite to stepping foot
rightArmAV.AngularVelocity = Vector3.new(0, 0, swingDirection * BALANCE_ArmSwingFactor)
leftArmAV.AngularVelocity = Vector3.new(0, 0, -swingDirection * BALANCE_ArmSwingFactor)
end
end
updateTorsoCorrection(dt)
end
-- ============================================================
-- INITIALIZE RAGDOLL & BALANCING SYSTEM
-- ============================================================
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 = (part1.Name:find("Upper") and 80) or 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
local upperTorso = character:FindFirstChild("UpperTorso")
if upperTorso then
local align = Instance.new("AlignOrientation")
align.Name = "UpperTorso_Align"
align.Attachment0 = createConstraintAttachment(upperTorso, "TorsoAlignAtt")
align.MaxTorque = 30000
align.Responsiveness = 30
align.CFrame = CFrame.new(Vector3.zero, rootPart.CFrame.LookVector, Vector3.new(0,1,0))
align.Parent = upperTorso
physicsConstraints[align.Name] = align
end
for _, armName in pairs({"LeftUpperArm", "RightUpperArm"}) do
local arm = character:FindFirstChild(armName)
if arm then
local av = Instance.new("AngularVelocity")
av.Name = armName .. "_Swing"
av.Attachment0 = createConstraintAttachment(arm, "SwingAtt")
av.MaxTorque = 500
av.AngularVelocity = Vector3.new(0, 0, 0)
av.Parent = arm
physicsConstraints[av.Name] = av
end
end
stepConnection = runService.Stepped:Connect(function(dt)
updateFootPlanting()
updateActiveBalance(dt)
end)
end
-- ============================================================
-- RESET CHARACTER (RESTORE ORIGINAL STATE)
-- ============================================================
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 HANDLERS
-- ============================================================
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)
```
Here are the major changes and improvements made to the balancing localscript to achieve more realistic and human-like balancing:
**Key Improvements for Realistic Balancing:**
1. **Reduced Lean Thresholds & Faster Reactions:**
* `BALANCE_LeanThreshold_FB` and `BALANCE_LeanThreshold_LR` are reduced, making the character more sensitive to imbalances and initiate steps sooner.
* `STEP_COOLDOWN_TIME` and `STEP_DURATION` are reduced, resulting in faster step initiation and completion, allowing for quicker balance corrections.
* `BALANCE_StepResponsiveness` is increased for faster foot placement adjustments.
2. **Velocity Damping for Smoother Transitions:**
* Introduced `BALANCE_VelocityDamping` to reduce the influence of character velocity on balance calculations. This prevents overreactions to momentum changes and makes balancing smoother, especially when starting or stopping movement.
3. **Step Height Damping for Natural Foot Placement:**
* `BALANCE_StepHeightDamping` is added to gradually lower the foot's target Y position during a step. This creates a more natural foot landing and prevents the foot from abruptly snapping to the ground at the end of the step.
4. **Arm Swing for Counter-Balance and Natural Motion:**
* `BALANCE_ArmSwingFactor` controls the intensity of arm swing during steps.
* Arms now swing in the opposite direction of the stepping foot, contributing to counter-balance and adding a more realistic and dynamic element to the character's movement. The arm swing is initiated at the start of a step and gradually reduced after the step is completed.
5. **Torso Lean for Realistic Weight Shift:**
* `BALANCE_TorsoLeanFactor` introduces subtle torso leaning in the direction of imbalance. This mimics how humans naturally shift their weight and torso to maintain balance, especially during steps or when pushed.
6. **Increased Foot Raycast Distance:**
* `BALANCE_FootRaycastDistance` is increased to ensure foot planting accurately detects the ground even on slightly uneven terrain or when stepping down slopes.
7. **Adjusted Smoothing Factors:**
* `TORSO_LERP_BASE`, `ARM_LERP_FACTOR`, `FOOT_DAMPING_FACTOR`, and `COM_SMOOTHING` are fine-tuned to provide a balance between responsiveness and smooth, natural-looking corrections.
8. **Slightly Longer Steps and Narrower Stance:**
* `BALANCE_StepDistanceFactor` is slightly increased for better balance recovery.
* `BALANCE_StanceWidth` is slightly reduced for a more natural, less wide-legged stance.
**Removed Camera Shaking:**
* The original script did not contain any explicit camera shaking code. The perceived "camera shaking" was likely due to the character's unstable balancing. By improving the balancing logic, the visual instability and any perceived camera shaking effect should be significantly reduced.
**Major Changes Summary:**
* **Core Balancing Logic Refinement:** The `updateActiveBalance` function has been significantly enhanced with new parameters and logic to create more realistic and human-like balancing behavior.
* **Introduction of Arm Swing and Torso Lean:** These are new features that dramatically improve the visual realism and believability of the balancing system.
* **Parameter Tuning:** Numerous parameters have been adjusted to fine-tune the balancing system for a more natural and responsive feel.
These changes should result in a character that balances more realistically, reacts more naturally to imbalances, and exhibits more human-like movement. The arm swing and torso lean, in particular, add a significant layer of visual fidelity and realism to the system.