The Full Sonic Script
Key RequiredUniversal

The Full Sonic Script

Universal ScriptPs4sroPs4sro 65K views 544 executesUpdated Jul 30, 2024
Unrated
00
Lua Script•388 lines (12194 chars)
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local ContextActionService = game:GetService("ContextActionService")
local TweenService = game:GetService("TweenService")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

-- --- CONFIGURATION ---
local CONFIG = {
	-- Movement
	DoubleJumpPower = 60,
	RollDuration = 0.5,
	RollDistance = 50,
	BaseWalkSpeed = 16,
	MaxWalkSpeed = 150,
	Acceleration = 30,
	BrakingDuration = 0.7,
	StopThreshold = 1,
	BaseFOV = 70,
	MaxFOV = 120,
	
	-- Tool
	DashSpeed = 200,
	DashMaxTime = 1,
	
	-- Assets
	RollAnimID = "rbxassetid://18537367238",
	RunAnimID = "rbxassetid://85752365414182",
	GreenCursorID = "rbxassetid://1460596350"
}

local DECELERATION = CONFIG.MaxWalkSpeed / CONFIG.BrakingDuration

-- --- STATE VARIABLES ---
local isMobileMode = false
local selectionMade = false
local currentCharacter = nil

-- --- GUI CREATION ---
local function createIntroGUI()
	local existing = player:WaitForChild("PlayerGui"):FindFirstChild("MaldixIntroGUI")
	if existing then existing:Destroy() end

	local screenGui = Instance.new("ScreenGui")
	screenGui.Name = "MaldixIntroGUI"
	screenGui.Parent = player:WaitForChild("PlayerGui")
	screenGui.ResetOnSpawn = false
	
	local frame = Instance.new("Frame")
	frame.Name = "MainFrame"
	frame.Size = UDim2.new(0, 400, 0, 350)
	frame.Position = UDim2.new(0.5, 0, 0.5, 0)
	frame.AnchorPoint = Vector2.new(0.5, 0.5)
	frame.BackgroundColor3 = Color3.new(0.14, 0.14, 0.14) 
	frame.BorderSizePixel = 0
	frame.Parent = screenGui
	
	local uiCorner = Instance.new("UICorner")
	uiCorner.CornerRadius = UDim.new(0, 12)
	uiCorner.Parent = frame
	
	local header = Instance.new("TextLabel")
	header.Text = "Maldix's Sonic Script"
	header.Size = UDim2.new(1, 0, 0, 50)
	header.BackgroundTransparency = 1
	header.TextColor3 = Color3.new(0.33, 1, 0.5) 
	header.Font = Enum.Font.FredokaOne
	header.TextSize = 28
	header.Parent = frame
	
	local desc = Instance.new("TextLabel")
	desc.Text = [[
Controls:
• WASD / Joystick: Move (Accelerate)
• Space: Jump (Double tap to Double Jump)
• R / Button: Roll (Spam to go fast)
• Dash Tool: Click in air to Homing Dash

Please select your device type:
]]
	desc.Size = UDim2.new(1, -40, 0, 200)
	desc.Position = UDim2.new(0, 20, 0, 50)
	desc.BackgroundTransparency = 1
	desc.TextColor3 = Color3.new(0.86, 0.86, 0.86)
	desc.Font = Enum.Font.GothamMedium
	desc.TextSize = 16
	desc.TextXAlignment = Enum.TextXAlignment.Left
	desc.TextYAlignment = Enum.TextYAlignment.Top
	desc.TextWrapped = true
	desc.Parent = frame
	
	local btnPC = Instance.new("TextButton")
	btnPC.Name = "PCButton"
	btnPC.Text = "PC"
	btnPC.Size = UDim2.new(0.4, 0, 0, 50)
	btnPC.Position = UDim2.new(0.05, 0, 1, -60)
	btnPC.BackgroundColor3 = Color3.new(0.23, 0.23, 0.23)
	btnPC.TextColor3 = Color3.new(1, 1, 1)
	btnPC.Font = Enum.Font.GothamBold
	btnPC.TextSize = 18
	btnPC.Parent = frame
	
	local btnCorner1 = Instance.new("UICorner")
	btnCorner1.CornerRadius = UDim.new(0, 8)
	btnCorner1.Parent = btnPC
	
	local btnMobile = Instance.new("TextButton")
	btnMobile.Name = "MobileButton"
	btnMobile.Text = "Mobile"
	btnMobile.Size = UDim2.new(0.4, 0, 0, 50)
	btnMobile.Position = UDim2.new(0.55, 0, 1, -60)
	btnMobile.BackgroundColor3 = Color3.new(0.23, 0.23, 0.23)
	btnMobile.TextColor3 = Color3.new(1, 1, 1)
	btnMobile.Font = Enum.Font.GothamBold
	btnMobile.TextSize = 18
	btnMobile.Parent = frame

	local btnCorner2 = Instance.new("UICorner")
	btnCorner2.CornerRadius = UDim.new(0, 8)
	btnCorner2.Parent = btnMobile
	
	local function closeGui()
		screenGui:Destroy()
	end
	
	btnPC.MouseButton1Click:Connect(function()
		isMobileMode = false
		selectionMade = true
		closeGui()
	end)
	
	btnMobile.MouseButton1Click:Connect(function()
		isMobileMode = true
		selectionMade = true
		closeGui()
	end)
end

-- --- MAIN LOGIC ---

local function onCharacterAdded(character)
	currentCharacter = character
	local humanoid = character:WaitForChild("Humanoid")
	local rootPart = character:WaitForChild("HumanoidRootPart")
	local animator = humanoid:WaitForChild("Animator")

	humanoid.WalkSpeed = CONFIG.BaseWalkSpeed
	local currentSpeed = CONFIG.BaseWalkSpeed
	local lastMoveDirection = Vector3.new(0, 0, -1)
	
	-- --- ANIMATIONS ---
	local runAnim = Instance.new("Animation")
	runAnim.AnimationId = CONFIG.RunAnimID
	local runTrack = animator:LoadAnimation(runAnim)
	runTrack.Priority = Enum.AnimationPriority.Movement
	runTrack.Looped = true

	local rollAnim = Instance.new("Animation")
	rollAnim.AnimationId = CONFIG.RollAnimID
	local rollTrack = animator:LoadAnimation(rollAnim)
	rollTrack.Priority = Enum.AnimationPriority.Action
	rollTrack.Looped = true

	local jumpAnim = Instance.new("Animation")
	jumpAnim.AnimationId = CONFIG.RollAnimID
	local jumpTrack = animator:LoadAnimation(jumpAnim)
	jumpTrack.Priority = Enum.AnimationPriority.Action
	jumpTrack.Looped = false

	-- --- TOOL SETUP ---
	local tool = Instance.new("Tool")
	tool.Name = "Dash Cursor"
	tool.RequiresHandle = false
	tool.CanBeDropped = false
	tool.Parent = player.Backpack
	local mouse = player:GetMouse()

	tool.Equipped:Connect(function() mouse.Icon = CONFIG.GreenCursorID end)
	tool.Unequipped:Connect(function() mouse.Icon = "" end)

	tool.Activated:Connect(function()
		local state = humanoid:GetState()
		local isInAir = (state == Enum.HumanoidStateType.Freefall or state == Enum.HumanoidStateType.Jumping)
		if isInAir then
			local targetPos = mouse.Hit.Position
			local startPos = rootPart.Position
			local direction = (targetPos - startPos).Unit
			local distance = (targetPos - startPos).Magnitude
			local duration = math.min(distance / CONFIG.DashSpeed, CONFIG.DashMaxTime)

			rollTrack:Play()
			local bv = Instance.new("BodyVelocity")
			bv.Velocity = direction * CONFIG.DashSpeed
			bv.MaxForce = Vector3.new(1e5, 1e5, 1e5)
			bv.P = 10000
			bv.Parent = rootPart
			
			local bg = Instance.new("BodyGyro")
			bg.CFrame = CFrame.lookAt(startPos, targetPos)
			bg.MaxTorque = Vector3.new(1e5, 1e5, 1e5)
			bg.P = 20000
			bg.Parent = rootPart

			task.delay(duration, function()
				if bv then bv:Destroy() end
				if bg then bg:Destroy() end
				rollTrack:Stop()
				rootPart.AssemblyLinearVelocity = Vector3.zero
			end)
		end
	end)

	-- --- DOUBLE JUMP ---
	local hasDoubleJumped = false
	local canTriggerDoubleJump = false
	
	humanoid.StateChanged:Connect(function(oldState, newState)
		if newState == Enum.HumanoidStateType.Landed then
			hasDoubleJumped = false
			canTriggerDoubleJump = false
		elseif newState == Enum.HumanoidStateType.Jumping then
			canTriggerDoubleJump = false
			task.wait(0.2)
			canTriggerDoubleJump = true
		elseif newState == Enum.HumanoidStateType.Freefall then
			if oldState == Enum.HumanoidStateType.Landed then
				canTriggerDoubleJump = true
			end
		end
	end)
	
	UserInputService.JumpRequest:Connect(function()
		local state = humanoid:GetState()
		local isInAir = (state == Enum.HumanoidStateType.Freefall or state == Enum.HumanoidStateType.Jumping)
		if canTriggerDoubleJump and not hasDoubleJumped and isInAir then
			hasDoubleJumped = true
			rootPart.AssemblyLinearVelocity = Vector3.new(rootPart.AssemblyLinearVelocity.X, CONFIG.DoubleJumpPower, rootPart.AssemblyLinearVelocity.Z)
			jumpTrack:Stop()
			jumpTrack:Play()
			humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
	end)

	-- --- ROLL LOGIC ---
	local rolling = false
	local holdingRollKey = false

	local function stopRolling()
		rolling = false
		rollTrack:Stop()
	end

	local function performRoll()
		local direction = rootPart.CFrame.LookVector
		local startPos = rootPart.Position
		local targetPos = startPos + (direction * CONFIG.RollDistance)
		local startTime = tick()
		local connection
		connection = RunService.RenderStepped:Connect(function()
			local elapsed = tick() - startTime
			if elapsed > CONFIG.RollDuration then
				connection:Disconnect()
				if holdingRollKey then performRoll() else stopRolling() end
				return
			end
			local rayParams = RaycastParams.new()
			rayParams.FilterDescendantsInstances = {character}
			rayParams.FilterType = Enum.RaycastFilterType.Exclude
			local rayResult = workspace:Raycast(rootPart.Position, direction * 3, rayParams)
			if rayResult and rayResult.Instance.CanCollide then
				connection:Disconnect()
				stopRolling()
				return
			end
			local alpha = elapsed / CONFIG.RollDuration
			local nextPos = startPos:Lerp(targetPos, alpha)
			rootPart.CFrame = CFrame.new(nextPos, nextPos + direction)
		end)
	end

	local function startRoll()
		if rolling then return end
		rolling = true
		rollTrack:Play()
		performRoll()
	end

	-- Define Input Handler
	local function handleRollInput(actionName, inputState)
		if inputState == Enum.UserInputState.Begin then
			holdingRollKey = true
			startRoll()
		elseif inputState == Enum.UserInputState.End then
			holdingRollKey = false
		end
	end

	-- Function to bind controls based on current selection
	local function bindControls()
		ContextActionService:UnbindAction("RollAction")
		-- Bind R key always, add TouchButton only if mobile
		ContextActionService:BindAction("RollAction", handleRollInput, isMobileMode, Enum.KeyCode.R)
		if isMobileMode then
			ContextActionService:SetTitle("RollAction", "Roll")
			-- Moved to Left Side (0.15 = 15% from left edge)
			ContextActionService:SetPosition("RollAction", UDim2.new(0.15, 0, 0.6, 0))
		end
	end

	-- Watch for selection change if not yet made
	task.spawn(function()
		while not selectionMade do
			task.wait(0.5)
		end
		bindControls()
	end)
	
	if selectionMade then
		bindControls()
	end

	-- --- MOVEMENT LOOP ---
	local function getUserInputVector()
		local moveVector = Vector3.new(0, 0, 0)
		-- Explicit assignment used to prevent compiler errors
		if UserInputService:IsKeyDown(Enum.KeyCode.W) then moveVector = moveVector + Vector3.new(0,0,-1) end
		if UserInputService:IsKeyDown(Enum.KeyCode.S) then moveVector = moveVector + Vector3.new(0,0,1) end
		if UserInputService:IsKeyDown(Enum.KeyCode.A) then moveVector = moveVector + Vector3.new(-1,0,0) end
		if UserInputService:IsKeyDown(Enum.KeyCode.D) then moveVector = moveVector + Vector3.new(1,0,0) end
		
		local success, module = pcall(function() return require(player.PlayerScripts.PlayerModule) end)
		if success and module then
			local controls = module:GetControls()
			if controls then
				local touchMove = controls:GetMoveVector()
				if touchMove.Magnitude > 0 then moveVector = touchMove end
			end
		end
		return moveVector
	end

	RunService.RenderStepped:Connect(function(dt)
		local inputVector = getUserInputVector()
		local isInputting = inputVector.Magnitude > 0.1
		
		if isInputting then
			if rootPart.AssemblyLinearVelocity.Magnitude > 1 then
				lastMoveDirection = Vector3.new(rootPart.AssemblyLinearVelocity.X, 0, rootPart.AssemblyLinearVelocity.Z).Unit
			else
				lastMoveDirection = rootPart.CFrame.LookVector
			end
			currentSpeed = math.min(currentSpeed + (CONFIG.Acceleration * dt), CONFIG.MaxWalkSpeed)
			humanoid.WalkSpeed = currentSpeed
		else
			if currentSpeed > CONFIG.BaseWalkSpeed then
				currentSpeed = math.max(currentSpeed - (DECELERATION * dt), CONFIG.BaseWalkSpeed)
				humanoid.WalkSpeed = currentSpeed
				humanoid:Move(lastMoveDirection, false)
			else
				currentSpeed = CONFIG.BaseWalkSpeed
				humanoid.WalkSpeed = CONFIG.BaseWalkSpeed
			end
		end
		
		local velocityMag = Vector3.new(rootPart.AssemblyLinearVelocity.X, 0, rootPart.AssemblyLinearVelocity.Z).Magnitude
		local intensity = math.clamp((velocityMag - CONFIG.BaseWalkSpeed) / (CONFIG.MaxWalkSpeed - CONFIG.BaseWalkSpeed), 0, 1)
		
		local targetFOV = CONFIG.BaseFOV + (intensity * (CONFIG.MaxFOV - CONFIG.BaseFOV))
		camera.FieldOfView = camera.FieldOfView + (targetFOV - camera.FieldOfView) * math.clamp(dt * 5, 0, 1)

		if velocityMag > 1 then
			if not runTrack.IsPlaying then runTrack:Play() end
			runTrack:AdjustSpeed(0.5 + (intensity * 2)) 
		else
			if runTrack.IsPlaying then runTrack:Stop() end
		end
	end)
end

-- Init
createIntroGUI() 
if player.Character then
	onCharacterAdded(player.Character)
end

player.CharacterAdded:Connect(onCharacterAdded)

Features & Information

r to roll hold r to hold roll custom jump animation speedy run(of course) and of course made in chatgpt cause im an idiot

Community Comments (7)

wokhard213wokhard213Jul 30, 2024(edited)

kinda buggy but it works ig

Ps4sroPs4sroJul 30, 2024(edited)

@darkbeing yeah it is

Ps4sroPs4sroJul 30, 2024(edited)

@wokhard213 i made it(chatgpt) in like 15 minutes

chat gpt scripts suck

Replying to @robloxtestinm63_hack
Ps4sroPs4sroDec 4, 2025

@robloxtestinm63_hack i cant code for *

CarinoCarinoDec 2, 2025

Please make mobile buttons but it’s so good I can’t use it but I do in script builder

Replying to @Carino
Ps4sroPs4sroDec 5, 2025

@Carino i update

More Scripts for Universal Scripts