Sadly I think they’ve left this on the backburner for bigger systems such as Server Authority. The last update to the character controllers was UpDirection a few months ago, and even that has basically ZERO documentation
Hello I have a question.
I would like to know how I could make it so i preserve momentum but so it doesnt get cancelled when I press W/A/D, because even when I useAirController.MoveMaxForce with a high value, it still just gets cancelled when i press any sort of movement key.
I kind of had the idea like in Ultrakill for example where it stays preserved but idk how.
Also, the explosions seems to be bugged
How do i add auto jumping? Every implementation that should make sense fails for some reason when i try to add it myself.
Hi, I’m late for this discussion, but if anyone struggling with the die state, you don’t need to do all the wonky fire server stuff, you can just detect when the Health drops to 0, and then set the evaluateStatemachine to true, now you can just ChangeState to Dead easily ( you don’t have to, but not changing it right away will sometimes cause a slight death delay from what I have tested), not sure if this method is better in performance, but it sure keeps your code clean
Honestly, as good of a tutorial as this is, I’m kind of sad that this is how it works.
I was excited about this “manual” sensor stuff because I have a game where I need to climb surfaces that aren’t just trusses. But to figure out that I basically need to re-code the controller and abandon the humanoid in a lot of ways means the amount of effort to convert an old, existing game that uses the Humanoid, to simply having better climbing detection, is basically the same amount of effort as just remaking the character’s control setup from scratch.
It’d be awesome if I could just slap on a climb sensor and be like “Alright Humanoid, you are a glorified character controller anyways, just use this!”. Like some sort of partial compatibility. That’d be nice.
Hi, I know this is very late. But, to anyone else wanting to add auto-jumping to their games, you shouldn’t use the ApplyImpulse method.
Instead, use a LinearVelocity instance. I’m not sure why, but using ApplyImpulse will cause weird inconsistencies when it comes to applying it at a fast rate. Replacing it with a LinearVelocity with a high enough MaxForce will fix this issue.
As to implementing auto-jumping, it is rather straight-forward as all you need is the information of if the player can jump. To give you an example, here is the method I’m using. Albeit, it is using ECS, but it shouldn’t differ in any way if you just do a RunService loop.
(The code might look a little weird because it’s roblox-ts compiled to luau)
-- This can be replaced with anything that will tell you if we are holding Space.
if ActionsController.IsPressed("Jump") then
local e = GET_LOCAL_ENTITY()
if not (e ~= 0 and e == e and e) then
return nil
end
local stateMachine = w:get(e, c.Properties.MovementStateMachine)
if not stateMachine then
return nil
end
local inAir = w:has(e, c.Tags.InAir)
local hasTimer = w:has(e, pair(c.Properties.Timer, c.Tags.Jump))
-- If we aren't in the air, we aren't on a jump cooldown and we haven't just landed:
-- We change our state to Jumping
if not inAir and not hasTimer and stateMachine.State ~= DEFAULT_MOVEMENT_STATES.Land then
stateMachine.State = DEFAULT_MOVEMENT_STATES.Jump
end
end
This is the general loop, checking if we’re able to jump and if we can we change our state to Jumping
Now, onto the actual Jumping part…
if state == (DEFAULT_MOVEMENT_STATES.Jump) then
-- Don't mind the changed variable, it's here because this is ran every frame.
-- we only want to apply the Jump once.
if changed then
stateMachine.Manager.ActiveController = stateMachine.AirController
local jumpImpulse = Vector3.new(0, 45, 0)
-- This is a wrapper that creates an Attachment at RootPart with a LinearVelocity inside of it.
-- It gets cleaned up after 0.05 seconds have passed.
ForceUtils.ApplyLinearVelocity(rootPart, {
Identificator = "Jump",
Direction = jumpImpulse,
MaxAxesForce = Vector3.new(0, 999999, 0),
Lifetime = 0.05,
})
-- I set the JumpCooldown here, you can do it any other way like task.delay()
w:set(e, pair(c.Properties.Timer, c.Tags.Jump), Timer.new(0, 0.125))
w:add(e, c.Tags.InAir)
end
break
end
MaxAxesForce is really important here. If it’s too low, you will get the same inconsistencies as you’d get with ApplyImpulse. With a big enough force there’s a chance you might fling yourself, so be careful and play around with the values until it feels just right.
For the climbing problem, the same thing happened to me. Basically, it doesn’t detect the ground, so it interrupts the climb and sets you into freefall. Instead of just checking for the ground before you freefall (which is if not Ground then . . . ). You should do if not Ground and not Climbing then ...
This makes it so you don’t fall while you’re climbing.
My code example
local function UpdateController()
local Ground = GetGround()
if not Ground and not ClimbSensor.SensedPart then -- see how I check the climbing part?
Controller.ActiveController = AirController
Humanoid:ChangeState(HumStates.Freefall)
elseif Ground and Ground.Instance then
Controller.ActiveController = GroundController
GroundSensor.HitNormal = Ground.Normal
GroundSensor.HitFrame = CFrame.new(Ground.Position)
GroundSensor.SensedPart = Ground.Instance
Humanoid:ChangeState(HumStates.Running)
end
local Pars = RaycastParams.new()
Pars.FilterDescendantsInstances = {Char}
Pars.FilterType = Enum.RaycastFilterType.Exclude
local Cast = workspace:Raycast(RootPart.Position, RootPart.CFrame.LookVector * 1.3, Pars)
if Cast and Cast.Instance then -- this is just where I set the climbing
Controller.ActiveController = ClimbController
ClimbSensor.HitNormal = Cast.Normal
ClimbSensor.HitFrame = CFrame.new(Cast.Position)
ClimbSensor.SensedPart = Cast.Instance
Humanoid:ChangeState(HumStates.Climbing)
else
ClimbSensor.SensedPart = nil -- this is so you dont climb away into infinity
end
end
How would I change the hip height for this?
Could this be used to switch gravity?
(woops did not mean to reply mb)
Could you update the videos with newest ones? I could do some ones if you don’t mind as medal clips are temporary and they’re gone right now.
it looks like there is an event nobody is talking about that ControllerPartSensor has, called OnSensorOutputChanged and it triggers while the sensor is detecting changes (so from my observations the event fires while i walk on the ground, and stops when i stand still or while im in the air)
i cant find any documentation for it or anybody asking about it even though it works. it doesnt pass any parameters, but you can use it like this:
groundsensor.OnSensorOutputChanged:Connect(function()
print(groundsensor.SensedPart)
end)
it would probably make the ControllerUpdate script more efficient. instead of doing updateControllerManagerSensor() every renderstepped, u only need to do it when that event fires. right?
(i just noticed in the original post you use raycasts instead anyway but for the people that dont this would be useful)
No
Wait 1 day please
It only detects ground, not air, water or anything else
I’m trying to code coyote time so my platforming feels smoother and i’m having a problem where theres no way to tell if the person fell off the platform naturally or if they specifically jumped off one. When the player jumps it switches back to ground immediately cuz of the raycast and back to jump after the raycast really left the ground. Is there any way to do coyote timing with this?
You can store the last time the player jumped and use it to check if they just jumped or not.
local lastJumpTime = 0
local function jump()
lastJumpTime = os.clock()
...
end
local function justJumped()
return os.clock() - lastJumpTime < 0.1
end
-- example
if justJumped() then
print("Player just jumped")
end
Roblox also has a platformer template you can look at for reference
I see, thank you!!!
If player falls off the map no death event fires and healthChanged doesnt work because the health didn’t change
Character Controllers are officially out! If this article needs updating, ping me with what to fix and I’ll get on it.