
KeylessUniversal

--- made by forsaken c:
workspace.Gravity = 100
local Config = {
--[[General]]
VISUALIZE_FEET_HB = false, -- Visualize Feet Hitbox
VISUALIZE_COLLIDE_AND_SLIDE = false,
STEP_OFFSET = 1.2,
MASS = 16,
AIR_FRICTION = 0.4,
FRICTION = 6,
GRAVITY = 10,
JUMP_VELOCITY = 30,
--[[Accel/Deccel]]
GROUND_ACCEL = 14,
GROUND_DECCEL = 10,
AIR_ACCEL = 52,
--[[General Speed]]
AIR_SPEED = 42,
RUN_SPEED = 32,
WALK_SPEED = 10,
CROUCH_SPEED = 10,
--[[Advanced Speed]]
AIR_MAX_SPEED = 36.5, -- The speed at which AIR_MAX_SPEED_FRIC is applied.
AIR_MAX_SPEED_FRIC = 3, -- The initial friction applied at max speed
AIR_MAX_SPEED_FRIC_DEC = .5, -- Amount multiplied to current max speed friction per 1/60sec
MIN_SLOPE_ANGLE = 40,
MAX_SLOPE_ANGLE = 75,
--[[Misc (Don't worry about these)]]
LEG_HEIGHT = 1.9+.3,
TORSO_TO_FEET = 3.1+1.9,
FEET_HB_SIZE = Vector3.new(1,0.1,1),
TORSO_HB_SIZE = Vector3.new(3,1,3),
FOOT_OFFSET_AMOUNT = 1.2
}
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local ids = {
"rbxassetid://7338942417",
"rbxassetid://7338942345",
"rbxassetid://7338942259",
"rbxassetid://7338942180",
"rbxassetid://7338942417",
}
local sounds = {}
for _, i in ipairs(ids) do
local sound = Instance.new("Sound")
sound.SoundId = i
sound.Volume = 2
sound.Parent = game.Workspace -- Or another appropriate parent
table.insert(sounds, sound)
end
export type Movement = {
Keys: {W: number, A: number, S: number, D: number, Space: number},
player: Player,
character: Model,
collider: Part,
vispart: Part?,
vis_coll_parts: {Part},
config: MovementConfig,
mover: LinearVelocity,
up_mover: LinearVelocity,
dt: number,
states: MovementStates,
}
export type MovementConfig = {
VISUALIZE_FEET_HB: boolean,
VISUALIZE_COLLIDE_AND_SLIDE: boolean,
LEG_HEIGHT: number,
FEET_HB_SIZE: Vector3,
TORSO_HB_SIZE: Vector3,
MASS: number,
GRAVITY: number,
FRICTION: number,
AIR_FRICTION: number,
GROUND_ACCEL: number,
GROUND_DECCEL: number,
AIR_ACCEL: number,
RUN_SPEED: number,
WALK_SPEED: number,
CROUCH_SPEED: number,
AIR_SPEED: number,
AIR_MAX_SPEED: number,
AIR_MAX_SPEED_FRIC: number,
AIR_MAX_SPEED_FRIC_DEC: number,
JUMP_VELOCITY: number,
STEP_OFFSET: number,
TORSO_TO_FEET: number,
MIN_SLOPE_ANGLE: number,
MAX_SLOPE_ANGLE: number,
}
export type MovementStates = {
grounded: boolean,
air_friction: number,
input_vec: number,
surfing: boolean,
jumping: boolean,
}
export type Vec3Mod = {x: number, y: number, z: number, ToVector3: (Vec3Mod) -> Vector3}
local Vec3Mod = {}
Vec3Mod.__index = Vec3Mod
function Vec3Mod.new(input: Vector3?) : Vec3Mod
input = input or Vector3.zero
return setmetatable({x = input.X, y = input.Y, z = input.Z}, Vec3Mod) :: Vec3Mod
end
function Vec3Mod:ToVector3()
return Vector3.new(self.x, self.y, self.z)
end
local Movement: Movement = {}
local Physics = {} :: Movement
local Shared : Movement= {}
local Trace : Movement= {}
local Collisions = {} :: Movement
local function visualizeRayResult(result: RaycastResult, origin: Vector3, direction: Vector3)
local position = result and result.Position or (origin + direction)
local distance = (origin - position).Magnitude
local p = Instance.new("Part")
p.Anchored = true
p.CanCollide = false
p.Size = Vector3.new(0.1, 0.1, distance)
p.CFrame = CFrame.lookAt(origin, position)*CFrame.new(0, 0, -distance/2)
p.Parent = workspace.Temp
return p
end
local function flattenVectorAgainstWall(moveVector: Vector3, normal: Vector3)
-- if magnitudes are 0 then just nevermind
if moveVector.Magnitude == 0 and normal.Magnitude == 0 then
return Vector3.zero
end
-- unit the normal (i its already normalized idk)
normal = normal.Unit
-- reflect the vector
local reflected = moveVector - 2 * moveVector:Dot(normal) * normal
-- add the reflection to the move vector = vector parallel to wall
local parallel = moveVector + reflected
-- if magnitude 0 NEVERMIND!!!
if parallel.Magnitude == 0 then
return Vector3.zero
end
-- reduce the parallel vector to make sense idk HorseNuggetsXD did all this thank u
local cropped = parallel.Unit:Dot(moveVector.Unit) * parallel.Unit * moveVector.Magnitude
return cropped
end
function Collisions:CollideAndSlide(wishedSpeed: Vector3)
local mod = 1
if wishedSpeed.Magnitude == 0 then
return wishedSpeed
end
-- get input vector
local inputVec = self.states.input_vec
local newSpeed = wishedSpeed
local hrp = self.player.Character.HumanoidRootPart
-- raycast var
local params = Shared.GetMovementParams(self)
-- wished speed modifier
wishedSpeed *= 2
-- direction amount var
local dirAmnt = 1.375 * (mod or 1)
local mainDirAmnt = 1.55 * (mod or 1)
-- stick var
local isSticking = false
local normals = {}
local stickingDirections = {}
local ldd = {dir = false, dist = false} -- lowest distance direction
local partsAlreadyHit = {}
-- destroy sticking visualizations
if Config.VISUALIZE_COLLIDE_AND_SLIDE then
for _, v in pairs(self.vis_coll_parts) do
v:Destroy()
end
end
local lookVecs = {
Vector3.new(0, (-Config.TORSO_TO_FEET) + Config.STEP_OFFSET, 0),
Vector3.new(0, 0, 0),
Vector3.new(0, 2, 0)
}
for _, v in pairs(lookVecs) do
local values = {}
local hval = {}
local currForDir
local currSideDir
local rayPos
if typeof(v) == "Vector3" then
rayPos = hrp.Position + v
else
rayPos = Vector3.new(hrp.Position.X, hrp.Parent[v].CFrame.Position, hrp.Position.Z)
end
-- right, front, back
if inputVec.X > 0 then
currForDir = hrp.CFrame.RightVector
table.insert(values, currForDir)
table.insert(hval, hrp.CFrame.LookVector * dirAmnt)
table.insert(hval, -hrp.CFrame.LookVector * dirAmnt)
-- left, front, back
elseif inputVec.X < 0 then
currForDir = -hrp.CFrame.RightVector
table.insert(values, currForDir)
table.insert(hval, hrp.CFrame.LookVector * dirAmnt)
table.insert(hval, -hrp.CFrame.LookVector * dirAmnt)
end
-- back, left, right
if inputVec.Z > 0 then
currSideDir = -hrp.CFrame.LookVector
table.insert(values, currSideDir)
table.insert(hval, hrp.CFrame.RightVector * dirAmnt)
table.insert(hval, -hrp.CFrame.RightVector * dirAmnt)
-- front, left, right
elseif inputVec.Z < 0 then
currSideDir = hrp.CFrame.LookVector
table.insert(values, currSideDir)
table.insert(hval, hrp.CFrame.RightVector * dirAmnt)
table.insert(hval, -hrp.CFrame.RightVector * dirAmnt)
end
if inputVec.Z == 0 and inputVec.X == 0 then
values[1] = wishedSpeed.Unit
table.insert(hval, CFrame.new(wishedSpeed.Unit).RightVector * dirAmnt)
table.insert(hval, -CFrame.new(wishedSpeed.Unit).RightVector * dirAmnt)
else
table.insert(values, wishedSpeed.Unit * dirAmnt)
end
-- middle directions
if currForDir and currSideDir then
for a, b in pairs(values) do
values[a] = b * mainDirAmnt
end
table.insert(values, (currForDir+currSideDir) * mainDirAmnt)
else
values[1] *= mainDirAmnt
table.insert(values, (values[1] + hval[1]).Unit * mainDirAmnt)
table.insert(values, (values[1] + hval[2]).Unit * mainDirAmnt)
table.insert(values, hval[1])
table.insert(values, hval[2])
end
for _, b in pairs(values) do
if not b then continue end
-- visualize ray using pos and direction
if Config.VISUALIZE_COLLIDE_AND_SLIDE then
table.insert(self.vis_coll_parts, visualizeRayResult(false, rayPos, b))
end
local result = workspace:Raycast(rayPos, b, params)
if not result then continue end
if (not ldd.dir or not ldd.dist) or (ldd.dist and ldd.dist < result.Distance) then
ldd.dir = b
ldd.dist = result.Distance
end
-- don't collide with the same part twice
if table.find(partsAlreadyHit, result.Instance) then continue end
table.insert(partsAlreadyHit, result.Instance)
-- get the movement direction compared to the wall
local _v = newSpeed.Unit * result.Normal
-- find active coordinate of comparison
for _, c in pairs({_v.X, _v.Y, _v.Z}) do
if math.abs(c) > 0 then
_v = c
break
end
end
-- if we are moving AWAY from the normal, (positive)
-- then do not flatten the vector.
-- it's not necessary.
-- you will stick.
-- stick.
if type(_v) == "number" and _v > 0 then
continue
end
if not isSticking then isSticking = true end
newSpeed = flattenVectorAgainstWall(newSpeed, result.Normal)
newSpeed -= result.Instance.Velocity
self.mover.PlaneVelocity = Vector2.new(newSpeed.X, newSpeed.Z)
--self.collider.Velocity = Vector3.new(newSpeed.X, (self.collider.Velocity.Y), newSpeed.Z) -- anti sticking has to be applied on collider velocity as well (resolves head & in air collision)
end
end
return newSpeed, isSticking and normals, isSticking and stickingDirections, isSticking and ldd.dir
end
export type Trace = {
startPos: Vector3,
endPos: Vector3,
fraction: number,
startSolid: boolean,
hitPart: Instance,
hitPoint: Vector3,
planeNormal: Vector3,
distance: number
}
local function HandleVisualization(self, cf, size)
if not Config.VISUALIZE_FEET_HB then
return
end
if not self.vispart then
self.vispart = Instance.new("Part", self.character)
self.vispart.CanCollide = false
self.vispart.Anchored = true
end
self.vispart.CFrame = cf
self.vispart.Size = size
end
function Shared.GetAngle(normal)
return math.deg(math.acos(normal:Dot(Vector3.yAxis)))
end
function Shared:IsGrounded(dir): (boolean, boolean?, RaycastResult?, number?)
local params = Shared.GetMovementParams(self)
local cf = CFrame.new(self.collider.Position) - Vector3.new(0, self.collider.Size.Y/2, 0)
local size = Config.FEET_HB_SIZE
dir = dir or Vector3.new(0,-1 * self.collider.Size.Y-Config.FOOT_OFFSET_AMOUNT,0)
local result = workspace:Blockcast(cf, size, dir, params)
HandleVisualization(self, cf, size)
if not result then
return false
end
local steepness = Shared.GetAngle(result.Normal)
local isSurfing = steepness >= Config.MIN_SLOPE_ANGLE and steepness <= Config.MAX_SLOPE_ANGLE
if isSurfing then
return false, true, result, steepness
end
return true, false, result
end
function Shared:RotateCharacter()
local collider = self.collider
local camera = workspace.CurrentCamera
local rotationLook = collider.Position + camera.CoordinateFrame.lookVector
collider.CFrame = CFrame.new(collider.Position, Vector3.new(rotationLook.x, collider.Position.y, rotationLook.z))
collider.RotVelocity = Vector3.new()
end
function Shared:GetMovementDirection(groundNormal)
local forward = self.Keys.W + -self.Keys.S
local side = self.Keys.A + -self.Keys.D
groundNormal = groundNormal or Vector3.new(0,1,0)
if forward == 0 and side == 0 then
self.states.input_vec = Vector3.zero
return Vector3.zero
else
self.states.input_vec = Vector3.new(-side, 0, -forward).Unit
local forwardMove = groundNormal:Cross(self.collider.CFrame.RightVector)
local sideMove = groundNormal:Cross(forwardMove)
return (forwardMove * forward + sideMove * side).Unit
end
end
function Shared:GetMovementParams()
local params = RaycastParams.new()
params.FilterDescendantsInstances = {self.character, workspace.CurrentCamera}
params.FilterType = Enum.RaycastFilterType.Exclude
params.RespectCanCollide = false
params.CollisionGroup = "PlayerMovement"
return params
end
function Shared:VectorMa(start: Vector3, scale: number, direction: Vector3)
return Vector3.new(
start.X + direction.X * scale,
start.Y + direction.Y * scale,
start.Z + direction.Z * scale
)
end
local Trace: Movement = {}
local numBumps = 1
local maxClipPlanes = 5
local _planes = {}
local numToCoord = {"X", "Y", "Z"}
local function ClipVelocity(self, input, normal, output, overbounce)
local newVelMod = Vec3Mod.new(output)
Trace.ClipVelocity(self, input, normal, newVelMod, overbounce)
return newVelMod:ToVector3()
end
function Trace:TraceBox(start: Vector3, destination: Vector3)
local contactOffset = 0.1 -- idk what this is
local longSide = math.sqrt(contactOffset * contactOffset + contactOffset * contactOffset)
local direction = (destination - start).Unit
local maxDistance = (start - destination).Magnitude + longSide
local result: Trace = {
startPos = start,
endPos = destination
}
local _, _, hit = Shared.IsGrounded(self, direction*maxDistance)
if hit then
result.fraction = hit.Distance / maxDistance
result.hitPart = hit.Instance
result.hitPoint = hit.Position
result.planeNormal = hit.Normal
result.distance = hit.Distance
else
result.fraction = 1
end
return result
end
function Trace:ClipVelocity(input: Vector3, normal: Vector3, output: Types.Vec3Mod, overbounce: number)
local angle = normal.Y
local blocked = 0x00 -- // Assume unblocked.
if angle > 0 then -- // If the plane that is blocking us has a positive z component, then assume it's a floor.
blocked = blocked or 0x01
end
if angle == 0 then -- // If the plane has no Z, it is vertical (wall/step)
blocked = blocked or 0x02
end
-- // Determine how far along plane to slide based on incoming direction.
local backoff = input:Dot(normal) * overbounce
--// iterate once to make sure we aren't still moving through the plane
for i = 1, 3, 1 do
local ci = numToCoord[i]
local change = normal[ci] * backoff
output[ci] = input[ci] - change
end
local adjust = output:ToVector3():Dot(normal)
if adjust < 0 then
output = output:ToVector3()-(normal * adjust)
end
-- Return blocking flags.
return blocked
end
function Trace:Reflect(velocity: Vector3, origin: Vector3)
self = self :: Movement
local d
local newVelocity = Vector3.zero
local blocked = 0
local numplanes = 1
local originalVelocity = velocity
local primalVelocity = velocity
local allFraction = 0
local timeLeft = self.dt
for bumpcount = 1, numBumps, 1 do
if velocity.Magnitude == 0 then
break
end
-- Assume we can move all the way from the current origin to the
-- end point.
local endp = Shared.VectorMa(self, origin, timeLeft, velocity)
local trace = Trace.TraceBox(self, origin, endp)
allFraction += trace.fraction
if trace.fraction > 0 then
-- actually covered some distance
originalVelocity = velocity
numplanes = 0
end
-- If we covered the entire distance, we are done
-- and can return.
if trace.fraction == 1 then
break
end -- moved the entire distance
-- If the plane we hit has a high z component in the normal, then
-- it's probably a floor
if trace.planeNormal.Y > Config.MAX_SLOPE_ANGLE then
blocked = blocked or 1
end
-- If the plane has a zero z component in the normal, then it's a
-- step or wall
if trace.planeNormal.Y == 0 then
blocked = blocked or 2 -- step/wall
end
-- Reduce amount of m_flFrameTime left by total time left * fraction
-- that we covered.
timeLeft -= timeLeft * trace.fraction
-- Did we run out of planes to clip against?
if numplanes >= maxClipPlanes then
-- this shouldn't really happen
-- Stop our movement if so.
velocity = Vector3.zero
break
end
-- Set up next clipping plane
_planes[numplanes] = trace.planeNormal
numplanes+=1
-- modify original_velocity so it parallels all of the clip planes
--
-- reflect player velocity
-- Only give this a try for first impact plane because you can get yourself stuck in an acute corner by jumping in place
-- and pressing forward and nobody was really using this bounce/reflection feature anyway...
if numplanes == 2 then
if _planes[1].Y > Config.MAX_SLOPE_ANGLE then
return blocked
else
newVelocity = ClipVelocity(self, originalVelocity, _planes[1], newVelocity, 1)
end
velocity = newVelocity
originalVelocity = newVelocity
else
local _i = 0
for i = 1, numplanes-1, 1 do
_i = i
newVelocity = ClipVelocity(self, originalVelocity, _planes[i], newVelocity, 1)
local _j = 0
for j = 1, numplanes-1, 1 do
_j = j
if j ~= i and velocity:Dot(_planes[j]) < 0 then
break
end
end
if _j == numplanes then
break
end
end
if _i ~= numplanes then
else
if numplanes ~= 3 then
velocity = Vector3.zero
break
end
local dir = _planes[1]:Cross(_planes[2]).Unit
d = dir:Dot(velocity)
velocity = dir * d
end
end
d = velocity:Dot(primalVelocity)
if d <= 0 then
velocity = Vector3.zero
break
end
end
if allFraction == 0 then
velocity = Vector3.zero
end
return velocity, blocked
end
local function ApplyMoverVelocity(self: Movement, velocity: Vector3): Vector2
local vel = Vector2.new(velocity.X, velocity.Z)
self.mover.PlaneVelocity = vel
return vel
end
function Physics:ApplyGroundVelocity(groundNormal: Vector3)
local wishDir = Shared.GetMovementDirection(self, groundNormal)
local wishSpeed = wishDir.Magnitude * Config.RUN_SPEED
-- normal friction
if self.states.air_friction <= 0 then
Physics.ApplyFriction(self)
else
-- friction that is applied due to the player reaching max speed while bhopping
local sub = Config.AIR_MAX_SPEED_FRIC_DEC * self.dt * 60
local curr = self.states.air_friction
local fric = curr - sub
if fric < 0 then
fric = curr + fric
end
Physics.ApplyFriction(self, math.max(1, fric/Config.FRICTION))
self.states.air_friction = math.max(0, curr - sub)
end
Physics.ApplyGroundAcceleration(self, wishDir, wishSpeed)
Collisions.CollideAndSlide(self, Vector3.new(self.mover.PlaneVelocity.X, 0, self.mover.PlaneVelocity.Y))
-- calculate & apply slope movement
if Shared.GetAngle(groundNormal) < 5 then
return
end
local curVel: Vector3 = Vector3.new(self.mover.PlaneVelocity.X, self.collider.Velocity.Y, self.mover.PlaneVelocity.Y)
local forVel: Vector3 = groundNormal:Cross(CFrame.Angles(0,math.rad(90),0).LookVector * curVel)
local yVel: number = 0
if forVel.Magnitude > 0 then
yVel = forVel.Unit.Y * curVel.Magnitude
end
self.collider.Velocity = Vector3.new(curVel.X, yVel, curVel.Z)
end
function Physics:ApplyGroundAcceleration(wishDir: Vector3, wishSpeed: number)
local addSpeed
local accelerationSpeed
local currentSpeed
local currentVelocity = Vector3.new(self.mover.PlaneVelocity.X, 0, self.mover.PlaneVelocity.Y)
local newVelocity = currentVelocity
-- get current/add speed
currentSpeed = currentVelocity:Dot(wishDir)
addSpeed = wishSpeed - currentSpeed
-- if we're not adding speed, dont do anything
if addSpeed <= 0 then return end
-- get accelSpeed, cap at addSpeed
accelerationSpeed = math.min(Config.GROUND_ACCEL * self.dt * wishSpeed, addSpeed)
-- you can't change the properties of a Vector3, so we do x, y, z
newVelocity += (accelerationSpeed * wishDir)
newVelocity = Vector3.new(newVelocity.X, 0, newVelocity.Z)
-- clamp magnitude (max speed)
if newVelocity.Magnitude > (Config.RUN_SPEED) then
newVelocity = newVelocity.Unit * math.min(newVelocity.Magnitude, (Config.RUN_SPEED))
end
-- apply acceleration
ApplyMoverVelocity(self, newVelocity)
end
function Physics:ApplyAirVelocity(normal)
normal = Vector3.new(0,1,0)
local vel = Vector3.new(self.mover.PlaneVelocity.X, 0, self.mover.PlaneVelocity.Y)
local wishDir = Shared.GetMovementDirection(self, normal)
local wishSpeed = wishDir.Magnitude * Config.AIR_SPEED
local currSpeed = vel.Magnitude
-- initiate extra friction for max speed
if currSpeed > Config.AIR_MAX_SPEED then
self.states.air_friction = Config.AIR_MAX_SPEED_FRIC
end
-- apply extra friction if necessary
if self.states.air_friction > 0 and not self.states.surfing then
Physics.ApplyFriction(self, 0.01 * self.states.air_friction, false)
end
Physics.ApplyAirAcceleration(self, wishDir, wishSpeed)
local refVel = Vector3.new(self.mover.PlaneVelocity.X, self.collider.Velocity.Y, self.mover.PlaneVelocity.Y)
ApplyMoverVelocity(self, Trace.Reflect(self, refVel, self.collider.Position))
end
function Physics:ApplyAirAcceleration(wishDir: Vector3, wishSpeed: number)
local currentSpeed
local addSpeed
local accelerationSpeed
local currentVelocity = Vector3.new(self.mover.PlaneVelocity.X, 0, self.mover.PlaneVelocity.Y)
-- get current/add speed
currentSpeed = currentVelocity:Dot(wishDir)
addSpeed = wishSpeed - currentSpeed
-- if we're not adding speed, dont do anything
if addSpeed <= 0 then return end
-- get accelSpeed, cap at addSpeed
accelerationSpeed = math.min(Config.AIR_ACCEL * self.dt * wishSpeed, addSpeed)
-- get new velocity
local newVelocity = currentVelocity + accelerationSpeed * wishDir
-- apply acceleration
ApplyMoverVelocity(self, newVelocity)
end
function Physics:ApplyFriction(modifier: number?, inAir: boolean?)
local vel
if inAir then
vel = self.collider.Velocity
else
vel = Vector3.new(self.mover.PlaneVelocity.X, 0, self.mover.PlaneVelocity.Y)
end
local speed = vel.Magnitude
modifier = modifier or 1
local drop = 0
local fric = inAir and Config.AIR_FRICTION or Config.FRICTION
local decel = Config.GROUND_DECCEL
local newSpeed
local control
-- if we're not moving, don't apply friction
if speed <= 0 then
return vel
end
-- ???
control = speed < decel and decel or speed
drop = control * fric * self.dt * modifier
if type(drop) ~= "number" then drop = drop.Magnitude end
-- ????????????
newSpeed = math.max(speed - drop, 0)
if speed > 0 and newSpeed > 0 then
newSpeed /= speed
end
vel *= newSpeed
ApplyMoverVelocity(self, vel)
if inAir then
local nv = self.mover.VectorVelocity
--self.collider.Velocity = Vector3.new(nv.X, self.collider.Velocity.Y * newSpeed, nv.Z)
end
end
local function Init()
Movement.Keys = {W = 0, S = 0, D = 0, A = 0, Space = 0}
Movement.player = game.Players.LocalPlayer
Movement.character = Movement.player.Character or Movement.player.CharacterAdded:Wait()
Movement.collider = Movement.character:WaitForChild("HumanoidRootPart")
Movement.vispart = false
Movement.vis_coll_parts = {}
Movement.config = Config
local mover = Instance.new("LinearVelocity", Movement.collider)
local a0 = Instance.new("Attachment", Movement.collider)
a0.Name = "MovementAttachment"
mover.Attachment0 = a0
mover.MaxForce = 10000000
mover.VelocityConstraintMode = Enum.VelocityConstraintMode.Plane
mover.PrimaryTangentAxis = Vector3.new(1,0,0)
mover.SecondaryTangentAxis = Vector3.new(0,0,1)
Movement.mover = mover
-- Will figure this out soon.
--[[local upMover = Instance.new("LinearVelocity", Movement.collider)
upMover.Attachment0 = Movement.collider:WaitForChild("MovementAttachment")
upMover.ForceLimitMode = Enum.ForceLimitMode.PerAxis
upMover.ForceLimitsEnabled = true
upMover.MaxAxesForce = Vector3.new(0, 100000, 0)
upMover.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
Movement.up_mover = upMover]]
Movement.states = {grounded = false, air_friction = 0, input_vec = Vector3.zero, surfing = false}
end
--
local function Gravity()
local mod = Movement.config.GRAVITY * Movement.dt
Movement.collider.AssemblyLinearVelocity = Vector3.new(
Movement.collider.AssemblyLinearVelocity.X,
Movement.collider.AssemblyLinearVelocity.Y - mod,
Movement.collider.AssemblyLinearVelocity.Z
)
end
local function Air()
Physics.ApplyAirVelocity(Movement)
local vel = Vector3.new(Movement.mover.PlaneVelocity.X, Movement.collider.AssemblyLinearVelocity.Y, Movement.mover.PlaneVelocity.Y)
vel = Trace.Reflect(Movement, vel, Movement.collider.CFrame.Position)
Movement.mover.PlaneVelocity = Vector2.new(vel.X, vel.Z)
Movement.collider.AssemblyLinearVelocity = vel
end
local function Ground(groundNormal: Vector3)
Physics.ApplyGroundVelocity(Movement, groundNormal)
end
local function Jump()
sounds[math.random(1, #sounds)]:Play()
Movement.temp_jump_last = tick()
Movement.states.jumping = true
Movement.collider.AssemblyLinearVelocity = Vector3.new(
Movement.collider.AssemblyLinearVelocity.X,
Movement.config.JUMP_VELOCITY,
Movement.collider.AssemblyLinearVelocity.Z
)
end
local function ProcessMovement()
local isGrounded, isSurfing, result = Shared.IsGrounded(Movement)
Movement.states.grounded = isGrounded or false
Movement.states.surfing = isSurfing or false
if Movement.collider.AssemblyLinearVelocity.Y < 0 then
Movement.states.jumping = false
end
Shared.RotateCharacter(Movement)
if Movement.states.jumping or not Movement.states.grounded then
local groundNormal = false
if isSurfing then
groundNormal = result.Normal
if Movement.collider.Velocity.Y > 0 then
--Physics.ApplyFriction(Movement, false, true)
end
end
Air(groundNormal)
Gravity()
elseif Movement.Keys.Space > 0 then
Jump()
Air()
else
Ground(result.Normal)
end
end
--
local function InputBegan(input: InputObject, gp: boolean)
if input.KeyCode and Movement.Keys[input.KeyCode.Name] then
Movement.Keys[input.KeyCode.Name] = 1
end
end
local function InputEnded(input: InputObject, gp: boolean)
if input.KeyCode and Movement.Keys[input.KeyCode.Name] then
Movement.Keys[input.KeyCode.Name] = 0
end
end
local function Update(dt)
Movement.dt = dt
ProcessMovement()
end
Init()
UserInputService.InputBegan:Connect(InputBegan)
UserInputService.InputEnded:Connect(InputEnded)
RunService.RenderStepped:Connect(Update)



2 seconds ago
not working