PhysicsCharacterController | Mover Constraints based character controller

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
    end

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.

2 Likes

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?

2 Likes

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

Performance wise, does the PhysicsCharactercontroller behaves better or is it heavier than using humanoid?

Asking cuz I am trying to figure out how to have more players in a PVP game.

it behaves differently from normal humanoids as it uses custom state, it isn’t that heavy and runs smoothly but it disables a few things like climbing as OP mentioned earlier.

1 Like

I think I fixed it. I replaced the suspension with a clamped impulse force.

A small contribution for dthecoolest’s effort. I’m learning a lot from him through the script he provided here. I also see his posts all over the devforum XD

if raycastResult  then
		local currentSpringLength = (raycastResult.Position - Vector3.new(humanoidRootPart.Position.X, 
			humanoidRootPart.Position.Y - humanoidRootPart.Size.Y/2, humanoidRootPart.Position.Z)).Magnitude
		local suspension = 1100
		local suspensionForceFactor = humanoidRootPart.AssemblyMass*suspension
		local damping = suspensionForceFactor/50		
		local springImpulseForce = (((hipHeight - currentSpringLength)^2) * (suspensionForceFactor / hipHeight^2))
			-(rootPart.AssemblyLinearVelocity.Y*damping)*deltaTime		
		springImpulseForce = ClampNumberAbs(springImpulseForce, 450)		
		rootPart:ApplyImpulse(Vector3.new(0,springImpulseForce,0))
end

And my clamp function…

local function ClampNumberAbs(number, maxNumber)
	if math.abs(number) <= maxNumber then
		return number
	else
		if number > 0 then
			return maxNumber
		else
			return -maxNumber
		end
	end
end

It’s almost the exact same as the X_O Jeep code, but some of the values are adjusted to work with applyimpulse. You guys will probably have to fiddle with the values. But by using impulse, deltaTime, and clamp, the player no longer bounces really high due to lag.

Thanks everyone. Please let me know what you think and if you have any questions.

3 Likes

Great! Where do I add this code, though? It would be nice to be able to fix PhysicsCharacterController’s main issue.

If I paste it in place of code starting at line 105 in HipHeight, there’s errors about HumanoidRootPart and deltaTime. (Ignore the ClampNumberAbs error, as that would be fixed if I pasted the other code above this.)

Ah… I put together my code based on dthecoolest’s “Old single script code” GitHub

Not exactly sure how it would fit into his new code. But essentially deltaTime is from your Stepped, RenderStepped, or Heartbeat. (I used stepped) Stepped will have two variables in the function

local Stepped = RunService.Stepped

local steppedConnection = Stepped:Connect(function(totalTime ,deltaTime)
-- all your good stuff that runs every frame goes in here
end)

And humanoidRootPart needs to be declared somewhere near the top of your script to be game.Players.LocalPlayer.Character.HumanoidRootPart.

Wherever it is that the new code does the downward raycast and applies spring forces, that is where this code goes.

Maybe dthecoolest can chime in because I couldn’t make heads or tails of the modular script. I’m keeping everything in one or two scripts like the people who made Celeste. It’s like painting on a canvas. And having everything split up makes it too hard for me to see the big picture.

(edit)

I just took a look at HipHeight.lua

And I think you just need to change “humanoidRootPart” to “rootPart”

dthecoolest declares “rootPart” to be self.PhysicsCharacterController.RootPart on line 35

His Stepped:Connect is in the PhysicsCharacterController module script. And that initializes all the core components I think. But it’s so intricate for my experience level. I have no idea where to pass in deltaTime :frowning:

(edit… AGAIN lol)

You know what, I think we could instead just try clamping the upward forces to avoid extreme outcomes during lag spikes. Um… But there are currently 17 vectorforces applying upwards and downward forces, and I have no idea how they play together.

2 Likes

Hello! Awesome resource! However, is there a way to disable sliding?

You can remove the slide component line in the main module itself or simply toggle comment for it.

--StarterPlayerScripts, runs once
local PhysicsCharacterController = require(script.PhysicsCharacterController)

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()

local AnimateFunction = require(script.Parent.AnimateModule)

local initChar = function(newCharacter : Model)
	local rootPart = newCharacter:WaitForChild("HumanoidRootPart")
	local humanoid : Humanoid = newCharacter:WaitForChild("Humanoid")
	humanoid.PlatformStand = true

    --Object auto destroys input when character is not in workspace (Destroyed)
	--Within InitUpdateDefaultControls()
    --Dirty but works
	repeat task.wait() 
		-- warn("Waiting until workspace")
	until newCharacter.Parent == workspace

	local characterController = PhysicsCharacterController.new(rootPart)
	characterController:AddDefaultComponents()
	--characterController:AddComponent("Slide") -- disabled after commenting but addons module are still there.
	-- characterController:AddComponent("AirStrafe") --Component is WIP

	characterController:Run()
	characterController:ConnectComponentsToInput()
	task.spawn(AnimateFunction, newCharacter, characterController) --Init animation based on default animate script
end

player.CharacterAdded:Connect(initChar)

initChar(character)
1 Like

Hello!
I was wondering, does the module have any particular bodypart requirements?
I ask this because I would like to use this controller on a custom character with only 5 components:
image
and it doesn’t seem to let me move at all. Thanks!

No it does not just a root part is required for the module object.

	local rootPart = newCharacter:WaitForChild("HumanoidRootPart")
	local characterController = PhysicsCharacterController.new(rootPart)

Seems like a different issue perhaps an incorrect root part due to StarterCharacter or the root part is not connected to the rest of the character and such.

1 Like

Alright, I’ll work on fixing the issue myself. Thanks for the info!