Rotate rectangle by touch/dragging finger

I have been trying to figure out a way to rotate a rectangle that is anchored in the center by touch or dragging on the rectangle object. Has anyone approached this issue before? Thanks.

If you are talking about a regular display object then you’ll have to calculate the rotation value and set it on the rectangle. The property to set is ‘.rotation’ The math is fairly simple and easily found on the internet. You can use my mathlib on the code exchange:

http://code.coronalabs.com/code/mathliblua

To apply it using touch you will need to listen for a user’s touch and respond to touch events - for this take a look at the guides:

http://docs.coronalabs.com/guide/index.html#events-and-listeners

If you want to rotate a physics rectangle, just create a normal rectangle and add a physics body to it (again, read the guides) and then use a touch joint when the user touches the rectangle. If you want it to rotate around its centre, rather than being dragged around the screen, you will want to create another object to anchor the rectangle in the centre.

This is a great learning exercise, so if this is all a bit complicated, start with just the bit at the top and take it step by step. Keep posting questions.

Thanks for the response, it makes more sense now and I will post code once I get something running correctly

So based on your pointers I came up with this:

display.setStatusBar(display.HiddenStatusBar); require("mobdebug").start(); local physics = require("physics"); physics.start(); physics.setDrawMode("hybrid"); local rect1 = display.newRect(160,240,100,10); rect1:setFillColor(0,1,0); rect1.anchorX = 0.5; rect1.anchorY = 0.5; physics.addBody(rect1,"dynamic",{density=0,friction=0.0,bounce=0.0}); local rect2 = display.newRect(160,240,25,25); rect2:setFillColor(1,0,0); physics.addBody(rect2, "static"); local joint = physics.newJoint("pivot", rect2, rect1, 160,240); local function dragBody(event) local body = event.target; local phase = event.phase; local stage = display.getCurrentStage(); if phase == "began" then stage:setFocus(body, event.id); body.isFocus = true; body.tempJoint = physics.newJoint("touch",body, event.x, event.y); elseif body.isFocus then if phase == "moved" then body.tempJoint:setTarget(event.x,event.y); elseif phase == "ended" or phase == "cancelled" then stage:setFocus(body,nil); body.isFocus = false; body.tempJoint:removeSelf(); end end return true end rect1:addEventListener("touch", dragBody);

but when I click off the rectangle it continues to move, is there a way to get it to ‘snap’ in place after the touch event is released? thanks

That’s pretty good. I don’t think you need to set the anchorX/Y values on rect1 though. Using rect2 as a physical anchor is right. Also, you can replace the stage declaration with a more global stage by using display.currentStage, which is the same display group. The elseif can also be replaced with just else.

To stop the rectangle rotating or moving after the touch ends you simply create a temporary weld elsewhere to fix it in place. Or you could temporarily have a touch joint hold it still. I would also 0 its angular and linear velocities, just to be sure. It depends if you want it still to be free-wheeling but still or just to lock. If you just want it to lock, add a weld.

So I tried setting the angular and linear velocities of the rectangle to 0 and angular velocity gets set to 0 but after the touch event occurred it picks back up its velocity so it keeps moving, why is that?

display.setStatusBar(display.HiddenStatusBar); require("mobdebug").start(); local physics = require("physics"); physics.start(); physics.setDrawMode("hybrid"); local rect1 = display.newRect(160,240,100,10); rect1:setFillColor(0,1,0); rect1.anchorX = 0.5; rect1.anchorY = 0.5; physics.addBody(rect1,"dynamic",{density=0,friction=0.0,bounce=0.0}); local rect2 = display.newRect(160,240,25,25); rect2:setFillColor(1,0,0); physics.addBody(rect2, "static"); local joint = physics.newJoint("pivot", rect2, rect1, 160,240); local function dragBody(event) local body = event.target; local phase = event.phase; local stage = display.getCurrentStage(); if phase == "began" then stage:setFocus(body, event.id); body.isFocus = true; body.tempJoint = physics.newJoint("touch",body, event.x, event.y); elseif body.isFocus then if phase == "moved" then body.tempJoint:setTarget(event.x,event.y); elseif phase == "ended" or phase == "cancelled" then stage:setFocus(body,nil); body.isFocus = false; body.tempJoint:removeSelf(); rect1.angularVelocity = 0; rect1:setLinearVelocity(0,0); end end return true end rect1:addEventListener("touch", dragBody);

Added a weldJoint when the touch event isn’t being used…works like a charm now, thanks very much

display.setStatusBar(display.HiddenStatusBar); require("mobdebug").start(); local physics = require("physics"); physics.start(); physics.setDrawMode("hybrid"); local rect1 = display.newRect(160,240,100,10); rect1:setFillColor(0,1,0); rect1.anchorX = 0.5; rect1.anchorY = 0.5; physics.addBody(rect1,"dynamic",{density=0,friction=0.0,bounce=0.0}); local rect2 = display.newRect(160,240,25,25); rect2:setFillColor(1,0,0); physics.addBody(rect2, "static"); local joint = physics.newJoint("pivot", rect2, rect1, 160,240); local function dragBody(event) local body = event.target; local phase = event.phase; local stage = display.getCurrentStage(); if phase == "began" then stage:setFocus(body, event.id); body.isFocus = true; body.tempJoint = physics.newJoint("touch",body, event.x, event.y); if body["weldJoint"] ~= nil then body.weldJoint:removeSelf(); end elseif body.isFocus then if phase == "moved" then body.tempJoint:setTarget(event.x,event.y); elseif phase == "ended" or phase == "cancelled" then stage:setFocus(body,nil); body.isFocus = false; body.tempJoint:removeSelf(); rect1.angularVelocity = 0; rect1:setLinearVelocity(0,0); body.weldJoint = physics.newJoint("weld", rect1, rect2, 160, 240); end end return true end rect1:addEventListener("touch", dragBody);

If you are talking about a regular display object then you’ll have to calculate the rotation value and set it on the rectangle. The property to set is ‘.rotation’ The math is fairly simple and easily found on the internet. You can use my mathlib on the code exchange:

http://code.coronalabs.com/code/mathliblua

To apply it using touch you will need to listen for a user’s touch and respond to touch events - for this take a look at the guides:

http://docs.coronalabs.com/guide/index.html#events-and-listeners

If you want to rotate a physics rectangle, just create a normal rectangle and add a physics body to it (again, read the guides) and then use a touch joint when the user touches the rectangle. If you want it to rotate around its centre, rather than being dragged around the screen, you will want to create another object to anchor the rectangle in the centre.

This is a great learning exercise, so if this is all a bit complicated, start with just the bit at the top and take it step by step. Keep posting questions.

Thanks for the response, it makes more sense now and I will post code once I get something running correctly

So based on your pointers I came up with this:

display.setStatusBar(display.HiddenStatusBar); require("mobdebug").start(); local physics = require("physics"); physics.start(); physics.setDrawMode("hybrid"); local rect1 = display.newRect(160,240,100,10); rect1:setFillColor(0,1,0); rect1.anchorX = 0.5; rect1.anchorY = 0.5; physics.addBody(rect1,"dynamic",{density=0,friction=0.0,bounce=0.0}); local rect2 = display.newRect(160,240,25,25); rect2:setFillColor(1,0,0); physics.addBody(rect2, "static"); local joint = physics.newJoint("pivot", rect2, rect1, 160,240); local function dragBody(event) local body = event.target; local phase = event.phase; local stage = display.getCurrentStage(); if phase == "began" then stage:setFocus(body, event.id); body.isFocus = true; body.tempJoint = physics.newJoint("touch",body, event.x, event.y); elseif body.isFocus then if phase == "moved" then body.tempJoint:setTarget(event.x,event.y); elseif phase == "ended" or phase == "cancelled" then stage:setFocus(body,nil); body.isFocus = false; body.tempJoint:removeSelf(); end end return true end rect1:addEventListener("touch", dragBody);

but when I click off the rectangle it continues to move, is there a way to get it to ‘snap’ in place after the touch event is released? thanks

That’s pretty good. I don’t think you need to set the anchorX/Y values on rect1 though. Using rect2 as a physical anchor is right. Also, you can replace the stage declaration with a more global stage by using display.currentStage, which is the same display group. The elseif can also be replaced with just else.

To stop the rectangle rotating or moving after the touch ends you simply create a temporary weld elsewhere to fix it in place. Or you could temporarily have a touch joint hold it still. I would also 0 its angular and linear velocities, just to be sure. It depends if you want it still to be free-wheeling but still or just to lock. If you just want it to lock, add a weld.

So I tried setting the angular and linear velocities of the rectangle to 0 and angular velocity gets set to 0 but after the touch event occurred it picks back up its velocity so it keeps moving, why is that?

display.setStatusBar(display.HiddenStatusBar); require("mobdebug").start(); local physics = require("physics"); physics.start(); physics.setDrawMode("hybrid"); local rect1 = display.newRect(160,240,100,10); rect1:setFillColor(0,1,0); rect1.anchorX = 0.5; rect1.anchorY = 0.5; physics.addBody(rect1,"dynamic",{density=0,friction=0.0,bounce=0.0}); local rect2 = display.newRect(160,240,25,25); rect2:setFillColor(1,0,0); physics.addBody(rect2, "static"); local joint = physics.newJoint("pivot", rect2, rect1, 160,240); local function dragBody(event) local body = event.target; local phase = event.phase; local stage = display.getCurrentStage(); if phase == "began" then stage:setFocus(body, event.id); body.isFocus = true; body.tempJoint = physics.newJoint("touch",body, event.x, event.y); elseif body.isFocus then if phase == "moved" then body.tempJoint:setTarget(event.x,event.y); elseif phase == "ended" or phase == "cancelled" then stage:setFocus(body,nil); body.isFocus = false; body.tempJoint:removeSelf(); rect1.angularVelocity = 0; rect1:setLinearVelocity(0,0); end end return true end rect1:addEventListener("touch", dragBody);

Added a weldJoint when the touch event isn’t being used…works like a charm now, thanks very much

display.setStatusBar(display.HiddenStatusBar); require("mobdebug").start(); local physics = require("physics"); physics.start(); physics.setDrawMode("hybrid"); local rect1 = display.newRect(160,240,100,10); rect1:setFillColor(0,1,0); rect1.anchorX = 0.5; rect1.anchorY = 0.5; physics.addBody(rect1,"dynamic",{density=0,friction=0.0,bounce=0.0}); local rect2 = display.newRect(160,240,25,25); rect2:setFillColor(1,0,0); physics.addBody(rect2, "static"); local joint = physics.newJoint("pivot", rect2, rect1, 160,240); local function dragBody(event) local body = event.target; local phase = event.phase; local stage = display.getCurrentStage(); if phase == "began" then stage:setFocus(body, event.id); body.isFocus = true; body.tempJoint = physics.newJoint("touch",body, event.x, event.y); if body["weldJoint"] ~= nil then body.weldJoint:removeSelf(); end elseif body.isFocus then if phase == "moved" then body.tempJoint:setTarget(event.x,event.y); elseif phase == "ended" or phase == "cancelled" then stage:setFocus(body,nil); body.isFocus = false; body.tempJoint:removeSelf(); rect1.angularVelocity = 0; rect1:setLinearVelocity(0,0); body.weldJoint = physics.newJoint("weld", rect1, rect2, 160, 240); end end return true end rect1:addEventListener("touch", dragBody);