Issue: transition.to and rotation

I have a group of objects and I need to rotate them. Each time I calculate the angle which is between -180…180 degrees.

If I use the command:
myGroup:rotate( myAngle )
there is no problem with the correct rotation, whenever angle changes. The best example - a steering wheel. It rotates how you need it, whenever clockwise or counterclockwise.

If I use
myGroup.rotation = myAngle
or
transition.to( myGroup, { rotation=myAngle, time=500, })
I have a problem, group rotates incorrectly, like it “gets stuck” on a certain angle of a certain position. And if I press on this position it moves clockwise and then counterclockwise, back and forth only, instead of, for example, only clockwise.

So like you handle a steering wheel on top you could rotate in only for180 degrees, then it stocked. Or from the right (left) side of a wheel and rotate only for +/-90 degrees, and then need to rotate it the opposite direction.

I read the API Reference and know that these two methods are not equal. So is there a way to make transition.to with first command?

Sorry, but I had trouble grokking that.

Are you saying rotations sometimes don’t occur?

– OR –

Are you saying the rotations sometimes go occur in the wrong (unexpected) direction?

I believe that you may be running into a few problems.

  1. Rotations are allowed to take any value, so you should probably normalize them every time before applying a transition.
    ssk has a feature for this: ssk.math.normRot( obj ) – Adjust value or rotation to range [0,360)
    https://roaminggamer.github.io/RGDocs/pages/SSK2/extensions/#math

  2. I don’t think transition.* is smart enough to take the shortest path to a rotation, so sometimes it takes the long path and other times it takes the short path.
    SSK has a fix for this too:
    https://roaminggamer.github.io/RGDocs/pages/SSK2/libraries/actions1/#face

function player.enterFrame( self )
    -- Rotate to face the  `targetAngle` at a rate of 180 degrees-per-second
    ssk.actions.face( self, { angle = self.targetAngle, rate = 180 } )
end

local obj = <some code to create an object>
obj.targetAngle = 0
obj.enterFrame = enterFrame
listen("enterFrame", obj)

... later you can change the facing angle:

obj.targetAngle = 90 -- Will start turning and face this way automatically

You don’t need to use SSK, just see what I did by looking at the SSK code which does the things I described above.

If your rotate an object 360 degrees and then you rotate it to 180, first it will rotate clockwise but then it will go from 360 to 180 meaning it will rotate counterclockwise.

See my updated response above.

local function normRot( toNorm )
   if( type(toNorm) == "table" ) then
      while( toNorm.rotation >= 360 ) do toNorm.rotation = toNorm.rotation - 360 end      
      while( toNorm.rotation < 0 ) do toNorm.rotation = toNorm.rotation + 360 end
      return 
   else
      while( toNorm >= 360 ) do toNorm = toNorm - 360 end
      while( toNorm < 0 ) do toNorm = toNorm + 360 end
   end
   return toNorm
end

Hey, those libs looks great!
Thanks for sharing it

I mean this:

Are you saying the rotations sometimes go occur in the wrong (unexpected) direction?

so with
myGroup.rotation = myAngle
I get what I expected.

But I need a transition, and I get unexpected direction. Transition results are equal to this command:
myGroup.rotation = myAngle

One final note. SSK assumes these things:

0 degrees is up/forward
90 degrees is right
180 degrees is down/backward
270 degrees is left.

For some reason (probably because this is what they did in geometry or some other math class) lots of folks treat 90 degrees as facing forward.

I specifically do not do that because in the context of game making (action games) that does not make sense to me. Since SSK was made for me first… this is just one thing I ask folks to know and adjust to.

Honestly, it’s quite hard to help without seeing some code. For example, the myGroup:rotate() function doesn’t do any transitioning for you, which leads me to believe you are using it in an enterFrame listener, but I can’t be sure. The reason why you are having more success with it than with setting the rotation value might be because the rotate() function adds myAngle to the current rotation, whereas setting the rotation value just sets the rotation of the group to myAngle, so they are doing two different things.

In any case, rotating to face a particular point is a tricky business. You need to account for whether the start rotation is bigger or smaller than the goal rotation and rotate in the appropriate direction. You also need to account for whether the difference between the two rotations is more or less than half a circle (180 degrees), which also affects which direction you need to rotate. The combination leads to four cases that you need to account for:

  • start rotation is less than end rotation, rotation difference < 180

  • start rotation is less than end rotation, rotation difference > 180

  • start rotation is greater than end rotation, rotation difference < 180

  • start rotation is greater than end rotation, rotation difference > 180

Like I said, tricky. Roaminggamer has offered a lot of help, but I understand that ssk can sometimes seem a bit complicated and overwhelming. Here’s a small sample project that may help you out:

local rect = display.newRect(display.contentCenterX, display.contentCenterY, 10, 1)
rect.anchorX = 0


local function normaliseRotation(r)
	while r < 0 do r = r + 360
	end
	while r > 360 do r = r - 360
	end
	return r
end

local function rotateRect(e)
	rect.rotation = normaliseRotation(rect.rotation)
	local startRotation = rect.rotation
	local endRotation = normaliseRotation(math.atan2(e.y - rect.y, e.x - rect.x)*180/math.pi)
	local angleDiff
	if startRotation < endRotation then
		if math.abs(startRotation - endRotation) < 180 then angleDiff = endRotation - startRotation
		else angleDiff = - (startRotation - endRotation + 360)
		end
	else
		if math.abs(startRotation - endRotation) < 180 then angleDiff = endRotation - startRotation
		else angleDiff = math.abs(endRotation + 360 - startRotation)
		end
	end
	transition.to(rect, {rotation = startRotation + angleDiff, time = math.abs(angleDiff*5)})
end

Runtime:addEventListener("tap", rotateRect)

All the code that I had, I already showed - really only three different commands.

Speaking of
myGroup.rotation = myAngle
and
transition.to( myGroup, { rotation=myAngle, time=500, })
I meant that they working with the same logic, in contrast to command
myGroup:rotate( myAngle )

So hasty, If I understood correctly (and I understood correctly because I fix my problem for me):

When myGroup:rotate( myAngle ) is applied to the group, it simply changing the coordinates of its elements. And the group has no parameter “current rotation”.

While myGroup.rotation or transition.to do not change the coordinates of the group, but dynamically rotate it to the desired angle. Therefore, the group has the “current rotation” parameter and for the correct rotation next time, it’s required to take into account the angle at which the group is already rotated.

I fix it like this

local finalRotateAngle = 0 --because group displayed without any rotation
--then any time:
local currentRotateAngle = --calculate it
finalRotateAngle = finalRotateAngle+currentRotateAngle 
transition.to( myGroup, { rotation=finalRotateAngle, time=500, })