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

최근에 받은 트랙백

글 보관함



코로나 SDK (Corona SDK)에서는 화면 전환을 위해서 transition.to를 사용합니다.
이것만 사용해서도 아주 다이나믹한 효과를 낼 수 있는데요.

Ricardo Rauber Pereira라는 개발자가 아주 보기 좋은 화면 전환 클래스들을 구현해서 이를 공개했습니다.
director.lua라는 파일인데 구글에서 써핑하셔도 쉽게 찾으실 수 있습니다.

이 클래스에는 총 12개의 화면 전환 효과가 있습니다.
("moveFromRight","overFromRight","moveFromLeft","overFromLeft",
"moveFromTop","overFromTop","moveFromBottom",
"overFromBottom","crossfade","fade","flip","downFlip"
)

파일은 아래에 제가 압축해서 올린 샘플파일에 있습니다. 받아보세요.
제가 가지고 있는 건 버전 1.3 이네요.
 - Version: 1.3
 - Made by Ricardo Rauber Pereira @ 2010
 - Blog: http://rauberlabs.blogspot.com/
 - Mail: ricardorauber@gmail.com
이 파일을 보면 블로그와 이메일도 있으니까 참조하시구요.
저 블로그에 가 보니까 지금은 1.4 버전이 나왔네요.
이 개발자는 브라질 사람인가 봐요.

리카르도는 소스코드를 무료로 공개하는 대신 블로그에서 Donation을 받네요.
자신의 노력의 결과물을 나눠서 다른 사람이 쉽게 사용할 수 있도록 공개하는 개발자들에게는 성의 표시를 해줘도 좋겠죠? 여유가 있으면 Donation을 해도 좋고 블로그에 광고가 있으면 광고를 눌러줘도 되고 아니면 메일이나 트위터로 고맙다고 메세지 날려줘도 좋을 거예요.. :) 그리고 리카르도가 만든 앱을 다운받거나 구매하는 것도 ....


이 director.lua를 이용하는 방법은 아래와 같습니다.

https://www.youtube.com/watch?v=RAktnj7nwos

1. main.lua가 있는 폴더에 director.lua를  복사해 넣는다.
2. require 한다 (local director = require("director"))
3. 그룹을 만든다. local mainGroup = display.newGroup()
4. directorView를 그룹에 넣는다. mainGroup:insert(director.directorView)
5. 화면 전환 할 때 changeScene을 이용한다. director:changeScene("scene2","downFlip")
-> 첫번째 파라미터는 다음 화면을 보여줄 파일 이름입니다. 이 경우 scene2.lua가 되겠죠.
-> 두번째 파라미터는 화면 전환 효과 입니다. 아까 봤던 8가지 중에 아무거나 한가지를 넣으시면 됩니다.

그 다음은 전환될 화면 (scene2.lua)에서는 어떻게 해야 하는지 보겠습니다.
1. 모듈 선언을 한다. module(..., package.seeall) -> 이것은 Corona SDK에서 모듈 사용하는 규칙입니다.
2. localGroup이라는 그룹을 만든다. local localGroup = display.newGroup()
3. 함수 new()를 만든다. function new()    return localGroup end
4. 함수 new()는 localGroup을 return한다.
5. 다른 화면으로 전환하려면 마찬가지로 changeScene을 이용한다.
director:changeScene("nextfile","downFlip")

이것만 지켜주시면 됩니다.


우리의 리카르도가 공개한 샘플 입니다. 파일은 아래에 있습니다.


파일을 열어보니까 제가 못 봤던 book이라는 폴더가 있네요.
실행해 보니까 SlideView 관련된 클래스 인가봐요.
화면 전환 뿐만 아니라 Slideview 관련해서도 편리한 기능을 제공하나봅니다.

이건 제가 공부 좀 해서 다음 기회에 소개해 드릴께요.

오늘은 팁으로 제가 이 director.lua 클래스를 사용할 때 화면 전환 효과를 랜덤하게 처리하고 있는데요.

그 소스를 소개해 드릴께요.

일단 randomScreen.lua 파일을 아래처럼 만들어 놓습니다.
module(..., package.seeall)
function init()
    RandomScreen()
end

function RandomScreen() 
    changes = {"moveFromRight","overFromRight","moveFromLeft","overFromLeft",
                "moveFromTop","overFromTop","moveFromBottom",
                "overFromBottom","crossfade","fade","flip","downFlip"}
    selectScreen = math.random(1,12)
    screenChange = changes[selectScreen]
    return screenChange   
       
end


그리고 main.lua에서 이 파일을 require합니다.

local changeScreen = require("randomScreen")

그리고 화면전환 하기 직전에 이 랜덤한 화면전환 기능 값을 받습니다.

randomScreen = changeScreen.RandomScreen();

그리고 director.lua의 cangeScene을 이용할 때 이 값을 넣습니다.

director:changeScene("파일이름", randomScreen);

이렇게 하면 랜덤하게 화면 전환 효과들을 이용할 수 있습니다.
저는 앱을 만들 때 이렇게 처리하면 편하더라구요.

오늘의 팁이였구요.

director.lua에서 파라미터 넘기기나 팝업 기능 사용하기는 직접 소스 보면서 해 보세요.

그렇게 어렵지 않을 겁니다.

그럼...
반응형

Comment

  1. kang 2012.01.30 03:52

    정말 멋진예네요~

    헌데 화면전환시에도 버튼이미지에 달아놓은 이벤트리스너가 살아있어서
    이부분 어떻게 활용해야 좋을지 궁금합니다 ㅠ ㅠ

    가령 다음화면으로 넘어왔음에도 특정 영역을 터치하면 그 전화면에
    터치 이벤트 리스너가 살아있네요

    • 솔웅 2012.01.30 04:08 신고

      우선 그 리스너를 단 객체(object)를 group화 했는지 살펴보시구요. 그리고 화면 전환 하기 전에 리스너나 타이머는 모두 remove 해 주시는게 좋습니다.
      제 경우엔 메모리가 계속 늘어나서 나중에는 속도가 느려지더라구요.
      전 display object들도 다 remove를 했는데도 계속 메모리가 쌓여서 director 클래스로 작업했던걸 새로 나온 storyboard API로 바꾸었습니다.
      지금 작업하시고 계시다면 storyboard API로 작업 하시기를 추천드리고 싶네요.

      감사합니다.


오늘은 Sprite Sheet과 timer와 관련된 팁을 다루겠습니다.

오늘 다룰 소스는 Peach Pellen 이라는 개발자가 자기 친구가 앱을 개발한다는 얘기를듣고 도움이 될까 해서 만든 주사위놀이 샘플 코드입니다. 그리고 그 코드를 공개해서 다른 분들도 도움이 되길 바란다고 하네요.

아래 링크로 가시면 관련 글을 보실 수 있습니다.

http://techority.com/2011/11/22/rolling-the-dice/

저 싸이트로 가시면 샘플 코드를 다운 받으실 수 있구요.
아래 저도 그 코드를 올려 놓을 테니까 여기서 곧바로 다운 받으셔도 됩니다.

실행 화면은 아래와 같습니다.


오른쪽 위에 하얀 사각형을 누르면 주사위가 막 돌다가 멈춥니다.
그러면 터미널에 두 주사위의 합이 출력 됩니다.

그런데 이상한건 제가 집에서 이 소스코드를 실행 했을 때는 두 주사위가 항상 같은 숫자만 나왔거든요.

그래서 다른 숫자가 나오도록 소스를 좀 수정했습니다.
그리고 터미널뿐만 아니라 화면에도 합계가 텍스트로 표시 되도록 했구요.

그런데 회사와서 이 코드를 돌려보니까 두 주사위가 항상 같은 숫자가 나오는게 아니더라구요.

집에 있는 컴퓨터는 윈도우고 회사에 있는건 맥이라서 그런가?

하여간 아래 코드는 제가 조금 수정한 코드입니다. 다운 받은 파일에 있는 소스랑은 아주 조금 다를거예요.


--Hide the status bar => 스테이터스 바 감추기
display.setStatusBar(display.HiddenStatusBar)

--Require sprite => 스프라이트 require하기. 스프라이트는 director.lua나 movieclip.lua 처럼 별도의 파일로 제공되는게 아니라 corona sdk 에 내장된 클래스 입니다. 따로 파일을 구하실 필요가 없습니다.
require "sprite"

=> 스크린 너비와 높이 구해 놓기
_W = display.contentWidth;
_H = display.contentHeight;

--The background image => 배경화면 표시하고 합계를 표시할 텍스트 선언하고 위치 정해 줌
local bg = display.newImage( "bg.png" )
local total = display.newText("", _W/2, _H-(_H/7), native.systemFontBold, 30)

--Total sum of dice after a roll => 주사위 합계 담을 변수
local diceTotal = 0

--States whether or not the dice may be rolled => 주사위 돌리기 가능한 상태인지 아닌지 콘트롤 할 변수
local canRoll = true

--Sprite Setup => 스프라이트를 셋업 함
local diceSheet = sprite.newSpriteSheet( "dice.png", 64, 64)

주사위로 사용할 이미지는 이 이미지 입니다.
일단 이 이미지를 diceSheet이라는 이름으로 만듭니다. 이 프레임의 크기는 64X64 입니다.

local diceSet = sprite.newSpriteSet( diceSheet, 1, 6 )
sprite.add( diceSet, "dice", 1, 6, 195, 0)
sprite.add( diceSet, "dice2", 1, 6, 210, 0)

위 diceSheet를 diceSet이라는 스프라이트 세트에 담습니다. 시작하는 프레임은 1 이고 총 6개 프레임이 있습니다.
이 diceSet를 add합니다. 신택스는 아래와 같습니다.
sprite.add( spriteSet, sequenceName, startFrame, frameCount, time, [loopParam] )
첫번째는 diceSet를 dice라는 sequenceName으로 add를 하는데 시작 프레임 1이고 전체 프레임 count는 6 입니다. time은 195입니다. 이것은 195ms에 이 diceSet을 한번 돌린다는 겁니다.
두번째 줄은 dice2인데 dice와 모두 같고 이 time이 210 입니다.
두 주사위가 6개 프레임의 돌아가는 시간이 다르니까 각각 다른 프레임이 나오겠네요.
윈도우에서 항상 똑 같은 주사위 이미지가 나온게 제가 착각한 건가 본데요?
혹시 여러분 윈도우에서 한번 돌려보시구 어떻게 나오나 잘 살펴 보세요.
저도 집에 돌아간 다음에 다시 돌려 봐야 겠네요.

--First Die
local dice = sprite.newSprite( diceSet )
dice.x = 120
dice.y = 200
dice:prepare("dice")

첫번째 sprite를 dice라는 변수에 담고 화면의 위치를 정해 줍니다.
스프라이트 쉬트 사용하는 순서가 이렇게 되네요.
sprite.newSpriteSheet => sprite.newSpriteSet => sprite.add => sprite.newSprite => spriteInstance:prepare => spriteInstance:play => spriteInstance:pause
따로 정리해서 외워도 좋겠는데요.

--Second Die
local dice2 = sprite.newSprite( diceSet )
dice2.x = 200
dice2.y = 300
dice2:prepare("dice2")

두번째 스프라이트 dice2를 선언합니다.

--Roll button
local rollBtn = display.newImage("die.png")
rollBtn.x = 280
rollBtn.y = 40

=> 버튼 이미지를 표시합니다.

--End Roll Function
local function endRoll()
    dice:pause()
    showTotal();
end

첫번째 주사위 play를 중지시키고 showTotal함수를 실행합니다.

local function endRoll2()
    dice2:pause()
    showTotal();
end

두번째 주사위 play를 중지시키고 showTotal함수를 실행합니다.

function showTotal()
    diceTotal = dice.currentFrame + dice2.currentFrame
    print(diceTotal) --Print the result in the terminal
    total.text="Total : " .. diceTotal;
    canRoll = true --Allow the dice to be rolled again
end

현재 프레임 = 주사위 눈 입니다. dice와 dice2의 현재 프레임을 구해서 더하면 주사위 두 눈의 합이 됩니다.
이것을 터미널에 print하고 스크린에도 표시합니다.
canRoll은 true로 세팅해서 버튼을 누르면 주사위가 돌아갈 수 있도록 합니다.

--Roll function
local function rollDice()
    if canRoll == true then
        canRoll = false --Prevent dice from being rolled again before the current role is over
        dice:play()
        dice2:play()
        randomTime = math.random(1500, 3500)
        timer.performWithDelay(randomTime, endRoll, 1)
        randomTime2 = math.random(1500, 3500)
        timer.performWithDelay(randomTime2, endRoll2, 1)
    end
end
canRoll을 false로 해서 버튼을 눌러도 주사위가 새로 돌지 않도록 막아 놓습니다.
dice와 dice2를 play시킵니다.
랜덤값 (1.5초 ~ 3.5초)를 구해서 timer를 사용해서 그 시간만큼만 돌고 dice를 멈추게 합니다. 다음줄은 dice2를 멈추게 합니다.

rollBtn:addEventListener("tap", rollDice)

버튼에 이벤트 리스너를 달아서 누르면 rollDice가 실행 되도록 합니다.



이렇게 하면 위 그림처럼 화면에 주사위 눈의 합이 출력 됩니다.

원래 소스가 두 주사위의 눈이 각각 다르게 표시되도록 하는 거였다면 제가 수정한 코드는 괜히 수정한 거네요. 이미 원래 소스에 이런 경우를 생각해서 스프라이트에서 프레임 플레이 시간을 각각 다르게 해 준건데......

어쨌든 남의 소스를 보고 나름대로 고쳐보는게 프로그래밍 배우는데 아주 큰 도움이 됩니다.

여러분들도 이 코드를 이용해서 이것저것 고치거나 기능을 덧붙이거나 하면 빨리 실력이 늘고 이 코드도 진짜로 여러분의 코드가 되고 여러분의 자산이 될 거예요.

그럼 다음 시간에 뵐께요......
반응형

Comment

  1. 익명 2011.11.28 19:10

    비밀댓글입니다

    • 솔웅 2011.11.29 02:49 신고

      Sample 은 이 강좌에 있는 dice.zip을 받으시면 그 안에 sprite로 사용한 주사위 이미지가 있습니다.
      그리고 코로나 SDK 다운 받은 폴더로 가시면 두개의 샘플 앱이 있습니다.
      sample-sprite 폴더에 있을 겁니다. 아마...
      하나는 흰 말이 뛰어가는 앱이고 다른 하나는 사람이 퓨마에 쫒기는 앱입니다.
      그 폴더 아래에 소스코드와 이미지들 모두 있으니 참조하세요. 연습하시려면 그 이미지만 따로 가져와서 직접 만들어 보셔도 될 것 같네요...

ui 버튼이용 하기 ui.newButton

2011. 11. 18. 06:52 | Posted by 솔웅



오늘은 ui.lua 클래스 파일을 이용해서 이미지 버튼을 만들겠습니다.

아래 파일을 먼저 받으세요.

이 파일을 열어서 분석해 보시면 newButton과 newLable을 이용할 수 있도록 만든 부분을 보실 수 있으실 거예요.




버튼을 만들면 이렇게 나옵니다.

소스를 볼까요?

display.setStatusBar (display.HiddenStatusBar)

require "ui"

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

peachbutton = ui.newButton{
default = "peach.png",
over = "peach2.png",
x = 80,
y = 200,
}

local textFieldButtonPress = function( event )
    print("Button Pressed")
end

txtFieldButton = ui.newButton{
        default = "peach.png",
        over = "peach2.png",
        onPress = textFieldButtonPress,
        text = "textField",
        x = 240,
        y = 200,
        size = 16,
        emboss = true
}

local titleLabel = ui.newLabel{
        bounds = { 15, 5, 290, 55 },
        text = "Native Display Objects",
        font = native.systemFontBold,
        textColor = { 240, 240, 90, 255 },
        size = 24,
        align = "center"
}

저 위에 이미지에 newLabel을 하나 추가했어요.

사용법은 간단합니다.
우선 ui.lua를 main.lua 파일이 있는 폴더에 같이 넣구요.
require "ui"를 합니다.

코드를 보시면 우선 백그라운드 이미지를 깔았고
첫번째 이미지 버튼을 만들었습니다.
이 버튼의 평상시 상태는 default로 설정된 peach.png 이미지가 나옵니다.
그리고 이 버튼을 누르면 peach2.png 이미지로 바뀌게 됩니다.

그리고 textFieldButtonPress 라는 함수를 만들었는데 이건 두번째 버튼을 클릭하면 호출되는 함수 입니다.

두번째 이미지 버튼은 첫번째 이미지 버튼에 없는게 몇개 있습니다.
우선 onPress = 는 눌렀을 때 함수를 호출합니다.
그리고 text = 버튼 위에 텍스트를 넣을 수 있구요.
size는 텍스트 사이즈 입니다.

이 두번째 이미지를 누르면 textFieldButtonPress 함수가 실행 됩니다.

그리고 마지막 번엔 newLabel 을 넣었습니다.
처음에 bounds ={} 를  이해하려면 ui.lua의 181번째 줄을 보시면 됩니다.

        local bounds = params.bounds
        local left = bounds[1]
        local top = bounds[2]
        local width = bounds[3]
        local height = bounds[4]

이렇게 돼 있는데요. 4개의 인수들이 있고 각각 왼쪽 위 너비 높이를 나타냅니다.

이렇게 ui.lua를 이용해서 이미지 버튼과 이미지 레이블을 사용하는 법을 알아봤습니다.

전체 소스 파일은 위에서 받으 실 수 있습니다.

코로나에서는 이렇게 ui.lua를 사용하지 않고 Widget API에서 버튼 기능을 제공합니다.
하지만 이것은 Corona build 2011.646 이후 버전에서만 가능합니다.

미리 한번 살펴 보면 신택스는 아래와 같습니다.
widget.newButton( [options] )
샘플예제를 보면요.
local widget = require "widget"
 
    local onButtonEvent = function (event )
        if event.phase == "release" then
            print( "You pressed and released a button!" )
        end
    end
 
    local myButton = widget.newButton{
        id = "btn001",
        left = 100,
        top = 200,
        label = "Widget Button",
        width = 150, height = 28,
        cornerRadius = 8,
        onEvent = onButtonEvent
    }
 
    -- Insert button into a group:
    someGroup:insert( myButton.view )
 
    -- Change the button's label text:
    myButton:setLabel( "My Button" )
 
    -- Remove the button
    display.remove( myButton )

widget을 rerquire 하구요. (이 widget은 ui.lua처럼 별도의 파일이 아니라 corona SDK 가 자체적으로 제공하는 겁니다. 물론 646 이후 버전에서요. 그러니까 별도의 파일을 구해서 폴더에 넣을 필요가 없습니다.)

widget.newButton{} 으로 구현합니다.
안에 내용은 id,left,top,lable,width 뭐 이런것들이 있구요.
아주 다양한 파라미터들이 있습니다.
이건 나중에 이 기능을 제가 사용할 수 있을 때 자세히 살펴 보겠습니다.

onEvent = 에 클릭했을 때 실행될 함수를 넣습니다.

아직 안정성이 검증되지 않은 코로나 SDK 버전에 있는 내용입니다.
지금은 Build 2011.591 버전이 공식적으로 배포되고 있습니다.

나중에 646 버전이 공식 인증된 공개 버전이 되고 제가 그 버전을 사용하게 되면 좀 더 자세히 정리해 볼까 합니다.

그럼 즐거운 주말 되세요.
반응형

Comment



오늘은 timer.performWithDelay를 이용해서 시간을 표시하는 소스를 보겠습니다.
일단 1초 단위로 화면을 갱신해 보일 겁니다.

이 소스를 조금 더 손 보시면 스탑와치도 가능 할 거예요.


소스를 볼까요?

display.setStatusBar(display.HiddenStatusBar)

_W = display.contentWidth;
_H = display.contentHeight;

local bg = display.newRect( 0, 0, 320, 480 )

local secsText = 00
local minsText = 0

local timeText = display.newText(minsText.. ":0" ..secsText, _W/2-60, _H/2-60, "Helvetica", 60)
timeText:setTextColor(43,100,154)

local function updateTime (event)
secsText = secsText + 1

if secsText < 10 then
secsText = "0" ..secsText
elseif secsText > 59 then
secsText = 00
minsText = minsText+1
end
timeText.text = minsText .. ":" ..secsText
end
timer.performWithDelay(1000, updateTime, 0)

처음 세 줄은 설명을 건너 뛰겠습니다.

bg 변수에 스크린 크기만한 사각형을 만듭니다.
(칼라가 지정 안 됐으니 디폴트 색인 흰색이 그려질 겁니다.)
그리고 초를 담을 변수와 분을 담을 변수를 만듭니다.

그리고 시간을 표시할 text를 만들고 칼라를 지정합니다.

그 다음에 updateTime 함수가 있습니다
파라미터로 event를 받는 걸로 봐서 어떤 이벤트 리스너에서 호출 할 겁니다.

일단 함수 내용을 볼까요?

위에 초를 담을 변수 secsText에 1을 더합니다.
그러니까 이 함수를 호출할 때마다 1씩 더해 지겠네요.

다음에 secsText가 10 이하이면 sectText 앞에 0을 붙입니다.
즉 secsText가 1 이면 여기서 01로 변환이 됩니다.
그리고 secsText가 59보다 크면 값을 00으로 바꾸고
minsText에 1을 더합니다.

그리고 timeText.text에 minsText:secsText 값을 할당합니다.

여기까지가 함수 updateTime의 내용입니다.

마지막 줄은 이 함수를 1초 단위로 부르는 timer 입니다.

이 소스는 9분 59초까지 1초 단위로 계속 올라가겠네요.
10분이 되면 다시 0:00이 되겠구요.

이 코드를 조금 변환하면 밀리세컨드부터 시간단위까지 나오게 할 수 있겠죠?
버튼을 만들어서 이 버튼을 누르면 updateTime을 불러오는 timer를 멈추게 하고

뭐 이런 기능들을 넣으면 stopWatch 앱이 될 겁니다.

직접 해 보시면 많이 도움이 되실 거예요.

그럼...
반응형

Comment


오늘은 버튼을 누르면 다음 화면이 나오는 기능을 알아보겠습니다.

기본적으로 이 기능은 API 내에 있는 Animation - transitions 를 사용합니다.
(저는 실제 앱 개발에서는 director.lua 클래스를 사용하고 있지만 이 클래스도 기본적으로 transitions 기능으로 구성 돼 있습니다.  director.lua 클래스 사용법도 조만간 정리할 예정입니다.)

transitions에는 아래와 같은 네가지 메소드가 있습니다.
transition.to() : handle = transition.to( target, params )
예) transition.to( square, { time=1500, alpha=0, x=(w-50), y=(h-50), onComplete=listener1 } )

transition.from() : handle = transition.from( target, params )
예) transition.from( square, { time=1500, x=(w-50), y=(h-50), onComplete=listener } )

transition.dissolve() : transition.dissolve( src, dst, duration, delayDuration )

transition.cancel() : transition.cancel(tween)
예) local trans1 = transition.to(currentTarget, { time=400, y=upperLimit, transition=easing.outQuad}) 
transition.cancel(trans1)




위 그림은 화면을 클릭하면 다음 화면으로 바뀌는 앱입니다.
압축파일은 아래에 있습니다.


소스코드를 분석해 보겠습니다.

-- Views Handler App
-- Developed by Carlos Yanez
-- Hide Status Bar
display.setStatusBar(display.HiddenStatusBar)
-- Add Default View
defaultView = display.newImage('defaultView.png')
-- Swap View Function
lastView = {}
local function swapViews(current, new, from)
    lastView = current
    if(from == 'down') then
        transition.from(new, {y = new.height * 2, onComplete = removeLastView})
    elseif(from == 'up') then
        transition.from(new, {y = -new.height, onComplete = removeLastView})
    elseif(from == 'left') then
        transition.from(new, {x = -new.width * 2, onComplete = removeLastView})
    elseif(from == 'right') then
        transition.from(new, {x = new.width * 2, onComplete = removeLastView})
    end
end

-- Remove Last View
removeLastView = {}
function removeLastView()
    lastView:removeSelf()
end

-- Change View Event Function
local modes = {'up', 'down', 'left', 'right'}
local changeView = {}
local changed = false
function changeView:tap(e)
    if(changed == false) then
        secondView = display.newGroup()
        bg = display.newImage('secondViewBg.png')
        button = display.newImage('button.png', 40, 223)

        secondView.insert(secondView, bg)
        secondView.insert(secondView, button)
        secondView:addEventListener('tap', changeView)
       
        swapViews(defaultView, secondView, modes[math.random(1, 4)])
        changed = true
    else
        defaultView = display.newImage('defaultView.png')
        defaultView:addEventListener('tap', changeView)
       
        swapViews(secondView, defaultView, modes[math.random(1, 4)])
        changed = false
    end
end

-- Listener
defaultView:addEventListener('tap', changeView)
 
처음에 statusBar 없애고 defaultView.png를 display합니다.
그리고 lastView 테이블을 선언합니다.

다음엔 swapViews함수가 있습니다. 이 함수는 current,new,from 이렇게 세 인자값을 받습니다.
함수 안에서 하는 일은 아래와 같습니다.
1. 아까 선언했던 lastView 테이블에 current를 넣습니다.
2. from이 down,up,left,right인지 체크가호 그에 따라 new의 좌표를 바꿔줍니다.
    transition이 끝나면 removeLastView 함수를 실행합니다.

다음은 removeLastView 테이블을 선언합니다.
removeLastView함수가 있고 이 함수는 lastView:removeSelf() 를 수행합니다.

다음 단계를 보면요
modes 테이블에 up,down,left,right를 넣습니다.
changeView 테이블을 선언하고 changed를 false로 선언합니다.

다음엔 changeView함수가 있습니다. tap 이벤트 리스너에서 호출될 함수입니다.
이 함수에서 하는 일은 다음과 같습니다.
1. changed가 false이면 secondView 라는 새 그룹을 만들고 secondViewBG.png를 display합니다.
2. button.png를 적당한 위치에 display합니다.
3. secondView그룹에 bg와 button을 insert합니다.
4. secondView그룹에 tab이벤트 리스너를 달고 탭이 이뤄지면 changeView를 호출합니다.
5. swapViews를 호출합니다. 인자로는 defaultView,secondView,modes 중 랜덤하게 선택된 값을 넘겨 줍니다.
6. changed=true로 바꿉니다.
7. changed가 false가 아니면 defaultView에 defaultView.png를 넣습니다.
8. tap 이벤트 리스너를 달고 탭이 일어나면 changeView를 호출합니다.
9. swapViews 함수를 호출하고 4개 인자를 전달합니다.
10. changed를 false로 선언합니다.

그리고 맨 마지막 줄엔 defaultView에 탭 이벤트 리스너를 달고 탭 이벤트가 일어나면 changeView 함수를 호출합니다.

이렇게 하면 버튼을 누를 때마다 화면이 전환되는 효과를 줄 수 있습니다.
화면전환도 위,아래,좌,우 측으로 화면이 이동하면서 바뀝니다.

보시면 아시겠지만 실제로는 다른 화면이 두개 있는 것이 아니라 객체들의 위치를 바꾸면서 화면 전환 효과를 주는 겁니다.

안드로이드의 Activity개념과는 다릅니다.

이 transition기능을 이용해서 다양한 화면 이동 효과를 내 보세요.
반응형

Comment


앱을 사용하다 일정 시간 동안 사용하지 않으면 핸드폰 화면이 black이 됩니다.

오랫동안 사용하지 않아도 화면이 계속 켜져 있는 상태로 둘 수 있는 방법이 있습니다.
system.setIdleTimer( enabled )

디폴트는 true입니다.
system.setIdleTimer( true )

true일 경우 이 idle timer가 작동을 하고 오랫동안 사용이 없으면 스크린이 흐려졌다가 sleep상태로 바뀝니다.

system.setIdleTimer( false )
위와 같이 false로 하면 오랫동안 사용하지 않아도 해당 앱이 계속 켜져있게 됩니다.
(맥이나 윈도우 시뮬레이터에서는 작동하지 않습니다.)

아마 앱을 tilt로 동작시키고 touch는 일어날 필요가 없는 앱이라면 이 기능이 반드시 필요할 수도 있겟네요.

많이 필요한 기능은 아니지만 알아두면 쓸모가 있겠죠?
반응형

Comment


요즘 만드는 앱에 다양한 애니메이션 효과를 사용하고 있습니다.
스피드 콘트롤 할 일이 많아서 찾아봤는데요.

mivieclip은 frame base라 근본적으로 frame 속도를 바꾸지 않는한 speed control 이 힘듭니다. (Sprites는 time base랍니다.)

http://blog.anscamobile.com/2010/06/improved-movieclip-library/ 에 있는 speed control 관련 내용을 볼께요.

하찮은 내용일 수도 있겠지만 코로나로 앱을 개발하면서 꼭 알아두어야 할, 그러지 않으면 반드시 한번은 에너지 낭비를 해야 될 그런 내용이더라구요.
한번쯤 정리해 두는것도 좋을 것 같습니다.

Richard HakinJune 8th, 2010 at 2:59 pm

We desperately need a speed control as the animation just play crazy

앱을 쌈박하게 만들기 위해서는 애니메이션 스피드 조절 기능이 완전 필요합니다.

PaulAugust 3rd, 2010 at 4:41 am

What is the best way to control the speed of the animation?

Would it be a case of using a timer and calling something like

myAnim:nextFrame()

애니메이션의 스피드 조절하는 가장 좋은 방법이 뭐지요?

myAnim:nextFrame() 함수를 타이머를 이용해서 부르는 것이 방법이 될까요?

EvanAugust 3rd, 2010 at 11:21 am

@Paul – your suggestion would probably work well in some cases, although if you had more than a few clips onscreen, you’d want to test it on target devices to see the performance. I don’t think timers are necessarily that expensive, but they might add up if you had a lot of functions constantly polling the system time.

The movieclip library is primarily designed for a Flash-like model, in which there’s a global framerate for everything. For greater timing control, or for complex animation cases, we’d recommend using Game Edition and the sprite-sheet feature.

In addition to using texture memory much more efficiently (one big image rather than lots of little ones), that feature includes an animated-sprite API that lets you set different animation speeds for different sprites, or even for different sequences within the same sprite. Also, it’s a time-based API rather than frame-based, so the total animation time will remain the same even on slower devices that need to drop frames — the engine automatically handles all this under the hood.

@Paul - 당신의 방법이 잘 적용되는 케이스도 있을 거예요. 아마 무비크립이 적거나 특정한 디바이스에서만 테스트를 원한다면 더 그럴거예요. 하지만 내 생각엔 타이머는 앱을 좀 헤비하게 만들것 같네요.

무비클립은 플래쉬를 모델로 디자인 된 겁니다. 기본적으로 전체 frame rate가 있고 이것이 모든 무비클립에 동일하게 적용됩니다. 좀 더 타이밍 콘트롤을 하고 싶거나 복잡한 애니메이션을 원한다면 Game Edition과 Sprite sheet를 사용하기를 권장합니다.

더군다나 sprite API는 메모리도 효율적으로 운용할 수 있어요. 그리고 각 애니메이션 마다 다른 스피드를 줄 수도 있구요. 스프라이트 애니메이션은 time base 입니다. movieclip은 프레임 베이스이구요. 그래서 사양이 낮은 기계에서도 각 애니메이션별 스피드 차이는 동일하게 나타날 겁니다.

TomAugust 16th, 2011 at 8:27 am

@Chan, You should use the Sprite library instead of the Movieclip library because it gives you better control over the animations. You do have a onComplete listener with Sprites.

@Chan, 무비클립 라이브러리 보다는 스프라이트 라이브러리를 이용해야 애니메이션의 속도 컨트롤이 더 수월합니다. 그리고 스프라이트에는 onComplete리스너도 있습니다.

=======  o ===== o ===== o ====== o ====== o ====== o ======

자 이렇게 애니메이션 기능의 스피드 조절을 위해서는 MovieClip보다는 스프라이트를 사용하라는 것이 코로나SDK의 기본 입장입니다.

그런데 Michael Hartlef 라는 개발자가 movieclip.lua를 수정해서 animation speed를 control 할 수 있는 방법을 개발해서 공유하고 있습니다.

 http://developer.anscamobile.com/code/modified-moviecliplua

위 싸이트에 가면 그 소스 코드가 있습니다.

따로 lua파일을 올리니까 이걸 다운 받으셔도 됩니다.

이 파일의 91번째 줄 에 있는 animSpeed =1.0 의 값을 바꾸시면 애니메이션의 속도를 조절 할 수 있습니다.

이것도 movieClip이기 때문에 framebase이고 이 스피드는 모든 애니메이션에 동일하게 적용됩니다.

애니메이션마다 각각 다른 스피드 컨트롤을 해야 된다면 여전히 movieClip보다는 Sprite를 사용하는 것이 좋습니다.

하지만 Michael Hartlef 이 발견한 방법도 아주 유용하네요.
저도 지금 개발하고 있는 앱에 Michael Hartlef이 개발한 방법을 사용하려고 합니다.

위 소스파일 중에 -- added by M.Hartlef June 12th, 2010 이렇게 주석이 달린 부분이 Hartlef가 추가한 부분입니다.

필요하신분은 이 코드 사용하면서 무료로 자기 노력의 결과물을 공유한 Hartlef에게 감사하는 마음을 가지셔야겠죠?

In everything, Give thanks.

그럼
반응형

Comment


오늘 다룰 예제는 작은 원을 드래그해서 큰 원에 닿으면 작은 원의 색을 바뀌게 하는 기능 입니다.

기본적으로 Physics와 Collision 이벤트 리스너를 이용합니다.
그리고 addBody에서 isSensor 기능을 이용하게 됩니다.


이 isSensor 기능이 오늘 보실 새로운 기능입니다. 그리고 두번째 예제에서 preCollision 이벤트도 덤으로 보시겠습니다.


결과 화면을 먼저 보시고 소스코드를 보겠습니다.



display.setStatusBar(display.HiddenStatusBar)

require ( "physics" )
physics.start()
physics.setGravity( 0, 0 )
local hero = display.newCircle( 40, 40, 30 )
hero.name = "hero"
physics.addBody(hero, "dynamic")

local function moveHero (event)
hero.x = event.x
hero.y = event.y
end
hero:addEventListener("touch", moveHero)

local colorSensor = display.newCircle( 160, 240, 80 )
physics.addBody(colorSensor, {isSensor = true})
colorSensor.alpha = 0.2

colorSensor:addEventListener("collision", colorSensor)
function colorSensor:collision (event)
if event.other.name == "hero" and event.phase == "began" then
red = math.random(0,255)
green = math.random(0,255)
blue = math.random(0,255)
hero:setFillColor(red, green, blue)
end
end

소스를 보시면 우선 status바를 없애고 physics를 require하고 중력을 0으로 합니다.
나중에 원 두개 다 dynamic으로 addBody를 할 거니까 중력을 0으로 하지 않으면 밑으로 떨어지겠죠?

그리고 hero라는 이름으로 작은 원을 만들고 name을 정해주고 dynamic으로 addBody를 해 줍니다.

이 작은 원에 touch 리스너 이벤트를 달아서 touch 한 지점에 위치하게 됩니다.
그러면 손가락으로 잡고 끌면 따라 오겠네요.

다음엔 큰 원을 만들고 addBody에서 isSensor = true로 선언합니다.

이 큰 원에 collision이벤트를 줍니다.
이 이벤트에서 불러오는 함수는 (colorSensor) event.other.name이 hero이고 이벤트가 시작 됐을 때 hero.setFillColor로 작은 원의 색을 바꿔 줍니다.

간단하죠?

여기서 isSensor를 false 로 하면 어떻게 될까요?
그러면 둘 다 dynamic으로 addBody를 했기 때문에 둘이 부딪히면 큰 원이 밀려 날 겁니다.

isSensor를 true로 한다는 얘기는 physics 객체가 와서 부딪혀도 밀려나지않고 제자리에서 센서의 역할만 한다는 의미입니다.

한가지만 더 볼까요?

이번 예제는 노란 사각형이 위에서 떨어지는데 첫번째 흰 줄은 그냥 통과하고 두번째 흰 줄에서 걸리는 프로그램 입니다.

local physics = require("physics")
physics.start()
display.setStatusBar( display.HiddenStatusBar )
 
local platform1 = display.newRect( 20, 200, 280, 15 )
platform1.myName = "platform1"
physics.addBody(platform1, "static")
 
local platform2 = display.newRect( 20, 320, 280, 15 )
platform2.myName = "platform2"
physics.addBody(platform2, "static")
 
local box = display.newRect( 20, 20, 50, 50 )
box:setFillColor(255,255,0,255)
box.myName = "box"
physics.addBody(box)
 
local function onLocalPreCollision( self, event )
        -- Let box pass through platform 1
        local platform = event.other
        if platform.myName == "platform1" then
                platform.isSensor = true
        end
end
 
box.preCollision = onLocalPreCollision
box:addEventListener( "preCollision", box )

첫번째 세줄은 그냥 넘어가겠습니다.
다음은 두개의 긴 사각형을 만들고 모두 static으로 선언합니다.
그리고 각각 myName에 이름을 할당합니다.

그리고 노란 정사각형을 만들로 addBody를 합니다. 디폴트로 dynamic이 선언됩니다.

여기에 노란 정사각형에 preCollision 이벤트를 달로 이 preCollision은 onLocalPreCollision 함수를 콜 합니다.

preCollision은 충돌하기 직전에 호출 되는 이벤트 입니다.
그러니까 onLocalPreCollision은 충돌되기 직전에 이것이 platform1 이면 isSensor를 true로 바꿔서 그냥 통과 시켜 버리게 됩니다.

오늘 배운 기능도 꽤 유용한 기능이네요.

잘 알아두면 게임 만들 때 아주 요긴하게 쓰일 것 같습니다.

그럼 다음 시간에 또 뵐께요.

반응형

Comment

SlideView 구현하기

2011. 11. 4. 06:36 | Posted by 솔웅


어떤 분이 질문을 하나 올려 주셨습니다.
비밀 댓글로 올려 주신걸로 봐서 구체적인 내용이 알려지길 원하시지는 않는 것 같네요.
그 분의 질문을 보면 SlideView 구현에 대해 알면 해결 될 수 있을 것 같습니다.
그래서 오늘은 SlideView에 대해서 알아 보도록 하겠습니다.


이 이미지는 Corona SDK를 깔면 기본으로 제공되는 샘플 코드 입니다.
사진을 좌우로 움직일수 있고 Terminal에 보면 현재 사진이 몇번 째 인지 그리고 dragDistance는 얼마인지 그리고 총 몇개의 이미지 중 몇번째인지 등의 정보가 나옵니다.

코로나 SDK에서 제공되는 샘플을 보면 원하는 기능을 생각보다 쉽게 구현하는 방법을 알 수 있습니다. 적극적으로 활용하기를 바랍니다.

일단 SlideView를 쉽게 구현하도록 코로나에서는 따로 클래스 파일을 제공합니다.

이 소스 코드도 열어서 분석 해 보시면 그렇게 어렵지는 않습니다.
그건 각자 해 보시고 오늘 글에서는 이 기능을 이용하는 방법을 살펴 보겠습니다.
일단 소스를 보겠습니다.

display.setStatusBar( display.HiddenStatusBar )

local slideView = require("slideView")
   
local myImages = {
    "myPhotos1.png",
    "myPhotos2.png",
    "myPhotos3.png",
    "myPhotos4.png"
}       

--slideView.new( myImages )
--slideView.new( myImages, "bg.png" )
slideView.new( myImages, nil, 40, 60 )

별거 없습니다. 이렇게 단 몇줄로 slideView 기능이 구현 됐습니다.
첫줄은 statusBar를 없애는 것이고
그 다음 줄이 slideView.lua를 이용하기 위해서 require 한 겁니다.
그리고 slideView에 넣을 이미지를 테이블(배열) 에 넣구요.
slideView.new()를 해 주시면 됩니다.
slideView.lua의 36번째 줄을 보시면
function new(imageSet,slideBackground,top,bottom) 으로 함수가 시작합니다.
전달하는 파라미터를 이미지세트와 백그라운드 이미지 그리고 top과 bottom값을 전달 할 수 있습니다.
아까 terminal에 뿌려줬던 텍스트들은 main.lua에 있지 않고 slideView.lua 파일 안에 있네요.
전체 소스 파일은 아래 파일을 다운 받으세요.


다음 예제를 소개해 드리겠습니다.


코로나 샘플 소스코드와 똑 같습니다. 다만 다른 점이 있다면 이미지세트를 불러오는게 아니라 다른 별도의 파일들을 불러오는 겁니다.
display.setStatusBar( display.HiddenStatusBar )

local slideView = require("slideView")
   
local mySlides = {
    "screen1",
    "screen2",
    "screen3",
}       

slideView.new( mySlides )

이것도 소스는 간단합니다. 단지 mySlides라는 테이블(배열)에 파일 이름이 들어가 있습니다.
대표로 screen1을 보시겠습니다.

module(..., package.seeall)

function new()
    local g = display.newGroup()
   
    local background = display.newRect(0,display.screenOriginY, display.contentWidth, display.contentHeight-display.screenOriginY)
    background:setFillColor(222, 222, 222)
    g:insert(background)
   
    local message = display.newText("Screen 1", 0, 0, native.systemFontBold, 16)
    message:setTextColor(0, 0, 0)
    message.x = display.contentWidth*0.5
    message.y = display.contentHeight*0.5
    g:insert(message)
   
    function g:cleanUp()
        g:removeSelf()
    end
   
    return g
end

첫번째는 다른 file(main.lua) 에서 require하려면 설정해 줘야 되는 모듈 부분입니다.
그리고 function new() 함수가 있습니다.
g라는 그룹이 그 다음줄에서 정위 되었구요. 배경 화면으로 사용될 사각형이 그려졌습니다. 이 사각형의 변수 이름은 background이고 color가 정해지고 그룹에 insert됐습니다.
그 다음에는 Screen이라는 텍스트를 message라는 변수에 담아서 display합니다.

이 샘플 예제는 아래 파일을 참조하세요.


Slide View 관련해서 알아 봤는데요.

혹시 코로나로 개발하시거나 공부하시면서 질문 사항 있으시면 주저마시고 댓글로 올려 주세요.

저도 찾아 보면서 공부가 많이 되네요.

혹시 질문 올리신 분께 충분히 답변이 됐는지 모르겠습니다.
더 질문이 있으시면 댓글 달아 주세요.

그럼....
반응형

Comment

  1. 익명 2011.11.06 01:16

    비밀댓글입니다

    • 솔웅 2011.11.06 12:47 신고

      프로젝트 진행하시는게 정확히 코로나 SDK로 하시는게 아니고 루아로 하는건가요? 저는 코로나 SDK에서 제공하는 API 만 알고 순수 루아쪽은 잘 몰라요.
      리스트 앞에 숫자 넣는 것은 제 글 TIP 카테고리에 보시면 '코로나에서 Table View (List View) 표현하기' 를 보시면 좀 도움이 되지 않을까 싶네요. 그리고 코로나에서는 아직 Tab Bar를 지원하지는 않아요. 다만 시험판 버전에서는 지원이 되는데요. http://blog.anscamobile.com/2011/11/official-tab-bar-widget-now-available/ 를 보시면 되실겁니다. 근데 이건 Daily Build 2011.653 을 받아야 되는데 유료 회원만 가능할 겁니다.
      코로나 SDK 가 아니면 제가 도움을 드리긴 좀 힘 들것 같습니다. 제 이메일은 solkit2011@yahoo.com 입니다.

  2. 익명 2011.11.07 00:13

    비밀댓글입니다

    • 솔웅 2011.11.07 09:11 신고

      제가 앱스토어에 올린 Spin the Bottle 1 앱에서는 DB에서 데이터를 가져와서 data에 담았습니다. 그리고 callback = function( row ) 에서 row.필드이름 을 쓰면 해당 필드값을 넣을 수 있습니다.
      좋은 방법은 data 테이블에 넣을 때 두 값을 넣으면 됩니다.
      local data = {}
      for i=1, 5 do
      data[i] = {}
      data[i].number = "List item ".. i
      local c = math.modf(i/5)
      data[i].menu = "Category ".. c + 1
      end
      data[1].number = "1"
      data[1].menu = "Set Menu"
      data[2].number = "2"
      data[2].menu = "Main Menu"
      data[3].number = "3"
      data[3].menu = "Salad"
      data[4].number = "4"
      data[4].menu = "Dessert"
      data[5].number = "5"
      data[5].menu = "Drink"

      그리고 callback 에서 아래와 같이 합니다.

      callback=function(row)
      local g = display.newGroup()

      local t1 = display.newText(row.number, 0, 0, native.systemFontBold, 16)
      t1:setTextColor(0, 0, 0)
      t1.x = math.floor(t1.width/2) + 12
      t1.y = 46
      g:insert(t1)

      local t2 = display.newText(row.menu, 100, 0, native.systemFontBold, 16)
      t2:setTextColor(0, 0, 0)
      t2.x = math.floor(t2.width/2) + 12
      t2.y = 46
      g:insert(t2)
      return g

      이 방법은 제 글 중 코로나에서 Table View (List View) 표현하기 두번째 시간 를 보시면 나옵니다.

      그리고 님의 경우에는 아래와 같이 간단하게 처리 하셔도 되지 않을까요? 단지 번호만 넣으시려면..

      local data = {}
      data[1] = "1. Set Menu"
      data[2] = "2. Main Menu"
      data[3] = "3. Salad"
      data[4] = "4. Dessert"
      data[5] = "5. Drink"

      TAB관련 해서는 로그 파일을 잘 보세요.

      main.lua 22번째 줄에서 처음 에러가 나네요.
      currentScreen = require(newScreen).new()
      그리고 이건 listView에서만 나호 나머지 screen2,screen3에서는 안 나구요. 그러면 listView의 어떤 부분이 에러인지 하나하나 잘 살펴 보셔야 할 거예요.
      혹시 제가 볼 시간이 날 지 잘 몰라서 확실히 한번 해 보겠다는 얘기는 못하겠네요.

      listView에서 하나하나 주석 처리하고 print로 출력하고 하면서 에러 잡아 보세요..

      그럼...

    • 솔웅 2011.11.07 09:37 신고

      listView에서 나는 에러를 보니까요.
      모듈사용 문제가 있네요.
      코로나 SDK에서는 외부 파일을 사용할 때는 모듈이라는 명령어를 써야 하고 function new() ... end 를 사용 해야 합니다.
      listView.lua맨 위에 screen2에 있는 것처럼 아래 코드를 추가하세요.
      module(..., package.seeall)

      그리고 function new() .... end 로 전체 코드를 감싸주시구요.

      그 안에 그룹을 만들어 주세요. screen2.lua에서 처럼
      local g = display.newGroup() 를 만들어 주시고 각 객체들을 이 그룹에 insert시키시구요.
      g:insert(background) -> 이것 처럼요.
      그리고 new()함수 end하기 전에
      function g:cleanUp()
      g:removeSelf()
      end

      return g
      이것을 넣어 주시구요. 이 그룹을 return하셔야 합니다.

      그러면 listView 불러올 때 에러는 안 납니다.
      그리고 밑에 탭바가 안 나타나는 문제는 listview가 이 탭바를 가렸기 때문이예요. listView를 위로 올리시면 그 뒤에 있는 탭바가 보이실 거예요.
      이걸 항상 보이게 하시려면 아래 처럼 해 보세요.
      local bottomBoundary = display.screenOriginY + 50

      이렇게 하면 리스트뷰를 한번 올렸다 내리면 tab bar가 보이게 될거예요.

      아주 처음부터 끝까지 보이게 하는 방법도 있을 겁니다.
      그건 직접 찾아 보세요.

      그럼.. 수고 하세요..


지난번 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에 대해 자세히 알아 봤습니다
그럼 모두 즐거운 주말 되세요.

반응형

Comment

이전 1 ··· 3 4 5 6 7 8 다음