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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
Emanuele Feronato의 블로그를 알게되서 가끔 들어가 보는데 아주 훌륭한 로직들을 많이 share하고 있네요.

눈내리는 효과, 물방울 올라가는 효과 두가지를 분석해 봤는데요.
여기 뱀 기어다니는 효과가 재밌어서 제 블로그에 정리해 둘까 합니다.
Emanuele는 String Avoider라고 이름을 붙였는데요. 제 눈에는 뱀 기어다니는걸로 보이더라구요.

아래 유튜브에도 올려놨는데 아주 간단한 코드로 이런 효과를 주고 있습니다.


아래 소스가 있습니다.

-- Declaring Variables
-- Stage Width and Height
local _W,_H = display.contentWidth,display.contentHeight
 
local tailLength = 5
local tailNodes = 200
local head = display.newCircle(_W/2,_H/2,10)
local nodes = {}
 
for i=1,tailNodes,1 do
    nodes[i] = {}
    nodes[i].x = head.x
    nodes[i].y = head.y
end
 
-- Creating Display Groups
local tailGroup = display.newGroup()
 
local function startDrag(event)
    -- Store the Target (the Head) in a variable
    local t = event.target
    if event.phase == "began" then
        -- When the touch started, set the Focus on the Head
        display.getCurrentStage():setFocus( t )
        t.isFocus = true
        -- Store initial Position of the Head
        t.x0 = event.x - t.x
            t.y0 = event.y - t.y
    elseif t.isFocus then
        if event.phase == "moved" then
            -- While moving the head
            -- remove every drawn line from the group
            for i=tailGroup.numChildren,1,-1 do
                tailGroup:remove(i)
            end
 
            -- Move the Head
            t.x = event.x - t.x0
            t.y = event.y - t.y0
 
            -- Create starting line and insert it into the group
            local line = display.newLine(head.x,head.y,nodes[1].x,nodes[1].y)
            tailGroup:insert(line)
            line.width = 20
 
            nodes[1].x = head.x
            nodes[1].y = head.y
 
            -- Loop through every tailNode and save new positions in table
            -- Note: LUA tables start with an index of 1 and not 0!
            for i=2,tailNodes,1 do
                local nodeAngle = math.atan2(nodes[i].y-nodes[i-1].y,nodes[i].x-nodes[i-1].x);
                nodes[i].x = nodes[i-1].x+tailLength*math.cos(nodeAngle)
                nodes[i].y = nodes[i-1].y+tailLength*math.sin(nodeAngle)
 
                -- You could use
                -- line:append(nodes[i].x,nodes[i].y)
                -- but this wouldn't allow you to alter each segment (fading the color, reducing linewidth etc.)
 
                -- Creating new line segments
                local nLine = display.newLine(nodes[i-1].x,nodes[i-1].y,nodes[i].x,nodes[i].y)
                -- c will store the colorvalue - starting from 255 and ending with 0 (white to black)
                local c = 255-i/tailNodes*255
                nLine:setColor(c)
                -- width will start at 20 and fade out to 0
                nLine.width = line.width-i/tailNodes*line.width
 
                -- Important: insert the new Line segments into the table!
                tailGroup:insert(nLine)
            end
        elseif event.phase == "ended" or event.phase == "cancelled" then
            -- Remove the focus
            display.getCurrentStage():setFocus( t, nil )
            t.isFocus = false
        end
    end
end
 
-- Create touch Eventlistener
head:addEventListener("touch",startDrag)

한번 분석해 볼까요?

첫줄은 모바일 기기의 가로 세로 길이를 변수에 담았구요.
tailLength와 tailNodes 변수에 숫자를 대입했습니다.
그리고 head라는 변수에 원을 그려 넣었구요. nodes 라는 테이블 변수를 만들었습니다.

다음 for 문에서는 tailNodes 값 만큼 nodes에 head를 넣습니다. 총 200개가 되겠죠?
nodes 테이블에는 작은 원 200개가 들어있습니다. 이걸 가지고 효과를 만들겁니다.

다음에는 Display Group을 만들었습니다. tailGroup이라는 이름으로요.

그 다음으로는 맨 밑에 있는 리스너를 보죠.
head에 touch 리스너를 달았네요. 맨 처음 그려진 head에 Listener가 달립니다.
이 head를 touch 하면 startDrag함수가 실행되구요. 그러면 began, moved, ended phases 에 따라서 Control 할 수가 있습니다.

이제 startDrag 함수를 분석해 보죠.
처음 변수 t를 만들고 여기에 event.target 즉 head 를 넣습니다.
이제 control을 해 보죠.
began 단계에서는 t에 setFocus를 합니다.
setFocus는 해당 리스너에 Focus를 둔다는 의미인데요. 제 블로그에서 setFocus로 검색하시면 관련 글이 뜰 겁니다. 참고 하세요.
그리고 t.x0 에는 현재 이벤트가 일어나는 x포인트에서 t(head)의 x포인트를 뺀 값을 넣습니다. t.y0에도 현재 이벤트가 일어나는 y포인트에서 t의 y포인트를 뺀 값을 넣구요.
began에서는 여기까지 하구요. t.isFocus가 계속 유지되고 있으면 moved 일 때 로직을 실행합니다.
moved에서 처음 하는 일은 tailGroup의 모든 구성요소들을 지우는 겁니다. 이전 이미지가 그대로 남아 있으면 움직이는 효과가 아니라 선이 그려지는 효과가 날겁니다.

이 지우는 기능을 빼면 이런 효과가 납니다. 한번 해 보셔도 재밌을 거예요. 이 로직도 잘 이용하면 좋은 효과를 낼 수 있겠는데요.

그리고  t.x와 t.y를 이벤트가 일어난 좌표에서 began에서 구했던 t.x0와 t.y0를 뺀 값을 넣습니다. 그러면 head가 움직이게 됩니다.

그리고 head의 x,y좌표에서 그 다음 node에 선을 긋습니다. (뱀의 몸이 이어지도록 보이겠죠?)
그리고 이 라인을 그룹에 insert하구요. 그 width를 20으로 설정합니다.
그리고 이 node 1을 head 위치로 옮깁니다.

다음에는 for문을 돌려서 모든 tailNode를 새로운 위치로 옮겨서 뱀 몸체가 기어가는 효과가 나도록 합니다.
우선 현재의 node와 이전 node 사이의 각도를 구해서 nodeAngle에 넣습니다.
두 점의 좌표를 가지고 각도를 구하는 공식은 math.atan2(y,x) 함수를 이용하시면 됩니다.
그리고 이 각도를 이용해서 각 node의 x,y좌표값을 얻습니다.
x는 math.cos(x) 함수를 이용하시고 y는 math.sin(x)를 이용하시면 됩니다.

이제 뱀 머리를 움직이면 이전 이미지들은 지워지고 이전 node 이미지들이 계속 따라오면서 움직이는 효과가 날 겁니다.

그런데 아직까지는 짧은 선이 계속 따라 올 거구요. 긴 뱀 꼬리가 따라오도록 하는 건 아래 로직에서 처리 해 줍니다.

아까는 head와 첫번째 node사이에 line을 그려 줬는데요. 이제는 node와 node들 사이에 라인을 그려 줍니다.
그리고 나서 색이 점점 검은색에 가까와 지도록 계산을 해서 이 새 라인의 색을 set 해 줍니다.
그러면 꼬리로 갈 수록 점점 색이 여리게 나올 겁니다.

여기까지 하면 이런 효과가 납니다.
아직 width를 설정해 주지 않아서 그렇습니다.
이 모양은 정자가 움직이는 모양 같네요.

이 node들 사이의 line의 두께는 뒤로 갈 수록 점점 줄어들도록 하는 공식을 넣습니다.
그리고 이 라인을 tailGroup에 insert합니다.

여기까지 하면 모든 움직이는 로직은 완료 됩니다.
마지막으로 유저가 손을 떼어서 이벤트가 완료 되면 setFocus를 해제합니다.

이러면 저 위에 유튜브 영상에서 보는 것처럼 뱀이 기어다니는 효과를 주실 수 있습니다.

이 소스에서는 math 함수 3개에 대해서 익숙해 지도록 연습하라고 권해 드리고 싶네요.
그러면 정말 아주 다양한 효과들을 낼 수 있습니다.
즐코딩 하시구요. ~~~~ ~~~~ 추천 추천 부탁 드려요...
반응형