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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
간단하지만 유용한 팁들

1. 애니메이션 일시 정지 시키고 다시 시작 시키기

local logo = display.newImage( "endinggreen.png", 100, 140 )
logo.isPaused = true;
function logo:enterFrame(event)
        -- do something like make the logo bounce around the edges of the screen
        logo:rotate(-5)
end
 
Runtime:addEventListener( "enterFrame", logo );
 
function logo:tap( event )
        if (logo.isPaused) then -- initially nil which is false anyways
                Runtime:removeEventListener( "enterFrame", self )
                logo.isPaused=false;
        else
                Runtime:addEventListener( "enterFrame", self )
                logo.isPaused = true;
        end
        return true -- we handled the event so don't propagate
end
 
logo:addEventListener( "tap", logo )

위 코드는 앱을 실행하면 이미지가 회전하도록 만들었습니다.
1. 이미지 logo를 만들고 isPaused=true를 해 준 상태에서 Runtime 이벤트 리스너로 logo:enterFrame 함수를 실행시키비다.
2. logo:enterFrame 함수에서는 이미지를 rotate 시켜 줍니다.

그 다음은 이미지를 한번 tap하면 회전이 정지하고 다시 tap하면 회전하도록 합니다.
1. logo 이미지에 이벤트 리스너 tap을 달고 logo 함수를 호출함
2. logo:tap 함수에서는 isPaused 가 true이면 회전을 멈추고 isPaused를 false로 만들고
   만약 isPaused가 false이면 Runtime enterFrame 리스너를 다시 시작합니다.

이 소스 코드는 http://developer.anscamobile.com/content/application-programming-guide-common-design-tasks 에 있습니다.
그런데 이 코드로는 내가 기대한 대로 안 되서 약간 수정했으니 참고하세요.

2. Managing Screen

인트로 화면 - 메인 화면 - 게임 화면 등 각 스크린 별 이동이 있어야 할 때 Group object를 사용하면 좋습니다.
각 화면별로 그룹을 만들어서 다음 화면으로 넘어갈 때 transitions를 사용하면 화면 이동의 효과가 있습니다.
저의 경우는 이런 화면 이동을 쉽게 하도록 도와주는 director.lua를 require해서 사용하고 있습니다.
저는 단지 director.lua의 함수를 불러와 사용하기 때문에 transition 기능은 따로 코딩하지 않습니다.
director.lua 사용법은 나중에 따로 다루도록 하겠습니다.

3. 앱 시작 종료 시 데이터 저장하기

local path = system.pathForFile( "data.txt", system.DocumentsDirectory )
 
-- io.open opens a file at path. returns nil if no file found
local file = io.open( path, "r" )
if file then
   -- read all contents of file into a string
   local contents = file:read( "*a" )
   print( "Contents of " .. path .. "\n" .. contents )
   io.close( file )
else
   -- create file b/c it doesn't exist yet
   file = io.open( path, "w" )
   local numbers = {1,2,3,4,5,6,7,8,9}
   file:write( "Feed me data!\n", numbers[1], numbers[2], "\n" )
   for _,v in ipairs( numbers ) do file:write( v, " " ) end
   file:write( "\nNo more data\n" )
   io.close( file )
end



위 코드는 게임 등을 만들 때 점수나 기타 저장 할 사항들을 저장하는 방법을 보여줍니다.
적당한 시점에 데이터들을 파일로 저장하고 다음 앱을 실행 할 때 불러와서 이어서 할 수 있도록 할 수 있습니다.
Corona DOC 에 소개 돼 있는 테크닉이긴 한데 그리 특별한 테크닉은 아니네요.
데이터가 많을 경우는 SQLite를 사용해도 되겠죠?

위 코드를 활용해서 Runtime system 이벤트 리스너로 앱 시작할 때 혹은 앱이 종료할 때 데이터를 저장할 수 있습니다.

local function shouldResume()
        -- return true or false depending on whether we need to resume
end
 
local function onSystemEvent( event )
        if event.type == "applicationExit" then
                -- save stuff to disk
        elseif event.type == "applicationStart" then
                if shouldResume() then
                        -- load stuff off disk
                else
                        -- start app up normally
                end
        end
end
 
Runtime:addEventListener( "system", onSystemEvent );


system 이벤트에서 event.type 이 applicationExit일 때나 applicationStart 일 때 어떠한 행위를 하도록 핸들링 하시면 됩니다.


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

이상으로 총 36강에 걸쳐 Ansca Mobile의 Corona SDK 웹 사이트에 있는 매뉴얼인 Corona DOC 를 다뤘습니다.
원본은 http://developer.anscamobile.com/resources/docs 에 있습니다.

이제 기본 교과서를 뗀 셈입니다.
다음엔 참고서를 가지고 좀 더 실용적인 응용방법을 터득해야겠죠?

지금 제가 개발 하면서 얻은 TIP은 따로 카테고리를 만들어서 소개해 드리고 있습니다.
그리고 개발을 편리하게 해 주는 3rd Party 제품들 소개 및 사용법 알아보는 코너도 괜찮을 것 같구요.
이것 외에 Corona SDK 에서 제공하는 기본 샘플 예제 분석 이나 다른 공개된 예제 분석 카테고리를 별도로 만들까 합니다.
남들이 짜 놓은 코드를 분석하는 것도 많은 도움이 됩니다.


혹시 여러분 중에서도 분석을 원하시는 다른 샘플 코드가 있으면 보내주세요.
같이 분석해 보죠.

제 이메일은 solkit2011@yahoo.com 입니다.

그리고 코드 분석 외에 다른 카테고리를 만들어서 공부하면 좋겠다는 의견도 있으면 댓글에 달아 주세요.
그러면 제 공부하고 또 그 경험을 다른 많은 분들과 공유하는데 많은 도움이 될 것 같습니다.

아무쪼록 제 글이 여러분들에게 조금이나마 도움이 되기를 바랍니다.

다음에 다른 카테고리에서 만나뵙겠습니다.

감사합니다.

P.S. 이 Corona DOC 카테고리에 있는 강좌 중에도 질문이 있으시면 댓글에 달아주세요.
성실히 답변해 드릴꼐요...

반응형

Corona SDK 프로그래밍 테크닉 3

2011. 11. 10. 18:02 | Posted by 솔웅


반응형
코로나는 외부 라이브러리를 만들거나 로딩할 때 루아 모듈 기능을 사용 합니다. 코로나 SDK와 함께 제공되는 여러 라이브러리들이 있습니다. (예:ui.lua, sprite.lua)
그리고 별도로 파일로 제공되는 라이브러리들도 있습니다. (예:Button, Movieclip, direction 등등)
개발자가 별도로 자신만의 모듈을 만들어 낼 수도 있습니다.

외부 라이브러리 만들기
가장 쉬운 예제는 아래 샘플 코드처럼 하는 겁니다. 이렇게 module() 부분을 넣으면 main.lua에서 불러와서 사용할 수 있습니다. 이 파일의 이름은 확장자가 .lua이어야 합니다.  이 파일을 main.lua와 같은 폴더에 넣습니다. (하위나 상위 폴더에 넣으면 안 되더라구요.)

module(..., package.seeall)
 
-- Declare the functions you want in your module
function hello()
        print ("Hello, module")
end

파일의 확장자는 .lua라야 합니다.

외부 라이브러리 로딩 하기
외부 라이브러리를 로드하려면 require(module name)을 사용해야 합니다.
그러면 외부 모듈 파일 안에 있는 함수를 사용 할 수 있습니다.

-- Load external library (should be in the same folder as main.lua)
local testlib = require("testlib")
 
-- Now the functions in "testlib.lua" are available here:
 
-- call the testlib library function hello(). The "slow" way
testlib.hello()
 
-- cache same function, if you call more than once
local hello = testlib.hello
 
-- now all future invocations are "fast"
hello()

위 예제 코드는 testlib.lua 파일을 require하는 겁니다. main.lua 윗부분에 require 라인을 넣습니다. 그러면 testlib.lua파일 안에 있는 함수(클래스)를 사용할 수 있습니다.
사용하는 방법은 require한 변수.함수 이름 (testlib.hello) 형식으로 사용할 수 있습니다.

Syntax
module(name[,…])
모듈을 생성합니다. 모듈 안에 있는 함수를 불러올 때 그 함수는 글로벌 함수 이거나 글로벌 변수에 대입되거나 해야 합니다. 즉 외부에서 곧바로 로컬 변수나 함수를 불러올 수 없습니다. 다면 외부에서 부른 글로벌 변수에 로컬 함수나 변수를 대입된 경우 사용할 수 있습니다. (좀 헛갈릴 수도 있는데요. 샘플 예제를 보면 쉽게 이해 되실거예요. 샘플 예제는 아래 '외부 모듈 접근 방법 예' 를 보시면 나옵니다.)

require(modname)
해당 모듈을 로딩 합니다.

외부 모듈 접근 방법 예

한 파일(main.lua)에 모든 코드를 다 넣으면 너무 길어서 프로그램을 짠 프로그래머도 이해하기 어려워 질 겁니다. 이 경우 모듈을 이용해서 외부 파일을 만들고 단지 이를 불러오게 되면 코드의 가독성이 높아 질 겁니다.
또한 그 코드가 여러곳에서 여러번 불릴때는 더 많은 코딩의 절약을 할 수 있습니다.

모듈 사용의 한 예를 보여드리겠습니다.
level1.lua
module(..., package.seeall)
 
function loadLevel()

-- create a group for this level
local screenGroup = display.newGroup()

-- create some content for this level
local levelText = display.newText( "Level One", 20, 20, "Helvetica-Bold", 24 )

-- insert the text into this level's group
screenGroup:insert( levelText )

-- calling loadlevel will return the display group
return screenGroup
end
위 코드를 level1.lua로 저장하세요.

그리고 main.lua에 아래 코드를 넣으세요.

local levelGroup = require( "level1" ).loadLevel()
이러면 main.lua를 실행시키면 level1.lua의 loadLevel() 함수가 실행 됩니다.
main.lua안에 있는 코드는 아래와 내용이 같습니다.

local levelGroup = require("level1")
levelGroup.loadLevel()

아래 하나를 더 볼까요?

module(..., package.seeall)

function testFunction1()
    print( "Test 1" )
end

function testFunction2()
    print( "Test 2" )
end

function testFunction3()
    print( "Test 3" )
end

위 코드를 gamefunctions.lua로 저장을 하구요.

local examplemod = require "examplemodule"

examplemod.testFunction1() -- prints "Test 1" to terminal
examplemod.testFunction2() -- prints "Test 2" to terminal
examplemod.testFunction3() -- prints "Test 3" to terminal

이 코드를 main.lua에 저장하세요.

그러면 터미널에 gamefunctions.lua에 있는 세개의 함수 실행결과 Test 1 Test 2 Test 3 가 찍힐 겁니다.

그러면 gamefunctions.lua를 아래와 같이 바꿔보세요.

-- define a local table to store all references to functions/variables
local M = {}

-- functions are now local:
local testFunction1 = function()
    print( "Test 1" )
end
-- assign a reference to the above local function
M.testFunction1 = testFunction1

local testFunction2 = function()
    print( "Test 2" )
end
M.testFunction2 = testFunction2

local testFunction3 = function()
    print( "Test 3" )
end
M.testFunction3 = testFunction3

-- Finally, return the table to be used locally elsewhere
return M

이 코드를 실행 해 보면 결과값이 같을 겁니다.
그런데 이 코드에는 module(..., package.seeall) 가 없습니다.
이것 없이 모든 변수와 함수를 local로 선언해서 사용했습니다.

이렇게 모듈을 로컬로 선언해서 사용할 수 있으면 메모리 관리에 더 좋은 잇점이 있을 겁니다.

루아에서 module() 기능은 점점 중요도가 떨어질 거라는 얘기도 있습니다.

기본적인 모듈 사용법을 숙지 하신 후 실제 프로젝트에서는 나중에 제시한 방법을 사용하시면 좋을 것 같습니다. 혹시 이런 방법에 한계가 있다면 기본 모듈 사용법을 사용해야 할 수도 있으니까요.

Corona API TuneUP

Display library

Properties:
display.contentCenterX -- (equals 0.5*display.contentWidth)
화면의 x좌표 중앙입니다.
display.contentCenterY -- (equals 0.5*display.contentHeight)
화면의 y좌표 중앙입니다.
display.contentScaleX -- (the ratio between content pixel and screen pixel width)
전체 화면 대비 x좌표의 픽셀 비율입니다.
display.contentScaleY -- (the ratio between content pixel and screen pixel height)
전체 화면 대비 y좌표의 픽셀 비율입니다.

Functions:
display.setDefault( key, ... )
Sets default color values for fill, stroke, text, line. See Display Object Color Defaults (below) for details.
오브젝트를 채우는 디폴트 칼라 값을 지정합니다. 자세한 사항은 아래에 설명 됩니다.
display.newGroup( [child1 [, child2 [, child3 ... ]]] )
With no arguments, this will create an empty group and set parent to root (original behavior).
newGroup을 만들 때 사용합니다. 이 그룹은 여러 객체들을 하나의 객체처럼 관리하기 위해 사용 됩니다. 괄호 안에 값들이 없으면 empty group을 생성하게 됩니다. 이렇게 빈 그룹을 먼저 생성 한 후에 insert 할 수 있습니다.
Display Objects
Properties:
object.contentBounds -- (equivalent to object.stageBounds which is deprecated)
객체의 Bound 입니다. 이전 버전에서는 object.stageBounds였었습니다.
object.contentWidth -- (equivalent to object.stageWidth which is deprecated)
객체의 너비입니다. 이전 버전에서는 object.stageWidth 였었습니다.
object.contentHeight -- (equivalent to object.stageHeight which is deprecated)
객체의 높이 입니다. 이전 버전에서는 object.stageHeight였었습니다.

Object methods:
object:toFront() -- moves object to visual front of its parent group (object.parent)
객체를 소속된 그룹의 맨 앞으로 가져 옵니다.
object:toBack() -- moves object to visual back of its parent group (object.parent)
객체를 소속 된 그룹의 맨 뒤로 가져 갑니다.
object:localToContent( x, y ) -- maps x,y in object's local coordinates to content coordinates
로컬 좌표를 content 좌표로 바꿉니다.
object:contentToLocal( x, y ) -- maps x,y in content coordinates to object's local coordinates
content좌표를 local 좌표로 바꿉니다.

Display Object Color Defaults
조 앞에서 잠시 선보였던 겁니다. 여기서 좀 더 자세히 살펴 보겠습니다.
display.setDefault( key, ... )
괄호 안에 들어올 수 있는 키들은 아래와 같습니다.
    •    "fillColor" corresponds to the default fill color for vector objects. The default is initially white.
    •    벡터 객체를 위한 디폴트 fill color. 디폴트 값은 흰색입니다.
    •    "strokeColor" corresponds to the default stroke color for vector objects. The default is initially white.
    •    벡터 객체를 위한 stroke color. 디폴트 값은 흰색입니다.
    •    "lineColor" corresponds to the default line color for line objects. The default is initially white.
    •    라인 객체를 위한 디폴트 라인 칼라. 디폴트 값은 흰색 입니다.
    •    "textColor" corresponds to the default text color for text objects. The default is initially white.
    •    텍스트 객체를 위한 디폴트 텍스트 칼라. 디폴트 값은 흰색입니다.

위 값들은 굳이 바꿀 필요가 있나 하는 생각이 들지만 혹시 모르죠 개발 하다 보면 바꿔야 할 때가 있을지도요…

아래는 fillColor에 대한 예제입니다.
display.setDefault( "fillColor", gray )
display.setDefault( "fillColor", gray, alpha )
display.setDefault( "fillColor", red, green, blue )
display.setDefault( "fillColor", red, green, blue, alpha )
반응형

Corona SDK 프로그래밍 테크닉 2

2011. 11. 9. 00:01 | Posted by 솔웅


반응형
루아(Lua)에서 로컬 변수의 중요성

루아에서 가장 일반적으로 추천되는 것이 로컬화를 하라 입니다. 각각의 코드 블럭에 있는 변수를 로컬로 선언하는 것입니다. 퍼포먼스를 향상시키기 위해 아주 중요한 사항 입니다.
    myFirstName = "John"        -- global variable
    local myLastName = "Doe"    -- local variable

위 보기와 같이 변수 선언 할 때 앞에 local을 쓰면 로컬 변수이고 아무것도 쓰지 않으면 글로벌 변수 입니다.

아래 코드를 보세요.

  local myTable = { 1, 2, 3, 4, 5 }

    local printThird = function()
        print( myTable[1], myTable[2], myTable[3] )
    end

위 코드에서 myTable은 로컬입니다.
아래 코드는 이 코드와 무엇이 다를까요?

  local myTable = { 1, 2, 3, 4, 5 }

    local printThird = function()
        local t = myTable

        print( t[1], t[2], t[3] )
    end

함수 printThird 한에 t라는 로컬 변수를 선언하고 이 함수 밖의 로컬 변수인 myTable을 이 함수 안의 로컬 변수에 대입했습니다.
이것만 봐서는 크게 잇점은 없어 보입니다만 실제 앱 전체적으로는 긍정적인 효과를 줄 수 있습니다.

아래 코드를 보세요.

-- localize math function
    local mRandom = math.random

    local newObject = function()

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

        -- listener function
        function obj:enterFrame()

            -- speed up access to (already local) mRandom function
            local mRandom = mRandom

            -- do stuff...
            local rand1 = mRandom( 1, 10 )
            local rand2 = mRandom( 1, 10 )
            local rand3 = mRandom( 1, 10 )

            print( rand1, rand2, rand3 )
        end

        -- start the listener
        Runtime:addEventListener( "enterFrame", obj )

        return obj
    end

newObject();

이 코드는 rand1,rand2,rand3 의 세 랜덤 값들을 프레임마다 계속 터미널에 찍어 줍니다.



함수 밖에 local mRandom을 선언해 놓고 함수 안에서(두 번째 depth의 함수) local mRandom을 새로 만들고 이 변수에 함수 밖의 mRandom을 대입했죠? 사실 함수 안의 local mRandom = mRandom 부분을 없애도 프로그램은 제대로 돌아갈 겁니다.
하지만 이렇게 함수 안에서 다시 로컬로 변수를 선언하게 되면 퍼포먼스에 긍정적인 효과를 줍니다.
이런식으로 로컬 변수를 활용하는 습관을 들이면 프로그램 하실 때 많은 도움을 줄 겁니다.


Forward Referencing

항상 모든 것을 로컬 변수로 사용하는 것은 때로는 어려울 수 있습니다. 이럴 때 forward referencing 방법을 쓰시면 도움이 될 겁니다.

아래 코드를 보세요.

예제 1 에러코드
  local myFunction = function()
        callback()  -- ERROR: function 'callback' does not exist yet
    end

    local callback()
        print( "Hello World" )
    end

    myFunction()    -- will produce error (see above)

위 코드에서 myFunction안에서 callback()을 부를 때는 이 callback()함수가 존재하지 않습니다.
그렇기 때문에 에러가 날 겁니다.

예제 2 forward Referencing 사용한 코드

    local callback    -- forward reference (for later use)

    local myFunction = function()
        callback()    -- output: "Hello World"
    end

    -- function below is still local, because of forward reference
    callback = function()
        print( "Hello World" )
    end

    myFunction()    -- no error; happy code :-)

위 코드에서는 callback변수를 myFunction 이전에 로컬로 선언합니다.
그러면 myFunction에서 callback()함수를 호출 했을 때 이 변수를 따라가서 아래에 있는 callback()함수를 사용합니다.
이렇게 미리 변수만 로컬로 선언해 놓는 것을 forward reference라고 합니다.


Copies vs. Direct References

그럼 이 로컬 변수는 실제 선언된 값의 카피를 가지고 있는지 point를 해서 direct Reference를 하고 있는지 의문이 값니다.
정답은 루아에서는 경우에 따라 다릅니다.
테이블을 로컬화 시켰다면 이것은 실제 테이블을 direct reference 할 것입니다. 그 외에 단순히 숫자나 문자, 함수 등이라면 이것은 copy개념으로 이해하셔야 합니다.

    local myTable = { key1 = "original" }

    local testFunc = function()
        local t = myTable

        t.key1 = "modified"
    end

    testFunc()

    print( myTable.key1 )   -- output: modified

위의 코드에서 myTable은 실제 테이블의 reference를 사용합니다. 그러므로 testFunc()함수 안의 로컬 변수 t를 이용해서 값을 수정하면 실제 table의 값도 수정됩니다.



반대로 copy개념인 다른 숫자나 문자나 함수등은 이 경우 원래 함수의 값이 수정 되지는 않겠죠?

    local myNumber = 1
    local myString = 1
    local myFunction = function() print( "Hello World!" ); end

    local testFunc = function()
        local n = myNumber
        local s = myString
        local f = myFunction

        n = 2
        s = "2"
        f = nil
    end

    testFunc()

    print( myNumber, myString, tostring(myFunction) )

    -- OUTPUT:  1    1    function: 0x24a2e60

위 샘플 코드를 보세요.
결과 값을 보면 testFunc() 함수 안에서 선언한 n,s,f라는 로컬 변수에 다른 값을 대입시켰지만 이 함수 밖에서는 이 값이 바뀌지 않고 원래 값인 1,1이 출력 됩니다.



이 두 개의 차이를 잘 아셔야 실제 코딩에서 헛갈리지 않습니다. table만 direct reference를 합니다. 그러니까 Corona Display Objects 의 경우 테이블을 사용하니까 이것도 direct reference를 하겠죠? 고로 함수 안에서 로컬변수를 만들어서 Corona Display Objects를 바꾼 다면 함수 밖에서 최초에 선언했던 값도 바뀌겠죠?

Clearing the Reference

로컬 변수를 더 이상 사용하지 않을 때가 있을 겁니다. 또는 상위의 글로벌 값을 더 이상 사용하고 싶지 않을 때도 있구요. 이럴 경우 로컬 변수에 nil을 대입하면 됩니다.

    local myVar = myGlobal
    myVar = nil

이 경우 테이블의 경우는 어떻게 될 까요? 함수 안의 로컬 변수를 nil시키면 상위의 글로벌 테이블 변수의 값도 nil이 될까요?
정답은 그렇지 않다 입니다. 로컬을 nil시키면 이것은 로컬과 글로벌 변수와의 관계만 끊게 되기 때문에 상위의 글로벌 테이블 변수의 값까지 nil이 되지는 않습니다.

좀 헛갈리시나요?
이해가 되시면 잘 이해하세요. 프로그래밍할 때 아주 도움이 되실 겁니다.
잘 이해가 안 되신다면 일단 버릇을 들이세요. 나중에 이해할 때가 올겁니다.



오늘 까지는 프로그래밍 테크닉 중에서 퍼포먼스와 관련된 사항들을 알아 봤습니다.

다음 시간엔 외부 모듈과 패키지 사용법에 대해 알아 보겠습니다.
반응형