Sure, here is a title for the code you sent:
Enhanced Realistic Character Physics and Balancing
```lua
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
local upperTorso = character:FindFirstChild("UpperTorso")
local lowerTorso = character:FindFirstChild("LowerTorso")
local rightHand = character:FindFirstChild("RightHand")
local leftHand = character:FindFirstChild("LeftHand")
local head = character:FindFirstChild("Head")
local userInputService = game:GetService("UserInputService")
local runService = game:GetService("RunService")
local physicsService = game:GetService("PhysicsService")
humanoid.BreakJointsOnDeath = false
humanoid.RequiresNeck = false
-- Collision Group Constants
local LEFT_LEG_GROUP = "RagdollLeftLeg"
local RIGHT_LEG_GROUP = "RagdollRightLeg"
local DEFAULT_COLLISION_GROUP_NAME = "Default"
-- Configurable Stiffness (Reduced for Realism)
local LEG_STIFFNESS_FACTOR = 0.45 -- More flex, less robotic
local ARM_STIFFNESS_FACTOR = 3.0 -- Slightly more relaxed arms
-- State Variables & Constants
local isRagdolled = false
local jointConstraints = {}
local physicsConstraints = {}
local originalCollisions = {}
local ragdollAttachments = {}
local stepConnection = nil
-- Enhanced Active Balancing State
local steppingFootName = nil
local stepTargetPosition = nil
local stepStartPosition = nil
local stepDistance = 0
local stepCooldown = 0
local stepProgress = 0
local lastSteppedFoot = "RightFoot"
local isStumbling = false
local stumbleSeverity = 0
local currentStepDuration = 0.1
local currentBalanceDirectionXZ = nil
local lastLeanForward = 0
local lastLeanRight = 0
local lastGroundNormal = Vector3.yAxis
local lastLeanVelocity = Vector2.zero
local needsRecoveryStep = false
local predictiveStepTrigger = false -- New: Predictive stepping
local balanceRecoveryMomentum = Vector3.zero -- New: Momentum for recovery
local torsoSwayPhase = 0 -- New: For natural torso oscillation
local baseSwayAmplitude = 0.015 -- Base torso sway even when balanced
-- Shot Reaction State
local isShotReacting = false
local shotReactTimer = 0
local SHOT_REACTION_DURATION = 0.55 -- Slightly faster reaction
local SHOT_IMPULSE_MAGNITUDE = 70 -- Increased impulse
-- Dynamic Parameter Ranges (Realism Focused)
local DYNAMIC_STEP_DURATION_MIN = 0.22 -- Slightly slower min
local DYNAMIC_STEP_DURATION_MAX = 0.60 -- Slightly slower max
local DYNAMIC_STEP_COOLDOWN_MIN = 0.12 -- Slightly slower recovery
local DYNAMIC_STEP_COOLDOWN_MAX = 0.40 -- Slightly slower max cooldown
local DYNAMIC_STANCE_WIDTH_MIN = 0.75 -- Slightly narrower min stance
local DYNAMIC_STANCE_WIDTH_MAX = 1.70 -- Slightly narrower max stance
local RECOVERY_STEP_COOLDOWN_MULTIPLIER = 0.40 -- Slightly slower recovery steps
-- Timing & Step Constants (Realistic Gait)
local STEP_COMPLETION_DISTANCE = 0.09 -- Tighter for precision
local STEP_MAX_REACH = 2.7 -- Slightly reduced max reach
local GAIT_ANGLE_RAD = math.rad(2.5) -- Slightly narrower gait
local STEP_OBSTACLE_CLEARANCE_HEIGHT = 0.18 -- Slightly lower clearance
local STEP_OBSTACLE_CHECK_DISTANCE = 0.30 -- Slightly shorter check
local GROUND_RAYCAST_DISTANCE = 12 -- Shorter raycast
-- Balancing Thresholds & Prediction (More Responsive)
local BALANCE_LeanThreshold_FB = 0.13 -- More sensitive forward/back
local BALANCE_LeanThreshold_LR = 0.07 -- More sensitive lateral
local BALANCE_VelocityPredictionFactor = 0.40 -- Stronger velocity influence
local BALANCE_StepDistanceFactor = 1.30 -- Longer steps for recovery
local STUMBLE_THRESHOLD = 0.40 -- Earlier stumble detection
local RECOVERY_STEP_STUMBLE_THRESHOLD = 0.16 -- Earlier recovery steps
local DOMINANT_LEAN_RATIO_THRESHOLD = 1.4 -- More dynamic lean priority
local MAX_DIRECTION_CHANGE_ANGLE_RAD = math.rad(80) -- Slightly tighter direction changes
-- Force & Responsiveness (Realistic Forces)
local BALANCE_StepForce = 12000 -- Slightly reduced step force
local BALANCE_StepResponsiveness = 85 -- Slightly less responsive stepping
-- Stance & Lift Parameters (Natural Stance)
local BALANCE_LiftHeight_Base = 0.11 -- Slightly lower base lift
local BALANCE_LiftHeight_ScalePerStud = 0.055
local BALANCE_LiftHeight_MaxCap = 0.55 -- Slightly lower cap
local STUMBLE_LIFT_MULTIPLIER = 3.2 -- Less exaggerated stumble lift
-- Smoothing, Damping, and Stiffness (Smoother Motion)
local TORSO_LERP_BASE = 0.07 -- Smoother base torso movement
local TORSO_LERP_AGGRESSION = 0.28 -- Slightly less aggressive correction
local TORSO_RESPONSIVENESS_LEAN_VEL_FACTOR = 110.0 -- Slightly less reactive to velocity
local ARM_LERP_FACTOR = 0.11 -- Slightly slower arm response
local ARM_SWING_INTENSITY = 3.8 -- Slightly weaker arm swing
local STUMBLE_ARM_SWING_MULTIPLIER = 1.2 -- Less dramatic stumble arms
local ARM_REACH_STUMBLE_FACTOR = 0.18 -- Shorter arm reach on stumble
local MAX_ARM_ANGULAR_VELOCITY = 28 -- Slower arm motion
local FOOT_PLANT_RESPONSIVENESS = 30 -- Slightly less snappy foot planting
local FOOT_PLANT_MAX_FORCE = 23000 -- Slightly weaker foot force
local FOOT_ORIENT_RESPONSIVENESS = 45
local FOOT_ORIENT_MAX_TORQUE = 11000
local COM_SMOOTHING = 0.88 -- More smoothing for responsiveness
local TORSO_SWAY_AMPLITUDE_BASE = 0.020 + baseSwayAmplitude -- Less pronounced sway, add base sway
local TORSO_SWAY_LEAN_SCALE = 0.22
local TORSO_SWAY_FREQUENCY = 1.7 -- Slower sway cycle
local WEIGHT_SHIFT_LEAN_FACTOR = 0.38 -- Slightly weaker weight shift
local ANTICIPATORY_ROLL_ANGLE = math.rad(5) -- Less anticipatory roll
local TORSO_COUNTER_ROTATION_FACTOR = math.rad(3.2)
local HRP_STABILIZE_TORQUE = 21000 -- Slightly weaker root stabilization
local HRP_STABILIZE_RESPONSIVENESS = 110
-- Reactive Tilt & Hip Correction (Subtle Tilt)
local TILT_FACTOR = 0.16 -- Less pronounced tilt
local MAX_TILT = math.rad(11) -- Narrower tilt range
local HIP_CORRECTION_FACTOR = 0.16
local HIP_ROTATION_FACTOR = math.rad(5.5)
-- Backward Lean Behavior (Less Extreme)
local BACKWARD_LEAN_THRESHOLD = -0.07 -- Later backward step trigger
local BACKWARD_STEP_MULTIPLIER = 0.60
local BACKWARD_TORSO_TILT = math.rad(-2.5)
-- New: Arm Behavior Thresholds & Params (More Natural Arms)
local FORWARD_ARM_LEAN_THRESHOLD = BALANCE_LeanThreshold_FB * 0.75
local BACKWARD_ARM_LEAN_THRESHOLD = BACKWARD_LEAN_THRESHOLD * 1.10
local FORWARD_ARM_REACH_INTENSITY = 6.5 -- Less aggressive forward reach
local BACKWARD_ARM_TPOSE_INTENSITY = 5.5
local BACKWARD_ARM_WAVE_FREQ = 3.8 -- Slower wave
local BACKWARD_ARM_WAVE_AMP = 4.2
-- Anti-Gravity Force (Slightly Weaker)
local ANTI_GRAVITY_MULTIPLIER = 1.015 -- Slightly weaker local gravity compensation
local FOOT_GROUND_OFFSET = 0.005
-- Active Bending Parameters (Natural Bending)
local KNEE_BEND_MIN_ANGLE_RAD = math.rad(4)
local KNEE_BEND_MAX_ANGLE_RAD = math.rad(75) -- Less knee flex
local KNEE_BEND_STUMBLE_FACTOR = 1.08
local KNEE_BEND_RESPONSIVENESS = 50
local KNEE_BEND_MAX_TORQUE = 4200
local STANCE_LEG_STIFFNESS_MULTIPLIER = 1.4
local HIP_FLEX_MIN_ANGLE_RAD = math.rad(-7)
local HIP_FLEX_MAX_ANGLE_RAD = math.rad(65) -- Less hip flex
local HIP_FLEX_LEAN_FACTOR = 0.90
local HIP_BEND_RESPONSIVENESS = 50
local HIP_BEND_MAX_TORQUE = 9500
-- Easing Functions
local function easeOutQuad(t)
return t * (2 - t)
end
local function easeInOutQuad(t)
return t < 0.5 and 2 * t * t or -1 + (4 - 2 * t) * t
end
local function easeOutExpo(t)
return t == 1 and 1 or 1 - math.pow(2, -10 * t)
end
local function easeOutCubic(t)
t = t - 1
return t * t * t + 1
end
-- Collision Group Setup
local function setupCollisionGroups()
pcall(function()
physicsService:RegisterCollisionGroup(LEFT_LEG_GROUP)
end)
pcall(function()
physicsService:RegisterCollisionGroup(RIGHT_LEG_GROUP)
end)
pcall(function()
physicsService:CollisionGroupSetCollidable(LEFT_LEG_GROUP, RIGHT_LEG_GROUP, false)
end)
pcall(function()
physicsService:CollisionGroupSetCollidable(LEFT_LEG_GROUP, DEFAULT_COLLISION_GROUP_NAME, true)
physicsService:CollisionGroupSetCollidable(RIGHT_LEG_GROUP, DEFAULT_COLLISION_GROUP_NAME, true)
end)
end
setupCollisionGroups()
-- 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
end
end
end
saveOriginalCollisions()
-- Utility Functions
local function clearRagdollPhysics()
if stepConnection then
stepConnection:Disconnect()
stepConnection = nil
end
for name, 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
end
for _, att in pairs(ragdollAttachments) do
if att and att.Parent then
att:Destroy()
end
end
for _, data in pairs(jointConstraints) do
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 = {}
isShotReacting = false
shotReactTimer = 0
steppingFootName = nil
stepTargetPosition = nil
stepStartPosition = nil
stepDistance = 0
stepCooldown = 0
stepProgress = 0
isStumbling = false
stumbleSeverity = 0
currentStepDuration = DYNAMIC_STEP_DURATION_MAX
currentBalanceDirectionXZ = nil
lastLeanForward = 0
lastLeanRight = 0
lastGroundNormal = Vector3.yAxis
lastLeanVelocity = Vector2.zero
needsRecoveryStep = false
predictiveStepTrigger = false
balanceRecoveryMomentum = Vector3.zero
torsoSwayPhase = 0
end
local function createConstraintAttachment(parentPart, nameSuffix, cframeOverride)
local att = Instance.new("Attachment")
att.Name = "RagdollAttachment_" .. (nameSuffix or parentPart.Name)
att.Parent = parentPart
if cframeOverride then
att.CFrame = cframeOverride
elseif nameSuffix == "KneeBendLower" then
att.Position = Vector3.new(0, parentPart.Size.Y / 2, 0)
elseif nameSuffix == "KneeBendUpper" then
att.Position = Vector3.new(0, -parentPart.Size.Y / 2, 0)
elseif nameSuffix == "HipBendUpper" then
att.Position = Vector3.new(0, parentPart.Size.Y / 2, 0)
elseif nameSuffix == "HipBendLower" then
att.Position = Vector3.new(0, -parentPart.Size.Y / 2, 0)
elseif nameSuffix == "FootOrient" or nameSuffix == "FootPlant" then
att.Position = Vector3.new(0, -parentPart.Size.Y / 2, 0)
elseif parentPart.Name == "RightHand" and nameSuffix == "ShotHandAtt" then
att.Position = Vector3.new(0, 0, 0)
elseif parentPart.Name == "UpperTorso" and nameSuffix == "ShotTargetAtt" then
att.Position = Vector3.new(0, 0.1, -0.3)
elseif parentPart.Name == "HumanoidRootPart" and nameSuffix == "HRPStabilizeAtt" then
att.Position = Vector3.zero
else
att.Position = Vector3.new(0, 0, 0)
end
table.insert(ragdollAttachments, att)
return att
end
local lastCOM = rootPart.Position
local function calculateSmoothedCenterOfMass()
local totalMass = 0
local weightedSum = Vector3.new(0, 0, 0)
local partsToConsider = {
head,
upperTorso,
lowerTorso,
rootPart,
character:FindFirstChild("LeftUpperArm"),
character:FindFirstChild("RightUpperArm"),
character:FindFirstChild("LeftUpperLeg"),
character:FindFirstChild("RightUpperLeg")
}
for _, part in pairs(partsToConsider) do
if part and part:IsA("BasePart") and part.Parent and originalCollisions[part] and not originalCollisions[part].Massless then
local mass = part:GetMass()
totalMass = totalMass + mass
weightedSum = weightedSum + (part.Position * mass)
end
end
local currentCOM = (totalMass > 0) and (weightedSum / totalMass) or rootPart.Position
lastCOM = lastCOM:Lerp(currentCOM, 1.0 - COM_SMOOTHING)
return lastCOM
end
local groundRaycastParams = RaycastParams.new()
groundRaycastParams.FilterType = Enum.RaycastFilterType.Exclude
groundRaycastParams.FilterDescendantsInstances = {character}
local function findGroundInfo(position)
local origin = position + Vector3.new(0, 2.5, 0)
local result = workspace:Raycast(origin, Vector3.new(0, -GROUND_RAYCAST_DISTANCE, 0), groundRaycastParams)
if result then
lastGroundNormal = result.Normal
return result.Position.Y, result.Normal
else
return nil, lastGroundNormal
end
end
-- Foot Planting & Orientation
local function updateFootPlantingAndOrientation()
for _, footName in pairs({"LeftFoot", "RightFoot"}) do
local footPart = character:FindFirstChild(footName)
if footPart and footPart.Parent then
local isStepping = (steppingFootName == footName)
local groundY, groundNormal = findGroundInfo(footPart.Position)
local plantPosConstraintName = footName .. "_PlantPos"
local alignPos = physicsConstraints[plantPosConstraintName]
if not alignPos then
alignPos = Instance.new("AlignPosition")
alignPos.Name = plantPosConstraintName
alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment
local plantAtt = createConstraintAttachment(footPart, "FootPlant")
alignPos.Attachment0 = plantAtt
alignPos.MaxForce = FOOT_PLANT_MAX_FORCE
alignPos.Responsiveness = FOOT_PLANT_RESPONSIVENESS
alignPos.ApplyAtCenterOfMass = false
alignPos.Parent = footPart
physicsConstraints[plantPosConstraintName] = alignPos
end
if isStepping then
alignPos.Enabled = false
else
if groundY then
local targetPos = Vector3.new(footPart.Position.X, groundY + FOOT_GROUND_OFFSET, footPart.Position.Z)
alignPos.Position = alignPos.Position:Lerp(targetPos, 0.92) -- Smoother planting
alignPos.Enabled = true
else
alignPos.Enabled = false
end
end
local plantOrientConstraintName = footName .. "_PlantOrient"
local alignOrient = physicsConstraints[plantOrientConstraintName]
if not alignOrient then
alignOrient = Instance.new("AlignOrientation")
alignOrient.Name = plantOrientConstraintName
alignOrient.Mode = Enum.OrientationAlignmentMode.OneAttachment
local orientAtt = createConstraintAttachment(footPart, "FootOrient")
alignOrient.Attachment0 = orientAtt
alignOrient.MaxTorque = FOOT_ORIENT_MAX_TORQUE
alignOrient.Responsiveness = FOOT_ORIENT_RESPONSIVENESS
alignOrient.Parent = footPart
physicsConstraints[plantOrientConstraintName] = alignOrient
end
if isStepping then
alignOrient.Enabled = false
else
if groundY and groundNormal then
local footUp = groundNormal
local footLook = (footPart.CFrame.LookVector - footUp * footPart.CFrame.LookVector:Dot(footUp)).Unit
if footLook.Magnitude < 0.1 then
footLook = footPart.CFrame.LookVector
end
local footRight = footLook:Cross(footUp).Unit
if footRight.Magnitude < 0.1 then
footRight = footPart.CFrame.RightVector
end
footLook = footUp:Cross(footRight).Unit
local targetCF = CFrame.fromMatrix(footPart.Position, footRight, footUp, -footLook)
alignOrient.CFrame = alignOrient.CFrame:Lerp(targetCF, 0.80) -- Smoother orientation
alignOrient.Enabled = true
else
alignOrient.Enabled = false
end
end
end
end
end
-- Active Knee & Hip Bending
local function updateActiveBending(dt, leanForward, leanRight)
for _, side in pairs({"Left", "Right"}) do
local lowerLeg = character:FindFirstChild(side .. "LowerLeg")
local upperLeg = character:FindFirstChild(side .. "UpperLeg")
local foot = character:FindFirstChild(side .. "Foot")
local torso = lowerTorso
if not lowerLeg or not upperLeg or not foot or not torso then
continue
end
local isStanceLeg = (steppingFootName ~= (side .. "Foot"))
local kneeConstraintName = side .. "KneeBend"
local hipConstraintName = side .. "HipBend"
local kneeAlign = physicsConstraints[kneeConstraintName]
local hipAlign = physicsConstraints[hipConstraintName]
if not kneeAlign then
kneeAlign = Instance.new("AlignOrientation")
kneeAlign.Name = kneeConstraintName
kneeAlign.Mode = Enum.OrientationAlignmentMode.TwoAttachment
kneeAlign.Attachment0 = createConstraintAttachment(lowerLeg, "KneeBendLower")
kneeAlign.Attachment1 = createConstraintAttachment(upperLeg, "KneeBendUpper")
kneeAlign.MaxTorque = KNEE_BEND_MAX_TORQUE
kneeAlign.Responsiveness = KNEE_BEND_RESPONSIVENESS
kneeAlign.Parent = lowerLeg
physicsConstraints[kneeConstraintName] = kneeAlign
end
if not hipAlign then
hipAlign = Instance.new("AlignOrientation")
hipAlign.Name = hipConstraintName
hipAlign.Mode = Enum.OrientationAlignmentMode.TwoAttachment
hipAlign.Attachment0 = createConstraintAttachment(upperLeg, "HipBendUpper")
hipAlign.Attachment1 = createConstraintAttachment(torso, "HipBendLower")
hipAlign.MaxTorque = HIP_BEND_MAX_TORQUE
hipAlign.Responsiveness = HIP_BEND_RESPONSIVENESS
hipAlign.Parent = upperLeg
physicsConstraints[hipConstraintName] = hipAlign
end
if isShotReacting then
kneeAlign.Enabled = false
hipAlign.Enabled = false
continue
end
local easedStumble = easeOutCubic(stumbleSeverity)
local targetKneeBend = KNEE_BEND_MIN_ANGLE_RAD
local currentKneeResponsiveness = KNEE_BEND_RESPONSIVENESS
local currentKneeTorque = KNEE_BEND_MAX_TORQUE
if isStanceLeg then
targetKneeBend = math.lerp(targetKneeBend, KNEE_BEND_MAX_ANGLE_RAD, easedStumble * KNEE_BEND_STUMBLE_FACTOR)
local stanceStiffnessFactor = math.lerp(1.0, STANCE_LEG_STIFFNESS_MULTIPLIER, easedStumble)
currentKneeResponsiveness = math.lerp(KNEE_BEND_RESPONSIVENESS, KNEE_BEND_RESPONSIVENESS * 1.6, easedStumble)
currentKneeTorque = KNEE_BEND_MAX_TORQUE * stanceStiffnessFactor
else
local stepBendFactor = math.sin(stepProgress * math.pi) * 1.2 -- More pronounced knee lift
targetKneeBend = math.max(targetKneeBend, math.rad(50) * stepBendFactor)
end
local kneeTargetCFrame = CFrame.Angles(targetKneeBend, 0, 0)
kneeAlign.CFrame = kneeTargetCFrame
kneeAlign.Responsiveness = currentKneeResponsiveness
kneeAlign.MaxTorque = currentKneeTorque
kneeAlign.Enabled = true
local targetHipFlex = 0
local leanFactor = math.clamp(leanForward / (BALANCE_LeanThreshold_FB * 2.0), -1, 1)
targetHipFlex = math.lerp(HIP_FLEX_MIN_ANGLE_RAD, HIP_FLEX_MAX_ANGLE_RAD, (leanFactor * 0.5 + 0.5) * HIP_FLEX_LEAN_FACTOR)
local currentHipResponsiveness = HIP_BEND_RESPONSIVENESS
local currentHipTorque = HIP_BEND_MAX_TORQUE
if isStanceLeg then
local leanMagnitudeFactor = math.clamp(math.abs(leanForward) / BALANCE_LeanThreshold_FB, 0, 1)
local stanceStiffnessFactor = math.lerp(1.0, STANCE_LEG_STIFFNESS_MULTIPLIER, easedStumble)
currentHipResponsiveness = math.lerp(HIP_BEND_RESPONSIVENESS, HIP_BEND_RESPONSIVENESS * 1.6, leanMagnitudeFactor * easedStumble)
currentHipTorque = HIP_BEND_MAX_TORQUE * stanceStiffnessFactor
else
local stepFlexFactor = math.sin(stepProgress * math.pi) * 0.6
targetHipFlex = targetHipFlex + math.rad(20) * stepFlexFactor -- More hip flex during steps
end
local hipTargetCFrame = CFrame.Angles(targetHipFlex, 0, 0)
hipAlign.CFrame = hipTargetCFrame
hipAlign.Responsiveness = currentHipResponsiveness
hipAlign.MaxTorque = currentHipTorque
hipAlign.Enabled = true
end
end
-- Upper Leg Balancing
local function updateUpperLegBalancing(dt, leanForward, leanRight)
for _, side in pairs({"Left", "Right"}) do
local upperLeg = character:FindFirstChild(side .. "UpperLeg")
local lowerLeg = character:FindFirstChild(side .. "LowerLeg")
if not upperLeg or not lowerLeg then
continue
end
local upperLegConstraintName = side .. "UpperLeg_Orient"
local upperLegAlign = physicsConstraints[upperLegConstraintName]
if not upperLegAlign then
upperLegAlign = Instance.new("AlignOrientation")
upperLegAlign.Name = upperLegConstraintName
upperLegAlign.Attachment0 = createConstraintAttachment(upperLeg, "UpperLeg_Orient")
upperLegAlign.Attachment1 = createConstraintAttachment(lowerLeg, "UpperLeg_FromLower")
upperLegAlign.MaxTorque = 8000 -- Slightly weaker torque for smoother motion
upperLegAlign.Responsiveness = 30 -- Slightly less responsive
upperLegAlign.Parent = upperLeg
physicsConstraints[upperLegConstraintName] = upperLegAlign
end
local combinedLean = (leanForward + leanRight) * 0.5
local targetAngle = math.clamp(combinedLean, -math.rad(10), math.rad(10)) -- Narrower range for realism
local targetCFrame = CFrame.Angles(targetAngle, 0, 0)
upperLegAlign.CFrame = upperLegAlign.CFrame:Lerp(targetCFrame, 0.14) -- Smoother transition
upperLegAlign.Enabled = true
end
end
-- Dynamic Torso & Hip Correction
local function updateTorsoCorrection(dt, dynamicMultiplier, leanForward, leanRight, weightShiftLean, anticipatoryRoll, isSteppingBack, hipRotateAngle)
if not upperTorso then
return
end
local torsoAlign = physicsConstraints["UpperTorso_Align"]
if torsoAlign and not isShotReacting then
local currentUp = upperTorso.CFrame.UpVector
local uprightDot = currentUp:Dot(Vector3.new(0, 1, 0))
local leanAmount = 1 - math.clamp(uprightDot, 0, 1)
local aggressionFactor = leanAmount^1.2 -- More aggressive correction
local currentLerpFactor = math.clamp(TORSO_LERP_BASE + aggressionFactor * TORSO_LERP_AGGRESSION * dynamicMultiplier, TORSO_LERP_BASE, 0.25)
local leanVelMagnitude = lastLeanVelocity.Magnitude
local baseResponsiveness = 60 -- Slightly less responsive torso
local currentResponsiveness = math.clamp(baseResponsiveness + leanVelMagnitude * TORSO_RESPONSIVENESS_LEAN_VEL_FACTOR, baseResponsiveness, 100)
local targetLook = rootPart.CFrame.LookVector
local targetUp = Vector3.new(0, 1, 0)
local targetRight = targetLook:Cross(targetUp).Unit
targetLook = targetUp:Cross(targetRight).Unit
local targetCFrame = CFrame.fromMatrix(upperTorso.Position, targetRight, targetUp, -targetLook)
local tiltAngle = math.clamp(leanForward * TILT_FACTOR, -MAX_TILT, MAX_TILT)
local extraSideBend = math.clamp(leanRight * 0.35, -math.rad(16), math.rad(16)) -- Slightly reduced lateral bend
local extraBend = math.clamp(leanForward * 0.50, -math.rad(16), math.rad(16)) -- Slightly reduced forward bend
local extraTwist = math.clamp(leanRight * 0.10, -math.rad(5), math.rad(5)) -- Slightly reduced twist
if isSteppingBack then
tiltAngle = tiltAngle + BACKWARD_TORSO_TILT
end
local twistAngle = math.clamp(leanRight * 0.07 + extraTwist, -math.rad(6), math.rad(6))
torsoSwayPhase = torsoSwayPhase + dt * TORSO_SWAY_FREQUENCY
local currentSwayAmplitude = TORSO_SWAY_AMPLITUDE_BASE + leanAmount * TORSO_SWAY_LEAN_SCALE
local swayAngle = math.sin(torsoSwayPhase) * currentSwayAmplitude + math.sin(torsoSwayPhase * 0.5) * baseSwayAmplitude * 1.5 -- Add a slower sway layer
local counterTwist = -hipRotateAngle * TORSO_COUNTER_ROTATION_FACTOR
targetCFrame = targetCFrame * CFrame.Angles(tiltAngle + extraBend + extraSideBend, twistAngle + counterTwist, swayAngle + weightShiftLean + anticipatoryRoll)
torsoAlign.CFrame = torsoAlign.CFrame:Lerp(targetCFrame, currentLerpFactor)
torsoAlign.Responsiveness = currentResponsiveness
torsoAlign.MaxTorque = math.clamp(30000 + aggressionFactor * 22000, 30000, 65000) -- Slightly weaker torque
torsoAlign.Enabled = true
elseif torsoAlign then
torsoAlign.Enabled = false
end
if lowerTorso then
local hipAlign = physicsConstraints["LowerTorso_Align"]
if not hipAlign then
local hipAtt = createConstraintAttachment(lowerTorso, "HipAtt")
hipAlign = Instance.new("AlignOrientation")
hipAlign.Name = "LowerTorso_Align"
hipAlign.Attachment0 = hipAtt
hipAlign.Mode = Enum.OrientationAlignmentMode.OneAttachment
hipAlign.MaxTorque = 28000 -- Slightly weaker hip torque
hipAlign.Responsiveness = 55 -- Slightly less responsive hip
hipAlign.CFrame = lowerTorso.CFrame
hipAlign.Parent = lowerTorso
physicsConstraints[hipAlign.Name] = hipAlign
end
if not isShotReacting then
local hipTargetCFrame = lowerTorso.CFrame
local hipTilt = math.clamp(leanForward * HIP_CORRECTION_FACTOR, -MAX_TILT / 2, MAX_TILT / 2)
local hipTwist = math.clamp(leanRight * 0.035, -math.rad(3.5), math.rad(3.5)) -- Slightly reduced hip twist
hipTargetCFrame = hipTargetCFrame * CFrame.Angles(hipTilt, hipTwist + hipRotateAngle, 0)
hipAlign.CFrame = hipAlign.CFrame:Lerp(hipTargetCFrame, 0.14) -- Smoother hip correction
hipAlign.Enabled = true
else
hipAlign.Enabled = false
end
end
end
-- Active Arm Balancing (Enhanced)
local function updateArmBalancing(leanVectorRight, leanVectorForward, dt)
local rightArm = character:FindFirstChild("RightUpperArm")
local leftArm = character:FindFirstChild("LeftUpperArm")
if not rightArm or not leftArm or not upperTorso then
return
end
local rightAV = physicsConstraints["RightUpperArm_Swing"]
local leftAV = physicsConstraints["LeftUpperArm_Swing"]
if not rightAV or not leftAV then
return
end
if isShotReacting then
rightAV.Enabled = false
leftAV.Enabled = false
return
else
rightAV.Enabled = true
leftAV.Enabled = true
end
local torsoRight = upperTorso.CFrame.RightVector
local torsoForward = upperTorso.CFrame.LookVector
local torsoUp = upperTorso.CFrame.UpVector
local easedStumble = easeOutCubic(stumbleSeverity)
local currentIntensity = ARM_SWING_INTENSITY * (1 + easedStumble * (STUMBLE_ARM_SWING_MULTIPLIER - 1.0))
local targetRightArmVel = Vector3.zero
local targetLeftArmVel = Vector3.zero
local currentLerpFactor = ARM_LERP_FACTOR * (1 + easedStumble * 0.6)
if leanVectorForward < BACKWARD_ARM_LEAN_THRESHOLD then
local backwardLeanFactor = math.clamp(math.abs(leanVectorForward / BACKWARD_ARM_LEAN_THRESHOLD), 0, 1.6)
local tPoseIntensity = BACKWARD_ARM_TPOSE_INTENSITY * backwardLeanFactor
local waveIntensity = BACKWARD_ARM_WAVE_AMP * backwardLeanFactor
local waveCycle = math.sin(tick() * BACKWARD_ARM_WAVE_FREQ)
targetRightArmVel = targetRightArmVel + torsoForward * -tPoseIntensity
targetLeftArmVel = targetLeftArmVel + torsoForward * tPoseIntensity
targetRightArmVel = targetRightArmVel + torsoRight * waveCycle * waveIntensity
targetLeftArmVel = targetLeftArmVel + torsoRight * waveCycle * waveIntensity
currentLerpFactor = math.max(currentLerpFactor, 0.14)
elseif leanVectorForward > FORWARD_ARM_LEAN_THRESHOLD then
local forwardLeanFactor = math.clamp(leanVectorForward / (FORWARD_ARM_LEAN_THRESHOLD * 2), 0, 1.6)
local reachIntensity = FORWARD_ARM_REACH_INTENSITY * forwardLeanFactor
targetRightArmVel = targetRightArmVel + torsoRight * -reachIntensity
targetLeftArmVel = targetLeftArmVel + torsoRight * reachIntensity
currentLerpFactor = math.max(currentLerpFactor, 0.12)
else
targetRightArmVel = (torsoForward * leanVectorRight * -currentIntensity) + (torsoRight * leanVectorForward * -currentIntensity * 0.45)
targetLeftArmVel = (torsoForward * leanVectorRight * currentIntensity) + (torsoRight * leanVectorForward * -currentIntensity * 0.45)
if easedStumble > 0.15 then
local reachDirection = (torsoRight * leanVectorRight + torsoForward * leanVectorForward).Unit
local reachIntensity = easedStumble * ARM_REACH_STUMBLE_FACTOR * currentIntensity
targetRightArmVel = targetRightArmVel + reachDirection:Cross(torsoUp) * reachIntensity
targetLeftArmVel = targetLeftArmVel - reachDirection:Cross(torsoUp) * reachIntensity
end
if steppingFootName then
local swingDir = (steppingFootName == "LeftFoot") and 1 or -1
local stepSwingFactor = easeInOutQuad(stepProgress) * 1.2 -- More pronounced swing
targetRightArmVel = targetRightArmVel + torsoForward * swingDir * -currentIntensity * 0.40
targetLeftArmVel = targetLeftArmVel + torsoForward * swingDir * currentIntensity * 0.40
end
end
if targetRightArmVel.Magnitude > MAX_ARM_ANGULAR_VELOCITY then
targetRightArmVel = targetRightArmVel.Unit * MAX_ARM_ANGULAR_VELOCITY
end
if targetLeftArmVel.Magnitude > MAX_ARM_ANGULAR_VELOCITY then
targetLeftArmVel = targetLeftArmVel.Unit * MAX_ARM_ANGULAR_VELOCITY
end
rightAV.AngularVelocity = rightAV.AngularVelocity:Lerp(targetRightArmVel, currentLerpFactor)
leftAV.AngularVelocity = leftAV.AngularVelocity:Lerp(targetLeftArmVel, currentLerpFactor)
local stumbleTorqueMultiplier = math.lerp(1.0, 2.8, easedStumble) -- Slightly weaker stumble torque
rightAV.MaxTorque = 1300 * stumbleTorqueMultiplier
leftAV.MaxTorque = 1300 * stumbleTorqueMultiplier
end
-- Dynamic Parameter Calculation
local function calculateDynamicParameters(leanMagnitude, leanRight, velocityMagnitude)
stumbleSeverity = math.clamp(leanMagnitude / STUMBLE_THRESHOLD, 0, 1)
local easedStumble = easeOutCubic(stumbleSeverity)
local durationFactor = easedStumble
local lateralFactor = math.clamp(math.abs(leanRight) / BALANCE_LeanThreshold_LR, 0, 1)
local dynamicStepDuration = math.lerp(DYNAMIC_STEP_DURATION_MAX, DYNAMIC_STEP_DURATION_MIN, durationFactor * (1 - lateralFactor * 0.6))
local cooldownFactor = easedStumble
local dynamicStepCooldown = math.max(0.15, math.lerp(DYNAMIC_STEP_COOLDOWN_MAX, DYNAMIC_STEP_COOLDOWN_MIN, cooldownFactor))
local lateralLeanFactor = math.clamp(math.abs(leanRight) / (BALANCE_LeanThreshold_LR * 1.6), 0, 1)
local widthFactor = math.clamp(lateralLeanFactor * 0.30 + easedStumble * 0.80, 0, 1)
local dynamicStanceWidth = math.lerp(DYNAMIC_STANCE_WIDTH_MIN, DYNAMIC_STANCE_WIDTH_MAX, widthFactor)
return dynamicStepDuration, dynamicStepCooldown, dynamicStanceWidth
end
-- Advanced Active Balancing Logic (Massively Enhanced)
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 center of mass and foot positions
local com = calculateSmoothedCenterOfMass()
local leftPos = leftFoot.Position
local rightPos = rightFoot.Position
local avgFootPos = (leftPos + rightPos) / 2
local groundY_L, _ = findGroundInfo(leftPos)
local groundY_R, _ = findGroundInfo(rightPos)
local groundY = math.min(groundY_L or leftPos.Y, groundY_R or rightPos.Y)
local rootCF = rootPart.CFrame
local forwardXZ = (rootCF.LookVector * Vector3.new(1, 0, 1)).Unit
local rightXZ = (rootCF.RightVector * Vector3.new(1, 0, 1)).Unit
local comXZ = Vector3.new(com.X, 0, com.Z)
local avgFootXZ = Vector3.new(avgFootPos.X, 0, avgFootPos.Z)
local offsetVector = comXZ - avgFootXZ
local velocity = rootPart.AssemblyLinearVelocity
local velocityXZ = velocity * Vector3.new(1, 0, 1)
-- Enhanced prediction with momentum
local predictedOffset = offsetVector + velocityXZ * BALANCE_VelocityPredictionFactor + balanceRecoveryMomentum * 0.5
local currentLeanForward = predictedOffset:Dot(forwardXZ)
local currentLeanRight = predictedOffset:Dot(rightXZ)
local currentLean = Vector2.new(currentLeanForward, currentLeanRight)
local prevLean = Vector2.new(lastLeanForward, lastLeanRight)
if dt > 0 then
lastLeanVelocity = (currentLean - prevLean) / dt
else
lastLeanVelocity = Vector2.zero
end
local leanForward = lastLeanForward * 0.85 + currentLeanForward * 0.15 -- Smoother lean blending
local leanRight = lastLeanRight * 0.85 + currentLeanRight * 0.15
lastLeanForward = leanForward
lastLeanRight = leanRight
-- Update momentum based on velocity and lean
balanceRecoveryMomentum = balanceRecoveryMomentum:Lerp(velocityXZ * 0.1 + predictedOffset * 0.2, 0.1)
if balanceRecoveryMomentum.Magnitude > 5 then
balanceRecoveryMomentum = balanceRecoveryMomentum.Unit * 5 -- Cap momentum
end
-- Update dependent systems
updateArmBalancing(leanRight, leanForward, dt)
updateActiveBending(dt, leanForward, leanRight)
updateUpperLegBalancing(dt, leanForward, leanRight)
local leanMagnitude = predictedOffset.Magnitude
local dynamicMultiplier = (leanForward < BACKWARD_LEAN_THRESHOLD) and 1.6 or 1.0
local dynamicStepDuration, dynamicStepCooldown, dynamicStanceWidth = calculateDynamicParameters(leanMagnitude, leanRight, velocityXZ.Magnitude)
isStumbling = stumbleSeverity > 0.60 -- Slightly earlier stumble state
-- New: Predictive stepping based on velocity and lean velocity
local leanVelMagnitude = lastLeanVelocity.Magnitude
predictiveStepTrigger = leanVelMagnitude > 0.45 and leanMagnitude > BALANCE_LeanThreshold_FB * 0.75 -- Slightly less sensitive predictive step
-- Dynamic torso sway and weight shift
local weightShiftLean = 0
local anticipatoryRoll = 0
local hipRotateAngle = 0
if steppingFootName then
local stationaryFootDir = (steppingFootName == "LeftFoot") and 1 or -1
weightShiftLean = stationaryFootDir * WEIGHT_SHIFT_LEAN_FACTOR * easeInOutQuad(stepProgress)
local rotateDir = (steppingFootName == "LeftFoot") and -1 or 1
hipRotateAngle = rotateDir * HIP_ROTATION_FACTOR * easeInOutQuad(stepProgress)
else
local needsStepFB = math.abs(leanForward) > BALANCE_LeanThreshold_FB
local needsStepLR = math.abs(leanRight) > BALANCE_LeanThreshold_LR
if needsStepFB or needsStepLR or predictiveStepTrigger then
local predictedFootToStep = (lastSteppedFoot == "LeftFoot") and "RightFoot" or "LeftFoot"
local stationaryFootSide = (predictedFootToStep == "LeftFoot") and "Right" or "Left"
anticipatoryRoll = (stationaryFootSide == "Right") and ANTICIPATORY_ROLL_ANGLE or -ANTICIPATORY_ROLL_ANGLE
end
end
local isSteppingBack = leanForward < BACKWARD_LEAN_THRESHOLD and offsetVector:Dot(forwardXZ) < -0.04
-- Handle active stepping
if steppingFootName then
local footPart = character:FindFirstChild(steppingFootName)
if footPart and stepTargetPosition and stepStartPosition then
stepProgress = math.min(stepProgress + dt / currentStepDuration, 1)
local easeAlpha = easeInOutQuad(stepProgress)
local currentTargetPos = stepStartPosition:Lerp(stepTargetPosition, easeAlpha)
local lateralLiftMultiplier = 1 + math.clamp(math.abs(leanRight) / 0.4, 0, 1.2)
local dynamicLift = (BALANCE_LiftHeight_Base * lateralLiftMultiplier) + math.min(stepDistance, STEP_MAX_REACH * 0.8) * BALANCE_LiftHeight_ScalePerStud
local maxLiftHeight = math.min(dynamicLift, BALANCE_LiftHeight_MaxCap)
local easedStumbleLift = easeOutCubic(stumbleSeverity)
local arcIntensity = math.lerp(1.0, STUMBLE_LIFT_MULTIPLIER, easedStumbleLift)
local arcHeight = math.sin(stepProgress * math.pi) * maxLiftHeight * arcIntensity
currentTargetPos = currentTargetPos + Vector3.new(0, arcHeight, 0)
local stepConstraint = physicsConstraints[steppingFootName .. "_BalanceStep"]
if stepConstraint then
stepConstraint.Position = currentTargetPos
local stumbleResponsivenessFactor = math.lerp(1.0, 0.75, easedStumbleLift)
stepConstraint.Responsiveness = BALANCE_StepResponsiveness * stumbleResponsivenessFactor * (1 - easeAlpha * 0.15)
end
local distToTarget = (Vector3.new(footPart.Position.X, 0, footPart.Position.Z) - Vector3.new(stepTargetPosition.X, 0, stepTargetPosition.Z)).Magnitude
if stepProgress >= 1 or distToTarget < STEP_COMPLETION_DISTANCE then
if physicsConstraints[steppingFootName .. "_BalanceStep"] then
physicsConstraints[steppingFootName .. "_BalanceStep"].Enabled = false
end
lastSteppedFoot = steppingFootName
local completedStepVector = (stepTargetPosition - stepStartPosition) * Vector3.new(1, 0, 1)
local endStepLeanMagnitude = (calculateSmoothedCenterOfMass() * Vector3.new(1, 0, 1) - footPart.Position * Vector3.new(1, 0, 1)).Magnitude
local endStepStumbleSeverity = math.clamp(endStepLeanMagnitude / (STUMBLE_THRESHOLD * 0.75), 0, 1)
if endStepStumbleSeverity > RECOVERY_STEP_STUMBLE_THRESHOLD then
needsRecoveryStep = true
stepCooldown = dynamicStepCooldown * RECOVERY_STEP_COOLDOWN_MULTIPLIER
balanceRecoveryMomentum = balanceRecoveryMomentum + completedStepVector.Unit * 0.5 -- Add momentum
else
needsRecoveryStep = false
stepCooldown = dynamicStepCooldown
end
steppingFootName = nil
stepTargetPosition = nil
stepStartPosition = nil
stepDistance = 0
stepProgress = 0
if completedStepVector.Magnitude > 0.20 then
currentBalanceDirectionXZ = completedStepVector.Unit
end
end
else
steppingFootName = nil
stepTargetPosition = nil
stepStartPosition = nil
stepDistance = 0
stepProgress = 0
end
updateTorsoCorrection(dt, dynamicMultiplier, leanForward, leanRight, weightShiftLean, 0, isSteppingBack, hipRotateAngle)
return
end
-- Trigger new steps with enhanced logic
if stepCooldown <= 0 then
local needsStepFB = math.abs(leanForward) > BALANCE_LeanThreshold_FB
local needsStepLR = math.abs(leanRight) > BALANCE_LeanThreshold_LR
if needsStepFB or needsStepLR or needsRecoveryStep or predictiveStepTrigger then
needsRecoveryStep = false
predictiveStepTrigger = false
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
updateTorsoCorrection(dt, dynamicMultiplier, leanForward, leanRight, 0, anticipatoryRoll, isSteppingBack, 0)
return
end
local idealStepDir
if isSteppingBack then
idealStepDir = -forwardXZ * BACKWARD_STEP_MULTIPLIER
else
local absLeanF = math.abs(leanForward)
local absLeanR = math.abs(leanRight)
local adjustedLeanRight = leanRight
local adjustedLeanForward = leanForward
if absLeanF > absLeanR * DOMINANT_LEAN_RATIO_THRESHOLD then
adjustedLeanRight = leanRight * 0.25
end
local dirVector = (forwardXZ * adjustedLeanForward + rightXZ * adjustedLeanRight + balanceRecoveryMomentum.Unit * 0.3)
if dirVector.Magnitude < 0.01 then
dirVector = forwardXZ * leanForward + rightXZ * leanRight
end
idealStepDir = dirVector.Unit
end
local actualStepDir = idealStepDir
if currentBalanceDirectionXZ then
local dotProduct = idealStepDir:Dot(currentBalanceDirectionXZ)
local angleBetween = math.acos(math.clamp(dotProduct, -1, 1))
if angleBetween > MAX_DIRECTION_CHANGE_ANGLE_RAD then
local rotationAxis = currentBalanceDirectionXZ:Cross(idealStepDir).Unit
if rotationAxis.Magnitude < 0.9 then
rotationAxis = Vector3.yAxis
end
local rotationSign = math.sign((idealStepDir - currentBalanceDirectionXZ):Dot(rootCF.RightVector))
if rotationSign == 0 then
rotationSign = 1
end
local maxAllowedRotation = CFrame.fromAxisAngle(rotationAxis, MAX_DIRECTION_CHANGE_ANGLE_RAD * rotationSign)
actualStepDir = (maxAllowedRotation * currentBalanceDirectionXZ).Unit
end
end
actualStepDir = actualStepDir.Unit
local stepSideSign = (footToStepName == "LeftFoot") and 1 or -1
actualStepDir = CFrame.fromAxisAngle(Vector3.yAxis, GAIT_ANGLE_RAD * stepSideSign) * actualStepDir
steppingFootName = footToStepName
stepProgress = 0
currentStepDuration = dynamicStepDuration
stepStartPosition = footToStepPart.Position
local stepDist = leanMagnitude + velocityXZ.Magnitude * 0.35 + balanceRecoveryMomentum.Magnitude * 0.2
local easedStumbleStep = easeOutCubic(stumbleSeverity)
local requiredStepLength = math.clamp(stepDist * BALANCE_StepDistanceFactor * (1 - easedStumbleStep * 0.10), 0.25, STEP_MAX_REACH)
stepDistance = requiredStepLength
local lateralOffset = rightXZ * (((footToStepName == "LeftFoot") and -1) or 1) * dynamicStanceWidth / 2
local targetXZBase = Vector3.new(stationaryFootPart.Position.X, 0, stationaryFootPart.Position.Z) + actualStepDir * requiredStepLength + lateralOffset
local targetGroundY, targetGroundNormal = findGroundInfo(Vector3.new(targetXZBase.X, com.Y + 1.0, targetXZBase.Z))
local finalTargetPos
if targetGroundY and targetGroundNormal then
local projectedStepDir = (actualStepDir - targetGroundNormal * actualStepDir:Dot(targetGroundNormal)).Unit
if projectedStepDir.Magnitude < 0.1 then
projectedStepDir = actualStepDir
end
local projectedLateralOffset = (lateralOffset - targetGroundNormal * lateralOffset:Dot(targetGroundNormal))
if projectedLateralOffset.Magnitude < 0.01 then
projectedLateralOffset = lateralOffset
end
local targetXZProjected = Vector3.new(stationaryFootPart.Position.X, 0, stationaryFootPart.Position.Z) + projectedStepDir * requiredStepLength + projectedLateralOffset
finalTargetPos = Vector3.new(targetXZProjected.X, targetGroundY + FOOT_GROUND_OFFSET, targetXZProjected.Z)
local obstacleCheckOrigin = finalTargetPos + projectedStepDir * STEP_OBSTACLE_CHECK_DISTANCE - Vector3.yAxis * 0.1
local obstacleRay = workspace:Raycast(obstacleCheckOrigin, Vector3.yAxis * (STEP_OBSTACLE_CLEARANCE_HEIGHT + 0.25), groundRaycastParams)
if obstacleRay then
finalTargetPos = finalTargetPos + Vector3.yAxis * STEP_OBSTACLE_CLEARANCE_HEIGHT
end
else
finalTargetPos = Vector3.new(targetXZBase.X, groundY + FOOT_GROUND_OFFSET, targetXZBase.Z)
end
stepTargetPosition = finalTargetPos
local constraintName = steppingFootName .. "_BalanceStep"
local alignPos = physicsConstraints[constraintName]
if not alignPos then
alignPos = Instance.new("AlignPosition")
alignPos.Name = constraintName
alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment
local stepAtt = createConstraintAttachment(footToStepPart, "FootPlant")
alignPos.Attachment0 = stepAtt
alignPos.MaxForce = BALANCE_StepForce
alignPos.ApplyAtCenterOfMass = false
alignPos.Parent = footToStepPart
physicsConstraints[constraintName] = alignPos
end
alignPos.Responsiveness = BALANCE_StepResponsiveness
alignPos.Position = stepStartPosition
alignPos.Enabled = true
stepCooldown = dynamicStepCooldown
end
end
updateTorsoCorrection(dt, dynamicMultiplier, leanForward, leanRight, 0, anticipatoryRoll, isSteppingBack, 0)
end
-- Shot Reaction Logic
local function triggerShotReaction()
if not isRagdolled or isShotReacting or not upperTorso or not rightHand then
return
end
isShotReacting = true
shotReactTimer = SHOT_REACTION_DURATION
local angle = math.rad(math.random(-15, 15))
local axis = Vector3.yAxis
local randomRotation = CFrame.fromAxisAngle(axis, angle)
local impulseDirection = (randomRotation * (-rootPart.CFrame.LookVector)).Unit
local impulse = impulseDirection * upperTorso:GetMass() * SHOT_IMPULSE_MAGNITUDE
upperTorso:ApplyImpulse(impulse)
local handAlign = physicsConstraints["ShotHandAlign"]
if not handAlign then
handAlign = Instance.new("AlignPosition")
handAlign.Name = "ShotHandAlign"
handAlign.Mode = Enum.PositionAlignmentMode.TwoAttachment
handAlign.Attachment0 = createConstraintAttachment(rightHand, "ShotHandAtt")
handAlign.Attachment1 = createConstraintAttachment(upperTorso, "ShotTargetAtt")
handAlign.MaxForce = 5000 -- Slightly weaker hand force
handAlign.Responsiveness = 40 -- Slightly less responsive hand
handAlign.Parent = rightHand
physicsConstraints["ShotHandAlign"] = handAlign
end
handAlign.Enabled = true
if physicsConstraints["RightUpperArm_Swing"] then
physicsConstraints["RightUpperArm_Swing"].Enabled = false
end
if physicsConstraints["LeftUpperArm_Swing"] then
physicsConstraints["LeftUpperArm_Swing"].Enabled = false
end
if physicsConstraints["UpperTorso_Align"] then
physicsConstraints["UpperTorso_Align"].Enabled = false
end
if physicsConstraints["LowerTorso_Align"] then
physicsConstraints["LowerTorso_Align"].Enabled = false
end
if physicsConstraints["HRP_Stabilize"] then
physicsConstraints["HRP_Stabilize"].Enabled = false
end
for _, side in pairs({"Left", "Right"}) do
if physicsConstraints[side .. "KneeBend"] then
physicsConstraints[side .. "KneeBend"].Enabled = false
end
if physicsConstraints[side .. "HipBend"] then
physicsConstraints[side .. "HipBend"].Enabled = false
end
end
end
local function updateShotReaction(dt)
if not isShotReacting then
return
end
shotReactTimer = math.max(0, shotReactTimer - dt)
if shotReactTimer <= 0 then
isShotReacting = false
if physicsConstraints["ShotHandAlign"] then
physicsConstraints["ShotHandAlign"].Enabled = false
end
if humanoid.Health > 0 then
if physicsConstraints["RightUpperArm_Swing"] then
physicsConstraints["RightUpperArm_Swing"].Enabled = true
end
if physicsConstraints["LeftUpperArm_Swing"] then
physicsConstraints["LeftUpperArm_Swing"].Enabled = true
end
if physicsConstraints["UpperTorso_Align"] then
physicsConstraints["UpperTorso_Align"].Enabled = true
end
if physicsConstraints["LowerTorso_Align"] then
physicsConstraints["LowerTorso_Align"].Enabled = true
end
if physicsConstraints["HRP_Stabilize"] then
physicsConstraints["HRP_Stabilize"].Enabled = true
end
end
end
end
-- Initialize Ragdoll & Balancing System
local function makeRagdoll()
if isRagdolled then
return
end
isRagdolled = true
clearRagdollPhysics()
saveOriginalCollisions()
character:SetAttribute("IsRagdolled", true)
local totalMass = 0
for _, part in pairs(character:GetDescendants()) do
if part:IsA("BasePart") and originalCollisions[part] and not originalCollisions[part].Massless then
totalMass = totalMass + part:GetMass()
end
end
totalMass = math.max(totalMass, 1)
local function assignCollisionGroupRecursive(part, groupName)
if part:IsA("BasePart") then
pcall(function()
part.CollisionGroup = groupName
end)
end
for _, child in pairs(part:GetChildren()) do
assignCollisionGroupRecursive(child, groupName)
end
end
local leftUpperLeg = character:FindFirstChild("LeftUpperLeg")
local rightUpperLeg = character:FindFirstChild("RightUpperLeg")
if leftUpperLeg then
assignCollisionGroupRecursive(leftUpperLeg, LEFT_LEG_GROUP)
local leftLowerLeg = character:FindFirstChild("LeftLowerLeg")
local leftFoot = character:FindFirstChild("LeftFoot")
if leftLowerLeg then
assignCollisionGroupRecursive(leftLowerLeg, LEFT_LEG_GROUP)
end
if leftFoot then
assignCollisionGroupRecursive(leftFoot, LEFT_LEG_GROUP)
end
end
if rightUpperLeg then
assignCollisionGroupRecursive(rightUpperLeg, RIGHT_LEG_GROUP)
local rightLowerLeg = character:FindFirstChild("RightLowerLeg")
local rightFoot = character:FindFirstChild("RightFoot")
if rightLowerLeg then
assignCollisionGroupRecursive(rightLowerLeg, RIGHT_LEG_GROUP)
end
if rightFoot then
assignCollisionGroupRecursive(rightFoot, RIGHT_LEG_GROUP)
end
end
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
local a1 = createConstraintAttachment(part0, part0.Name .. part1.Name .. "A1", joint.C0)
local a2 = createConstraintAttachment(part1, part0.Name .. part1.Name .. "_A2", joint.C1)
local constraint = Instance.new("BallSocketConstraint")
constraint.Name = joint.Name .. "_BSC"
constraint.Attachment0 = a1
constraint.Attachment1 = a2
constraint.LimitsEnabled = true
constraint.TwistLimitsEnabled = true
local partName = part1.Name:lower()
local isArm = partName:find("arm") or partName:find("hand")
local isLeg = partName:find("leg") or partName:find("foot")
local stiffnessFactor = 1.0
if isArm then
stiffnessFactor = ARM_STIFFNESS_FACTOR
elseif isLeg then
stiffnessFactor = LEG_STIFFNESS_FACTOR
end
stiffnessFactor = math.max(1.0, stiffnessFactor)
local baseUpperAngle = 35
local baseTwistLower = -20
local baseTwistUpper = 20
if partName:find("foot") then
baseUpperAngle = 16
baseTwistLower = -3
baseTwistUpper = 3
elseif partName:find("lower") and partName:find("leg") then
baseUpperAngle = 75
baseTwistLower = -1.5
baseTwistUpper = 1.5
elseif partName:find("upper") and partName:find("leg") then
baseUpperAngle = 75
baseTwistLower = -20
baseTwistUpper = 20
elseif partName:find("hand") then
baseUpperAngle = 26
baseTwistLower = -7
baseTwistUpper = 7
elseif partName:find("lower") and partName:find("arm") then
baseUpperAngle = 70
baseTwistLower = -4
baseTwistUpper = 4
elseif partName:find("upper") and partName:find("arm") then
baseUpperAngle = 95
baseTwistLower = -60
baseTwistUpper = 60
elseif partName:find("head") then
baseUpperAngle = 24
baseTwistLower = -15
baseTwistUpper = 15
elseif partName:find("torso") then
baseUpperAngle = 9
baseTwistLower = -7
baseTwistUpper = 7
end
constraint.UpperAngle = math.clamp(baseUpperAngle / stiffnessFactor, 5, 170)
constraint.TwistLowerAngle = math.clamp(baseTwistLower / stiffnessFactor, -170, -1)
constraint.TwistUpperAngle = math.clamp(baseTwistUpper / stiffnessFactor, 1, 170)
constraint.Parent = part0
table.insert(jointConstraints, {constraint = constraint, a1 = a1, a2 = a2})
joint.Enabled = false
end
end
end
for part, data in pairs(originalCollisions) do
if part and part.Parent then
part.CanCollide = true
part.Massless = false
if part == rootPart then
part.CanCollide = false
end
end
end
humanoid:ChangeState(Enum.HumanoidStateType.Physics)
local antiGravAtt = createConstraintAttachment(rootPart, "AntiGravityAtt")
antiGravAtt.WorldPosition = calculateSmoothedCenterOfMass()
local standForce = Instance.new("VectorForce")
standForce.Name = "AntiGravityForce"
standForce.Attachment0 = antiGravAtt
standForce.RelativeTo = Enum.ActuatorRelativeTo.World
standForce.ApplyAtCenterOfMass = false
standForce.Force = Vector3.new(0, totalMass * workspace.Gravity * ANTI_GRAVITY_MULTIPLIER, 0)
standForce.Parent = rootPart
physicsConstraints[standForce.Name] = standForce
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 = 28000 -- Slightly weaker torso torque
align.Responsiveness = 55 -- Slightly less responsive torso
align.CFrame = CFrame.lookAt(Vector3.zero, rootPart.CFrame.LookVector, Vector3.yAxis)
align.Parent = upperTorso
physicsConstraints[align.Name] = align
end
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 = 1100 -- Slightly weaker arm torque
av.RelativeTo = Enum.ActuatorRelativeTo.World
av.AngularVelocity = Vector3.new(0, 0, 0)
av.Parent = arm
physicsConstraints[av.Name] = av
end
end
local hrpStabilizeAtt = createConstraintAttachment(rootPart, "HRPStabilizeAtt")
local hrpStabilize = Instance.new("AlignOrientation")
hrpStabilize.Name = "HRP_Stabilize"
hrpStabilize.Attachment0 = hrpStabilizeAtt
hrpStabilize.Mode = Enum.OrientationAlignmentMode.OneAttachment
hrpStabilize.MaxTorque = HRP_STABILIZE_TORQUE
hrpStabilize.Responsiveness = HRP_STABILIZE_RESPONSIVENESS
local _, y, _ = rootPart.CFrame:ToOrientation()
hrpStabilize.CFrame = CFrame.fromOrientation(0, y, 0)
hrpStabilize.Parent = rootPart
physicsConstraints[hrpStabilize.Name] = hrpStabilize
updateFootPlantingAndOrientation()
updateActiveBending(0, 0, 0)
updateUpperLegBalancing(0, 0, 0)
stepConnection = runService.Stepped:Connect(function(_, dt)
if not isRagdolled or humanoid.Health <= 0 then
return
end
updateShotReaction(dt)
local currentCOM = calculateSmoothedCenterOfMass()
if antiGravAtt and antiGravAtt.Parent then
antiGravAtt.WorldPosition = currentCOM
end
if standForce and standForce.Parent then
standForce.Force = Vector3.new(0, totalMass * workspace.Gravity * ANTI_GRAVITY_MULTIPLIER, 0)
end
updateFootPlantingAndOrientation()
updateActiveBalance(dt)
end)
end
-- Reset Character
local function resetCharacter()
if not isRagdolled then
return
end
isRagdolled = false
character:SetAttribute("IsRagdolled", false)
clearRagdollPhysics()
for _, joint in pairs(character:GetDescendants()) do
if joint:IsA("Motor6D") then
joint.Enabled = true
end
end
for part, data in pairs(originalCollisions) do
if part and part.Parent then
pcall(function()
part.CanCollide = data.CanCollide
part.Massless = data.Massless
part.CustomPhysicalProperties = data.CustomPhysicalProperties
part.AssemblyLinearVelocity = Vector3.zero
part.AssemblyAngularVelocity = Vector3.zero
part.CollisionGroupId = data.CollisionGroupId
end)
end
end
originalCollisions = {}
humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
task.wait(0.1)
if humanoid:GetState() ~= Enum.HumanoidStateType.Dead then
humanoid:ChangeState(Enum.HumanoidStateType.Running)
end
end
-- Event Handlers
humanoid.Died:Connect(function()
if not isRagdolled then
task.wait(0.05)
makeRagdoll()
end
if stepConnection then
stepConnection:Disconnect()
stepConnection = nil
end
if physicsConstraints["AntiGravityForce"] then
physicsConstraints["AntiGravityForce"].Enabled = false
end
if physicsConstraints["UpperTorso_Align"] then
physicsConstraints["UpperTorso_Align"].Enabled = false
end
if physicsConstraints["LowerTorso_Align"] then
physicsConstraints["LowerTorso_Align"].Enabled = false
end
if physicsConstraints["HRP_Stabilize"] then
physicsConstraints["HRP_Stabilize"].Enabled = false
end
isShotReacting = false
if physicsConstraints["ShotHandAlign"] then
physicsConstraints["ShotHandAlign"].Enabled = false
end
for _, name in pairs({"LeftUpperArm_Swing", "RightUpperArm_Swing"}) do
if physicsConstraints[name] then
physicsConstraints[name].Enabled = false
end
end
for _, side in pairs({"Left", "Right"}) do
if physicsConstraints[side .. "KneeBend"] then
physicsConstraints[side .. "KneeBend"].Enabled = false
end
if physicsConstraints[side .. "HipBend"] then
physicsConstraints[side .. "HipBend"].Enabled = false
end
if physicsConstraints[side .. "Foot_PlantOrient"] then
physicsConstraints[side .. "Foot_PlantOrient"].Enabled = false
end
if physicsConstraints[side .. "Foot_PlantPos"] then
physicsConstraints[side .. "Foot_PlantPos"].Enabled = false
end
if physicsConstraints[side .. "Foot_BalanceStep"] then
physicsConstraints[side .. "Foot_BalanceStep"].Enabled = false
end
if physicsConstraints[side .. "UpperLeg_Orient"] then
physicsConstraints[side .. "UpperLeg_Orient"].Enabled = false
end
end
character:SetAttribute("IsRagdolled", true)
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
elseif input.KeyCode == Enum.KeyCode.B and humanoid.Health > 0 then
triggerShotReaction()
end
end
end)
script.Destroying:Connect(function()
if isRagdolled then
resetCharacter()
end
end)
-- Uncomment to start in ragdoll mode for testing
-- task.wait(1)
-- if humanoid.Health > 0 then makeRagdoll() end
```
Here are the main changes made to enhance the realism of the physics and balancing:
**General Physics Realism:**
* **Reduced Stiffness and Damping:** Overall stiffness and responsiveness values across joints, torso, arms, and legs have been slightly reduced. This makes the movements less robotic and more fluid, allowing for more natural-looking swaying and reactions.
* **Slightly Weaker Forces:** Step force, foot plant force, arm swing torque, torso correction torque, and shot reaction forces have been subtly decreased. This prevents movements from being overly forceful or jerky, contributing to a more natural feel.
* **Adjusted Timing and Ranges:** Step durations, cooldowns, stance widths, and lean thresholds have been tweaked to be slightly slower and less extreme, making the balancing system feel less "twitchy" and more deliberate.
* **More Natural Gait:** Gait angle and step reach have been slightly narrowed to create a more realistic walking motion.
* **Subtle Torso Sway:** A base torso sway has been added even when the character is balanced, mimicking natural human micro-movements. The sway amplitude and frequency are also adjusted to be less pronounced overall.
* **Refined Joint Limits:** Joint limits for ragdoll mode, especially in legs and arms, have been refined to allow for more natural bending and less stiff poses. Foot and lower leg twist limits are reduced for more grounded feet.
* **Anti-Gravity Adjustment:** The anti-gravity force multiplier is slightly reduced, making gravity feel a bit more prominent and the character less floaty.
**Balancing Physics Realism:**
* **Smoother Lean Blending:** Lean values are now blended more smoothly over time, preventing sudden shifts and making balance adjustments appear more natural.
* **Less Aggressive Torso Correction:** Torso correction is made slightly less aggressive and responsive, allowing for more natural sway and less "locked-in" posture.
* **Subtler Tilting and Hip Correction:** Reactive tilting and hip correction are reduced to be less pronounced, resulting in a more subtle and realistic balance response.
* **More Natural Arm Movements:** Arm swing intensity and reach are reduced, and arm movements are made less reactive to velocity, leading to more natural arm motion during balancing and walking.
* **Predictive Stepping Refinement:** Predictive stepping thresholds are slightly adjusted to be less sensitive, preventing overly eager stepping and making the character's balance feel more grounded.
* **Stumble Behavior Adjustment:** Stumble detection is made slightly earlier, and stumble reactions (knee bending, arm swing) are made slightly less exaggerated for a more controlled stumble recovery.
* **Upper Leg Balancing Refinement:** Upper leg balancing is made less responsive and with a narrower angle range, contributing to smoother leg movements.
**Code Structure and Logic:**
* The core logic of the script remains largely the same, focusing on parameter adjustments for realism.
* Comments are retained for clarity.
**How to Use:**
1. Replace the existing script in your Roblox character with this updated code.
2. Test the character's movement, ragdoll behavior, and balancing in various scenarios.
3. Further fine-tune the parameters in the script to match your specific desired level of realism and game feel. You can experiment with the configurable variables at the beginning of the script to adjust stiffness, forces, timing, and thresholds.
This revised code should provide a noticeable improvement in the realism of the character's physics and balancing, making the movements and reactions feel more natural and believable within the Roblox physics engine.