Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Using a rotation transition does not work correctly when going from 180 to -180 degrees.
Started by infinite.replay Jul 07 2017 02:43 AM

- - - - -
4 replies to this topic
transition rotation rotate degrees angle

Best Answer infinite.replay , 07 July 2017 - 11:18 AM

I have come up with a solution to this issue. It might not be the most elegant solution but seems to work quite well.

Basically i check if the angle that has rotated is greater than 180 degrees which means it won't be taking the shortest route. Then I have an if, else statement which checks if the angle is positive or negative.

 

If it's negative I set the current rotation of the object to 360 minus the absolute value of the rotation which mimics the number being a positive value greater than 180.

If it's positive I do something similar but with -360 plus the object rotation.

 

Below is the new onTravel function with the checking in place. Also here is a new gyazo gif showing it working.

https://gyazo.com/8760e99da565b79b93b15202aa37f1b3

 

local function onTravel(mouseX, mouseY)
  local function Flag()
    travelFlag = true
    --Fires a bullet in the direction of the player facing
    fireBullet()
  end
  if travelFlag then
    local prevAngle = Player.pGroup.rotation
    local newAngle = (getImageRotation(Player.pGroup.x, Player.pGroup.y, mouseX, mouseY)) * -1
    local difference = math.abs(prevAngle - newAngle)
    if difference > 180 then
      if Player.pGroup.rotation <= 0 then
        Player.pGroup.rotation = 360 - math.abs(Player.pGroup.rotation)
      else
        Player.pGroup.rotation = -360 + Player.pGroup.rotation
      end
    end
    print("Angle = " .. newAngle)
    travelFlag = false
    transition.to( Player.pGroup, { time=500, rotation = newAngle, onComplete=Flag } )
  end
end

[TOPIC CONTROLS]
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

infinite.replay

[GLOBAL: userInfoPane.html]
infinite.replay
  • Observer

  • 6 posts
  • Corona SDK

Currently there is a player object that rotates on a pivot depending on where you tap on the screen. This works correctly and it will choose the correct angle to transition to.

 

I have set it so the top half of the players rotational value goes from 0 to -180 and the bottom half 0 to 180. This results in the left side having a jump from 180 to -180, therefore the player object moves all the way around its pivot.

 

I have also tested it with having the bottom half go from 0 - 180 and then it continues on the top half going from 181 - 360 degrees. This results in the same issue but on the right side where the angle goes from 360 to 0 degrees.

 

I'm wondering if there is a decent way to fix this problem are it's visually quite annoying. Thanks  :D

 

I took a gyazo gif screenshot showing a visual example of my problem:

https://gyazo.com/7635a8347758cb4e260be6d4a8021232

 

Below is my code which is responsible for handling the rotation of the player object. It's a mixture of my own code and bits that I've read from other forum posts.

 

local function getImageRotation(x1,y1,x2,y2)
  local PI = 3.14159265358
  local deltaY = y2 - y1
  local deltaX = x2 - x1
  local angleInDegrees = (math.atan2( deltaY, deltaX) * 180 / PI) * -1
  local mult = 10^0
  return math.floor(angleInDegrees * mult + 0.5) / mult
end
-------------------------------------------------
local function onTravel(mouseX, mouseY)
  local function Flag()
    travelFlag = true
    --Fires a bullet in the direction of the player facing
    fireBullet()
  end
  if travelFlag then
    local newAngle = (getImageRotation(Player.pGroup.x,Player.pGroup.y,mouseX,mouseY))*-1
    print("Angle = " .. newAngle)

    travelFlag = false
    transition.to( Player.pGroup, { time=500, rotation = newAngle, onComplete=Flag } )
  end
end
-------------------------------------------------
local function beginRotate( event )
  local phase = event.phase
  local t = event.target
  if phase == "began" then
    onTravel(event.x, event.y)
  end
end



[TOPIC: post.html]
#2

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,588 posts
  • Corona SDK

The trick to solving this is to ensure that:
 
1. The transition's rotation delta < 180 degree in total
2. The object's rotation is normalized in such a way to ensure the target angle is left or right such that transition travels the shortest distance.
 
Tip: I've solved this a few times before so it is doable, but I don't have time right now to dig up my solution.  
You can solve this  on your own I am sure, or hire a hitman.
 
 
 
Shortest distance examples
1. Current angle 0 desired angle 270 --> transition to angle: -90
 
2. Current angle -90 desired angle 180 --> transition to angle: -180
 
3. Current angle -180 desired angle 45 --> Normalized object angle to 180, then transition to angle: 45

Edited by roaminggamer, 07 July 2017 - 08:04 AM.


[TOPIC: post.html]
#3

anaqim

[GLOBAL: userInfoPane.html]
anaqim
  • Contributor

  • 770 posts
  • Corona SDK

Hi, I had this issue but I think i solved it by making sure both start and stop number was on the same side of 0.

 

I cant dig up the code right now but remember angles can be anything, 2540 deg if you want, its all a sping on the 360 scale.



[TOPIC: post.html]
#4

infinite.replay

[GLOBAL: userInfoPane.html]
infinite.replay
  • Observer

  • 6 posts
  • Corona SDK

  Best Answer

I have come up with a solution to this issue. It might not be the most elegant solution but seems to work quite well.

Basically i check if the angle that has rotated is greater than 180 degrees which means it won't be taking the shortest route. Then I have an if, else statement which checks if the angle is positive or negative.

 

If it's negative I set the current rotation of the object to 360 minus the absolute value of the rotation which mimics the number being a positive value greater than 180.

If it's positive I do something similar but with -360 plus the object rotation.

 

Below is the new onTravel function with the checking in place. Also here is a new gyazo gif showing it working.

https://gyazo.com/8760e99da565b79b93b15202aa37f1b3

 

local function onTravel(mouseX, mouseY)
  local function Flag()
    travelFlag = true
    --Fires a bullet in the direction of the player facing
    fireBullet()
  end
  if travelFlag then
    local prevAngle = Player.pGroup.rotation
    local newAngle = (getImageRotation(Player.pGroup.x, Player.pGroup.y, mouseX, mouseY)) * -1
    local difference = math.abs(prevAngle - newAngle)
    if difference > 180 then
      if Player.pGroup.rotation <= 0 then
        Player.pGroup.rotation = 360 - math.abs(Player.pGroup.rotation)
      else
        Player.pGroup.rotation = -360 + Player.pGroup.rotation
      end
    end
    print("Angle = " .. newAngle)
    travelFlag = false
    transition.to( Player.pGroup, { time=500, rotation = newAngle, onComplete=Flag } )
  end
end



[TOPIC: post.html]
#5

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,588 posts
  • Corona SDK

Same idea packaged as a function:

 

https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2017/07/shortestPathTransition.zip

local function shortestPathTransition( obj, targetAngle, dps, myEasing )
   targetAngle = targetAngle or 0
   local dps = dps or 0
   dps = dps/1000 -- degrees per second
   local myEasing = myEasing or easing.linear

   -- Instant Turn
   if(dps < 0 ) then
      obj.rotation = targetAngle      

   -- Timed Turn
   else
      -- Normalize angles
      while( obj.rotation < 0 ) do obj.rotation = obj.rotation + 360 end
      while( obj.rotation >= 360 ) do obj.rotation = obj.rotation - 360 end
      while( targetAngle < 0 ) do targetAngle = targetAngle + 360 end
      while( targetAngle >= 360 ) do targetAngle = obj.rotation - 360 end

      -- Stop any prior transitions on this object
      transition.cancel( obj )

      -- Calc initial tween angle
      local tweenAngle = obj.rotation - targetAngle

      -- Adjust to shortest path
      if(tweenAngle >= 180) then
         targetAngle = targetAngle + 360
         tweenAngle  = targetAngle - obj.rotation
      elseif(tweenAngle <= -180) then
         targetAngle = targetAngle - 360
         tweenAngle  = targetAngle - obj.rotation
      end   

      -- Calc rotation time
      local rotateTime = math.abs(math.floor(tweenAngle / dps))

      -- Transition
      transition.to( obj, { rotation = targetAngle, time = rotateTime, transition = myEasing } )
   end
   obj.targetAngle = targetAngle
end


--
-- Test it
--
local arrow = display.newImageRect( "arrow.png", 80, 80 )
arrow.targetAngle = 0 
arrow.x = display.contentCenterX
arrow.y = display.contentCenterY

local label = display.newText( "TBD", arrow.x, arrow.y + 200 )

function label.enterFrame( self )
   self.text = string.format("Arrow Rotation: %d   Rotation Target: %d", arrow.rotation, arrow.targetAngle)
end
Runtime:addEventListener("enterFrame",label)


local function test()
   local targetAngle = math.random( -360, 360 )
   shortestPathTransition( arrow, targetAngle, 360 )
   timer.performWithDelay( 1500, test )
end

test()





[topic_controls]
[/topic_controls]