How do I move an entire model very smoothly without using roblox physics, or the heartbeat service?

How do I move an entire model very smoothly without using roblox physics, or the heartbeat/runservice service?

I have tried to make a boat move using cframe for my game, I was currently using the heartbeat service for this but later found out that when multiple people join with their each assigned boat it uses a massive amount of the “Recv” data resource from the server to the client. This huge amount of usage causes it to be very laggy on low end devices and even causes my high end computer with very low ping to recieve very choppy gameplay.

Obviously the heartbeat service causes way too much lag as each boat spawned uses around 90kb/s of the “Recv” data resource. And by the end of the games development there will be up to 100 boats spawned in each server.

Here is the part of the script that controls the movement CFrame of the boat:

Running = game:GetService("RunService").Heartbeat:Connect(function(dt)
	Model:SetPrimaryPartCFrame(Model.PrimaryPart.CFrame * CFrame.new(0,1 * ((101.15 + ((Health.Value / MaxHealth.Value) * 2)) - Model.PrimaryPart.Position.Y) * dt, -1 * (Speed.Value * 1.1) * dt)) -- move forwards and set Y level

	if GoRight == true then
		if Model.PrimaryPart.Orientation.Z >= -20 then
			Model:SetPrimaryPartCFrame(Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * dt, 0, math.rad((-1 * turnspeed * dt) * 0.7))) -- move vanity rotation
		end
		Model:SetPrimaryPartCFrame(Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * dt, math.rad(-1 * turnspeed * dt), 0)) -- move boat rotation
	else
		if GoLeft == true then
			if Model.PrimaryPart.Orientation.Z <= 20 then
				Model:SetPrimaryPartCFrame(Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * dt, 0, math.rad((1 * turnspeed * dt) * 0.7))) -- move vanity rotation
			end
			Model:SetPrimaryPartCFrame(Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * dt, math.rad(1 * turnspeed * dt), 0)) -- move boat rotation
		else
			if Model.PrimaryPart.Orientation.Z < 0 then
				Model:SetPrimaryPartCFrame(Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * dt, 0, math.rad((1 * turnspeed * dt) * 0.7))) -- move straight rotation
			end
			if Model.PrimaryPart.Orientation.Z > 0 then
				Model:SetPrimaryPartCFrame(Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * dt, 0, math.rad((-1 * turnspeed * dt) * 0.7))) -- move straight rotation
			end
		end
	end
end)

I’m just wondering if theres any ways to do this a lot more efficiently with very smooth movement (atleast 60fps)

I’ve tried using the tweenservice and it’s probably the way to go but I just don’t have enough knowledge with tweenservice.

But if anybody knows how to move my models CFrames while using Tweenservice or something else smooth then I would heavily appreciate it if you could tell me how.

1 Like

I think you’ll be better off having the clients themselves control the boat physics that they’re steering.

Have the server give network ownership of the boat’s assembly to the client, and then run your physics updates locally.

1 Like

Some of the issues I have is would there have to be a lot of FireServer commands fired as this can still cause lag having to do this 60 times a second?

Could exploiters heavily manipulate the physics of the boat?

And even if I did make the client fire the server with each movement change of the boat without any remote event lag, would there still be a lot of lag from the server setting the CFrame from what the client has sent, (because in my game the other players still have to see the other peoples boats move in real time.)

am pretty sure its impossible for movement to appear smooth if its being controlled by other than the person seeing that movement

1 Like

He said to use network ownerships. That does not involve the exchange of remotes, it’s handled internally by the server-client replicator which already can cause lag.

The problem of exploiting is something you will have to figure out yourself by developing your own anti-exploits

1 Like

Sounds to me like TweenService could help you
https://developer.roblox.com/en-us/api-reference/class/TweenService

2 Likes

The simplest option really is to use body movers or attachments and NetworkOwnership. Exploitable sure but roblox has done all the hard work of replication for you and you probably won’t get much better.

The alternative is to come up with some custom replicator. As a sketch:

  • boats don’t physically exist on the server at all, but there is CFrame info
  • clients send inputs to the server
  • server uses inputs to change data
  • for a given client:
    • server streams their boats data constantly
    • server gives data for the other boats occasionally (based on distance or at the request of the client)
  • clients create copies of everyone’s boats just for themselves
  • clients interpolate between data they receive intermittently, using body movers or CFrame:Lerp or tween service or whatever
  • you can get fancy as you wish, predicting velocities and all sorts of things

All that’s pretty complex and there’s a lot of ways to go wrong, which is why “accept the chance of exploits and use physics” is the easiest option.

4 Likes

Hm, I don’t really understand how to use “network ownerships” then, are there any sites I can read up about it which links best to my problem?

Although I’d rather have most of this Server Side as exploiters will be an issue, and these boats have an awful lot of variables to it which even one changed by the exploiter can ruin the game and it would be hard to keep track of them all

Yes, this is what I tried using and I’d love to be able to use it as it would probably solve my issue best

Unfortunately I don’t know how to use tweenservice properly and when I use it only one boat movement can be executed at one time.(You will understand what I just said when I show the video)

Here is the footage for the heartbeat service being used for my boats. The performence is perfectly fine there but I am on studio and theres just one player in the server. With more players or on low end devices it is unplayable:

Heartbeat service boat movements

Here is the footage for when I tried to use the tweenservice, as you can see only one movement plays at a time and the boat is moving slower then I wanted it to because of the loop delay:

Tween Service movement

I obviously lack knowlege with the tweenservice and how to make it tween two movements at the same time.

Here’s the code I used for the tween service:

while Living == true do 
	local goal = {}
	goal.CFrame = Model.PrimaryPart.CFrame * CFrame.new(0,1 * ((101.15 + ((Health.Value / MaxHealth.Value) * 2)) - Model.PrimaryPart.Position.Y) / 10, -1 * (Speed.Value * 1.1) / 10) -- movw forward and set y 
	
	local Tween = TweenService:Create(Model.PrimaryPart, TweenInfo.new(0.1), goal)
	Tween:Play()
	if GoRight == true then
		if Model.PrimaryPart.Orientation.Z >= -20 then
			local goal = {}
			goal.CFrame = Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * 0.1, 0, math.rad((-1 * turnspeed * 0.1) * 0.7))

			local Tween = TweenService:Create(Model.PrimaryPart, TweenInfo.new(0.1), goal)
			Tween:Play()
			-- move vanity rotation
		end
		local goal = {}
		goal.CFrame = Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * 0.1, math.rad(-1 * turnspeed * 0.1), 0)

		local Tween = TweenService:Create(Model.PrimaryPart, TweenInfo.new(0.1), goal)
		Tween:Play()
		-- move boat rotation
	else
		if GoLeft == true then
			if Model.PrimaryPart.Orientation.Z <= 20 then
				local goal = {}
				goal.CFrame = Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * 0.1, 0, math.rad((1 * turnspeed * 0.1) * 0.7))

				local Tween = TweenService:Create(Model.PrimaryPart, TweenInfo.new(0.1), goal)
				Tween:Play()
				-- move vanity rotation
			end
			local goal = {}
			goal.CFrame = Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * 0.1, math.rad(1 * turnspeed * 0.1), 0)

			local Tween = TweenService:Create(Model.PrimaryPart, TweenInfo.new(0.1), goal)
			Tween:Play()
			-- move boat rotation
		else
			if Model.PrimaryPart.Orientation.Z < 0 then
				local goal = {}
				goal.CFrame = Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * 0.1, 0, math.rad((1 * turnspeed * 0.1) * 0.7))

				local Tween = TweenService:Create(Model.PrimaryPart, TweenInfo.new(0.1), goal)
				Tween:Play()
				-- move straight rotation
			end
			if Model.PrimaryPart.Orientation.Z > 0 then
				local goal = {}
				goal.CFrame = Model.PrimaryPart.CFrame * CFrame.Angles(1 * (0 - Model.PrimaryPart.Orientation.X) * 0.1, 0, math.rad((-1 * turnspeed * 0.1) * 0.7))

				local Tween = TweenService:Create(Model.PrimaryPart, TweenInfo.new(0.1), goal)
				Tween:Play()
				-- move straight rotation
			end
		end
	end
	wait(0.1)
end

I really just don’t know how to use tweenervice properly for this, do you think you could write a line on what you would do for the tweenservice with my scenario?

1 Like

Thanks for the reply.

Just don’t really think this would work entirely well with the fact that people can even join your boat if they choose to in the game, this would cause a lack of performence with people on a players boat.

There’s always trade-offs with networked games.

However, with this method, you can do things like prioritize a single boat (the one you’re on) for replication. That boat gets data sent every frame, and maybe even use optimistic updates to make things really smooth for the captain, and prediction to make things smooth for the sailors.

Thinking about it, sure, I’ll try it, I’ll let you know on what happens

You might want to try your original Heartbeat version, and try to accumulate your values for the CFrame with one pass and move the model in one go so you don’t call SetPrimaryPartCFrame() too many times. And really SetPrimaryPartCFrame() will be deprecated so you should be using model:PivotTo() instead, which also saves the need to weld your model together.

One way you can create multiple movement vectors with Tweening is to tween the variables separately. So create x,y,z,yaw,pitch,roll values and tween those values not the actual models CFrame. You can check when these values change using x.Changed:Connect(new_value) etc., and accumulate the movements into a single CFrame and then apply the changes once every frame on whichever step system you find the most pleasing.

It’s not an easy problem, just a warning! That’s why I suggested “just deal with the problems roblox physics provides” :slight_smile:

For an idea of how crazy you could go with it, this is a great talk about how overwatch does their networking:

Quite right, there certainly isn’t a trivial solution to this, it could (and probably will) take several weeks to perfect!

1 Like

Don’t fret over exploiters manipulating physics, you can always just do periodic checks on the server if the boat’s position jumped unnaturally and simply teleport it back. Very low chance someone would go out of their way to reverse-engineer your system just to get a speed boost

Whenever working with player movement, you should always prioritize more on efficiency rather than security since trading off for the latter not only makes your gameplay choppy but also adds unnecessary code that can get overwhelming. It’s healthy to make compromises when reasonable and you’ll thank your codebase later

As I realize that you want to move a boat, and rotate it at the same time, you should the align position and align orientation constrains.
How to Tween a Model using Align Position & Orientation in Roblox Studio - YouTube

but if you want to, you can use TweenService
Advanced Roblox Scripting Tutorial #10 - TweenService / Tween (Beginner to Pro 2019) - YouTube

The biggest difference between them is that TweenService cannot have multiple forces acting on it while the Alignment Constraints can. For that reason, TweenService is used mostly for one axis animations like rotating a door or driving a car is a single direction. Personally I would prefer you to use the alignment constraints as they have more of a practical use for things with more direction, like a boat.

If you plan to have your ship curve on its course, and not use the Alignment Constraints, have the Beziers plugin that can allow you to create such paths for a tween to follow on.

I’ve finished it and managed to make it 60x less data consuming! I made it so the captain of the ship streams their position every 0.2 seconds and it interpolates onto other peoples screens using CFrame:Lerp.

There were a few issues I encountered but I managed to alter it to work!

1 Like

I made a module that allows you to tween models!