I’d go with CFrame movement which is pretty much adjusting the HRP’s cframe each physics step based on whatever velocity or target position you want
here’s an example of one implementation I did with it:
Roblox Game Development - Example of CFrame Movement - YouTube
The Local Script used in StarterPlayerScripts
local Humanoid = script.Parent.Humanoid
local HumanoidRootPart = script.Parent.HumanoidRootPart
type AppliedVelocity = {velocity: CFrame | Vector3, endTime: number}
local appliedVelocities: AppliedVelocity = {} -- array of velocities to apply and the time to end them
-- add a velocity to the table to be handled
local function ApplyVelocity(velocity: CFrame, length: number)
table.insert(
appliedVelocities, {
velocity = velocity,
endTime = os.clock() + length,
})
print(string.format("{Time=[%.0f]}\tApplied [%s] for [%d] seconds", os.clock(), tostring(velocity), length))
end
-- setup the function to detect inputs to apply velocities
local function OnInputBegan(input, caughtByGui)
if caughtByGui then return end
if input.KeyCode == Enum.KeyCode.One then
print(input.KeyCode)
ApplyVelocity(Vector3.new(0, 0, 10), 2) -- 2 seconds of being pushed in the Z direction at 10 studs per second
elseif input.KeyCode == Enum.KeyCode.Two then
print(input.KeyCode)
ApplyVelocity(Vector3.new(0, 10, 0), 1) -- 1 second of being pushed in the Y direction at 10 studs per second
elseif input.KeyCode == Enum.KeyCode.Three then
print(input.KeyCode)
ApplyVelocity(Vector3.new(10, 0, 0), 3) -- 3 second of being pushed in the X direction at 10 studs per second
elseif input.KeyCode == Enum.KeyCode.Four then
print(input.KeyCode)
ApplyVelocity(CFrame.new(0, 0, 10), 2) -- 2 second of being pushed in the Z direction at 10 studs per second in object space of the character. so it changes based on the direction the HRP faces
elseif input.KeyCode == Enum.KeyCode.Five then
print(input.KeyCode)
ApplyVelocity(Vector3.new(0, 0, -50), .5) -- .5 seconds of being pushed in the Z direction at -50 studs per second
end
end
game:GetService("UserInputService").InputBegan:Connect(OnInputBegan)
-- handle simulating the velocities each physics step
local function StepPhysics(dTime)
local currentCFrame = HumanoidRootPart.CFrame
local currentTime = os.clock()
Humanoid.PlatformStand = ( #appliedVelocities > 0 ) -- set platform standing if we have velocities to apply. this will stop the user from doing things like preventing us from applying rotation
HumanoidRootPart.Velocity = if ( #appliedVelocities > 0 ) then Vector3.new() else HumanoidRootPart.Velocity -- ignore roblox's velocities if we're applying velocity
local index = 1
while index <= #appliedVelocities do -- using while loop because length of table may change
local appliedVelocity: AppliedVelocity = appliedVelocities[index]
local velocity = appliedVelocity.velocity
local endTime = appliedVelocity.endTime
if endTime < currentTime then -- if appliedVelocity has expired, skip and remove
table.remove(appliedVelocities, index)
else -- else, apply the movement and increment the index
if typeof(velocity) == "Vector3" then
currentCFrame += ( Vector3.new():Lerp(velocity, dTime) ) -- pretty much (distance = velocity * time) e.g., if velocity is Vector3.new(0,0,4) and dTime is 0.5, this returns Vector3.new(0,0,2)
else -- if cframe
currentCFrame *= ( CFrame.new():Lerp(velocity, dTime) ) -- this does the same as Vector3, but it uses objectSpace instead of world space. you'll probably use this more doing knockback stuff
end
index += 1
end
end
-- apply all movements at the same time
HumanoidRootPart.CFrame = currentCFrame
end
game:GetService("RunService").Heartbeat:Connect(StepPhysics)
This one pretty much just applies a velocity for N seconds on the character whenever the buttons 1,2,3 or 4 are pressed on the keyboard
It has the issues of:
- not completely negating roblox’s velocities, so there’s going to be a constant downward velocity when applying velocities
- this can be negated by applying an upward velocity
- allowing characters to go through thin walls at high speeds
- even roblox has this, so this is probably neglegible
- default animations don’t play when platform standing
- you’ll probably be playing custom animations, so the character won’t look like it’s not animated during this