So, I'm trying to implement this standard gesture to my program. But I can't find any examples on how to do it. I'm trying to figure it out on my own, but it's taking time and my employer is waiting. Can anyone give me a hand on this?
I just put a code that i write to my game, so it might be not really understandable. If you have a question, just ask it.
Also, this example of Pinch-to-zoom only use the center point as zoom point. Edit: change a little bit, since the code error on three or more finger...
@ExCx well from theoretical point of view, to detect if two fingers are touching the screen you can check for count of elements in event.allTouches table. If there are two, you than need to calculate the vector's length between two touches. That's all on touches begin event.
After that on touches move you need to monitor how vector's length between touches changes and scale whole scene (or any other object/layer that contains your visible elements) accordingly.
That's actually a pretty interesting thing to implement, that I would want to try myself, problem is I have so many things to do, that this one would make a list probably only next year
local minimumZoom =0.2localfunction touchEVENT(self, event)local touchGet = event.touch
local id = touchGet.id
local zoomGroup = self.zoomGroup
-- id is the finger numberlocal touch = self.touch
-- It just happen that i like to use some checking like thislocal scalingHappen =falselocal movingHappen =falselocal otherID =2if id ==2then otherID =1end--Function to get range between touch/coordlocalfunction getDelta(point1,point2)local dx = point1.x - point2.x
local dy = point1.y - point2.y
returnmath.sqrt( dx*dx + dy*dy )endif id ==1or id ==2thenif event.type=="touchesBegin"thenifnot self.idTouchExist[otherID]then
self.x0 = touchGet.x - ( self:getX()* zoomGroup:getScaleX())
self.y0 = touchGet.y - ( self:getY()* zoomGroup:getScaleX())--Get the first Finger
touch[id].x = touchGet.x
touch[id].y = touchGet.y
elseifnot self.idTouchExist[id]then--Get the second Finger
touch[id].x = touchGet.x
touch[id].y = touchGet.y
-- get Length between touch and original Zoomlocal deltaTouch = getDelta(touch[1],touch[2])if deltaTouch >0then
zoomGroup.deltaTouch = deltaTouch
zoomGroup.oriScale = zoomGroup:getScaleX()end-- TouchC is a Temporary that i use to take center of 1 & 2local touchC ={}
touchC.x =(touch[1].x + touch[2].x)*0.5
touchC.y =(touch[1].y + touch[2].y)*0.5
self.x0 = touchC.x - ( self:getX()* zoomGroup:getScaleX())
self.y0 = touchC.y - ( self:getY()* zoomGroup:getScaleX())end
self.idTouchExist[id]=trueelseif event.type=="touchesMove"then-- save Touch Pos
touch[id].x = touchGet.x
touch[id].y = touchGet.y
-- If there is one Touch then moveifnot self.idTouchExist[otherID]then
self:setX((touchGet.x - self.x0)* zoomGroup.scaleRev )
self:setY((touchGet.y - self.y0)* zoomGroup.scaleRev )
movingHappen =true-- If there is second finter Then Scale (and move) elselocal deltaTouch = getDelta(touch[1],touch[2])local scaler = deltaTouch / zoomGroup.deltaTouch
if scaler >0thenlocal scaleResult = zoomGroup.oriScale * scaler
zoomGroup:setScale( scaleResult,scaleResult )
scalingHappen =trueendlocal touchC ={}
touchC.x =(touch[1].x + touch[2].x)*0.5
touchC.y =(touch[1].y + touch[2].y)*0.5
self:setX((touchC.x - self.x0 )* zoomGroup.scaleRev )
self:setY((touchC.y - self.y0 )* zoomGroup.scaleRev )end
event:stopPropagation()-- you can put limitation of X,Y max and min in hereif self:getY()< self.downLimit then self:setY( self.downLimit )elseif self:getY()> self.upLimit then self:setY( self.upLimit )endif self:getX()< self.rightLimit then self:setX( self.rightLimit )elseif self:getX()> self.leftLimit then self:setX( self.leftLimit )end-- and this for limit scalingif scalingHappen thenif zoomGroup:getScaleX()>1.0then
zoomGroup:setScale(1.0,1.0)elseif zoomGroup:getScaleX()< minimumZoom then
zoomGroup:setScale( minimumZoom,minimumZoom )end
zoomGroup.scaleRev =1/zoomGroup:getScaleX()end-- Using animation also nice...-- So just give some condition to let ENTER_FRAME event do the scaling / moving after-- And then let them move like this:-- if self.targetX ~= self:getX() then -- local delta = ( self:getX() - self.TargetX ) * 0.1-- if delta > 0 then delta = ceil(delta) else delta = floor(delta) end-- self:setX( self:getX() - delta )-- end -- But it more complexer than that, i thinkelseif event.type=="touchesEnd"thenif self.idTouchExist[otherID]then
self.x0 = touch[otherID].x -( self:getX()* zoomGroup:getScaleX())
self.y0 = touch[otherID].y - ( self:getY()* zoomGroup:getScaleX())end
event:stopPropagation()
self.idTouchExist[id]=nilendendreturntrueendlocal zoomGroup = Sprite.new()
zoomGroup:setScale(0.4,0.4)
zoomGroup:setPosition( application:getContentWidth()/2,application:getContentHeight()/2)local imageOrGroup = Sprite.new()
imageOrGroup.bitmap = Bitmap.new(Texture.new("Bunny.jpg"))
imageOrGroup.bitmap:setAnchorPoint(0.5, 0.5)
imageOrGroup.zoomGroup = zoomGroup
imageOrGroup:addChild(imageOrGroup.bitmap)
zoomGroup:addChild(imageOrGroup)
stage:addChild(zoomGroup)-- Some of initialization that i need for Pinch-Zoom
zoomGroup.scaleRev =1/zoomGroup:getScaleX()
imageOrGroup.touch ={}
imageOrGroup.touch[1]={}
imageOrGroup.touch[2]={}
imageOrGroup.idTouchExist ={}local halfWidth = imageOrGroup:getWidth()*0.5local halfHeight = imageOrGroup:getHeight()*0.5
imageOrGroup.leftLimit = halfWidth
imageOrGroup.rightLimit = -halfWidth
imageOrGroup.upLimit = halfHeight
imageOrGroup.downLimit = -halfHeight
imageOrGroup:addEventListener(Event.TOUCHES_BEGIN, touchEVENT,imageOrGroup)
imageOrGroup:addEventListener(Event.TOUCHES_MOVE, touchEVENT,imageOrGroup)
imageOrGroup:addEventListener(Event.TOUCHES_END, touchEVENT,imageOrGroup)
Yep, the basic is just like @ar2rsawseen said. But i didn't use event.allTouches
Dang. My code is really not interesting since i just take it from my game. And this code can handle movement & scaling in the same time.
I used another group called zoomGroup to handle things better. And to change the zoom center point, i think we just need to change child's x,y and zoomGroup's x,y. Don't know if there is another trick. Anyone?
There is no setAnchorPoint for sprite because <trollmode> @atilim does not now how to implement one :P </trollmode>
But seriously its very hard to implement it, because Sprite may have other indefinite amount of children, etc. And I even doubt that all of scenarios are covered in GiderosCodingEasy
Uh, yeah... Is not that easy to implement the code in GiderosCodingEasy for zooming. It might because x and y are different after scaling, or might be the width and height that also change during zoom. Might be easier for just change the child's position and zoomGroup's position.
There is no setAnchorPoint for sprite because <trollmode> @atilim does not now how to implement one :P </trollmode>
But seriously its very hard to implement it, because Sprite may have other indefinite amount of children, etc. And I even doubt that all of scenarios are covered in GiderosCodingEasy
While the complexities are infinite, if each sprite that has children has an upto date bounds when a child is added or moved, then the number of children problem should not be valid, it is then a matter of transversing through the hierarchy.
Having an anchorPoint for a shape or sprite is very important to be able to rotate them correctly. Not everything looks good rotated from the top left corner. A work around however could be if the Sprite is *renderedToBitmap* and then we can set the anchorPoint.
If it were easy then who would have asked for it, and would it be a challenge for @Atilim?
@tkhnoman I implemented your whole code. It's working good but I have 2 serious issues.
First is that center point issue. It always takes 0,0 as anchor. I honestly couldn't understand your formula about this ("self:setX(((touchC.x - self.x0 ) * zoomGroup.scaleRev))" part) and it doesn't work for me either.
Second thing is the limiting issue. I want that no blank space to be seen while zooming and/or moving. It would always be limited precisely by the dimensions of the stage. In your formula, the limitations always change.
For the record, I'm using whole stage. Which means I add every child of the stage to the "imageOrGroup" sprite instead. Remaining of the code is same.
Lastly I tried @ar2rsawseen's setAnchorPoint feature for sprites too, but it didn't make any change.
I really can't believe there isn't a clear example/library/plugin about this. Isn't this a standard feature to be found in a mobile software?
@ExCx Nope, it's not a standard feature for mobile software.
I used touchC (in the middle of first touch and second touch) to detect the movement while scaling. So if the finger rotated while zooming, it will remain in the same position, since it detect the center.
That scaleRev (1/scale) is used to maintain speed of movement. It will also needed when you detect touch, to get the real coordinate instead of zoomed coordinate.
For limiting issue, you need to do some more calculation again. I use that limitation for my game, which might not good for other's. Also for anchor point too. Perhaps it depends on what kind of game/app you are making.
OK, so I'm over the limiting issue now. It was actually basic maths, which is my weak side (and yeah I'm calling myself a programmer). But I figured it out somehow. For example I implemented your formula on zooming ratio, which is:
self:setScale(( current distance between fingers / initial distance between fingers) * initial scale of self)
In this, I see genius. I'd never figure out that by myself (I was just adding/substracting a constant value to the scale ratio before, which actually works, but it was rough and really uncomfortable).
Anyway, I'm still studying your code and immersing it into mine. I'll post a working code with an open explanation here when it's finished. This zooming thing ate my whole week and I don't want anyone else to stuck on this issue.
Comments
If you have a question, just ask it.
Also, this example of Pinch-to-zoom only use the center point as zoom point.
Edit: change a little bit, since the code error on three or more finger...
What is the syntax...
http://www.giderosmobile.com/forum/discussion/comment/4370#Comment_4370
After that on touches move you need to monitor how vector's length between touches changes and scale whole scene (or any other object/layer that contains your visible elements) accordingly.
That's actually a pretty interesting thing to implement, that I would want to try myself, problem is I have so many things to do, that this one would make a list probably only next year
Likes: hgvyas123
Dang. My code is really not interesting since i just take it from my game.
And this code can handle movement & scaling in the same time.
I used another group called zoomGroup to handle things better.
And to change the zoom center point, i think we just need to change child's x,y and zoomGroup's x,y.
Don't know if there is another trick. Anyone?
Likes: ExCx
https://github.com/ar2rsawseen/GiderosCodingEasy/blob/master/GiderosCodingEasy.lua
Likes: tkhnoman
And yeah, i wonder why there is no setAnchorPoint for sprite. It will be very handy, rather than setting the child's x and y by ourself.
<trollmode>
@atilim does not now how to implement one :P
</trollmode>
But seriously its very hard to implement it, because Sprite may have other indefinite amount of children, etc. And I even doubt that all of scenarios are covered in GiderosCodingEasy
It might because x and y are different after scaling, or might be the width and height that also change during zoom. Might be easier for just change the child's position and zoomGroup's position.
Tired for trying. I will let it be.
Having an anchorPoint for a shape or sprite is very important to be able to rotate them correctly. Not everything looks good rotated from the top left corner. A work around however could be if the Sprite is *renderedToBitmap* and then we can set the anchorPoint.
If it were easy then who would have asked for it, and would it be a challenge for @Atilim?
Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
Cool Vizify Profile at https://www.vizify.com/oz-apps
@tkhnoman I implemented your whole code. It's working good but I have 2 serious issues.
First is that center point issue. It always takes 0,0 as anchor. I honestly couldn't understand your formula about this ("self:setX(((touchC.x - self.x0 ) * zoomGroup.scaleRev))" part) and it doesn't work for me either.
Second thing is the limiting issue. I want that no blank space to be seen while zooming and/or moving. It would always be limited precisely by the dimensions of the stage. In your formula, the limitations always change.
For the record, I'm using whole stage. Which means I add every child of the stage to the "imageOrGroup" sprite instead. Remaining of the code is same.
Lastly I tried @ar2rsawseen's setAnchorPoint feature for sprites too, but it didn't make any change.
I really can't believe there isn't a clear example/library/plugin about this. Isn't this a standard feature to be found in a mobile software?
I used touchC (in the middle of first touch and second touch) to detect the movement while scaling. So if the finger rotated while zooming, it will remain in the same position, since it detect the center.
That scaleRev (1/scale) is used to maintain speed of movement. It will also needed when you detect touch, to get the real coordinate instead of zoomed coordinate.
For limiting issue, you need to do some more calculation again. I use that limitation for my game, which might not good for other's. Also for anchor point too.
Perhaps it depends on what kind of game/app you are making.
If you give me a hint, i might got a clue of it.
self:setScale(( current distance between fingers / initial distance between fingers) * initial scale of self)
In this, I see genius. I'd never figure out that by myself (I was just adding/substracting a constant value to the scale ratio before, which actually works, but it was rough and really uncomfortable).
Anyway, I'm still studying your code and immersing it into mine. I'll post a working code with an open explanation here when it's finished. This zooming thing ate my whole week and I don't want anyone else to stuck on this issue.
Likes: ar2rsawseen, Averett