Determining of DisplayObject Rotation

Hi all,

I’m trying to determine the direction of rotation of one of my DisplayObjects.  I’m using the  touch event to spin a ring using my finger.  I’ve tried this simple code to determine direction in the moved phase of the event:

if (spinner.rotation == lastAngle) then rotationDirection = "EQUAL!?" elseif (spinner.rotation \> lastAngle) then rotationDirection = "clockwise" else rotationDirection = "counterclockwise" end lastAngle = spinner.rotation

I’m amazed at how often the above logic comes back as “Equal” even though the ring is clearly moving.  Any ideas how to get around this?

Also accepting clever ways of getting around the issue of moving counter-clockwise and crossing the 360/0 mark.  :slight_smile:

Thanks.

Without the full handler code, my next comments are only hypothesis, but I would presume that the above is being used, in it’s entirety, in the “moved” event phase. If that is the case, then it would appear that the value of  lastAngle is being constantly assigned to  spinner.rotation during the “moved” event. Therefore, your first argument is always going to be valid.

To the point of how to solve the issue: you might be better off coming at the problem from a different angle (pun intended, because I’m gangster like that). Perhaps it would be useful to check the delta of the user’s event coordinates at the beginning and end of the touch, in order to determine in which direction the wheel is spinning?

Here’s a snippet from a wheel-of-fortune project I was working on a few moons ago. It can identify in which direction the wheel is rotating. It’s super lo-fi, but it does work:

EDIT: this was designed in landscape.

-- simple rotation around an axis with both touch and enterFrame listeners display.setStatusBar( display.HiddenStatusBar ) system.activate("multitouch") local wheelGroup = display.newGroup() local centerX = display.contentCenterX local centerY = display.contentCenterY local bottomY = display.contentHeight-60 local devH = display.contentHeight local rotG = 0 -- rate at which objects will rotate local xStart local yStart local xEnd local yEnd local dx = 0 local decRot = 10 local circRad = display.contentWidth/40 local wheelRadX = display.contentWidth/2.5 local wheelRadY = display.contentHeight/3 local debugLog = print local wheelSpin = false local wheelImgBack local wheelImg local setSize = 40 local decrementValue = 40 local kCenterX = display.contentCenterX local kCenterY = display.contentCenterY local kValRot = 3 local kDecLimit = 10 local kDecRot = 3.5 local Debugtext = "DEBUG TEXT\_ " local textTable={ Debugtext.."CONTINUE TOUCH", Debugtext.."TOUCH DEACTIVATED", Debugtext.."CANNOT INTERACT WITH SPINNER", Debugtext.."READY SPIN", Debugtext.."YOU WIN!!!", Debugtext.."Clockwise", Debugtext.."Counterclockwise", Debugtext.."Prize triggered", } local function createGUI() --wheelImgBack = display.newImageRect("wheel.png",wheelRadX,wheelRadY) wheelImgBack = display.newCircle(0,0,wheelRadY) wheelImgBack.x, wheelImgBack.y = centerX, centerY wheelImgBack.rotation = wheelImgBack.rotation-10 wheelImgBack.alpha = 1 wheelImg = display.newRect(0,0,wheelRadY/4,wheelRadY/4) wheelImg.x, wheelImg.y = centerX, centerY wheelImg.rotation = wheelImg.rotation-10 wheelImg:setFillColor(0) local hudText = display.newText("DEBUG TEXT PROMPT", centerX, bottomY-20, system.nativeFont, setSize) hudText:setFillColor(1) function changeText(a,b) hudText.text = a hudText.size = b or setSize end wheelGroup:insert(wheelImgBack) wheelGroup:insert(wheelImg) wheelGroup:insert(hudText) end local function winCondition(a,b) --debugLog("checking win here") wheelSpin = false if a ~= 0 then changeText(textTable[#textTable]) end end local function equalRotation() local t = 1 local r = devH/4 -- radius local numPoints = 12 -- amount of circle objects local rG = 1 -- starting amount of rotation local angleStep = 2 \* math.pi / numPoints local t = {} local circle = {} for i = 1, numPoints do circle[i] = display.newCircle(0,0,circRad/32) circle[i].x = centerX + r\*math.cos(i\*angleStep) circle[i].y = centerY + r\*math.sin(i\*angleStep) circle[i].hit = false circle[i].alpha = 0 wheelGroup:insert(circle[i]) end local function onSpin( ) if rotG then if rotG \< decrementValue and rotG \> -decrementValue then if rotG \< -1 then rotG = rotG+(decRot/22) elseif rotG \> 1 then rotG = rotG-(decRot/22) end end if rotG \< 2 and rotG \> -2 then winCondition(rotG, circle[rN]) rotG = 0 end if rotG \> decrementValue then rotG = rotG-decRot --debugLog("rotG = "..rotG) end if rotG \< -decrementValue then rotG = rotG+decRot --debugLog("rotG = "..rotG) end rG = rG+rotG --debugLog("wheelImg.rotation = "..wheelImg.rotation) for i = 1, numPoints do wheelImgBack.rotation = wheelImgBack.rotation+(rotG/15) t[i] = ((rG) \* .014)+ (i\*angleStep) circle[i].x = (r \* math.cos(t[i])) +centerX circle[i].y = (r \* math.sin(t[i])) +centerY --debugLog("circle["..i.."].x = "..circle[i].x) --debugLog("circle["..i.."].y = "..circle[i].y) end --wheelImg.rotation = (wheelImgBack.rotation-200) wheelImg.x, wheelImg.y = circle[1].x, circle[1].y end end Runtime:addEventListener( "enterFrame", onSpin ) end local function spinObject(event) if not wheelSpin then --local t = event.target local t = wheelImgBack local phase = event.phase --print("Phase: " .. phase) if (phase == "began") then display.getCurrentStage():setFocus( t ) t.isFocus = true -- Store initial position of finger t.x1 = event.x t.y1 = event.y xStart = event.xStart yStart = event.yStart startTime = event.time --print("start time " .. startTime) elseif t.isFocus then if (phase == "moved") then t.x2 = event.x t.y2 = event.y local angle1 = 360/math.pi \* math.atan2(t.y1 - t.y , t.x1 - t.x) local angle2 = 360/math.pi \* math.atan2(t.y2 - t.y , t.x2 - t.x) local rotationAmt = angle1 - angle2 t.rotation = t.rotation - rotationAmt --rotG = rotG - (rotationAmt/8) --print("---------------------") --print("t.rotation = ", t.rotation) --print("t.x2 = ", t.x2) --print("t.y2 = ", t.y2) --print("t.x1 = ", t.x1) --print("t.y1 = ", t.y1) --print("xStart = ", xStart) --print("yStart = ", yStart) --setGroup.rotation = setGroup.rotation - rotationAmt t.x1 = t.x2 t.y1 = t.y2 elseif (phase == "ended") or (phase == "cancelled") then xEnd = event.x yEnd = event.y print("---------------------") local combStart = xStart+yStart local combEnd = xEnd+yEnd --print("xStart = ", xStart) --print("yStart = ", yStart) --print("xEnd = ", xEnd) --print("yEnd = ", yEnd) --print("combStart = ", combStart) --print("combEnd = ", combEnd) display.getCurrentStage():setFocus( nil ) t.isFocus = false local deltaTime = event.time - startTime if deltaTime \< 500 then rotG = (combEnd-combStart)/1.5 if xEnd \< kCenterX then rotG = rotG\*-1 end print("rotG = "..rotG) if rotG \> 0 then changeText(textTable[#textTable-2]) elseif rotG \< 0 then changeText(textTable[#textTable-1]) end wheelSpin = true end end end end return true end equalRotation() createGUI() wheelImgBack:addEventListener( "touch", spinObject ) -------------------------------------------------

Hey Alex,

Thanks for taking the time.

You’re right that all of this is taking place inside the “moved” phase.  However the  rotation -checking logic is executing before  lastAngle is assigned.  This logic would work if the angles weren’t identical from time to time.

Your code looks like it’s determining direction when the user crosses the middle of the screen. The ring I’m spinning is completely visible so players could spin their fingers a complete 360 degrees before letting go if they wanted to.  If I were to use a vector between the starting touch and the stopping touch, then a user that rotated 359 degrees would see the ring to spin in the wrong direction.

Ignoring when the values are equal works 99% of the time, so there’s that.  Maybe I’ll use your vector suggestion but update it over time throughout the “moved” phase. I’ll let you know if it works out.

@ddubya82 It is absolutely possible, and very likely, for the rotation of the object to be equal between calls to the moved event due to timing issues and the speed of the rotation. I agree with Alex that you will need to come at this from a different angle. :wink: You are on to something when you say “Ignoring when the values are equal works 99% of the time, so there’s that.” I am inclined to believe that you should disregard the cases where the values are equal since that often means that the time between move events was insufficient for the rotation to change. However, there is still that 1% so you will need to devise another means to reconcile that. Again, I think Alex makes excellent points about detecting the delta in the user’s movement to determine “rotation”. You will likely need to go that route.

Other priorities have come up so I won’t be able to do the delta-checking for a little bit.  I’ll try to come back and post my code when I do.

Thanks again!

Without the full handler code, my next comments are only hypothesis, but I would presume that the above is being used, in it’s entirety, in the “moved” event phase. If that is the case, then it would appear that the value of  lastAngle is being constantly assigned to  spinner.rotation during the “moved” event. Therefore, your first argument is always going to be valid.

To the point of how to solve the issue: you might be better off coming at the problem from a different angle (pun intended, because I’m gangster like that). Perhaps it would be useful to check the delta of the user’s event coordinates at the beginning and end of the touch, in order to determine in which direction the wheel is spinning?

Here’s a snippet from a wheel-of-fortune project I was working on a few moons ago. It can identify in which direction the wheel is rotating. It’s super lo-fi, but it does work:

EDIT: this was designed in landscape.

-- simple rotation around an axis with both touch and enterFrame listeners display.setStatusBar( display.HiddenStatusBar ) system.activate("multitouch") local wheelGroup = display.newGroup() local centerX = display.contentCenterX local centerY = display.contentCenterY local bottomY = display.contentHeight-60 local devH = display.contentHeight local rotG = 0 -- rate at which objects will rotate local xStart local yStart local xEnd local yEnd local dx = 0 local decRot = 10 local circRad = display.contentWidth/40 local wheelRadX = display.contentWidth/2.5 local wheelRadY = display.contentHeight/3 local debugLog = print local wheelSpin = false local wheelImgBack local wheelImg local setSize = 40 local decrementValue = 40 local kCenterX = display.contentCenterX local kCenterY = display.contentCenterY local kValRot = 3 local kDecLimit = 10 local kDecRot = 3.5 local Debugtext = "DEBUG TEXT\_ " local textTable={ Debugtext.."CONTINUE TOUCH", Debugtext.."TOUCH DEACTIVATED", Debugtext.."CANNOT INTERACT WITH SPINNER", Debugtext.."READY SPIN", Debugtext.."YOU WIN!!!", Debugtext.."Clockwise", Debugtext.."Counterclockwise", Debugtext.."Prize triggered", } local function createGUI() --wheelImgBack = display.newImageRect("wheel.png",wheelRadX,wheelRadY) wheelImgBack = display.newCircle(0,0,wheelRadY) wheelImgBack.x, wheelImgBack.y = centerX, centerY wheelImgBack.rotation = wheelImgBack.rotation-10 wheelImgBack.alpha = 1 wheelImg = display.newRect(0,0,wheelRadY/4,wheelRadY/4) wheelImg.x, wheelImg.y = centerX, centerY wheelImg.rotation = wheelImg.rotation-10 wheelImg:setFillColor(0) local hudText = display.newText("DEBUG TEXT PROMPT", centerX, bottomY-20, system.nativeFont, setSize) hudText:setFillColor(1) function changeText(a,b) hudText.text = a hudText.size = b or setSize end wheelGroup:insert(wheelImgBack) wheelGroup:insert(wheelImg) wheelGroup:insert(hudText) end local function winCondition(a,b) --debugLog("checking win here") wheelSpin = false if a ~= 0 then changeText(textTable[#textTable]) end end local function equalRotation() local t = 1 local r = devH/4 -- radius local numPoints = 12 -- amount of circle objects local rG = 1 -- starting amount of rotation local angleStep = 2 \* math.pi / numPoints local t = {} local circle = {} for i = 1, numPoints do circle[i] = display.newCircle(0,0,circRad/32) circle[i].x = centerX + r\*math.cos(i\*angleStep) circle[i].y = centerY + r\*math.sin(i\*angleStep) circle[i].hit = false circle[i].alpha = 0 wheelGroup:insert(circle[i]) end local function onSpin( ) if rotG then if rotG \< decrementValue and rotG \> -decrementValue then if rotG \< -1 then rotG = rotG+(decRot/22) elseif rotG \> 1 then rotG = rotG-(decRot/22) end end if rotG \< 2 and rotG \> -2 then winCondition(rotG, circle[rN]) rotG = 0 end if rotG \> decrementValue then rotG = rotG-decRot --debugLog("rotG = "..rotG) end if rotG \< -decrementValue then rotG = rotG+decRot --debugLog("rotG = "..rotG) end rG = rG+rotG --debugLog("wheelImg.rotation = "..wheelImg.rotation) for i = 1, numPoints do wheelImgBack.rotation = wheelImgBack.rotation+(rotG/15) t[i] = ((rG) \* .014)+ (i\*angleStep) circle[i].x = (r \* math.cos(t[i])) +centerX circle[i].y = (r \* math.sin(t[i])) +centerY --debugLog("circle["..i.."].x = "..circle[i].x) --debugLog("circle["..i.."].y = "..circle[i].y) end --wheelImg.rotation = (wheelImgBack.rotation-200) wheelImg.x, wheelImg.y = circle[1].x, circle[1].y end end Runtime:addEventListener( "enterFrame", onSpin ) end local function spinObject(event) if not wheelSpin then --local t = event.target local t = wheelImgBack local phase = event.phase --print("Phase: " .. phase) if (phase == "began") then display.getCurrentStage():setFocus( t ) t.isFocus = true -- Store initial position of finger t.x1 = event.x t.y1 = event.y xStart = event.xStart yStart = event.yStart startTime = event.time --print("start time " .. startTime) elseif t.isFocus then if (phase == "moved") then t.x2 = event.x t.y2 = event.y local angle1 = 360/math.pi \* math.atan2(t.y1 - t.y , t.x1 - t.x) local angle2 = 360/math.pi \* math.atan2(t.y2 - t.y , t.x2 - t.x) local rotationAmt = angle1 - angle2 t.rotation = t.rotation - rotationAmt --rotG = rotG - (rotationAmt/8) --print("---------------------") --print("t.rotation = ", t.rotation) --print("t.x2 = ", t.x2) --print("t.y2 = ", t.y2) --print("t.x1 = ", t.x1) --print("t.y1 = ", t.y1) --print("xStart = ", xStart) --print("yStart = ", yStart) --setGroup.rotation = setGroup.rotation - rotationAmt t.x1 = t.x2 t.y1 = t.y2 elseif (phase == "ended") or (phase == "cancelled") then xEnd = event.x yEnd = event.y print("---------------------") local combStart = xStart+yStart local combEnd = xEnd+yEnd --print("xStart = ", xStart) --print("yStart = ", yStart) --print("xEnd = ", xEnd) --print("yEnd = ", yEnd) --print("combStart = ", combStart) --print("combEnd = ", combEnd) display.getCurrentStage():setFocus( nil ) t.isFocus = false local deltaTime = event.time - startTime if deltaTime \< 500 then rotG = (combEnd-combStart)/1.5 if xEnd \< kCenterX then rotG = rotG\*-1 end print("rotG = "..rotG) if rotG \> 0 then changeText(textTable[#textTable-2]) elseif rotG \< 0 then changeText(textTable[#textTable-1]) end wheelSpin = true end end end end return true end equalRotation() createGUI() wheelImgBack:addEventListener( "touch", spinObject ) -------------------------------------------------

Hey Alex,

Thanks for taking the time.

You’re right that all of this is taking place inside the “moved” phase.  However the  rotation -checking logic is executing before  lastAngle is assigned.  This logic would work if the angles weren’t identical from time to time.

Your code looks like it’s determining direction when the user crosses the middle of the screen. The ring I’m spinning is completely visible so players could spin their fingers a complete 360 degrees before letting go if they wanted to.  If I were to use a vector between the starting touch and the stopping touch, then a user that rotated 359 degrees would see the ring to spin in the wrong direction.

Ignoring when the values are equal works 99% of the time, so there’s that.  Maybe I’ll use your vector suggestion but update it over time throughout the “moved” phase. I’ll let you know if it works out.

@ddubya82 It is absolutely possible, and very likely, for the rotation of the object to be equal between calls to the moved event due to timing issues and the speed of the rotation. I agree with Alex that you will need to come at this from a different angle. :wink: You are on to something when you say “Ignoring when the values are equal works 99% of the time, so there’s that.” I am inclined to believe that you should disregard the cases where the values are equal since that often means that the time between move events was insufficient for the rotation to change. However, there is still that 1% so you will need to devise another means to reconcile that. Again, I think Alex makes excellent points about detecting the delta in the user’s movement to determine “rotation”. You will likely need to go that route.

Other priorities have come up so I won’t be able to do the delta-checking for a little bit.  I’ll try to come back and post my code when I do.

Thanks again!