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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
지난번 Corona DOC 로 공부하면서 Sprite Sheets 는 직접 예제를 다루면서 한번 해 보기로 했죠?

오늘 직접 예제를 보면서 하겠습니다.


위 그림을 가지고 작업하겠습니다.
한개의 큰 이미지에서 부분만 잘라서 하나의 이미지처럼 사용하는 기법이 Sprite Sheets Animation 입니다.

이 애니메이션에서 가장 먼저 할 일은 sprite를 require하는 것이고 그 다음이 newSpriteSheet를 생성하는 겁니다.
이 newSpriteSheet를 하기 전에 개발자가 먼저 체크해 둬야 할게 있습니다.

sprite.newSpriteSheet( spriteSheetFile, [baseDir,]  frameWidth, frameHeight )
신택스는 위와 같은데요. spriteSheetFile은 이미지 이름이 들어가면 되구요. baseDir은 그냥 옵션 입니다. 그 다음이 sprite로 사용될 프레임의 너비와 높이를 넣어야 됩니다.
저 위 person.png 파일에서 보면 12개의 사람 이미지가 있는데 이 각 이미지의 너비와 높이를 말합니다.

아마 이걸 구할 수 있는 프로그램이 있을겁니다. 아니면 이걸 디자인한 디자이너에게 물어봐도 되구요. 그냥 포토샵 같은데서 값을 구해도 될 것 같은데...

저는 그냥 코로나에서 해 봤습니다.


보시면 x,y좌표가 각각 32.36이 나왔죠? 소스를 볼까요?

display.setStatusBar (display.HiddenStatusBar)
local _W,_H = display.contentWidth,display.contentHeight;

local bgImg = display.newImage ("background.png")
local person = display.newImage ("person.png")

local xLoc = display.newText("",_W/2, (_H/2) - 15, native.systemFontBold, 18);
local yLoc = display.newText("",_W/2, (_H/2) + 15, native.systemFontBold, 18);
xLoc:setTextColor(0,0,0)
yLoc:setTextColor(0,0,0)

    local function touchScreen(event)
        local x,y = event.x,event.y;
        local phase = event.phase;

        if phase == "began" then
            xLoc.text = "X= " .. x;
            yLoc.text = "Y= " .. y;
        elseif phase == "moved" then
            xLoc.text = "X= " .. x;
            yLoc.text = "Y= " .. y;
        elseif phase == "ended" then
            xLoc.text = "X= " .. x;
            yLoc.text = "Y= " .. y;
        end
    end

bgImg:addEventListener( "touch", touchScreen );

우선 작업하기 편하게 statusBar를 없애고 스크린 너비와 높이를 변수에 담습니다.
그리고 background.png와 person.png를 display합니다.
x,y좌표를 설정하지 않았으니까 디폴트로 0,0이 x,y좌표 일 겁니다.

그리고 가운데 좌표를 뿌려줄 텍스트를 빈칸으로 만들어 놓고 색을 검은색으로 설정했습니다.

맨 마지막 줄 보면 백그라운드 이미지에 리스너를 달았습니다.
그리고 touchScreen 함수를 보면요.
각 이벤트 마다(began,moved,ended) x,y좌표를 화면에 표시하도록 만들었습니다.

저는 마우스를 저 12개 사람 이미지 중 첫번째 뒷모습 이미지의 적당한 곳에 이동 시켰습니다.

그랬더니 x=32,y=36이 나왔네요. 전 이 값을 sprite의 너비와 높이로 사용 할 겁니다.

그럼 이제 본격적으로 sprite sheet animation을 만들어 볼까요?
들어가기에 앞서 4개의 이미지를 더 받아 보세요.

4개의 화살표 이미지 입니다. (이것도 사실 하나가지고 돌려가면서 쓸 수 있습니다. 메모리를 절약하시려면요.)

display.setStatusBar (display.HiddenStatusBar)
--> Hides the status bar

----------------------------------------------------------------------
--                                BASICS                                --
----------------------------------------------------------------------
require "sprite"
-- Very important!

local background = display.newImage ("background.png")
-- Sets the background

자 첫 부분은 편의상 statusBar를 없애는 코딩을 했습니다.
그리고 두번째로는 sprite sheets animation을 사용하기 위해서 sprite를 require 했습니다. 주석으로 아주 중요하다고 돼 있죠?
그 다음엔 백그라운드 이미지를 그렸습니다.

----------------------------------------------------------------------
--                                SPRITE                                --
----------------------------------------------------------------------
local herosheet = sprite.newSpriteSheet("person.png", 32, 36)
-- Our sprite sheet
-- 32 is the width of each "box", this is the image width divided by the number of images across
-- 36 is the height of each "box", this is the image height divided by the number of images down
   
    local heroset = sprite.newSpriteSet (herosheet, 1, 12)
    sprite.add (heroset, "heroleft", 10, 3, 300, 0)
    sprite.add (heroset, "heroright", 4, 3, 300, 0)
    sprite.add (heroset, "heroup", 1, 3, 300, 0)
    sprite.add (heroset, "herodown", 7, 3, 300, 0)
-- The sprite set uses images 1 to 12 (all of them)
-- "heroup" starts at image 1 and includes 3 frames. (In this case 1, 2, 3.)
-- The "300" indicates .3 seconds per frame, the "0" indicates it will repeat until we stop it.
   
    local hero = sprite.newSprite (heroset)   
    hero.x = 160
    hero.y = 200
-- We insert out hero sprite
   
    hero:prepare("herodown")
-- We prepare the sprite   

이제 sprite 이미지를 설정하겠습니다.
newSpriteSheet 신택스는 위에서 보셨죠?
person.png를 spriteSheet로 이용할 거고 각 프레임의 크기는 너비 32,높이 36을 할 겁니다.
그 다음 newSpriteSet을 합니다. 신택스는 아래와 같습니다.
sprite.newSpriteSet (spriteSheet, startFrame, frameCount)
위에 코드를 보면 스프라이트 세트로 herosheet을 쓸거고 프레임은 1번에서부터 12번까지 있다는 내용입니다.

sprite.add
sprite.add( spriteSet, sequenceName, startFrame, frameCount,time,[loopParam] )
파라미터를 보면 스프라이트 세트와 이름 그리고 시작 프레임과 프레임 숫자와 시간 그리고 루프 도는 횟수등이 들어갑니다.
위 코드를 보면
sprite.add (heroset, "heroleft", 10, 3, 300, 0)
heroset에 heroleft라는 이름으로 10프레임부터 3개 프레임을 0.3초 간격으로 표시를 하고 루프는 무한대라는 의미입니다.

sprite.newSprite
스프라이트의 새 인스턴스를 생성합니다. 한마디로 메모리 공간을 확보해 둔다는 의미겠죠. 스프라이트는 Corona DOC 강좌에서 살펴 봤듯이 DisplayObject 입니다.
신택스는 sprite.newSprite( spriteSet ) 입니다.

위 코드를 보면 heroset을 newSprite으로 정하고 x,y 위치를 지정했습니다.

prepare

스프라이트 인스턴스에서 실행되고 있는 애니메이션 시퀀스를 stop시킵니다. 그리고 이 시퀀스의 첫번째 프레임으로 갑니다. 루프 counter도 reset됩니다.
spriteInstance.play()가 사용되면 다시 플레이 됩니다.
신택스는 spriteInstance:prepare( [sequence] ) 입니다.

위의 코드에서는 hero 인스턴스를 herodown에서 stop된 상태로 준비 합니다.
person.png 이미지에서 보면 세번째 줄 첫번째 이미지가 나타나겠죠?

----------------------------------------------------------------------
--                                ARROWS                                --
----------------------------------------------------------------------   
up = display.newImage ("up.png")
up.x = 250
up.y = 380
---
down = display.newImage ("down.png")
down.x = 250
down.y = 440
---
left = display.newImage ("left.png")
left.x = 210
left.y = 410
---
right = display.newImage ("right.png")
right.x = 290
right.y = 410
-- The arrow images to move our sprite

그 다음은 화살표들을 display합니다.
아까 언급한 대로 한개의 이미지 가지고 rotate 시키면서 4개의 화살표를 만들 수 있습니다. 이건 여러분이 직접 해 보세요.
이 부분은 많이 다뤄본 부분이기 때문에 별다른 코멘트 없이그냥 넘어 갑니다.

----------------------------------------------------------------------
--                                MOVEMENT                            --
----------------------------------------------------------------------
local motionx = 0
local motiony = 0
local speed = 4
-- These are required below; change the speed if you wish to experiment but not motionx or motiony.

local function stop (event)
    if event.phase =="ended" then
        motionx = 0
        motiony = 0
        hero:pause()
    end
end
Runtime:addEventListener("touch", stop )
-- Here we state that we don't want the sprite animated or moving if we aren't pressing an arrow

local function movehero (event)
hero.x = hero.x + motionx
hero.y = hero.y + motiony
end
timer1 = timer.performWithDelay(1,movehero,0)
-- The function to move the hero; it's on a timer but you could also use a Runtime listener


function touchup (event)
motionx = 0
motiony = -speed
hero:prepare("heroup")
hero:play("heroup")
end
up:addEventListener("touch", touchup)
-- When the up arrow is touched, play hero up and move the hero upwards

function touchdown (event)
motionx = 0
motiony = speed
hero:prepare("herodown")
hero:play("herodown")
end
down:addEventListener("touch", touchdown)
-- When the down arrow is touched, play hero down and move the hero downwards

function touchleft (event)
motionx = -speed
motiony = 0
hero:prepare("heroleft")
hero:play("heroleft")
end
left:addEventListener("touch", touchleft)
-- When the left arrow is touched, play hero left and move the hero left

function touchright (event)
motionx = speed
motiony = 0
hero:prepare("heroright")
hero:play("heroright")
end
right:addEventListener("touch", touchright)
-- When the right arrow is touched, play hero right and move the hero right

이제 sprite도 모두 세팅 됐고 화살표도 세팅 됐으니 화살표를 누르면 animation이 일어나도록 할 차례입니다.

첫번째로 motionx,motiony,speed 변수를 생성합니다. 값은 0,0,4 로 설정해 둡니다.

Runtime:addEventListener("touch", stop )
그 다음 첫번째 이벤트 리스너로 Runtime 이벤트 리스너가 있습니다.
아무 화살표도 누르고 있지 않으면 애니메이션을 그 상태로 stop시키는 부분 입니다.
stop함수를 보면 motionx,motiony모두 0으로 세팅하고 hero 스프라이트는 pause()시킵니다.
마우스 up일 경우 이 함수가 실행 되게 됩니다.

timer1 = timer.performWithDelay(1,movehero,0)
그 다음은 timer 입니다. 위 코드는 0.001초마다 movehero를 실행한다는 의미 입니다.
맨 마지막 0은 루프로서 무한대를 의미합니다.
movehero함수를 보면 hero.x와 hero,y를 motionx와 motiony만큼씩 이동하도록 설정했습니다.
위 Runtime 리스너가 작동되면 motionx와 motiony 모두 0이니까 움직이질 않겠고 sprite도 pause돼 있으니까 정지 돼 있을 겁니다.

up:addEventListener("touch", touchup)
이제 위쪽 화살표에 리스너를 달았습니다.
그 내용을 보면 x좌표는 0이고 motiony를 -speed만큼씩 변화시킵니다.
speed는 4로 설정 돼 있고 Corona는 위로 올라 갈수록 y좌표가 줄어드니까 이렇게 되면 이미지는 위로 올라갈 겁니다.
그리고 play는 heroup을 설정했으니 이에 해당하는 3개의 프레임이 연달아 display될겁니다. 이 화살표를 누르고 있는 동안 timer가 계속 실행 될 겁니다.
(사실은 안 누르고 있을 때도 계속 실행됩니다.)
그럼 0.001초마다 motiony좌표를 4씩 마이너스 해 주고 timer에서 호출하는 movehero함수에서 hero 스프라이트 위치를 변경시켜 줍니다.
이럼으로서 스프라이트 이미지 3개가 번갈아 가면서 출력되고 그 위치도 4픽셀씩 바뀌니까 자연스러운 애니메이션 효과를 얻을 수가 있습니다.

나머지 버튼들도 마찬가지로 작동합니다.


이러면 화살표를 누르는 대로 이미지가 걸어가는 애니메이션 효과를 낼 수 있습니다.

아래 전체 소스코드와 이미지들이 있습니다.


오늘은 sprite sheets animation에 대해 자세히 알아 봤습니다
그럼 모두 즐거운 주말 되세요.

반응형

Time Bar 만들기

2011. 10. 27. 22:06 | Posted by 솔웅


반응형
오늘은 timer.performWithDelay()를 이용해서 시간이 지날수록 점점 줄어드는 Time Bar를 만들어 보겠습니다.

Corona DOC에서 배운 기본 테크닉으로 응용해 본 건데요.


앱을 시작하면 저 흰 Bar가 점점 줄어드는 겁니다.

그럼 소스를 보겠습니다.

--Show a basic white bar
local timeBar = display.newRect( 20, 165, 280, 20 )
timeBar:setReferencePoint(display.BottomLeftReferencePoint)
timeBar.x = 20
timeBar.y = 165

--Make the bar shrink over time
local function loseTime (event)
timeBar.width = timeBar.width - 2
timeBar:setReferencePoint(display.BottomLeftReferencePoint)
timeBar.x = 20
end
gameTimer = timer.performWithDelay(100,loseTime, 0)

1. display.newRect로 사각형을 그립니다.
    color를 지정해 주지 않았는데 그러면 기본으로 White가 그려집니다.
    display.newRect( [parentGroup,] left, top, width, height )
2. ReferencePoint를 BottomLeft로 줍니다.
    이러면 Rect의 왼쪽 아래가 기준이 되서 x좌표 20 과 y좌표 165 가 그려집니다.
3. timeBar 의 x좌표와 y좌표를 그려줍니다.
    이미 newRect에서 x,y가 설정 돼 있기 때문에 이 부분은 생략되도 됩니다.
    혹시 x,y좌표를 바꿀 필요가 있을 때 이용하시면 됩니다.
4. 그 다음은 loseTime(event) 함수 부분입니다.
   event를 파라미터로 받는 걸로 봐서 어떤 리스너에서 호출되는 겁니다.
   그 리스너는 맨 마지막에 있는 timer.performWithDelay 입니다.
   신택스는 아래와 같습니다.
   timer.performWithDelay( delay, listener [, iterations] )
   위 예제에서는 delay가 100으로 돼 있기 때문에 0.1초마다 한번씩 loseTime을 콜 합니다.
   맨 마지막에 0으로 돼 있는건 이걸 계속 호출한다는 내용입니다.
   3으로 돼 있으면 3번 호출합니다.
5. loseTime(event) 함수 내용은요.
    실행 될 때마다 timeBar의 width를 -2 합니다.
    ReferencePoint는 여전히 BottomLeft로 하고 x좌표는 계속 20으로 합니다.

이런식으로 간단히 Time Bar 효과를 냈습니다.

살펴본 김에 Listener에 대해 자세히 한번 살펴 보고갈까요?


이 예제는 하얀색 Bar는 점점 줄어들고 녹색 공은 좌우로 랜덤하게 움직이는 겁니다.
그리고 Function Listener와 Table Listener에서는 Terminal에 일정 시간별로 print를 하구요.

아래 전체 소스 코드를 보겠습니다.

--Show a basic white bar
local timeBar = display.newRect( 20, 165, 280, 20 )
timeBar:setReferencePoint(display.BottomLeftReferencePoint)
timeBar.x = 20
timeBar.y = 165

--Make the bar shrink over time
local function loseTime (event)
timeBar.width = timeBar.width - 2
timeBar:setReferencePoint(display.BottomLeftReferencePoint)
timeBar.x = 20
end
gameTimer = timer.performWithDelay(100,loseTime, 0)

-- Function Listener
 local function listener( event )
    print( "Function listener called" )
 end
 
 timer.performWithDelay(500, listener,0 )
 
 -- Table Listener
 local listener1 = {}
function listener1:timer( event )
   print( "Table listener called" )
end
 
timer.performWithDelay(700, listener1,0 )


function newBall()
    local randomPosition = 100 + math.random(200)
    ballPosition = display.newImage("b3.png")
    ballPosition.y = 240
 
    -- wrap spawnBall and randomPosition inside a closure
    local myclosure = function()
        local randomPosition = 100 + math.random(200)
        return spawnBall( randomPosition )
    end
   
    timer.performWithDelay(180, myclosure, 0)
   
    --spawnBall()
    function spawnBall(pos)
        print( "randomPosition = " .. pos )
        ballPosition.x = pos
    end
end

newBall()

Function Listener 윗 부분은 처음에 살펴 봤던 부분이라서 넘어갑니다.

Function Listener는 TimeBar에서 우리가 사용했던 형식 입니다.
일정 시간이 지나면 해당 함수(Function)를 호출합니다.

그 다음 Table Listener 는 Table(배열) 값에 대해 리스너를 달 때 사용합니다.
소스코드와같이 함수를 테이블 이름 : timer(event) 형식으로 답니다.
timer.performWithDelay부분은 같습니다.

newBall()
함수 안에서 함수를 호출하는데 거기서 Parameter를 던져주는 방법에 대한 예제입니다.
처음에는 볼의 이미지를 그리고 그 y좌표를 설정했습니다.

그리고 performWithDelay에서 0.18초마다 myclosure를 호출합니다.
myclosure에서는 random한 값을 만들고 이를 spawnBall에 파라미터를 전달해 주면서 호출합니다.
spawnBall() 함수는 이 랜덤 값 파라미터를 받아서 볼의 x좌표를 세팅해 줍니다.

이렇게 하면 공은 좌우로 아주 랜덤하게 움직입니다.

오늘 샘플 코드는 아래에 있습니다. 그럼 즐공 하세요.






반응형


반응형
오늘은 간단한 팁을 하나 살펴 보겠습니다.

소스와 이미지는 위 압축파일 안에 있습니다.


오늘의 팁은 배경화면이 계속 위에서 아래로 내려가는 효과 입니다.
이 소스에서는 두개의 이미지를 사용했습니다.
갤러그같은 게임에 응용할 수있을 것 같습니다.

소스를 분석 해 볼까요?

-------------------------------------------------------------------------
-- SCROLLING GAME BACKGROUND --
--------------------------------------------------------------------------

local localGroup = display.newGroup()
--First half of scrolling background
local bg1 = display.newImage("background1.png")
bg1:setReferencePoint(display.CenterLeftReferencePoint)
bg1.x = 0
bg1.y = 0
localGroup:insert(bg1)

-- Second half of scrolling background
local bg2 = display.newImage("hutbg.png")
bg2:setReferencePoint(display.CenterLeftReferencePoint)
bg2.x = 0
bg2.y = 480
localGroup:insert(bg2)

local tPrevious = system.getTimer()
local function move(event)
local tDelta = event.time - tPrevious
tPrevious = event.time

-- Change this to adjust the speed of the background
local yOffset = ( 0.15 * tDelta )

bg1.y = bg1.y + yOffset
bg2.y = bg2.y + yOffset

if bg1.y > 720 then
bg1:translate( 0, -480*2)
end
if bg2.y > 720 then
bg2:translate(0, -480*2)
end
end

-- Gets the background moving
Runtime:addEventListener( "enterFrame", move )

우선 localGroup을 만들구요.
배경 이미지 두개를 설정합니다.
하나는 x=0,y=0으로 위치를 지정하구요. 다른 하나는 x=0, y=480으로 지정했습니다.
(y는 이미지 높이를 생각해서 지정하셔야 할 겁니다.)

그리고 이 두 이미지를 localGroup에 insert하구요.

다음엔 system.getTimer()를 해서 tPrevious라는 변수에 넣습니다.
그 다음 move(event) 함수를 만듭니다.
이 함수는 맨 아래에 있는 리스너에서 호출 될 건데요. 맨 아래에 있는 리스너는 Runtime리스너이고 enterFrame으로 파라미터가 돼 있네요.
앱이 시작하면서 이 함수가 호출 될 겁니다.

이 함수 내용을 보면요.
tDelta 변수에 event.time - tPrevious 를 담구요. tPrevious에는 새롭게 이벤트 시간을 담습니다.
그리고 yOffset변수에는 대강의 이미지 스피드를 계산해 넣습니다.
이 부분은 여러분이 원하는 숫자를 맘대로 넣어보세요.
1을 넣으면 아주 천천히 흘러갈 테고 큰 숫자를 넣을 수록 아주 빨리 흘러갈 겁니다.

그리고 첫번째 백그라운드 이미지의 y 좌표를 이 속도 만큼 + 해 줍니다.
bg1.y 가 720 보다 크면 첫번째 백그라운드 이미지를 translate 합니다.
여기에 나오는 y좌표의 숫자 480, 720 등은 배경이미지의 높이에 따라 적당히 바꿔 주시면 됩니다.

첫번째 백그라운드 이미지 처럼 두번째 백그라운드 이미지도 속도만큼 y좌표를 옮겨주고 720보다 크면 translate 시킵니다.

이렇게 하면 두개의 배경 이미지가 계속 번갈아가며 아래로 흐릅니다.

오늘은 배경 이미지 스크롤 다운에 대한 간단한 팁을 알아봤습니다.

반응형