Needing help with a pid type system for my game

I’m making a game about skateboarding that im not using skateboardplatform for, i need to make something of a pid system for turning, im currently using that for the forwards part and it works nice but when turning the board just keeps going in the same direction from when you were pushing, ive seen people suggest making a counter force that pushes the board the way you want to turn, but quite honestly i have no clue how i would do that. Thanks for reading and for any future help.

I’ve been following this tutorial PID control pt. 1: Proportional Control by ThanksRoBama

function pcontroller(kP, set_point, process_value)
	local e = set_point - process_value
	local CO = kP * e
	return CO
end
TOP_SPEED = 20
ZERO = Vector3.new(0,0,0)
FORWARD = Vector3.new(0,0,10)
KP = 7
function update_controls()
	local force = ZERO
	force += game:GetService("UserInputService"):IsKeyDown(Enum.KeyCode.W) and FORWARD or ZERO
	
	local set_point = force * TOP_SPEED
	local process_value = Character.PrimaryPart.Velocity
	local control_output = pcontroller(KP, set_point, process_value)
	pushForce.Force = control_output
end
game:GetService("RunService").Heartbeat:Connect(update_controls)

1 Like

Hi! Wall of text incoming, sorry :sweat_smile:


To make the skateboard turn you have to change the direction/angle somehow, and make most or all of the forwards velocity go into the new direction. Skateboards turn kind of like four wheel drive cars, in that all fours wheels turn out of line with the board, causing the rolling direction of the wheels to be out of line with the board, with the front and back wheels turning in opposite directions. This makes the board steer in a circular arc instead of straight ahead.

Here’s a (not super relevant but cool) video showing how leaning turns into turning the wheels, which causes steering:

There’s countless ways you could implement steering, but if you want it to “feel real”, it makes sense to mirror the way you steer a skateboard IRL, i.e. by leaning left/right. More lean = tighter circular arc = more curvature = harder steering. On a skateboard you can control the amount of lean precisely because it’s just a matter of shifting your weight around, so it’s kind of hard to make it work with e.g. pressing A/D on a keyboard, because that doesn’t give a range of steering, only full steer left or right, or no steer at all. On a controller it might make sense to use an analog stick to control lean. One way you could have decent leaning on keyboard is to have the lean slowly grow while you hold A/D, so it takes like a second to get up to the tightest turns. Obviously this give less control and is less like real skating, but hey what can you do? Maybe add a modifier so that just pressing A/D builds up to half lean, and pressing left shift at the same time builds up to full lean?

Anyway, no matter how you control the amount of lean, that needs to be translated into some amount of turning. Since a given lean angle equates to some wheel angle, and since a constant wheel angle makes the skateboard follow a circular arc of constant curvature, the skateboard just needs to turn by some number of degrees per second for a given lean angle.

So there is some formula that converts any lean angle into a number of degrees per second of turning that accurately simulates skateboard steering.

Here’s an article that talks a bit about how to convert this lean angle into a steering angle:

Apparently it can depend on a couple of factors like how the hardware is built, but one example it gives of a “steering curve” is a linear function, so if we use such a function then that’s realistic for at least some skateboard setups. A linear function looks like this:

f(x)=a*x

In this case x is lean angle, f(x) is steering angle in radians (or degrees) per second., and a is just some factor that determines the slope of the function, or how responsive the skateboard feels to steer. a just needs to be tuned to something that feels good. It’d be cool to let players customize all of those factors, but for a first iteration let’s just go with that function.

So in summary, you need to figure out a way to determine lean angle, and we can convert that to steering angle in radians per second by multiplying the lean angle by some number that we tune. Steering responds pretty much instantly to leaning, so it’s probably not necessary to use a PID controller for the steering angle. It might make the controls feel better to use a PID controller to convert controller or keyboard into lean angle however.

Once you can determine the steering angle, you need to somehow control the angular velocity of the skateboard, which you can do in a bunch of ways. Parts have a property called AngularVelocity that you can set directly, which seems to me like it’d work just fine. Another approach is to use a Torque, which is like a VectorForce is to (linear, or just “normal”) velocity. I.e. applying a torque makes the angular velocity grow over time. But if the controls should feel snappy and realistic instead of floaty and unresponsive, I think just setting AngularVelocity would be better.


There’s a bunch of ways to make the system I described better, like adding skid / drifting when you steer sharply at high speed. I think it makes sense to get the basics down first tho.

Hope this helps, if you have any questions feel free to ask :slight_smile: I don’t mind writing some example code either, but I might not have time right away.

2 Likes

Actually, I think maybe steering isn’t so much about leaning, but controlling the angle of the board with your ankles. Anyway, just replace “lean” with “ankle angle” xD

1 Like

Oh my gosh! That is a wall of text, Thank you for taking your time to write that out. My current system for turning uses a BodyAngularVelocity while youre pressing the key, then a counter force once you let go to remove floatyness. I didnt do any calculations for that i just trialed and errored; here’s that

if actionName == 'TurnRight' then
		if inputState == Enum.UserInputState.Begin then
			mode = 'right'
			--anims
			TurnRight:Play(0.3)
			--the actual turning
			local BodyAngularVelocity = Instance.new("BodyAngularVelocity", Board) 
			BodyAngularVelocity.maxTorque = Vector3.new(0, math.huge, 0)
			BodyAngularVelocity.angularvelocity = Vector3.new(0, -2, 0)
			--the velocity thing has to be removed or it just keeps spinning
			table.insert(trash, BodyAngularVelocity)
		else
			if mode == 'left' then
				TurnRight:Stop(0.3)
				CleanUp()
			else
				TurnRight:Stop(0.3)
				Coast:Play(0.3)
				CleanUp()
			end
			-- counter steer to make it not very floaty, kinda jank but HEY! im scripting it so its my call
			local BodyAngularVelocity = Instance.new("BodyAngularVelocity", BF)
			BodyAngularVelocity.maxTorque = Vector3.new(0, math.huge, 0)
			BodyAngularVelocity.angularvelocity = Vector3.new(0, 0.3, 0)
			table.insert(trash, BodyAngularVelocity)
			mode = nil
			wait()
			CleanUp()
		end
	end

Cleanup is a function that just removes it :slight_smile: , when you talk about using a “ankle angle” would that be best to use cframes? or some other thing. Like i said earlier than you so much for taking your time to write that.

Heres my follow-up:
I’ve made a sort of sketchy prototype that works, but it doesnt encorporate that steering maths cause i dont know how to.

local UIS = game:GetService("UserInputService")
local keydown = false

roll = 0
steer = 0
down = false
game:GetService("RunService").Heartbeat:Connect(function()
	if UIS:IsKeyDown(Enum.KeyCode.A) == true and roll < 15 then
		down = true
		while down do
			roll = roll + .1
			steer = steer -.1
			wait(.5)
		end
	end
	if UIS:IsKeyDown(Enum.KeyCode.A) == false then
		down = false
	end
	if UIS:IsKeyDown(Enum.KeyCode.D) == true and roll > -15 then
		down = true
		while down do
			roll = roll -.1
			steer = steer +.1
			wait(.5)
		end
	end
	if UIS:IsKeyDown(Enum.KeyCode.D) == false then
		down = false
	end
	

	workspace.Model.Deck.Orientation = Vector3.new(0, 0, roll)
	workspace.Model.TruckFront.Orientation = Vector3.new(0,steer,0)
	workspace.Model.TruckBack.Orientation = Vector3.new(0,-steer,0)
end)

You have to keep tapping the key to make it move smoothly idk how to change that lol

Would be using a pid for the pushing mechanics be better? when i used it i noticed it stopped to quickly and im not too sure how that would be remedied.

I’ve remade this but instead using animations, makes it cleaner but leaves out the maths :thinking: Not sure if thats the right approach but it looks nice

Is it possible to calculate the turning circle/ speed using this thing i made not sure how relivent it is kinda thought it was cool