[SOLVED] Tweens Doesn't work as Intended

Hello Developers!
I just want a clear answer on how I could achieve this; Rotating and Moving the cframe of an Instance in the Same tween. I want them both to be CFrame, if possible.

Here is my tween script:

function Smooth_Turn(enemyModel, enemySpeed, cframe)
	
	local tweenTime = (enemyModel.HumanoidRootPart.Position - Vector3.new(cframe.X, cframe.Y, cframe.Z)).Magnitude / enemySpeed
	
	local info = TweenInfo.new(
		tweenTime,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.Out,
		0,
		false,
		0
	)
	
	print("ran the tween function")
	
	local turnTween = TweenService:Create(enemyModel.PrimaryPart, info, {CFrame = cframe})
	turnTween:Play()
	turnTween.Completed:Wait()
	turnTween:Destroy()
end

Here is where cframe is defined:

local nnoc = nextNode.CFrame * CFrame.new(pathOffset, 1, pathZOffset)
Smooth_Turn(enemyModel, enemy.Speed, enemyModel.HumanoidRootPart.CFrame:Lerp(nnoc, 0.1))

For now it kinda turns, but way too slowly. I just want it to turn faster. tweenTime takes about half a second.

Thanks for your attention!

1 Like

CFrame already is the position and the rotation combined so there’s no need to tween both the CFrame and the rotation, the CFrame alone is enough

but my problem is that the rotation needs to like tween faster than the movement itself.
I tried using two tweens in the same time but it didnt work out. Only one of them was running.

Are you making a turret that follows the enemy’s hrp position?

no, I am making a enemy turn smoothly to the next “waypoint”. The rest of the movement is handled in another function.

(basically a zombie moving through set waypoints, like a tower defense)

I recommend you watch this video by GnomeCode, it will explain how to do that

I don’t want to follow this guy’s tutorial. Plus, it uses humanoids and :MoveTo which are very unoptimized and bad for the type of gameplay that I want. But thanks anyways I guess.

if you don’t know how rotation works with CFrames, I doubt humanoid being unoptimized is your biggest problem (also the video in the logic would be the same, with or without the humanoid), maybe go focus up on the beginner tutorials before worrying about buzzy topics regarding the humanoid.

also lerping alone might be better than a tween in this case, youre just going to lerp AND recreate/play a tween every single time the NPC needs to turn? doesn’t sound great in my opinion, but idk you seem very careful about optimization apparently… but yeah anyways, just make the CFrame face the next tile and discard the Y axis lol.

When I started, I watched ALL of the tutorials.
It didn’t look good at all.
For lerping, my system relied on lerping before but someone said that using tweens is less complicated and stuff. I have been stuck on turning smoothly these past few days and I don’t think I am that bad of a scripter.

this will stop the enemy in its course. Already tried that

sounds like a design flaw, making the enemy face a tile shouldn’t stop its movement, unless your movement is CFrame based? in which case, thats bad if you need physics… but still you can just offset the cframe.

Well, here is my script that handles everything. For now, it justs make him stop on the tile and when he finishes turning it starts going to the next tile.

function Smooth_Turn(enemyModel, enemySpeed, cframe)
	
	local tweenTime = (enemyModel.HumanoidRootPart.Position - Vector3.new(cframe.X, cframe.Y, cframe.Z)).Magnitude / enemySpeed
	
	local info = TweenInfo.new(
		tweenTime,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.Out,
		0,
		false,
		0
	)
	
	print("ran the tween function")
	
	local turnTween = TweenService:Create(enemyModel.PrimaryPart, info, {CFrame = CFrame.lookAt(enemyModel.HumanoidRootPart.CFrame, Vector3.new(cframe.X, cframe.Y, cframe.Z)})
	turnTween:Play()
	turnTween.Completed:Wait()
	turnTween:Destroy()
end

function Move_Enemy(enemy, enemyID)
	local enemyModel = workspace.Enemies[enemyID]
	local pathOffset = enemyModel:GetAttribute("PathOffset")
	local nodeAttribute = enemyModel:GetAttribute("Node")
	local pathZOffset

	for x, node in pairs(workspace.Path.Nodes:GetChildren()) do
		if node:GetAttribute("Direction") == "Left" then pathZOffset = -pathOffset else pathZOffset = pathOffset end
		local previousNode = workspace.Path.Nodes:FindFirstChild(tostring(x - 1)) or workspace.Path.StartNode
		local nextNode = workspace.Path.Nodes:FindFirstChild(tostring(x + 1))
	
		local nodeOffsetCFrame = node.CFrame * CFrame.new(pathOffset, enemy.HipHeight, pathZOffset)
		local pnoc = previousNode.CFrame * CFrame.new(pathOffset, enemy.HipHeight, pathZOffset)

		local distance = (nodeOffsetCFrame.Position - pnoc.Position).Magnitude
		local tweenTime = distance / enemy.Speed

		local tweenInfo = TweenInfo.new(tweenTime, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
		local movementTween = TweenService:Create(enemyModel.PrimaryPart, tweenInfo, {CFrame = nodeOffsetCFrame})

		movementTween:Play()
		movementTween.Completed:Wait()
		movementTween:Destroy()

		if nextNode then
			local nnoc = nextNode.CFrame * CFrame.new(pathOffset, 1, pathZOffset)
			--Smooth_Turn(enemyModel, enemy.Speed, enemyModel.HumanoidRootPart.CFrame:Lerp(nnoc, 0.1))
			workspace.Enemies[enemyID]:PivotTo(CFrame.lookAt(workspace.Enemies[enemyID].HumanoidRootPart.Position, Vector3.new(nnoc.X, nnoc.Y, nnoc.Z)))
		else
			enemyModel:Destroy()
			table.remove(Database, enemyID)
		end
	end
end

and i know it isnt very optimized, but I tried doing the basics for optimizing.

and if Cframes are that unoptimized, what do I use? Vector3’s?

You’ll need to do something like this then if you want to use CFrame (tested on a server script):

local tweenInfo = TweenInfo.new(
	2, -- Change the speed to what you want
	Enum.EasingStyle.Linear,
	Enum.EasingDirection.In,
	0,
	false,
	0)

local tweenService = game:GetService"TweenService"

local zombie = workspace.Part

local tweens = {}

local waypoints = workspace.Waypoints

for i in waypoints:GetChildren() do
	tweens[i] = tweenService:Create(zombie, tweenInfo, {CFrame = waypoints[i].CFrame})
end

for _, tween in tweens do
	tween:Play()
	tween.Completed:Wait()
end

Edit for extra info: The waypoints will need to be named in numerical order for it to work though like so:
Screenshot 2023-12-17 164902

I already have a system for that, the only thing remaining is for the part itself to turn smoothly.

my problem is that the zombie turns smoothly but it stops on the waypoint, then when the turn is finished, it restarts moving. I tried using coroutine.wrap but that just made the tween not run.

Im gonna update the title too, since its not accurate.

If you test the script I just gave you, it solves the problem you have where the part isn’t turning smoothly

it should, problem is that it will turn all along the path. Not directly after it reached the waypoint.

make properties a table

local properties = {
	CFrame = cframe,
	Orientation = --  however you want to rotate (must be vector3)
}
	local turnTween = TweenService:Create(enemyModel.PrimaryPart, info, properties)

If you want to turn the part when it reaches the waypoint instead then it would be very difficult to get it to look smooth, plus personally I don’t think it would be very realistic if the part represents a zombie

i dont want it to be too snappy either. Snappy looks too barebones.
a game like “Tower Battles” represents the sort of movement im trying to mimicking.