Having issues wth Tweening in my Boat script

So I have a script that detects when a player in a boat changes direction, and accounts for it. Using tweenservice, it can go left, right, backwards forwards etc. and has acceleration. One issue it has is then when you turn (via detecting a change in steer), I can’t figure out a way to readjust the velocity of the boat to change in the direction of the rotation change, and the boat can suddenly be going backwards after you turn it.

Here’s my code, could someone help me?

script.Parent.MaxSpeed = script.Parent.Settings.MaxSpeed.Value
local Vehicle = script.Parent.Parent
local VehicleSeat = script.Parent
maxspeed = script.Parent.MaxSpeed
script.Parent.BodyPosition.position = script.Parent.Position
script.Parent.BodyGyro.cframe = script.Parent.CFrame
value1 = 0 --- real time speed
local bv = VehicleSeat:FindFirstChild("BodyVelocity")
local clone = bv:Clone()
local driver = false

local TweenService = game:GetService("TweenService")
local speed_Tween
local turn_Tween
local turny = TweenInfo.new(5)
local speedy = TweenInfo.new(5)
local BodyVel = script.Parent.BodyVelocity
VehicleSeat.Changed:Connect(function()
	if speed_Tween ~= nil then
		speed_Tween:Cancel()
		speed_Tween = nil
	end	
	if turn_Tween ~= nil then
		turn_Tween:Cancel()
		turn_Tween = nil
	end	
	if VehicleSeat.Throttle == 1 then
		speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=(maxspeed*script.Parent.CFrame.lookVector)})
		--script.Parent.BodyVelocity.velocity = script.Parent.CFrame.lookVector*value1
	elseif VehicleSeat.Throttle == 0 then
		speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=0*script.Parent.CFrame.lookVector})
	elseif VehicleSeat.Throttle == -1 then
		speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=((-maxspeed)*script.Parent.CFrame.lookVector)})
	end
	if VehicleSeat.Steer == 1 then
		turn_Tween = TweenService:Create(script.Parent.BodyGyro, turny, {CFrame=(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,- script.Parent.Settings.TurnSpeed.Value,0))})
		if VehicleSeat.Throttle == 1 then
			BodyVel.Velocity=(maxspeed*script.Parent.CFrame.lookVector)
		elseif VehicleSeat.Throttle == -1 then
			BodyVel.Velocity=(-maxspeed*script.Parent.CFrame.lookVector)
		end
	elseif VehicleSeat.Steer == 0 then
		turn_Tween = TweenService:Create(script.Parent.BodyGyro, turny, {CFrame=(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,0,0))})
	elseif VehicleSeat.Steer == -1 then
		turn_Tween = TweenService:Create(script.Parent.BodyGyro, turny, {CFrame=(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,script.Parent.Settings.TurnSpeed.Value,0))})
		if VehicleSeat.Throttle == 1 then
			BodyVel.Velocity=(maxspeed*script.Parent.CFrame.lookVector)
		elseif VehicleSeat.Throttle == -1 then
			BodyVel.Velocity=(-maxspeed*script.Parent.CFrame.lookVector)
		end
	end
	if speed_Tween then
		speed_Tween:Play()
	end
	if turn_Tween then
		turn_Tween:Play()
	end
	if script.Parent.Position.Y > script.Parent.BodyPosition.Position.Y then
		script.Parent.Driving.Value = false
		value1 = 0
		script.Parent.BodyVelocity.velocity = script.Parent.CFrame.lookVector*value1
	end
end)
1 Like

Here’s a GIF that describes the issue better than I did:
https://gyazo.com/d858591dcfef5b201b2635ddccc29657

Notice how the velocity of the boat doesn’t change or follow the direction/orientation of the bow

1 Like

The BodyGyro won’t slow down your velocity in a given direction; if you want to apply the rotation to the velocity vector, you need to tween the velocity vector along with the gyro, too.

That would mean you need to cancel the speed_tween and reset it to tween to the same max velocity, but in the same direction as your body gyro orientation.

Okay before I suggest a solution (I have a few in mind) I need to know, how does .Changed get called? Is it based on position changing? That would be good to know, I don’t really have any experience with seats.

It gets called when the Throttle/Steer property gets changed, i.e. when the player controlling it changes control direction

I did this to make an easy cross-platform capability

Okay then I think I know how to deal with it. Here’s the explanation:

Since you can only detect when the player changes directions, let’s say you make the BodyGyro tween for 5 seconds, as you have it. Because of this, you should also tween the velocity to the same direction; how much you delay that is up to you if you want a gliding effect or if you want to be as responsive as possible.

Next you need to set up an event for Tween:Completed (I think that’s the syntax, search it up just in case). The reason for this is because if someone holds left for more than 5 seconds, you won’t get a VehicleSeat.Changed event so you won’t be able to reset the Tween to keep turning. Thus you need the Completed event to reapply the same Tween that you used initially, for both the Gyro and the Velocity objects.

And that should do it, try it and see what happens. Good luck!

How would I tween the velocity in that particular direction besides straight?

Before, you were using the lookvector for the velocity tween. But now you should use the look vector that the gyro uses, still applying the same magnitude speed multiplier to it.

Could you post what the resulting code may look like?

Well there’s a couple of things that stand out…

	if VehicleSeat.Throttle == 1 then
		speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=(maxspeed*script.Parent.CFrame.lookVector)})
		--script.Parent.BodyVelocity.velocity = script.Parent.CFrame.lookVector*value1
	elseif VehicleSeat.Throttle == 0 then
		speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=0*script.Parent.CFrame.lookVector})
	elseif VehicleSeat.Throttle == -1 then
		speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=((-maxspeed)*script.Parent.CFrame.lookVector)})
	end

This here should be dealt with after you check if you are turning. Otherwise you will end up rewriting speed_Tween for no reason.

For your body velocity, it should look like this

-- Here's your body gyro code for Steer == 1:
turn_Tween = TweenService:Create(script.Parent.BodyGyro, turny, {CFrame=(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,- script.Parent.Settings.TurnSpeed.Value,0))})

-- Here's what your body velocity should be if Throttle == 1:
speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity= maxspeed*(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,- script.Parent.Settings.TurnSpeed.Value,0)).LookVector})

You should modify the remaining code using this construct. All it is doing is setting the velocity in the direction of the look vector, while also applying the multiplier maxspeed. For the remaining cases, you just deal with negative signs.

Got this code, but it’s still just as clunky.

script.Parent.MaxSpeed = script.Parent.Settings.MaxSpeed.Value
local Vehicle = script.Parent.Parent
local VehicleSeat = script.Parent
maxspeed = script.Parent.MaxSpeed
script.Parent.BodyPosition.position = script.Parent.Position
script.Parent.BodyGyro.cframe = script.Parent.CFrame
value1 = 0 --- real time speed
local bv = VehicleSeat:FindFirstChild("BodyVelocity")
local clone = bv:Clone()
local driver = false

local TweenService = game:GetService("TweenService")
local speed_Tween
local turn_Tween
local turny = TweenInfo.new(5)
local speedy = TweenInfo.new(5)
local BodyVel = script.Parent.BodyVelocity
VehicleSeat.Changed:Connect(function()
	if speed_Tween ~= nil then
		speed_Tween:Cancel()
		speed_Tween = nil
	end	
	if turn_Tween ~= nil then
		turn_Tween:Cancel()
		turn_Tween = nil
	end	
	if VehicleSeat.Steer == 1 then
		turn_Tween = TweenService:Create(script.Parent.BodyGyro, turny, {CFrame=(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,- script.Parent.Settings.TurnSpeed.Value,0))})
		if VehicleSeat.Throttle == 1 then
			speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity= maxspeed*(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,- script.Parent.Settings.TurnSpeed.Value,0)).LookVector})
		elseif VehicleSeat.Throttle == -1 then
			speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity= -maxspeed*(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,- script.Parent.Settings.TurnSpeed.Value,0)).LookVector})
		end
	elseif VehicleSeat.Steer == 0 then
		turn_Tween = TweenService:Create(script.Parent.BodyGyro, turny, {CFrame=(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,0,0))})
	elseif VehicleSeat.Steer == -1 then
		turn_Tween = TweenService:Create(script.Parent.BodyGyro, turny, {CFrame=(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,script.Parent.Settings.TurnSpeed.Value,0))})
		if VehicleSeat.Throttle == 1 then
			speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity= maxspeed*(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0,script.Parent.Settings.TurnSpeed.Value,0)).LookVector})
		elseif VehicleSeat.Throttle == -1 then
			speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity= -maxspeed*(script.Parent.BodyGyro.cframe * CFrame.fromEulerAnglesXYZ(0, script.Parent.Settings.TurnSpeed.Value,0)).LookVector})
		end
	end
	if not speed_Tween then
		if VehicleSeat.Throttle == 1 then
			speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=(maxspeed*script.Parent.CFrame.lookVector)})
			--script.Parent.BodyVelocity.velocity = script.Parent.CFrame.lookVector*value1
		elseif VehicleSeat.Throttle == 0 then
			speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=0*script.Parent.CFrame.lookVector})
		elseif VehicleSeat.Throttle == -1 then
			speed_Tween = TweenService:Create(BodyVel, speedy, {Velocity=((-maxspeed)*script.Parent.CFrame.lookVector)})
		end
	end	
	if speed_Tween then
		speed_Tween:Play()
	end
	if turn_Tween then
		turn_Tween:Play()
	end
	if script.Parent.Position.Y > script.Parent.BodyPosition.Position.Y then
		script.Parent.Driving.Value = false
		value1 = 0
		script.Parent.BodyVelocity.velocity = script.Parent.CFrame.lookVector*value1
	end
end)

What does this line do? It’s nested in your if statement at the end. If you are resetting the position during the Tween then it’s probably why it’s still clunky.

that line just makes sure that the boat doesn’t go above the waterline in terms of y positioning.