How Would I Get A Model to Rotate and Tween at the Same Time?

I have this Model of a circle made up of parts. All the parts are arranged in a circle, and there is a invisible PrimaryPart in the middle of the circle.
What I’m trying to do is tween the model up and down and rotate it at the same time, but I want to play them seperatly and at the same time.
For example, I want the tweening up and down to have a circlular easing style, but I want the rotation to have a linear easing style.

However, I can’t get both of them to play at the same time. Someone reccomended to me that I put the model inside another model and tween one and rotate the other. I’m hesitant on doing that, as I feel like it could lead to some problems and also cause disorganization.

Is using 2 seperate models the only solution I have? I can’t find another way to solve this, and I need some help.

1 Like

Create 2 tweens on Vector3 and CFrame values and use Changed:Connect to gather the values and apply them to your model with PivotTo(), i.e.

local Model = game.Workspace.Model;

-- this is the movement vector
local UpDownVec = Vector3.new(); -- this is the actual vector to apply to your model
-- this is a position vector that we tween instead of the models actual position
local udValue = Instance.new("Vector3Value");
-- this connects the tween and sends the changed value to this function, everytime the tween changes it
udValue.Changed:Connect(function(Progress)
-- each change (by the tween) is sent here, so we set the actual movement vector to the new value
	UpDownVec = Progress; -- UpDownVector now contains the value that is being tweened
end)

-- this is the rotation frame, the one used to rotate the model
local RotateFrame = CFrame.new();
-- this is a rotationValue that we tween instead of the actual models CFrame
local rotateValue = Instance.new("CFrameValue");
-- again this gets fired everytime the rotateValue changes (during tweening)
rotateValue.Changed:Connect(function(Progress)
	RotateFrame = Progress; -- take the tweens progress (rotateValue) and apply it to the actual CFrame we need
end)

-- we must spawn two functions to actually play the tweens, if we don't they will happen at the same time -- and wont function correctly, there are better ways but this is a simple example
spawn(function()
-- make the movement tween, TweenInfo will contain the easing style you want, Elastic, Bounce, Quad...
	local tweenUpDown = game.TweenService:Create(udValue,TweenInfo.new(5),{Value=Model:GetPivot().Position+Vector3.new(0,100,0)});
	tweenUpDown:Play(); -- play the tween, this function will stay until the tween is complete
end)
-- make and play the rotation tween, using again your different easing type, Quad, Bounce, Linear...
spawn(function()
	local tweenRotate = game.TweenService:Create(rotateValue,TweenInfo.new(5),{Value=Model:GetPivot()*CFrame.Angles(1,0,0)});
	tweenRotate:Play();
end)

Now on render step or whichever frame rate you want to use accumulate the changes and apply them:

-- Now here we can apply the changes to both of those tweens, this is a fast running function and will pick up on all of those changes in the tweens
game:GetService("RunService").RenderStepped:Connect(function()

-- all we do here is use PivotTo() to move and rotate the model at the same time at a much higher rate so we get a nice smooth movement.
	Model:PivotTo(CFrame.new(UpDownVec)*RotateFrame);

end)

Can you break this down for me? I don’t really understand the code.

here is a demo project
MoveModel.rbxl (33.7 KB)

and here is the script

local runService = game:GetService("RunService")
local model = workspace.Model
local position = Vector3.new(0, 10, -20)
local moveAmount = 5
local moveSpeed = 1
local rotateSpeed = 1
local cFrame = CFrame.new()
local counter = 0

runService.Heartbeat:Connect(function(deltaTime)
	counter += moveSpeed * deltaTime
	local offset = Vector3.new(0, math.sin(counter) * moveAmount, 0)
	cFrame *= CFrame.fromAxisAngle(Vector3.yAxis, rotateSpeed * deltaTime)
	model:PivotTo(cFrame + position + offset)
end)
2 Likes

Unfortunately, although a good example, the OP distinctly asked for a method to use two different Easing methods to move the model. Hence why it has to be done with Tweening, your method does not include fancy stuff like, bounce, elastic etc…

Cab you explain this (Or break it down) please? I’m new to this, and I have a really hard time trying to understand script, so It would help If I understood what the sections of script do

local function RigModel(Model : Model,Tween : Tween)
	local Primary = Model.PrimaryPart
	Primary:SetAttribute("Anchored",Primary.Anchored) --Saves parts anchored state
	Primary.Anchored = true 
	
	for _,v : BasePart in pairs(Model:GetDescendants()) do -- goes trough all the children inside the model
		if v:IsA("BasePart") and v ~= Model.PrimaryPart then 
			v:SetAttribute("Anchored",v.Anchored) -- Saves parts anchored state again
			local M6D = Instance.new("Motor6D",Model.PrimaryPart) -- Creates a "Weld" (I use motor6D since it doesnt break on position updates)
			M6D.Part0 = Model.PrimaryPart
			M6D.Part1 = v
			M6D.C0 = M6D.Part0.CFrame:Inverse() -- Inverses (basically inverts the cframe [Example: CFrame.new(1,1,1):Inverse() == CFrame.new(-1,-1,-1)])
			--[[
			The nature of welds is that they dont set the positon and orientation relative to the world, they create their own "space" around them (Like offsetted world)
			and i inverse the cframe to "fit" it in the m6d space
			--]]
			M6D.C1 = M6D.Part1.CFrame:Inverse() -- Same as the last one
			v.Anchored = false
			
			task.spawn(function() -- Creates a function that wont yield (wont stop the code from running)
				Tween.Completed:Wait() -- waits until the tween is completed
				M6D:Destroy() -- Destroys the Motor6D
				v.Anchored = v:GetAttribute("Anchored") -- retrives the anchored state and removes it
				v:SetAttribute("Anchored",nil)
			end)
		end
	end
	
	Tween:Play() -- you get it 
	Tween.Completed:Wait()-- you get it 
	
	Primary.Anchored = Primary:GetAttribute("Anchored") -- you get it 
	Primary:SetAttribute("Anchored",Primary.Anchored) -- you get it 
end

The original solution I had in mind was to use a tween on the model’s PrimaryPart to move the model up and down, and rotate the PrimaryPart. I just used the linear and circular easingstyles to explain how I wanted it to move. This is a good example, but one major drawback is that It may be hard to control, since with a tween, you can choose where it moves, versus using a vector sine wave, which may be harder to adjust.

Plus, I don’t know very well how this script works.

made a better solution

local function RigModel(Model : Model,Tween : Tween)
	local Primary = Model.PrimaryPart
	Primary:SetAttribute("Anchored",Primary.Anchored) --Saves parts anchored state
	Primary.Anchored = true 
	
	for _,v : BasePart in pairs(Model:GetDescendants()) do -- goes trough all the children inside the model
		if v:IsA("BasePart") and v ~= Model.PrimaryPart then 
			v:SetAttribute("Anchored",v.Anchored) -- Saves parts anchored state again
			local M6D = Instance.new("Motor6D",Model.PrimaryPart) -- Creates a "Weld" (I use motor6D since it doesnt break on position updates)
			M6D.Part0 = Model.PrimaryPart
			M6D.Part1 = v
			M6D.C0 = M6D.Part0.CFrame:Inverse() -- Inverses (basically inverts the cframe [Example: CFrame.new(1,1,1):Inverse() == CFrame.new(-1,-1,-1)])
			--[[
			The nature of welds is that they dont set the positon and orientation relative to the world, they create their own "space" around them (Like offsetted world)
			and i inverse the cframe to "fit" it in the m6d space
			--]]
			M6D.C1 = M6D.Part1.CFrame:Inverse() -- Same as the last one
			v.Anchored = false
			
			task.spawn(function() -- Creates a function that wont yield (wont stop the code from running)
				Tween.Completed:Wait() -- waits until the tween is completed
				M6D:Destroy() -- Destroys the Motor6D
				v.Anchored = v:GetAttribute("Anchored") -- retrives the anchored state and removes it
				v:SetAttribute("Anchored",nil)
			end)
		end
	end
	
	Tween:Play() -- you get it 
	Tween.Completed:Wait()-- you get it 
	
	Primary.Anchored = Primary:GetAttribute("Anchored") -- you get it 
	Primary:SetAttribute("Anchored",Primary.Anchored) -- you get it 
end

Alright, I’m starting to worry that what I’m trying to do is way outside of my skill range. The problem with all of the scripts here is that I can’t understand it well, as I am still relatively new to coding. So, once again, I’m going to ask if this can be broken down and explained. I haven’t had much experience with coding like this, so I don’t understand a lot of this, such as Motor6d

alr give me some time to write it down

Alright, take all the time you need.

I just added a load of comments to my original post, hopefully it will be clearer for you.

1 Like

Again a good example, but it doesn’t use two different EasingStyles to do the movements. The OP distinctly asked for two different styles of moving and rotating the model

I’ll actually let him explain it and just ditch the idea of trying to use EasingStyles, since I think what I’m trying to do is more complicated than EasingStyles.

Edit: I still prefer that I use Tweening, but I’ll still be more open to other methods if the method I’m trying to do runs into a bug.

Oh wait, you are trying to rotate a model constantly and move it up and down?

I guess i can create something rq wait

Mine does exactly that, and is about as simple an example as you can get. Just run it, break into the code, and analyse what happens with prints().

I honestly don’t think it’s worth your time, the OP obviously has no methods to run, analyse, and critique any method over another, save time and keep coding instead.

local function RotateUpAndDown(Model : Model,RotationSpeed : number,YSpeed : number)
	YSpeed = YSpeed or 1
	RotationSpeed = RotationSpeed or 0.5
	local connection 
	
	local PrimaryNormalCF = Model.PrimaryPart.CFrame
	
	connection = game:GetService("RunService").Heartbeat:Connect(function()
		Model:SetPrimaryPartCFrame(PrimaryNormalCF + Vector3.new(0,math.sin(time())*YSpeed,0))
		PrimaryNormalCF *= CFrame.Angles(0,math.rad(RotationSpeed),0)
	end)
end

Done rotates object around it self (using math)

Give me a second, I’m going to read the script, test it out, and do something else outside of Roblox Studio, and I’ll be back with results.