-- Delta Fixed: 100% Mathematically Accurate Classic Sonic Engine local Players = game:GetService("Players") local RunService = game:GetService("RunService") local player = Players.LocalPlayer local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") local rootPart = character:WaitForChild("HumanoidRootPart") -- 1:1 SONIC RETRO PHYSICS GUIDE SETTINGS local ACCELERATION_STEP = 0.45 -- Precise, fixed buildup increment per frame local NATURAL_FRICTION = 0.965 -- Slippery classic ground drift deceleration local BALL_ROLL_DECEL = 0.992 -- Slippery sliding friction multiplier local RETRO_RUN_CAP = 34.0 -- Precise 6 pixels per frame flat-ground ceiling local SPINDASH_LAUNCH = 90.0 -- Explosive release velocity local RETRO_JUMP = 72.0 -- Classic pixel-height jump arc -- Internal Physics States local currentMomentum = 16.0 local isSpindashing = false local isRollingBall = false local spinDegrees = 0 -- Configure baseline properties securely humanoid.UseJumpPower = true humanoid.JumpPower = RETRO_JUMP humanoid.WalkSpeed = 0.1 -- Disable native ground handling to stop instant speed overrides -- BUILD MOBILE HUD OVERLAY local ScreenGui = Instance.new("ScreenGui", player:WaitForChild("PlayerGui")) ScreenGui.ResetOnSpawn = false local function buildTouchButton(text, pos, color) local btn = Instance.new("TextButton", ScreenGui) btn.Text = text btn.Position = pos btn.Size = UDim2.new(0, 120, 0, 45) btn.BackgroundColor3 = color btn.TextColor3 = Color3.fromRGB(255, 255, 255) btn.Font = Enum.Font.SourceSansBold btn.TextSize = 15 Instance.new("UICorner", btn).CornerRadius = UDim.new(0, 10) return btn end local SpindashBtn = buildTouchButton("SPINDASH", UDim2.new(0.70, 0, 0.50, 0), Color3.fromRGB(0, 110, 220)) -- EXECUTOR-SAFE PHYSICS DRIVER (Bypasses Humanoid velocity clamping loops) local physicsMotor = rootPart:FindFirstChild("SonicBypassForce") or Instance.new("BodyVelocity", rootPart) physicsMotor.Name = "SonicBypassForce" physicsMotor.MaxForce = Vector3.new(45000, 0, 45000) -- Leaves Y free so jumping handles natively physicsMotor.Velocity = Vector3.new(0,0,0) -- Container for the visual model sphere local visualSpinSphere = nil -- RETRO VISUAL GENERATOR local function deleteSpinBall() if visualSpinSphere then visualSpinSphere:Destroy() visualSpinSphere = nil end for _, part in pairs(character:GetChildren()) do if part:IsA("BasePart") and part.Name ~= "HumanoidRootPart" then part.Transparency = 0 end end if character:FindFirstChild("Animate") and not character.Animate.Enabled then character.Animate.Enabled = true end end local function spawnSpinBall() deleteSpinBall() local targetColor = Color3.fromRGB(0, 100, 255) local headPart = character:FindFirstChild("Head") if headPart and headPart:IsA("BasePart") then targetColor = headPart.Color end for _, part in pairs(character:GetChildren()) do if part:IsA("BasePart") and part.Name ~= "HumanoidRootPart" then part.Transparency = 1 end end if character:FindFirstChild("Animate") and character.Animate.Enabled then character.Animate.Enabled = false end local sphere = Instance.new("Part") sphere.Name = "SonicSpinSphere" sphere.Size = Vector3.new(3.8, 3.8, 3.8) sphere.Shape = Enum.PartType.Ball sphere.Color = targetColor sphere.Material = Enum.Material.Neon sphere.CanCollide = false sphere.Parent = character local weld = Instance.new("Weld") weld.Name = "SpinWeldObject" weld.Part0 = rootPart weld.Part1 = sphere weld.C0 = CFrame.new(0, -0.4, 0) weld.Parent = sphere visualSpinSphere = sphere end -- TRACK AIRBORNE AND LANDED STATUS CHANGES humanoid.StateChanged:Connect(function(_, newState) if newState == Enum.HumanoidStateType.Freefall or newState == Enum.HumanoidStateType.Jumping then isRollingBall = true spawnSpinBall() elseif newState == Enum.HumanoidStateType.Landed then if isRollingBall then isRollingBall = false deleteSpinBall() end end end) -- CORE RETRO STEP PHYSICS RUNNER LOOP RunService.Heartbeat:Connect(function(deltaTime) if not character or not rootPart or not humanoid or not physicsMotor then return end local flatVelocity = Vector3.new(rootPart.AssemblyLinearVelocity.X, 0, rootPart.AssemblyLinearVelocity.Z).Magnitude -- Clean rolling state exit upon dropping below floor limits if isRollingBall and flatVelocity < 4 and not isSpindashing and humanoid.FloorMaterial ~= Enum.Material.Air then isRollingBall = false deleteSpinBall() physicsMotor.Velocity = Vector3.new(0, 0, 0) end -- ROTATE GEOMETRIC SPHERE if (isRollingBall or isSpindashing) and visualSpinSphere then local weld = visualSpinSphere:FindFirstChild("SpinWeldObject") if weld then local speedMultiplier = (flatVelocity < 5) and 180 or (flatVelocity + 45) * 35 spinDegrees = (spinDegrees + (deltaTime * speedMultiplier)) % 360 weld.C0 = CFrame.new(0, -0.4, 0) * CFrame.Angles(math.rad(-spinDegrees), 0, 0) end end -- ABSOLUTE FREEZE CONSTRAINT DURING SPINDASH CHARGING (Blocks joystick movement completely) if isSpindashing then rootPart.AssemblyLinearVelocity = Vector3.new(0, rootPart.AssemblyLinearVelocity.Y, 0) physicsMotor.Velocity = Vector3.new(0, 0, 0) return end -- READ JOYSTICK VECTOR FOR CUSTOM MOMENTUM CALCULATION local thumbstickInput = humanoid.MoveDirection if thumbstickInput.Magnitude > 0.05 then if isRollingBall then -- Low Friction Slidings: Keeps rolling momentum drifting smoothly currentMomentum = math.max(16, currentMomentum * (BALL_ROLL_DECEL ^ (deltaTime * 60))) physicsMotor.Velocity = rootPart.CFrame.LookVector * currentMomentum else -- FIXED ACCELERATION STEP: Gradually builds up velocity the longer you hold forward if currentMomentum < RETRO_RUN_CAP then currentMomentum = math.min(RETRO_RUN_CAP, currentMomentum + (ACCELERATION_STEP * deltaTime * 60)) else -- Slow down gradually if launched over the running limit via a Spindash currentMomentum = math.max(RETRO_RUN_CAP, currentMomentum - (0.25 * deltaTime * 60)) end physicsMotor.Velocity = thumbstickInput * currentMomentum end -- Smooth turning vector orientation mapping rootPart.CFrame = CFrame.new(rootPart.Position, rootPart.Position + Vector3.new(thumbstickInput.X, 0, thumbstickInput.Z)) else -- Slippery friction decay coefficient when letting go of the stick currentMomentum = math.max(0, currentMomentum * (NATURAL_FRICTION ^ (deltaTime * 60))) if currentMomentum <= 1 then physicsMotor.Velocity = Vector3.new(0, 0, 0) else local driftHeading = rootPart.CFrame.LookVector physicsMotor.Velocity = Vector3.new(driftHeading.X * currentMomentum, 0, driftHeading.Z * currentMomentum) end end end) -- BUTTON CONNECTIONS USING EXECUTOR-SAFE MOBILE TOUCH TRIGGERS SpindashBtn.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then local flatVelocity = Vector3.new(rootPart.AssemblyLinearVelocity.X, 0, rootPart.AssemblyLinearVelocity.Z).Magnitude if flatVelocity > 10 and humanoid.FloorMaterial ~= Enum.Material.Air then -- MID-RUN BALL ROLL isRollingBall = not isRollingBall if isRollingBall then currentMomentum = flatVelocity spawnSpinBall() SpindashBtn.BackgroundColor3 = Color3.fromRGB(0, 220, 220) else deleteSpinBall() SpindashBtn.BackgroundColor3 = Color3.fromRGB(0, 110, 220) end elseif flatVelocity <= 10 then -- STATIC CHARGE SPINDASH: Locks position immediately isSpindashing = true spawnSpinBall() SpindashBtn.BackgroundColor3 = Color3.fromRGB(230, 40, 40) end end end) SpindashBtn.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then if not isSpindashing then return end isSpindashing = false SpindashBtn.BackgroundColor3 = Color3.fromRGB(0, 110, 220) -- EXPLOSIVE PROPULSION BLAST: Forced high-velocity forward vector release currentMomentum = SPINDASH_LAUNCH local launchHeading = rootPart.CFrame.LookVector rootPart.AssemblyLinearVelocity = Vector3.new(launchHeading.X * currentMomentum, rootPart.AssemblyLinearVelocity.Y, launchHeading.Z * currentMomentum) physicsMotor.Velocity = Vector3.new(launchHeading.X * currentMomentum, 0, launchHeading.Z * currentMomentum) isRollingBall = true end end) -- Cleanup UI sequences upon character death humanoid.Died:Connect(function() deleteSpinBall() if physicsMotor then physicsMotor:Destroy() end ScreenGui:Destroy() end)