So, I have this LocalScript that controls the movement for the player (movement is replicated on the server just without tween animations) and right now I just can’t figure out how to stop the player from glitching a bit while stepping on the lilypads. The character also can float above or clip into it if they squish themselves (by holding down a movement button like wasd) before the lilypad has finished its sinking animations.
LocalScript:
local userInputService = game:GetService("UserInputService")
local tweenService = game:GetService("TweenService")
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage.remotes
local player = script.Parent:WaitForChild("actualCharacter")
local bottomAttachment = player:FindFirstChild("BottomAttachment", true)
local playerHead = script.Parent:WaitForChild("Head")
local playerTorso = script.Parent:WaitForChild("Torso")
local humanoid = script.Parent:WaitForChild("Humanoid")
local bodyPosition = player.Parent:WaitForChild("cameraFollowPart"):WaitForChild("BodyPosition")
local localPlayer = Players.LocalPlayer
local mouse = localPlayer:GetMouse()
local barrierLeft = Workspace.barrierLeft
local barrierRight = Workspace.barrierRight
local distance = 8
local jumpHeight = 6
local moveTime = 0.14
local rotateTime = 0.21
local squishTime = 0.2
local moveCooldowns = {}
local moveCooldownTime = moveTime
local moveTweenInfo = TweenInfo.new(moveTime, Enum.EasingStyle.Quart)
local rotateTweenInfo = TweenInfo.new(rotateTime, Enum.EasingStyle.Back)
local squishTweenInfo = TweenInfo.new(squishTime, Enum.EasingStyle.Back)
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {barrierLeft, barrierRight, player.Parent}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
local initialPlayerSize = player.Size
local initialPlayerPositionY = player.Position.Y
local viewGridSpace = false
local isMoving = false
local isSquishing = false
local platformOffset = Vector3.new()
local facingDirection = "back"
local currentPlatform = nil
local connection
function keyDownSquish(mode: boolean, x, z)
if not x then x = 0 end
if not z then z = 0 end
local currentY = player.Position.Y
isSquishing = true
if mode then
local newSize = Vector3.new(initialPlayerSize.X + 0.5, initialPlayerSize.Y - 3, initialPlayerSize.Z)
local sizeDiffY = (initialPlayerSize.Y - newSize.Y) / 2
tweenService:Create(player, squishTweenInfo, {
Size = newSize,
Position = Vector3.new(player.Position.X, currentY - sizeDiffY, player.Position.Z)
}):Play()
else
tweenService:Create(player, squishTweenInfo, {
Size = initialPlayerSize,
Position = Vector3.new(player.Position.X + x, currentY, player.Position.Z + z)
}):Play()
task.delay(squishTime, function()
isSquishing = false
end)
end
end
function shortestRotation(currentY, targetY)
local delta = (targetY - currentY) % 360
if delta > 180 then
delta = delta - 360
end
return delta
end
function handleLilypadInteraction(hitPart)
currentPlatform = hitPart
platformOffset = player.Position - hitPart.Position
local tweenInfo = TweenInfo.new(0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
local originalPosition = hitPart.Position
local sinkPosition = originalPosition - Vector3.new(0, 0.8, 0)
local sinkTween = tweenService:Create(hitPart, tweenInfo, {Position = sinkPosition})
local riseTween = tweenService:Create(hitPart, tweenInfo, {Position = originalPosition})
connection = RunService.RenderStepped:Connect(function()
if currentPlatform and currentPlatform:IsDescendantOf(Workspace.environment.obstacles) then
if isSquishing then
player.Position = Vector3.new(player.Position.X, currentPlatform.Position.Y + platformOffset.Y - 1.5, player.Position.Z)
else
player.Position = Vector3.new(player.Position.X, currentPlatform.Position.Y + platformOffset.Y, player.Position.Z)
end
end
end)
sinkTween:Play()
sinkTween.Completed:Wait()
riseTween:Play()
riseTween.Completed:Wait()
if connection then
connection:Disconnect()
end
end
function moveCharacter(direction: string)
if not direction or isMoving then return end
if connection then
connection:Disconnect()
end
isMoving = true
local targetPosition
local cameraNextPosition
if direction == "front" then
targetPosition = player.Position + Vector3.new(0, 0, -distance)
cameraNextPosition = Vector3.new(0, 0, -distance)
elseif direction == "back" then
targetPosition = player.Position + Vector3.new(0, 0, distance)
cameraNextPosition = Vector3.new(0, 0, distance)
elseif direction == "left" then
targetPosition = player.Position + Vector3.new(-distance, 0, 0)
cameraNextPosition = Vector3.new(-distance, 0, 0)
elseif direction == "right" then
targetPosition = player.Position + Vector3.new(distance, 0, 0)
cameraNextPosition = Vector3.new(distance, 0, 0)
end
if targetPosition then
local raycastResult = workspace:Raycast(targetPosition + Vector3.new(0, 5, 0), Vector3.new(0, -10, 0), raycastParams)
if raycastResult then
local attachment = raycastResult.Instance:FindFirstChild("Attachment")
if attachment and bottomAttachment then
local desiredY = attachment.WorldPosition.Y - (bottomAttachment.WorldPosition.Y - player.Position.Y)
targetPosition = Vector3.new(targetPosition.X, desiredY, targetPosition.Z)
else
targetPosition = Vector3.new(targetPosition.X, initialPlayerPositionY, targetPosition.Z)
end
end
local yDiff = math.abs(targetPosition.Y - player.Position.Y)
local adjustedJumpHeight = math.max(jumpHeight, yDiff + 2) --clearance for steeper jump e.g: falling into water
local midPoint = player.Position:Lerp(targetPosition, 0.5) + Vector3.new(0, adjustedJumpHeight, 0)
local jumpUp = tweenService:Create(player, TweenInfo.new(moveTime / 2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {
Position = midPoint
})
local jumpDown = tweenService:Create(player, TweenInfo.new(moveTime / 2, Enum.EasingStyle.Sine, Enum.EasingDirection.In), {
Position = targetPosition
})
jumpUp:Play()
bodyPosition.Position += cameraNextPosition
if facingDirection ~= direction then
local targetY
if direction == "front" then
targetY = 0
elseif direction == "back" then
targetY = -180
elseif direction == "left" then
targetY = 90
elseif direction == "right" then
targetY = -90
end
if targetY then
local rotationDelta = shortestRotation(player.Orientation.Y, targetY)
tweenService:Create(player, rotateTweenInfo, {
Orientation = player.Orientation + Vector3.new(0, rotationDelta, 0)
}):Play()
end
facingDirection = direction
end
jumpUp.Completed:Wait()
jumpDown:Play()
jumpDown.Completed:Wait()
isMoving = false
local raycastResult = workspace:Raycast(player.Position + Vector3.new(0, 5, 0), Vector3.new(0, -10, 0), raycastParams)
if raycastResult then
local hitPart = raycastResult.Instance
if hitPart and hitPart.Name == "lilypad" then
handleLilypadInteraction(hitPart)
else
currentPlatform = nil
end
end
remotes.movementEvent:FireServer(direction, player.Position)
remotes.rotateEvent:FireServer(direction)
else
isMoving = false
end
end
bodyPosition.Position = playerTorso.Position + Vector3.new(-6, 0, -24)
player.Orientation = Vector3.new(0, -180, 0)
playerHead.Orientation = Vector3.new(0, -180, 0)
playerTorso.Orientation = Vector3.new(0, -180, 0)
humanoid.WalkSpeed = 0
humanoid.JumpPower = 0
--commences the squishing
userInputService.InputBegan:Connect(function(input, gameProcessedEvent)
if not userInputService.KeyboardEnabled then return end
if gameProcessedEvent then return end
if isMoving then return end
local key = input.KeyCode
if key == Enum.KeyCode.W or key == Enum.KeyCode.Up
or key == Enum.KeyCode.S or key == Enum.KeyCode.Down
or key == Enum.KeyCode.A or key == Enum.KeyCode.Left
or key == Enum.KeyCode.D or key == Enum.KeyCode.Right then
keyDownSquish(true)
end
end)
function canMove(key)
local now = tick()
if moveCooldowns[key] and now < moveCooldowns[key] then
return false
end
moveCooldowns[key] = now + moveCooldownTime
return true
end
--actually moves the character
userInputService.InputEnded:Connect(function(input, gameProcessedEvent)
if not userInputService.KeyboardEnabled then return end
if gameProcessedEvent then return end
if isMoving then return end
local key = input.KeyCode
if (key == Enum.KeyCode.W or key == Enum.KeyCode.Up) and canMove("front") then
keyDownSquish(false, 0, -distance)
moveCharacter("front")
elseif (key == Enum.KeyCode.S or key == Enum.KeyCode.Down) and canMove("down") then
keyDownSquish(false, 0, distance)
moveCharacter("back")
elseif (key == Enum.KeyCode.A or key == Enum.KeyCode.Left) and canMove("left") then
keyDownSquish(false, -distance, 0)
moveCharacter("left")
elseif (key == Enum.KeyCode.D or key == Enum.KeyCode.Right) and canMove("right") then
keyDownSquish(false, distance, 0)
moveCharacter("right")
end
end)
--commences the squishing for mouse
mouse.Button1Down:Connect(function()
if not userInputService.KeyboardEnabled then return end
if isMoving then return end
keyDownSquish(true)
end)
--releases the squishing and does movement for mouse
mouse.Button1Up:Connect(function()
if not userInputService.KeyboardEnabled then return end
if isMoving then return end
if not canMove("front") then return end
keyDownSquish(false, 0, -distance)
moveCharacter("front")
end)
--mobile stuff
userInputService.TouchStarted:Connect(function(input, gameProcessedEvent)
if gameProcessedEvent then return end
if isMoving then return end
keyDownSquish(true)
end)
userInputService.TouchTap:Connect(function(touchPosition, gameProcessedEvent)
if gameProcessedEvent then return end
if isMoving then return end
if not canMove("front") then return end
keyDownSquish(false, 0, -distance)
moveCharacter("front")
remotes.movementEvent:FireServer("front")
remotes.rotateEvent:FireServer("front")
end)
userInputService.TouchSwipe:Connect(function(input)
if input == Enum.SwipeDirection.Up then
keyDownSquish(false, 0, -distance)
moveCharacter("front")
elseif input == Enum.SwipeDirection.Down then
keyDownSquish(false, 0, distance)
moveCharacter("back")
elseif input == Enum.SwipeDirection.Left then
keyDownSquish(false, -distance, 0)
moveCharacter("left")
elseif input == Enum.SwipeDirection.Right then
keyDownSquish(false, distance, 0)
moveCharacter("right")
end
end)
if viewGridSpace then
local part = Instance.new("Part")
part.Size = Vector3.new(8, 10.5, 8)
part.Anchored = false
part.CanCollide = false
part.CastShadow = false
part.Transparency = 0.7
part.Name = "GridSpacePart"
part.Parent = player.Parent
local weld = Instance.new("WeldConstraint")
weld.Part0 = player.Parent:WaitForChild("GridSpacePart")
weld.Part1 = player
weld.Parent = part
RunService.Heartbeat:Connect(function()
part.Position = player.Position
part.CFrame = CFrame.new(part.Position) * CFrame.Angles(0, 0, 0)
end)
end