Movement in the flow of cos and sin

Why is the part moving along an infinity
trajectory instead of a circular one?


What I get

What I want to get


Code

local p = workspace.Part
local s, positive = 0, true

game:GetService("RunService").PostSimulation:Connect(function(DeltaTime)
	p.Rotation = p.Rotation:Lerp(p.Rotation + Vector3.new(math.cos(math.pi * 2 / 180 * s), math.sin(math.pi * 2 / 180 * s), 0), DeltaTime * 15)
	
	if positive then
		s += 1
		if s == 180 then
			s = 0
			positive = false
		end
	else
		s -= 1
		if s == -180 then
			s = 0
			positive = true
		end
	end
end)

image


Please explain why this is happening and what I need to do to fix it.

Use Quaternions:

local p = workspace.Part
local s, positive = 0, true

game:GetService("RunService").PostSimulation:Connect(function(DeltaTime)
    local rotationDelta = math.rad(s)
    local rotationQuaternion = CFrame.Angles(0, rotationDelta, 0)
    local currentRotation = p.CFrame
    local newRotation = currentRotation * rotationQuaternion

    p.CFrame = currentRotation:Lerp(newRotation, DeltaTime * 15)
	
	if positive then
		s = (s + 1) % 360
		if s == 180 then
			positive = false
		end
	else
		s = (s - 1) % 360
		if s == 0 then
			positive = true
		end
	end
end)

Previous:
Capture

Quaternion version:
Capture - Copy
Oops, it’s supposed to be in the opposite direction. (drawing)

1 Like

Thanks for trying, but you didn’t quite understand what I want. Everything works correctly in the main part of the script. I want the part to rotate along the x and y axes, and the transition between rotation to a negative and positive environment is carried out along a circular trajectory, instead of an infinite one.

If it’s still not clear, look at how the addition of widgets on iOS is implemented. In the preview, they rotate slightly as if showing themselves from different sides and they do it along a circular trajectory.

I presume this is the effect you’re going for?

Your s value (the degrees) is going from -180 to 180 which involves returning to the same value multiple times. In order to solve this, you shouldn’t start decrementing s, since 360 degrees is the same as -360, -180 is the same as 180 degrees. You’re basically telling it to reverse everything it did already (and go backwards), which is what creates that infinity-like motion from earlier.

By going from 0 to 360, you’re making sure it’s always increasing the angle, and once it gets past 360, you set it to 0 so you’re still in the space of [0, 360].

	p.Rotation = p.Rotation:Lerp(p.Rotation + Vector3.new(math.cos(math.rad(s)) , math.sin(math.rad(s)), 0), DeltaTime * 15)

	s += 1
	
	if s >= 360 then
		s = 0
	end

(also you don’t need to manually convert degrees into radians, there’s math.rad())
(there also doesn’t seem to be a limit for x, on math.rad(x), but I’d reset it after 360 anyways)

1 Like

At first I thought so too, but the problem is that changing -180 and 180 by 360 increases the amplitude of rotation, and rotation will be carried out only by positive numbers. That is, it rotates only up and to the right, then returns to its original position. My problem is the specific transition from -180 to 180 and vice versa, that is, instead of continuing to rotate from the bottom and smoothly rise up, the part rises to the top and goes to the left. That is, the axis of its rotation itself is incorrect (as a symbol of infinity).

It seems that you thought that I meant that it should rotate only in +, although I want it to rotate in + and -, but the transition from these 2 positions was like a circle, not like infinity (axis of rotation).

I think that’s a problem of the initial rotation? If I start at 0,0,0 rotation (horizontal) it looks like this:

I’m not really sure how you’d be able to get from where we are to your desired effect, sorry.

Maybe store the rotation values of each corner being pushed “down” and lerp through them?

I tried making it work with quaternions. It should give the desired effect, like the video you sent in. You might need to put the quaternion class in a module script.

local Quaternion = {}
Quaternion.__index = Quaternion

function Quaternion.new(w, x, y, z)
	local invLength = 1 / math.sqrt(w * w + x * x + y * y + z * z)

	local self = setmetatable({
		W = w * invLength,
		X = x * invLength,
		Y = y * invLength,
		Z = z * invLength,
	}, Quaternion)

	return self
end

function Quaternion.fromAxisAngle(axis, angle)
	local sin = math.sin(angle / 2)
	return Quaternion.new(math.cos(angle / 2), sin * axis.X, sin * axis.Y, sin * axis.Z)
end

function Quaternion:Conjugate()
	return Quaternion.new(self.W, -self.X, -self.Y, -self.Z)
end

function Quaternion.__mul(self, other)
	local w = self.W * other.W - self.X * other.X - self.Y * other.Y - self.Z * other.Z
	local x = self.X * other.W + self.W * other.X - self.Z * other.Y + self.Y * other.Z
	local y = self.Y * other.W + self.Z * other.X + self.W * other.Y - self.X * other.Z
	local z = self.Z * other.W - self.Y * other.X + self.X * other.Y + self.W * other.Z

	return Quaternion.new(w, x, y, z)
end

function Quaternion:ToAxisAngle()
	local angle = math.acos(self.W) * 2
	local sin = math.sin(angle / 2)
	local axis = Vector3.new(self.X, self.Y, self.Z) / sin
	return axis, angle
end

function Quaternion:ToCFrame()
	local nx = self * Quaternion.new(0, 1, 0, 0) * self:Conjugate()
	local ny = self * Quaternion.new(0, 0, 1, 0) * self:Conjugate()
	local nz = self * Quaternion.new(0, 0, 0, 1) * self:Conjugate()
	
	nx = Vector3.new(nx.X, nx.Y, nx.Z)
	ny = Vector3.new(ny.X, ny.Y, ny.Z)
	nz = Vector3.new(nz.X, nz.Y, nz.Z)
	
	return CFrame.fromMatrix(Vector3.zero, nx, ny, nz)
end

function Quaternion:Slerp(other, t)
	local rel = other * self:Conjugate()

	local axis, angle = rel:ToAxisAngle()
	angle *= t
	return Quaternion.fromAxisAngle(axis, angle)
end

local p = workspace.Part
local s, positive = 0, true

local origin = p.CFrame

local speedMultiplier = 100

game:GetService("RunService").PostSimulation:Connect(function(DeltaTime)
	local rot = math.pi / 8
	
	local identity = Quaternion.new(1, 0, 0, 0)
	
	local up = Quaternion.new(math.cos(rot / 2), 0, 0, math.sin(rot / 2))
	local right = Quaternion.new(math.cos(rot / 2), math.sin(rot / 2), 0, 0)
	
	local final = up:Slerp(identity, math.sin(math.rad(s))) * right:Slerp(identity, math.cos(math.rad(s)))
	
	p.CFrame = origin * final:ToCFrame()
	
	s += DeltaTime * speedMultiplier
end)
3 Likes

Thank you, you did what I wanted! Great job!