반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

Tutorial: Detecting Touches in Corona

2012. 4. 12. 22:03 | Posted by 솔웅


반응형

Tutorial: Detecting Touches in Corona

오늘날 가장 구별되는 모바일 디바이스의 특징은 Touch screen 이라는 겁니다. 그러니까 개발자로서 코로나로 만드는 앱에 대해 유저에게 좀 더 리얼하고 풍부한 효과를 주기 위해  어떻게 user의 touch를 감지하는지를 정확히 아는 것은 중요합니다.

이 touch 에 대해 여러분들이 생각하는 것보다 더 많은 생각해야 할 것이 있습니다. 이 튜도리얼에서는 단지 touch를 어떻게 감지하는지 이외에도 user touch의 다양한 경우에 대해서도 생각해 보고 이것을 앎으로서 구현하고자 하는 기능을 좀 더 정확히 구현할 수 있도록 도움을 드리고자 합니다.

이 튜토리얼을 끝내시게 되면 touch phases, focus, dragging 같은 것들에 대해 좀 더 친숙해 지실겁니다. 이 튜토리얼은 가장 single touch 에 대해서만 다루겠습니다. Multi-touch는 좀 더 깊이 들어가야 할 주제입니다. 이 주제에 대해서는 나중에 다루겠습니다. 그 멀티 터치에 대해서 알기 위해서도 이 기본 touch 이벤트에대해 먼저 이해하는 것이 중요합니다.

만약 이 글을 읽고 계시는 분이 Corona event들에 대해 친숙하지 않으시다면 Corona Event Model Explained 를 먼저 읽어 보시기를 권합니다. 그 글을 읽으시면 이벤트와 리스너에 대해 이해하실 수 있을 겁니다. 그 글을 통해서 touch event에 대한 기본적인 내용도 아실 수 있습니다. event model에 대해 읽어 보신 후 이 튜토리얼로 돌아와서 보신다면 완벽하게 이해하실 수 있을 겁니다.





Detecting User Touches

User touch는 두가지 방법으로 감지 됩니다. 각 객체단위로 감지될 수 있고 전체 스크린 단위로 감지될 수 있습니다. (Runtime이나 global touch 가 전체 스크린 단위에서 감지 되는 겁니다.) 이 두가지가 어떻게 다른지에 대해 이해하는 것은 아주 중요합니다.

Per-object touches

우선 다른 특정한 이벤트가 감지되기 전에 이벤트를 add 해야 합니다. add 방법은 두가지 방법이 있습니다. table 리스너를 이용하던가 function 리스너를 이용하는 방법입니다.
아래 두개에 대한 예제가 있습니다.

Table Listener A와 Table Listener B는 기본적으로 같습니다. 약간의 다른점만이 있을 뿐입니다. 예제를 보면서 그 다른 점을 공부해 보세요.


* Table Listener A


local obj = display.newImage( "image.png" )

-- touch listener
function obj:touch( event )
    -- 'self' parameter exists via the ':' in function definition
end

-- begin detecting touches
obj:addEventListener( "touch", obj )


* Table Listener B

local obj = display.newImage( "image.png" )

-- touch listener
local function onObjectTouch( self, event )
    -- 'self' parameter must be defined via function argument
end

-- begin detecting touches
obj.touch = onObjectTouch
obj:addEventListener( "touch", obj )


* Function Listener

local obj = display.newImage( "image.png" )

-- touch listener
local function onObjectTouch( event )
    -- no 'self' parameter exists in this scenario
end

-- begin detecting touches
obj:addEventListener( "touch", onObjectTouch )


리스너 함수 안에서 어떤 일이 벌어지고 있는지에 대해서는 잠시 후에 다루겠습니다. 여기서는 객체에 어떻게 리스너가 add 되는지에 대해 주목해 주세요. 위 예제는 코로나에서 touch 이벤트에 대해 리스너를 다는 모든 방법에 대해 보여줍니다. 세번째 예제를 예를 들어 설명해 볼께요.

"유저가 obj를 touch 하면 onObjectTouch() 함수를 call 한다."


NOTE : 한 번 touch를 하면 touch event는 여러번 dispatch 됩니다. 그 이유는 touch 이벤트 안에는 여러개의 phases가 있기 때문입니다. 이 부분은 중요합니다. 잠시 후에 여기에 대해서 자세히 다루겠습니다.

Runtime Touches

여러분은 또한 Runtime 에 global 하게 touch 리스너를 add 할 수 있습니다. 그렇게 하면 전체 screen에서 touch 이벤트를 감지하게 될 겁니다. Runtime touch는 function listener 만 사용할 수 있습니다. 아래 예제가 있습니다.

local function onScreenTouch( event )
    -- no 'self' parameter
end

-- begin listening for screen touches
Runtime:addEventListener( "touch", onScreenTouch )


How to STOP detecting touches

특정 object에 대한 (global Runtime object도 포함해서) touch 이벤트 listening을 중지하시려면 addEventListener() 하셨던 방법대로 간단히 removeEventListener()를 call 하시면 됩니다.

-- example 1:
obj:removeEventListener( "touch", obj )

-- example 2:
obj:removeEventListener( "touch", onObjectTouch )

-- example3 :
Runtime:removeEventListener( "touch", onScreenTouch )


VERY IMPORTANT: Runtime 이벤트 리스너는 절대 자동적으로 remove 되지 않습니다. 만약 global Runtime object에 어떤 리스너를 add 하셨다면 이 리스너가 필요 없게 될 때 반드시 여러분이 직접 remove 해 주셔야 합니다. 그렇지 않으면 버그나 충돌이 일어날 수 있습니다.

개별적인 객체에 add된 이벤트 리스너는 그 객체(object)가 remove 될 때 자동적으로 remove 됩니다. 하지만 그 객체에 있는 리스너가 필요 없을 때 여러분이 직접 remove 해 주실것을 권장합니다. 항상 같은 객체에 같은 이벤트를 여러번 add 하는 일이 없도록 주의하셔야 합니다.

Touch Event Phases

초보 코로나 개발자분들은 touch listener를 사용하면서 몇가지 문제점에 당면하기 쉽습니다. 왜냐하면 유저가 한번 touch 할 때마다 listener fucntion이 한번만 call 될 걸로 기대하고 있기 때문입니다. 하지만 유저가 한번 Touch 한다고 그 리스너 함수가 한번만 call 되는 것이 아닙니다. 터치 이벤트가 발생하면 아래와 같은 phases 가 있습니다.

    began – 유저가 touch 하는 순간에 감지 됨
    moved – 유저가 터치한 손가락을 움직일 때 감지 됨
    ended & cancelled – 유저가 터치한 손가락을 화면에서 뗄 때 감지됨. 혹은 어떤 이유에서건 이 touch 이벤트가 cancel 됐을 때 감지 됨


이 touch phase가 감지될 때마다 지정한 터치 이벤트 리스너 함수가 call 되는 것입니다. 유저가 터치할 때마다 최소한 두번 내지는 세번 이 함수가 call 되는 것입니다.

그렇기 때문에 여러분들은 터치 했을 때 구현될 동작들이 어떤 phase에서 할 것인지 결정 하셔야 합니다. 그리고 나서 각 phase가 일어날 때 그 동작을 하도록 코딩을 하셔야 합니다. 아래 그 예제가 있습니다.

local function onObjectTouch( self, event )
    if event.phase == "began" then

        -- Touch has began

    elseif event.phase == "moved" then

        -- User has moved their finger, while touching

    elseif event.phase == "ended" or event.phase == "cancelled" then

        -- Touch has ended; user has lifted their finger
    end

    return true    -- IMPORTANT
end


위 예제 처럼 각 phase 를 if 문에서 감지하고 해당 phase에 여러분들이 넣고 싶은 동작들을 넣으시면 됩니다. 한번 터치 할 때마다 4가지 phase가 발생하고 이 phase가 발생할 때마다 onObjectTouch 함수가 call 되는 겁니다. 위의 예제처럼 대부분 ended와 cancelled phase는 같이 다룹니다.

To return true, or not to—THAT is the question.

위 예제 마지막에 보면 제가 OMPORTANT라고 주석을 단 것을 보실 수 있을 겁니다. 함수 끝에는 이와 같이 true 나 false 가 return 되어야 합니다.

touch 리스너가 true를 return 하면 그 touch 가 successful 하다는 의미입니다. touch 된 그 객체는 그 시간에 touch 됐음을 알립니다. 그렇게 함으로서 그 다음 event 가 제대로 dispatch 되게 됩니다. 그 object 밑에 touch 될 다른 object가 있다면 그 touch는 그 아래 object 에는 해당되지 않을 겁니다. 왜냐하면 그 위의 object 가 이미 그 touch에 대해 사용했기 때문입니다.

만약 리스너가 false를 return 했다면 (아무것도 return 하지 않아도 false를 return 한 것과 같습니다.) 그 touch는 뭔가 잘 못 됐다고 판단될 것이고 오직 began phase 만이 감지되고 끝날 겁니다. 이 경우 만약에 그 객체 밑에 터치 리스너를 감지하는 다른 객체가 있다면 그 터치 이벤트는 그 다음 객체에 전달이 될 겁니다. (이것을 이용하면 한 번의 터치로 두개의 object 에서 어떤 동작이 일어나도록 할 수 있을 겁니다. 단지 fault를 return 한 함수에서는 began phase 밖에 사용 못 하겠죠.)

이 부분이 새로 코로나 개발을 시작하는 분 들에게 혼동을 주기도 합니다. 이러한 touch 이벤트를 사용할 경우에 꼭 함수 끝날 때 true를 return 해야 한다는 것만 잊지 마세요.

Touch Focus

터치 리스너를 셋업했다면 아마 아래와 같은 모습을 하고 있을 겁니다.

local function onObjectTouch( self, event )
    if event.phase == "began" then

        -- specify the global touch focus
        display.getCurrentStage():setFocus( self )
        self.isFocus = true

    elseif self.isFocus then
        if event.phase == "moved" then

            -- do something here; or not

        elseif event.phase == "ended" or event.phase == "cancelled" then

            -- reset global touch focus
            display.getCurrentStage():setFocus( nil )
            self.isFocus = nil
        end
    end

    return true
end


해당 객체에 대한 터치 이벤트가 감지 됐을 때 touch focus를 global로 선언하는 것도 중요합니다. 이것은 began phase 단계에서 해야 합니다. 그리고  ended/cancelled phase 에서 이 Touch focus를 reset 시키게 됩니다. 그래야지 다른 객체들에 대한 touch 를 감지할 수 있게 됩니다. 이러한 내용들은 위 예제에 모두 구현돼 있습니다.

이 touch focus를 사용하신다면 힘들게 type 할 필요 없이 그냥 저 위의 예제를 복사해서 붙여 넣기만 하시면 됩니다.

Other Event Properties

터치 이벤트에는 phase 이외에도 여러가지 프로퍼티들이 있습니다. 아래 내용을 참고하세요.

    event.id – 해당 touch 이벤트에 대한 유닉한 구분자. (multi-touch에서 주로 사용됨)
    event.name – 해당 이벤트의 이름
    event.phase – 유저가 touch 한 현재의 phase 상태. began, moved, ended, cancelled 등이 있습니다.
    event.target – touch 된 object; self랑 똑 같은 의미임
    event.time – 이벤트가 일어난 이후부터 지금까지의 시간
    event.x, event.y – 이벤트가 일어난 x,y 좌표
    event.xStart, event.yStart – 최초 이벤트가 일어난 x,y 좌표. (began의 좌표가 됨). 유저가 drag 하게 될 경우 현재의 위치와 최초의 위치를 구분해서 사용할 수 있음
. 사용 예제를 보시려면 여기를 클릭 하세요.

프로퍼티에 대해 좀 더 자세한 내용을 알고 싶으시면 event API reference page를 참조하세요.

코로나에서는 이 touch 이벤트 이외에 tap 이벤트도 지원합니다. 이것은 touch 이벤트이지만 phase가 없습니다. 이것은 유저가 스크린 내 한 객체에 아주 빠른 tap이 일어나는것을 감지하고자 할 때 유용합니다.

tap 이벤트에 대한 자세한 정보는 Basic Interactivity 에 있습니다. 터치 이벤트에 대해 좀 더 자세히 할고 싶으시면 Events and Listeners documentation의 Touch Event 섹션을 보실것을 권장합니다.

반응형