PhysicsCharacterController | Mover Constraints based character controller

Oh! That sounds easier than I was thinking. I’m going to have to try “porting” the script over. If sitting is fixed, everything could be brought over.

Speaking of sitting, indeed, it disables PlatformStand the moment a character sits. I tried adding a line to my script that forcibly enables it again after transitioning from the Seated humanoid state, but it doesn’t prevent the NaN bug.
If I jump almost immediately after sitting, it may not become NaN; One time, it just combined both of the movement scripts. If the NaN glitch starts, movement also begins to feel laggy, almost as if network ownership of the player was given to the server.



Alright seating and NaN should be fixed for the most part now.

Unfortunately this came with a new quirk for seating as this still uses the default Humanoid seating system (needs to create seat weld like original Humanoid) so it does this:

  1. default humanoid.Jump to get out the seat
  2. original jump impulse of the controller.

This causes the character to jump slightly higher, I tried decreasing the jump power on this special case but it seems the value needs adjusting.

--HumanoidOnSeat component
--240 less impulse force to account for this odd humanoid unseat behavior
        self._PlatformStanding = humanoid.PlatformStanding:Connect(function(active)
            if active == false and humanoid.SeatPart == nil then
                local debounceTime = data._Model:GetAttribute("JumpDebounceTime")
                local Jump = data:GetComponent("Jump")
                humanoid.PlatformStand = true
                if not debounce then
                    debounce = true
--240 less impulse force to account for the humanoid unseating behavior
                    Jump:_RawJump(data, 240)

                    debounce = false

Also the delay in seating is due to me testing it with studio network lab enabled:

Also now JumpPower and JumpDebounceTime has been added as additional attributes for playing around with if needed.

Updates should be both in the place file and GitHub


Thanks for the quick update! I just updated the script used in my game and seats indeed now work! This is almost perfect, but there’s two things that aren’t:

  • No CanCollide ray check - It seems collision detection is done using a downward raycast from the player. This mostly works, but it doesn’t ignore invisible technical parts that have CanQuery set but not CanCollide. I fixed this by changing line 109 of HipHeight to “if raycastResult and raycastResult.Instance.CanCollide then”.

  • VR controller support? - This is probably way out-of-scope of this script since it involves someone else’s system, but I use Nexus VR Character Model, which seems to just send the usual humanoid-moving commands to move the player around when in smooth locomotion mode.

    When I tested out this script in VR, the player couldn’t move at all, so I just edited the main script so it doesn’t load the physics-based movement code if VR is detected.

    Momentum in VR would be neat, but I suppose it isn’t required.

Still, tough, your script works very well as-is, and will hold me over until Roblox’s potential new character controller is added to the engine. :woot:


Well, I brought over my Animate script, and got it to work with this script. I’ve found one oddity with it in the process, though. Functions like Humanoid.Jumping and Humanoid.FreeFalling normally receive an argument that’s true if entering the state and false if exiting it, but PhysicsCharacterController seems to send “false” to it when the state is entered, and only then.

I was trying to fix my script earlier, even adding print statements to see when my functions were running, and that led to this discovery. In my copy of the script, I edited line 117 of the main script to be:


Now my jump and fall animations play, and mostly everything works. I just wanted to update with my findings about the state machine giving the connections a different value than default Roblox logic.

EDIT: Oh, yes, and an update about the whole “PhysicsCharacterController doesn’t support VR controllers” thing that I said: It actually does, which I found out when Nexus VR Character Model randomly stopped initializing properly. For some reason, Nexus VR is what breaks PhysicsCharacterController, not controllers specifically.


Getting used to it but I’ve found a bug where player character is possible to enter a part that were collided, also somehow collision group makes your character assigning the position like climbing on a mesh in the test place if humanoids were walking towards.


With Roblox’s new beta character controller announced, I’ve returned to say that as of now, I still think PhysicsCharacterController, even with its lack of swimming and climbing noted in the first post, is better than it for satisfyingly smooth movement. :woot: This video shows how smoothly I can curve around obstacles, which I can’t do with Roblox’s official physics-based movement (as it slows me down any time I change directions, unlike here, where turns smoothly transfer momentum).

My settings for PhysicsCharacterController
  • Bounce: 15
  • FlatFriction: 150
  • HipHeight: 3.306 (edited calculation in scripts somewhere for better foot alignment)
  • JumpDebounceTime: 0.175
  • JumpPower: 425
  • Suspension: 1000
  • WalkSpeed: 45.25
  • XZDragFactorVSquared: 0.177

In case this isn’t worthy of a reply to this topic, I have a bug to report; If your game or Roblox Studio lags for any reason, the physics completely break, usually making my character bounce very high into the air, becoming impossible to recover from if enough lag happens.

If merely moving your mouse cursor around the 3D viewport makes Studio lag like my computer unfortunately does, try shaking it around constantly and as the framerate stays low, your character may go flying… This reminds me of a very iffy game engine named CopperCube, where the framerate dropping also made physics unstable. When someone tried my baseplate, I guess they had a really bad frame rate, as they bounced like this constantly while I had PhysicsCharacterController on.


Apologies for being a little late. A lot of the concepts to do with ModuleScripts are new to me. I’m attempting to add a sliding animation for the slide mechanic. No matter what I try I can’t seem to connect the Slide module to the Animation module. Do you mind walking me through how you would go about adding animations for any modules in the AddonComponents folder?

Apologies for the delays, currently to implement slide animation you would have to edit with the state machine in the main module script, then switch the state in the slide module, then go to the animate script to add in your custom play animation on state events.

It’s a bit messy which is why I’m trying to think of a better way to handle state and such perhaps if the state machine can be edited from the additional component modules instead.

Also with sliding I encountered some collision issues as the hip height is not rigid, however I found a solution which is to use a rolling sphere as the collision box which is what EgoMoose and X_O has done for their character controllers.

I 100% recommend discarding the player module
In my opinion
It just has so many limitations ;-;

i like how u used attributes tho :smiley:

nice work btw!

1 Like

does humanoid.Running or humanoid events work on this in general? ive tried connect the Running event and nothing happens
also, will animations play if you just local anim = animator:LoadAnimation(animation) anim:Play()

1 Like

Nope, humanoid is mostly disabled except for seated since it is in platform stand.

I had to use a custom state machine which works but is kinda messy to replace the default humanoid state events.

--In the AnimateModule

    -- connect events
    --Hook up 
    local stateSignals = characterController._StateSignals
    local connection1 = stateSignals.Running:Connect(onRunning)
    local connection2 = stateSignals.Jumping:Connect(onJumping)
    local connection3 = stateSignals.FreeFalling:Connect(onFreeFall)
    local connections = {connection1, connection2, connection3} --To disconnect later
    local stateMachine = characterController._StateMachine
    stateMachine.on_land = function()
        pose = "Running"
        onRunning(0) --Reset to standing idle animation

They should be able to play unless the default animate script overwrites them or something like that.

1 Like

Hey thanks for this awesome script. I just had a question about the old script code. Why does it return nil?

No problem.

For the old script code it returns nil to prevent module script warning that it doesnt return anything.

Also its in a module script because its not used anymore and to ensure the script doesnt run.

1 Like

Ahhhh ok. I’m new, so I thought that was some like deep guru technique to optimize memory or something haha. Thanks for your reply!

hence why each time Landed didn’t work along with Cam bobbing i thought it was my script bugging, do i have to replace default Landed event to the ones from module if i have to achieve my camera effect?

Actually nvm, also i was about to ask how could i access the Attribute to make my sprinting system through script but already found because then OOP is so confused that i thought it won’t detect and set attributes.

Question here. Why is every setting a constant? I could try changing the attributes manually but when using the slide module, everything will go back to normal.
I’m probably not going to use the slide module and make my own but I still think it would be great to be able to change those values inside the .new() function.

The values can’t be changed through the code :sad:

Also a few things I’d change:

  • jump power to +650
  • on the alingOrientation, the MaxAngularVelocity to 7.5~10 and Responsiviness to 35~50
  • bounce to 15

You can actually change these values anytime once the object is created, there is a reason why it uses attributes and not a constant variable syntax such as JUMP_HEIGHT.

Just like in the example video where I changed the speed and drag factor which you could do via script afterwards.

However the slide module is indeed the issue as it resets it back to normal because it’s rushed and incomplete since it has to set the friction to 0 for the sliding movement and back to normal which uses default values and not the ones user set.

Probably needs a rework.

Hope this clears things up.

Thanks for the input for reference settings I could use.


Yeah that was a bit of a misunderstanding, I knew I could change the attribute but there is not a “default value” in case the previous value is lost. Like example the slide module. While I agree it needs a rework, I still think having a default value helps a lot.

That out of the way, I would like to mention that there is absolutely no documentation. I don’t blame you at all! It’d just be a lot of work for a small minority really. However…

Please explain how to make a new component and how the Input works.

I’m considering adding this to my game but I’m struggling a lot with this.
If this is just something basic about OOP then linking a previous tutorial on how this work would be fine too! But at least a way to understand it.
I’m getting an idea of how it works but it’s taking much longer than it should

Thanks again for your attention. (luv ur modules)

Edit: What does suspension mean again?
Edit2: Why are there so many vector forces? I know raycasting is fine but what’s the point?


Can you briefly comment on how you handled slopes? I’m trying to make something similar but with linear velocity constraints instead of vector forces. Been trying to pick apart your code for days now XD

Sadly this is a limitation with roblox itself. I think it’s because the steps we our scripts (renderstepped, stepped, heartbeat) are on an entirely different step than the physics. So whenever there’s lag or framerate loss, the time between each frame changes dramatically which means the updates to vectorforce, which we update every frame, slips.

If we have 60 frames in a second, vectorforce updates 60 times. But let’s say that we get a huge lag spike and get 1 frame per second and vectorforce is set to… idk (0,50000,0). Under 60 fps vectorforce would probably hit (0,50000,0) 1 out of ~60+ frames. But when we have 1 fps (maybe physics is updating 10 times), you’re effectively magnifying what should have been a split second and dragging it out 10 times than what it should have been.

Roblox simply isn’t built to give developers full control like Unity’s (or Unreal’s) FixedUpate which runs at the same frequency as their physics. It’s built to be ~easy to use and fully cross platform.

To counteract this limitation, to get reliable results while getting smooth physics, we need to respect physics controlled and manipulated by Roblox’s internal physics step. This means that if we set something every frame that we do so knowing that we don’t actually have control every frame.

Honestly right now it kind of sounds impossible… But I’m trying my best. Atm I’m thinking about using a lineforce instead of vectorforce for the spring. The benefit being that if there is step slippage (idk that’s what I’m calling it from now on) that the internal physics will not overshoot the desired target. The worst thing that could happen is that the object arrives at the destination too early or too late. Which is a lot better than flinging up to the moon XD

1 Like