What other methods to make NPC kart's steering look better?

This is my third time talking about NPC kart issues. Because the last two threads I posted had been neglected without any reply. Anyway, back onto main topic.

Before this and the two other threads existed, I had been thinking about kart with AI feature for it and had researched by looking for some useful threads on DevForum page, but only found very few of them (the people from there mostly marked Roblox’s built-in pathfinding, which is only for humanoid, as solutions for the whole NPC kart) and yet they weren’t good for my taste.
A while later, I stumbled upon this video on YouTube and made up main tasks for NPC kart. They would be:

  • Always set its Throttle value to 1 (forward)
  • Force it to drive on first node point of track then check next node by using magnitude (depends on kart’s top speed)
  • Use signed angle to check angles between kart and current node points, while it depends on kart’s facing direction. If angle is positive, its Steer value will be set to -1 (left) and vice versa

These tasks did good job for NPC kart, but then I realized that it did not reach the goal like what I saw from that video I showed above. Here’s the only problem: Its steering behavior always turned left and right, and barely drove straight (sometimes got stuck when hit wall), which caused unnatural driving

Code:
(AI script)

local function GetHeadingToPoint(point)
	local cframe = DriveSeat.CFrame - DriveSeat.Position + Vehicle.PrimaryPart.Position
	return cframe:PointToObjectSpace(point)
end

--Middle of update section
if currentNode then
	--print("Go")
	while IsAlive() do
		task.wait(Rate)
		local heading = GetHeadingToPoint(currentNode.Position)
		if heading.Magnitude > GetMaxSpeed() / 2 then
			local dirToMovePosition = (Vehicle.PrimaryPart.Position - currentNode.Position).Unit
			local angleToDir = Math.SignedAngle(Vehicle.PrimaryPart.CFrame.LookVector.Unit, dirToMovePosition, Vector3.FromNormalId(Enum.NormalId.Top))
			--print(angleToDir)
			--[[if angleToDir > 0 then
				DriveSeat.SteerFloat = 1
			else
				DriveSeat.SteerFloat = -1
			end]]
			--[[if angleToDir > 5 then
				DriveSeat.SteerFloat = 1
			elseif angleToDir < -5 then
				DriveSeat.SteerFloat = -1
			else
				riveSeat.SteerFloat = 0
			end]]
			heading = GetHeadingToPoint(currentNode.Position)									
			if heading.Unit.Z < -0.25 then
				if math.abs(math.deg(math.atan2(heading.X, -heading.Z))) > 5 then
					DriveSeat.SteerFloat = -math.sign(heading.Z) * math.deg(math.atan2(heading.X, -heading.Z))
				else
					DriveSeat.SteerFloat = 0
				end
			else
				if angleToDir > 0 then
					DriveSeat.SteerFloat = 1
				else
					DriveSeat.SteerFloat = -1
				end
			end
		else
			break
		end
	end
end
--

(Kart script)

local Movement = Vector2.new()

Main.DriveSeat.Changed:Connect(function(property)
	if property == "Steer" then
		Movement = Vector2.new(Main.DriveSeat.Steer, Movement.Y)
	elseif property == "Throttle" then
		Movement = Vector2.new(Movement.X, Main.DriveSeat.Throttle)
	end
end)

local Rotate = 0

local function Steer(direction, amount)
	Rotate = (MaxSpeed.Turn.Value * direction) * amount
end

--Middle of update section
if Movement.X ~= 0 then
	local dir = Movement.X > 0 and (Values.IsCursed.Value and 1 or -1) or (Values.IsCursed.Value and -1 or 1)
	local amount = math.abs(Movement.X)
	Steer(dir, amount)
end

--if not (Values.IsStunned.Value or Values.IsFlying.Value or Finished.Value) and IsGoing.Value then
	BodyAngularVelocity.AngularVelocity = Vector3.new(0, CurrentSpeed.Turn, 0)
	CurrentSpeed.Turn = Math.Lerp(CurrentSpeed.Turn, Rotate * (CurrentSpeed.Movement < 0 and -1 or 1) * (not hit and Stats.TurnSpeed.AerialTurnFactor.Value or 1), dt * Stats.TurnSpeed.TurnTorque.Value)
	Rotate = 0
--end
--

To be summarized, comparing my NPC kart to the one from YouTube video, it has unnatural steering behavior. And if you guys understand what topic says, what alternate methods to improve NPC kart’s steering?

p/s: I used BodyAngularVelocity to make it steer, with these following properties:

  • MaxTorque: Vector3.new(0, math.huge, 0)
  • P: 3000
2 Likes

Bump this thread because no one responded to me

but only found very few of them (the people from there mostly marked Roblox’s built-in pathfinding, which is only for humanoid, as solutions for the whole NPC kart) and yet they weren’t good for my taste.

This isn’t true, the Roblox pathfinder is in no way humanoid dependent (aside from jump height, which is fixed and would be disabled in this case) See PathfindingService | Roblox Creator Documentation Honestly I think you would be totally fine to use this, you could even use the new PathfindingModifier | Roblox Creator Documentation to make the NPC cart go after the power-up blocks (at least that’s what I think those are). Keep in mind that PathfindingModifiers are still in their studio beta phase and aren’t enabled online yet, but I suspect that will happen very soon.

Have you considered using a BodyGyro | Roblox Creator Documentation instead of a BodyAngularVelocity? This would give you far more control over where the cart is facing as you simply assign the BodyGyro’s CFrame value to your next waypoint.

Is there some reason you prefer to use the BodyAngularVelocity over a BodyGyro? Is it to more accurately simulate what a normal player would experience? If that’s the case we can discuss that a bit more.

I did a test with BodyGyro. It did steer sharply. But only drawback is that when NPC kart passed few waypoints, it randomly steered in misdirection


(this happened after 10th node)

Here’s the code that manages AI steering

--if not (Values.IsStunned.Value or Values.IsFlying.Value or Finished.Value) and IsGoing.Value then
	if Values.TargetNode.Value then
		print("CURRENT WAYPOINT: "..Values.TargetNode.Value.Name)
		BodyGyroSteer.CFrame = BodyGyroSteer.CFrame:Lerp(CFrame.lookAt(Main.Hitbox.Position, Values.TargetNode.Value.Position), dt * Stats.AITurnSpeed.TurnTorque.Value)
	else
		BodyGyroSteer.CFrame = CFrame.new()
	end
--end

I have a reason for that, is that the NPC kart from the YouTube video I provided used modified Roblox’s All-terrain Vehicle gear. Which only contained three BodyMovers:

  • BodyGyro (for fixing kart orientation)
  • BodyAngularVelocity (for steering)
  • BodyVelocity (for acceleration)

That’s why I took inspiration from that system and make my own version. However, I still am questioning how did it drive normally, unlike mine.

I did a test with BodyGyro. It did steer sharply. But only drawback is that when NPC kart passes few waypoints, it randomly steers in misdirection

That appears to be an issue with how you’re picking your waypoints. On waypoint 11 it was circling back to the previous waypoint.

I have a reason for that, is that the NPC kart from the YouTube video I provided used modified Roblox’s All-terrain Vehicle gear. Which only contained three BodyMovers:

I think having those three makes sense for player controlled carts, but for an NPC cart I think the BodyGyro is better suited for steering since the bot has no reason to control the cart incrementally like a player does.

That barely is the case as the print logs indicated the next waypoint (the print code line is above the BodyGyro CFrame

What did it do when trying to reach waypoint 11? It looks like it was trying to circle back to the previous waypoint in the video but I could be wrong since it cuts out a bit early.

The BodyGyro CFrame is always facing at next node point and there is no way it can have that such weak rotation power. Still, I believe other BodyMovers are behind of this mess but I 100% doubt they did it.

Ok so I’m going to leave this last response here. But somehow I managed to get it working by “adjusting” BodyMovers properties and editing some codes I wrote a little bit. Despite of no major changes for them, they magically fixed the problem.
Anyway, thank you for contributing to helping me.