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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

New Game Center Request APIs

2012. 1. 29. 07:04 | Posted by 솔웅


반응형
얼마전에 Corona SDK의 Apple Game Center관련 새로 지원되는 기능에 대해 다뤘었습니다. (보시려면 여기를 누르세요.)

Corona는 거기에 덧붙여서 gameNetwork.request()에 몇개의 기능을 추가했습니다.
오늘은 이 추가된 기능에 대해서 살펴 보겠습니다.
이 기능을 테스트 하려면 Daily Build 2012.730 버전을 다운 받으셔야 합니다.


loadPlayerPhoto

이 Request는 유저의 게임센터 아바타를 로드할 겁니다. 그리고 이것을 display object로 생성할 겁니다.

gameNetwork.request( "loadPlayerPhoto",
{
        playerID = "G:0123456789",
        size="Small" -- "Small" or "Normal"
        listener=requestCallback
})

이 예제에서 보듯이 player의 아바타인 이 display object는 event.data(photo property)내의 requestCallback 함수를 통해서 접근할 수 있습니다.
이 request와 관련된 다른 중요한 property들이 더 있습니다.

그러므로 이 기능을 여러분의 앱에 사용하시기 이전에 업데이트된gameNetwork.request() documentation을 참조해 주세요.
(loadPlayerPhoto 부분을 참고 하시면 됩니다.)

    playerID (string)
    alias (string)
    isFriend (boolean)
    isAuthenticated (boolean)
    isUnderage (boolean)
    friends (array)
    photo (display object)


loadAchievementImage

이 기능은 loadPlayerPhoto request와 비슷합니다. 이미지를 로드하는 대신에 특정 achievement를 로드하는 것만 다릅니다.
이것은 achievement를 위해 여러분이 iTunes Connect에 올린 이미지를 가리킵니다. 그러니까 그 이미지가 코로나에서 지원하는 이미지에 합당해야 합니다. (24-bit PNG 파일이 가장 좋습니다.)

gameNetwork.request( "loadAchievementImage",
{
        achievementDescription=
        {
                identifier="com.appledts.twenty_taps"
        },
        listener=requestCallback
})

loadPlayerPhoto 처럼 이 request도 Corona display object를 생성합니다.
다만 이것은 event.data 테이블 내의 image 프로퍼티를 통해 접근합니다.
좀 더 자세한 사항을 보시려면 gameNetwork.request() API documentation의 loadAchievementImage부분을 봐 주세요.

title (string)
achievedDescription (string)
unachievedDescription (string)
maximumPoints (integer)
isHidden (boolean)
image (display object)

Placeholder Images
이 외에 소개될 2개의 기능은 애플의 complete/incomplete achievement image에 대한 것들입니다.
- loadPlaceholderCompletedAchievementImage
- loadIncompleteAchievementImage

이것은 Corona display object 형태로 애플에서 제공하는 achievement image(confirm/inconfirm)들을 리턴합니다.
만약에 유저가 자신의 이미지를 올리지 않았을 경우에 표시될 이미지들 입니다.
이 기능과 관련한 API는 loadAchievementImage와 같습니다. 다만 특정 achievement data를 명시하지 않는것만 다릅니다.
display 객체는 callback 리스너의 event.data 테이블의 image 프로퍼티를 통해서 접근될 수 있습니다.

Other Requests

loadFriends 같은 다른 request들도 있습니다. 이 request들도 위 request들을 사용하는 것과 같은 방식으로 코딩을 하시면 됩니다.  API documentation을 참조하세요.
아직까지 애플(iOS)의 게임센터 지원은 코로나 유료 사용자에만 한합니다.

이 글에 원본은 여기 가시면 보실 수 있습니다.

반응형

New Native Web and Video API’s 소개

2012. 1. 25. 07:07 | Posted by 솔웅


반응형
Using the New Native Web and Video API’s

native webView와 native video 객체와 관련한 새로운 API가 새로 나왔습니다.
이전의 Web Popupsvideo playback 의 한계를 많이 보완했습니다.

***** Web Views vs. Web Popups

이 두가지 콘셉은 상당히 비슷하지만 몇가지 다른 것이 있습니다.
- 한번에 한개 이상의 WebView를 가질 수 있음
- WebView는 다른 display객체들 처럼 회전시키거나 이동시킬 수 있다.
  (이 객체들이 native 객체들이기 때문에 아직까지는 group에 포함시킬수는 없음)
- phisics body를 적용할 수 있다.
- WebView는 반드시 확실하게(명시적으로) remove시켜야 함

***** Video Objects

Native 비디오 객체들은 그것을 계승한 media.playVideo보다 훨씬 유연합니다.

- 비디오 객체의 사이즈를 specify할 수 있다.(전체 스크린이 아니어도 된다.)
- 한 화면에 여러개의 VIDEO 객체들이 있을 수 있다.
- 비디오의 특정 위치를 프로그래밍으로 찾아서 사용할 수 있다.
- 프로그램에서 비디오를 play 시키고 pause 시킬 수 있다.
- 다른 display 객체들처럼 비디오 객체들도 움직이거나 회전시킬 수 있다.
  (이 객체들이 native 객체들이기 때문에 아직까지는 group에 포함시킬수는 없음)
- physics body를 적용할 수 있다.



***** native.newWebView() 사용법

신택스(syntax)는 아래와 같습니다.
native.newWebView( left, top, width, height [, listener] )

보다 자세한 사항은  API documentation 에 있습니다.

아래 예제가 있습니다.
local webView = native.newWebView( 0, 0, 320, 250 )
webView:request( "http://coronasdk.tistory.com" )

이 예제는 320*250 크기의 웹뷰를 생성하고 그 안에 http://coronasdk.tistory.com 를 display합니다.
request()함수 안에는 local HTML파일도 불러올 수 있습니다.

아래 예제는 웹뷰에 리스너를 단 예제입니다.

local function webListener( event )
    local url = event.url  -- reference to url being requested

    if event.errorCode then  -- is nil when there is no error
        native.showAlert( "Error!", event.errorMessage, { "OK" } )
    end
end

local webView = native.newWebView( 0, 0, 320, 250, webListener )
webView:request( "http://fakewebsitethatshouldthrowerror.com" )

보시면 webListener라는 리스너를 달아서 에러가 발생하면 alert화면을 띄워 줍니다.
이벤트의 파라미터로는 event.url, event.errorCode, event.errorMessage 가 있습니다.

아래 예제는 WebView를 회전(rotate) 시키는 예제 입니다.

local webView = native.newWebView( 0, 0, 100, 100 )
webView:request( "http://coronasdk.tistory.com/" )
webView.x = 200
webView.y = 240
webView.rotation = 45

-- let's close the webView
webView:removeSelf()
webView = nil

좀 더 자세한 사항은 API Documentation을 참조하세요.

그리고 샘플 코드는 /SampleCode/Interface/WebViewPhysics 에 있습니다.
(이 샘플 파일은 Corona build 2012.731 이후 버전에 있습니다.)

- 로컬 content를 load 할 때 약간의 버그가 있습니다. 이 버그는 조만간 수정될 예정입니다. -

***** native.newVideo()usage

native video object는 리스너가 없기 때문에 이해하가 더 쉽습니다.
비디오 객체를 생성하고 위치를 찾거나 이동시키거나 회전시키거나 physics를 적용하는 일만 하면 됩니다.
그리고 이 모든 일이 끝났으면 remove시키면 됩니다.

신택스는 아래와 같습니다.
native.newVideo( left, top, width, height )

보다 자세한 내용은 여기를 참조하세요.

아래 예제는 비디오 객체를 생성하고 비디오 파일을 로딩하고 특정 지점을 찾고 움직이고 회전시키고 그리고 최후에는 이 객체를 remove 시키는 예제 입니다.

local video = native.newVideo( 0, 0, 220, 275 )
 
-- load a video and jump to 0:30
video:load( "myvideo.m4v", system.DocumentsDirectory )
video:seek( 30 )
 
-- play video and move/rotate
video:play()
video.x = 50
video.y = 150
video.rotation = 90
 
-- pause the video and remove
video:pause()
video:removeSelf()
video = nil

예제 파일은  SampleCode/Interface/VideoViewPhysics 에서 볼 수 있습니다.
(이 샘플 파일은 Corona build 2012.731 이후 버전에 있습니다.)
보다 자세한 사항은 API documentation을 이용하세요.

위 내용 원본은 여기에 있습니다.
이 원본을 바탕으로 제가 간단히 요약 번역 했습니다.

native display들은 시뮬레이터에서 지원하지를 못합니다.
그리고 Corona build 2012.731 버전은 2012년 1월 24일 올라왔고 아직 유료 사용자에게 밖에 공개가 안 돼 있습니다.

다음에는 샘플코드를 분석해 보도록 하겠습니다.


반응형

Facebook Single Sign-On

2012. 1. 21. 00:46 | Posted by 솔웅


반응형
이 글은 Implementing Facebook Single Sing-On 으로 올라온 Corona SDK 튜토리얼을 번역한 글입니다.
이번주에 Corona SDK에서 새로 추가된 주요 기능에 대한 튜토리얼은 Game Center Integration, ScrollView Widget, Facebook Single Sign-On 이렇게 세가지 입니다.
오늘 facebook Single Sign-On을 다루면 모두 다 소화하고 가는 거네요.

Implementing Facebook Single Sign-On
Jonathan Beebe

Corona SDK 의 Facebook Single Sign-On 기능은 iOS와 Android 모바일 디바이스에서 가능합니다. (그리고 만들기에 안드로이드 쪽이 사용이 훨씬 간편합니다.)
이 기능은 Corona Build 2011.707 이후 버전에서만 지원 됩니다.
오늘 날짜 기준으로 Corona에서는 Build 2011.704를 무료 배포 버전으로 오픈했기 때문에 유료 구매자가 아니면 707 버전이 공개될 때까지 기다리셔야 됩니다.

***** Your Facebook App

우선 여러분의 앱을 Facebook에 등록해야 합니다.
그러면 여러분은 App ID를 받으실 수 있습니다. 그 이후에 앞으로 진행하는 일들을 하실 수 있습니다.
아래 링크를 따라가서 여러분의 앱을 등록 하세요.

https://developers.facebook.com/apps

저도 이 부분은 등록을 한적이 한번도 없어서요 자세히 설명을 드리지는 못하겠네요.

우선 위 링크로 가면 이 화면이 나옵니다. (facebook에 로그인을 안 했으면 로그인 하라는 화면부터 나올 겁니다.)



Create New App 버튼을 누르면 App Display Name 과 App Namespace를 넣으라는 화면이 나옵니다.

오른쪽에 ?에 마우스를 올려 놓으시면 그게 뭔지 설명이 나옵니다.

그리고 동의란을 체크하고 Continue버튼을 누릅니다.




다시한면 Security Check를 위한 화면이 나오고 해당 텍스트를 입력 한 다음에 submit을 누르면....




이렇게 핸드폰 번호나 신용카드로 인증과정을 사전에 거쳐야 다음 사항을 진행 할 수 있다고 나옵니다.
그 다음 과정은 진행해 보지 않았습니다.
지금 회사라서 주말에 집에서 차근차근 해 봐야겠습니다.

일단 이 과정들을 모두 거쳐서 햅이 등록됐다고 치고 다음 단계를 보겠습니다.


앱이 등록된 후 Native iOS App 이나 Native Android App를 체크해야 합니다.

iOS앱에서는 iOS Bundle ID가 Corona를 빌드한 provisioning profile과 같아야 합니다. 그리고 iPhone App Store ID 란에 여러분의 iTunes App ID를 넣어야 합니다. 여러분의 앱이 iTunesConnect 포탈에 추가 됐으면 여러분의 앱에 App Store ID가 부여 됐을 겁니다. (앱을 업로드하기 이전에도 받을 수 있습니다.)

아직 여러분의 앱이 iTunes App ID가 없다면 다른 iTunes App ID를 쓰셔도 됩니다. 하지만 나중에 앱이 ID를 받게 되면 곧바로 바꾸셔야 합니다.

제대로 된 bundle ID와 정확한 iTunes ID를 입력하는 것은 아주 중요합니다. 그렇지 않으면 여러분 앱에 Facebook을 implementation 하지 못합니다.

***** build.settings
이 부분은 iOS에만 해당 되는 것입니다. 안드로이드용 앱에는 build.settings에 아무것도 할 필요가 없습니다.

iOS는 CFBundleURLTypes키를 넣어야 합니다. 그 안에 CFBundleURLSchemes 키를 넣기 위해서요.
아래 샘플 코드가 있습니다.
settings = {
    iphone = {
        plist = {
            UIApplicationExitsOnSuspend = false,
            CFBundleURLTypes =
            {
                {
                    CFBundleURLSchemes =
                    {
                        "fbXXXXXXXXXXXXXX",
                    }
                }
            }
        }
    }
}
괄호가 너무 복잡하게 돼 있죠? 그냥 이거 복사해서 쓰세요.

디폴트로 UIAppLicationExitsOnSuspend는 기본으로 false로 세팅돼 있습니다.  그래서 이 부분을 명시하지 않아도 false로 세팅됩니다.
위에 굳이 false로 선언한 것은 만약 이것이 false가 아니면 작동하지 않기 때문에 그것을 remind하기 위해 넣었습니다.
"fbXXXXXXXXXXXXXX 부분에 X표시들은 여러분의 Facebook App ID를 넣으셔야 할 부분입니다.
이 CFBundleURLSchemes에 더 많은 URL schemes를 넣으실 수 있습니다.
CFBundleURLSchemes =
{
    "fb1234567890",
    "YourCustomScheme",
    "AndAnotherOne"
}

Test, test, test!
이렇게 세팅하면 facebook 싱글 사인 온 작업을 할 수 있습니다.
하지만 반드시 테스트를 해 보는건 기본이죠.

facebook.login()함수를 call했을 때 Facebook app이 디바이스에 인스톨 돼 있으면 그것이 나올 겁니다. 그리고나서 당신의 앱을 인증하라고 나올 겁니다. (아직 한번도 인증과정을 거치지 않았다면요)
facebook앱이 없다면 웹브라우저가 실행 될 겁니다. 그리고 유저가 로그인 과정을 다 거치면 다시 원래 앱으로 돌아 올 겁니다.
(이 과정에서 UIApplicationExitsOnSuspend 에 false로 선언되어 있어야 제대로 동작합니다.)

자 여기까지가 Facebook Single Sign-On 을 implemation하는 과정입니다.
facebook쪽 등록하는 부분하고 Corona SDK에서 iPhone인 경우 build.setting에 등록하는 부분이 좀 작업을 해야 하네요.
반면에 안드로이드 쪽은 Facebook쪽에만 등록을 시키면 그 다음엔 별다른 세팅 없이 진행 시킬 수 있나 봅니다.

여기까지 완료 됐으면 이제 facebook single sign on 을 할 기본 환경이 갖춰진 것이구요.
이제 여러분이 코딩하면서 facebook을 이용하면 됩니다.


여기를 클릭하시면 위와같이 Corona SDK의 facebook관련 API를 보실 수 있구요.


SampleCode-Networking-facebook으로 가시면 위와같은 샘플 앱이 있습니다.

이 부분은 나중에 자세히 다뤄 보겠습니다.

반응형

Game Center Integration (iOS)

2012. 1. 20. 06:26 | Posted by 솔웅


반응형
Tutorial: Game Center Integration (iOS)
Jonathan Beebe

iOS의 게임센터를 이용할 수 있는 기능이 daily build 2012.725에 포함 됐습니다.
이 iOS 의 Game Center를 어떻게 이용해야 하는지 살펴보겠습니다.
이번에 선보인 iOS Game Center 이용 기능은 기존의 gameNetwork API의 연장입니다.

iOS Game Center를 이용하기 위해 Apple side에서 어떤 일을 해야 하고 (iTunes Connect), 코로나에서는 어떤 셋업이 필요한지 그리고 여러분의 앱에서 이 기능을 제대로 이용하려면 어떻게 해야 되는지에 대해 간단히 알아보겠습니다.

Game Center는 다른 game networks(OpenFeint, Papaya)와 비교해서 좀 다른식으로 적용해야 합니다. build.settings와 System Event 파트를 다룰 때 이 부분을 이야기 하겠습니다.

***** iTunes Connect
Link: Visit the iTunes Connect Portal

Game Center를 이용하기 위해 Apple 쪽에서 해야 할 일에 대해서는 자세히 언급하지는 않겠습니다.
간단히 말해서 여러분은 여러분의 앱에서 Game Center 기능을 이용하기 위해 우선 Leaderboard(high score boards)와 Achievements 를 생성해야 합니다.
또한 개개의 Leaderboard와 Achievement ID도 할당해 줘야 합니다.

1. Manage Your Application 링크를 클릭


2. 여러분의 앱 아이콘을 클릭


3. 오른쪽에 있는 파란색의 Manage Game Center 버튼을 클릭



4. Enable 버튼을 클릭


5. Leaderboard 와 Achievements 부분에 있는 Edit(Set up) 을 클릭


6. Leaderboards와 Achievements를 추가


7. 각각의 Leaderboard ID와 Achievement ID



물론 여러분의 게임에 Achievements(혹은 Leaderboard)를 넣고 싶지 않다면 이 부분은 그냥 지나가셔도 됩니다.

***** build.settings

주목:이 파트는 여러분들이 Game Center를 이용하려고 하던지 OpenFeint와 Game Center를 동시에 이용하려고 하던지 어떤 경우에도 아주 중요합니다.
만약 여러분이 OpenFeint를 사용하지 않고 오직  Game Center만 사용하려고 한다면 코로나에 openfeint 컴포넌트를 앱에서 빼내 버리고 컴파일 하라고 알려줘야 합니다.

그 방법은 아래와 같이 components 테이블에 빈 대괄호만 넣어 주면 됩니다.
settings =
{
    iphone =
    {
        components = {}
    }
}

OpenFeint와 Game Center 를 모두 사용하시려면 openfeint 컴포넌트를 사용할 거라고 build.settings에서 확실히 알려 줘야 합니다.
settings =
{
    iphone =
    {
        components = { "openfeint" }
    }
}

build.settings와 관련해서 알아야 할 또 하나는 Leaderboards 와 Achievements를 셋업하지 않고 테스트만 하고자 할 경우 여러분은 GKTapper Xcode 샘플을 이용해서 구현 할 수 있습니다.
그러기 위해서는 아래와 같이 하시면 됩니다.
settings =
{
    iphone =
    {
        plist =
        {
            CFBundleIdentifier = "com.appledts.GKTapper",  -- temporary line for testing!
        },
        components = {}
    }
}

***** System Events and initCallback
일반적으로 gameNetwork.init()은 main.lua 처음 시작할 때 불려 집니다. 그리고 앱이 끝날 때까지 다시 불려질 일은 없습니다. Game Center를 이용하는 방법은 이 gameNetwork.init()과 비교해서 크게 두가지 다른 점이 있습니다.
Game Center는 iOS안의 system-wide 프로세스입니다. 유저들은 앱이 suspend될 때마다 Game Center에서 로그 아웃 됩니다. 그래서 앱이 applicationStart 시스템 이벤트를 Receive 받을 때마다 gameNetwork.init()을 불러야 할 겁니다. 이것이 첫번째 다른 점 입니다.
두번째 다른 점은 init request가 마무리 됐을 때 한번 불려지는 initCallback 함수 입니다.

아래 소스는 network 제공자로 Game Center를 사용할 때 gameNetwork.init()을 어떻게 사용해야 하는지를 보여주는 코드입니다.

local gameNetwork = require "gameNetwork"
local loggedIntoGC = false

-- called after the "init" request has completed
local function initCallback( event )
    if event.data then
        loggedIntoGC = true
        native.showAlert( "Success!", "User has logged into Game Center", { "OK" } )
    else
        loggedIntoGC = false
        native.showAlert( "Fail", "User is not logged into Game Center", { "OK" } )
    end
end

-- function to listen for system events
local function onSystemEvent( event )
    if event.type == "applicationStart" then
        gameNetwork.init( "gamecenter", initCallback )
        return true
    end
end
Runtime:addEventListener( "system", onSystemEvent )

initCallback 리스너 함수를 보면 event 테이블에는 한개의 프로퍼티 event.data 가 있습니다. 이것은 Game Center 에 로그인이 성공하면 true가 되고 실패하면 nil이 됩니다.
유저가 로그인 상태가 아닐 경우 체크해서 어떤 핸들링을 할 수가 있습니다.
좀 더 자세한 사실은 gameNetwork.init() documentation 를 보세요.


***** Game Center Request
Game Center를 사용할 때 아래 열거한 것들을 gameNetwork.request()와 함께 사용될 수 있습니다.
    •    setHighScore
    •    loadScores
    •    loadLocalPlayer
    •    loadPlayers
    •    loadFriends
    •    loadAchievements
    •    unlockAchievement
    •    resetAchievements
    •    loadAchievementDescriptions
    •    loadFriendRequestMaxNumberOfRecipients
    •    loadLeaderboardCategories

보다 자세한 용도를 보려면 gameNetwork.init() documentation 를보세요.
대부분의 위 request들은 요청한 데이터를 받기 위한 리스너를 가질 수 있습니다.
Game Center request(요청) 들에서 가장 헛갈리는 부분은 이 이벤트 테이블을 통해서 어떤 데이터가 당신의 callback 리스너에 전달 되느냐 입니다. 다시 한번 말씀드리자면 보다 자세한 사항은 gameNetwork.init() documentationOfficial Game Kit Documentation 안의 해당 페이지를 통해 알아 볼 수 있습니다.
만약에 fail일 경우 JSON 스트링을 통해 event table에 encode됩니다. 그리고 전화기의 native alert으로 전화기 화면에 표시 됩니다. 아래 샘플 예제를 참조하세요.
local gameNetwork = require "gameNetwork"
local json = require "json"

-- callback listener for Game Center requests
local function requestCallback( event )
    if event.type == "loadFriends" then
        local data = json.encode( event.data )
       
        -- show encoded json string via native alert
        native.showAlert( "event.data", data, { "OK" } )
    end
end

gameNetwork.request( "loadFriends", { listener=requestCallback } )

***** Corona GCTapper Sample
이상으로 GameCenter의 아주 기본적인 사항들을 살펴 봤습니다.
Corona SDK에서는 Apple의 GKTapper Xcode Sample 을 수정해서 Corona에서 사용할 수 있도록 했습니다.
아래 링크를 클릭하시면 그것을 이용한 샘플 코드를 받아 보실 수 있습니다.
View the Corona Game Center Sample Project on Github

위 내용 원본은  Corona SDK 웹싸이트에있습니다.
http://blog.anscamobile.com/2012/01/tutorial-game-center-integration-ios/

오늘은 iOS Game Center를 사용하기 위한 기초 지식을 공부했구요.
다음엔 위에 다운 받은 샘플 파일을 분석할 생각입니다.

반응형

New, More Flexible ScrollView Widget

2012. 1. 15. 06:55 | Posted by 솔웅


반응형
오늘은 2012.721 버전 이후부터 적용된 ScrollView Widget 에 대해 알아 보겠습니다.

그러니 이 기능을 직접 테스트 해 보시려면 2012.721 버전 이상되는 Corona SDK를 설치 하셔야 합니다.

확인해 보니까 지금 일반에 공개된 버전은 2011.704 버전이네요.
유료 사용자가 아닌 경우는 일반에 공개된 이후에 사용하실 수 있을 겁니다.

아래 내용의 원본은 여기 입니다.
그리고 자세한 API를 보시려면 여기를 클릭하시면 됩니다.

그럼 이번에 새로 추가된 기능에 대해 알아보겠습니다.

About Widgets

코로나의 위젯 API는 간단히 여러 플랫폼에 적용되는 코딩을 위해 만들어 졌습니다. 간단한 몇줄의 코드로 버튼, 탭 바, 슬라이더, Picker Wheel, Lists, 스크롤 되는 콘텐트 등을 스크롤 뷰 위젯에 덧 붙일 수 있는 기능을 제공합니다.
모바일 기기는 그 크기가 제한되어 있기 때문에 어느 기종이나 앱에 스크롤 되는 기능이 많이 필요합니다. 코로나에서는 이러한 기능을  scrollView Widget 에서 제공 합니다.

Basic Usage

스크롤 뷰 위젯의 가장 기본적인 이용법은 단지 몇개의 파라미터들을 정하고 디스플레이할 객체를 만들고 그 객체를 스크롤 뷰에 넣으면 됩니다.
아래에 그 예제가 있습니다.

local widget = require "widget"
local scroller = widget.newScrollView{
    width = 320,
    height = 480,
    scrollWidth = 700,
    scrollHeight = 1000
}

local obj = display.newImage( "myobject.png" )
obj.x = 400
obj.y = 500

scroller:insert( obj )

코드를 한번 보겠습니다.
- 첫번째로 widget을 require 합니다.
- 파라미터를 정합니다. 320,480은 아이폰의 해상도 입니다.
- scrollWidth,scrollHeight 파라미터는 스크롤뷰가 어디까지 스크롤 되는지를 정해 줍니다. 이 값은 어떤 객체가 주어지느냐에 따라 자

동적으로 늘어날 겁니다.
- 객체를 생성하고 위치를 설정합니다.
- 이 객체를 스크롤 뷰에 넣습니다.
이것이 스크롤 뷰를 사용하는 가장 기본적인 방법입니다.

위 코드만 가지고도 (이미지 파일만 있다면) 기본적인 스크롤 기능을 구현할 수 있습니다.

위 아래로 움직이지 않고 옆으로 움직이도록 하려면 아래 코드를 덧붙이기만 하면 됩니다.
scroller.content.verticalScrollDisabled = true
디폴트는 아래 코드 입니다.
scroller.content.horizontalScrollDisabled = true

Clipping the Width and Height

스크롤 뷰가 화면 전체를 차지하지 않고 일부분만 차지하도록 하려면 어떻게 할까요?
현재로서는 bitmap mask를 사용하는 방법 밖에는 없습니다.
이 방법도 코로나 개발팀에서 아주 간단히 코딩할 수 있도록 신경을 썼다고 합니다.
특정 크기로 스크롤 뷰를 만들기 위해 비트맵 마스크를 사용하려면 간단히 그 크기(width,height)를 정하는 파라미터를 세팅하고  해당하는 비트맵 마스크를 만듭니다. 그리고 그 비트맵 마스크의 파일 이름으로 maskFile 파라미터를 사용합니다.

    local widget = require "widget"
 
    local list = widget.newTableView{
        width = 320,
        height = 410,
        maskFile = "mask320x410.png"
    }
위 예제는 테이블 뷰에 사용했구요, 스크롤 뷰에서도 똑같이 사용하시면 됩니다.

Buttons and Touchable Objects

주로 ScrollView 에 버튼 같이 터치 리스너를 받아 들이는 object(객체)들을 넣는 방법을 알아볼 겁니다.
스크롤 하기 위해 터치할 때하고 버튼을 누를 때 터치하고 구별해야 쓸모있는 스크롤뷰가 될 수 있겠죠?

이 기능을 사용하려면 takeFocus()를 사용하시면 됩니다.

local widget = require "widget"
local scroller = widget.newScrollView{
    width = 320,
    height = 480,
    scrollWidth = 700,
    scrollHeight = 1000
}

-- event listener for button widget
local function onButtonEvent( event )
    if event.phase == "moved" then
        local dx = math.abs( event.x - event.xStart )
        local dy = math.abs( event.y - event.yStart )
       
        -- if finger drags button more than 5 pixels, pass focus to scrollView
        if dx > 5 or dy > 5 then
            scroller:takeFocus( event )
        end
   
    elseif event.phase == "release" then

        print( "Button pushed." )
    end

    return true
end

local button = widget.newButton{
    label = "My Button"
    top = 300,
    left = 300,
    onEvent = onButtonEvent
}

scroller:insert( button )

위와 같이 버튼을 만들고 이 버튼을 scroller 에 insert를 합니다.

그리고 리스너 이벤트 함수인 onButtonEvent 에서 takeFocus를 구현해 주면 됩니다.
이벤트가 moved 일 때 5픽셀 이상 움직이면 scroller 에 포커스를 맞추고 그렇지 않고 손가락을 떼면 버튼에 포커스를 맞추게 됩니다.

Scroll Events
스크롤 뷰 이벤트의 기본 파라미터에는 아래와 같은 것들이 있습니다.
event.name - 항상 scrollEvent 입니다.
event.target - 실제 이벤트가 할당된 스크롤뷰 위젯에 대한 레퍼런스 입니다.
event.type - beganScroll, endedScroll,movingToTopLimit,movingToBottomLimit,movingToLeftLimit, movingToRightLimit 가 있습니다.
Limit 를 설정하게 되면 그 숫자에 따라 스크롤 뷰가 끝에 다다랐을 때 탄력있게 움직이게 됩니다.

Further Reading and Resources

이 기능들을 자세히 보려면 CoronaSDK의 샘플 코드 중 Interface 폴더에 ㅇㅆ는 WedgetDemo를 보시면 됩니다.
이 기능들은 최근에 발표된 화면(Scene) 전환 API인 Storyboard API와 어울려서 사용할 수도 있습니다.

그럼 샘플코드인 WidgetDemo 앱을 살펴 보겠습니다.


첫번째는 테이블 뷰입니다. (안드로이드에서는 리스트 뷰라고 합니다.)
손가락으로 터치후 움직이면 아래위로 리스트들이 보입니다.
그리고 특정 Row를 터치하면 그 로우에 해당하는 화면으로 넘어갑니다.



여기서 back 버튼을 누르면 이전 테이블 뷰(리스트 뷰) 화면으로 넘어갑니다.
이 소스를 보면 최근에 발표된 storyboard 기능을 사용해서 화면을 넘기고 있습니다.

나중에 이 소스를 분석하면 아주 좋은 공부가 되겠네요.

아래 탭을 보시면 테이블 뷰 이외에 scrollView 와 Other 가 있습니다.


스크롤 뷰를 누르면 이 화면이 나옵니다.
상하 좌우로 움직이면 이미지가 스크롤 됩니다.



그리고 others를 누르면 위 화면과 같이 Slide Bar 와 Date Picker 가 나옵니다.
아주 좋은 샘플이네요.

오늘은 최근에 새로 발표된 Corona SDK 의 Scroll View Widget 에 대해 알아봤습니다.
반응형


반응형
크리스마스 이브날 Build Up 된 CoronaSDK 2011.715 버전에서 Email, SMS 보내는 기능이 추가 됐습니다.

CoronaSDK의 단점인 Mobile Native 기능 사용에 많은 제한이 있었는데 하나 둘 지원을 해 주고 있습니다.

이 기능은 배포버전이 아니라 최신 Build 버전이기 때문에 유료 가입자만 사용할 수 있습니다.
CoronaSDK 2011.715 버전을 다운 받아서 upgrade 해야 하는데 유료 가입자만 이 것을 다운 받을 수 있거든요.

이메일과 문자메세지 보내는 기능 API를 살펴 보겠습니다.

기본 신택스는 아래와 같습니다.
native.showPopup(name)
native.showPopup(name, options)

샘플을 보겠습니다.

local options =
{
   to = "john.doe@somewhere.com",
   subject = "My High Score",
   body = "I scored over 9000!!! Can you do better?",
   attachment = { baseDir=system.DocumentsDirectory, filename="Screenshot.png", type="image" },
}
native.showPopup("mail", options)

options를 보면 들어가 있는 요소들은 받는이,메일제목,본문, 첨부 등이 있습니다.
만약에 메일 본문에서 HTML을 사용 가능하도록 하려면 아래와 같이 하면 됩니다.

local options =
{
   to = { "john.doe@somewhere.com", "jane.doe@somewhere.com" },
   cc = { "john.smith@somewhere.com", "jane.smith@somewhere.com" },
   subject = "My High Score",
   isBodyHtml = true,
   body = "<html><body>I scored over <b>9000</b>!!! Can you do better?</body></html>",
   attachment =
   {
      { baseDir=system.DocumentsDirectory, filename="Screenshot.png", type="image" },
      { baseDir=system.ResourceDirectory, filename="MyLogo.png", type="image" },
   },
}
native.showPopup("mail", options)

이 샘플에서는 받는이 이외에 참조인까지 넣었습니다. 그리고 isBodyHtml = true 를 해서 본문에 html 태그를 사용할 수 있도록 했습니다.

문자메세지 보내는 샘플은 아래와 같습니다.

local options =
{
   body = "I scored over 9000!!! Can you do better?"
}
native.showPopup("sms", options)

문자메세지 내용은 body에 넣으면 됩니다.
이러면 팝업윈도우에 주소록이 나와서 고를 수 있게 해 준다고 합니다.

그리고 여러명에게 보낼 때는 아래와 같이 하면 됩니다.

local options =
{
   to = { "1234567890", "9876543210" },
   body = "I scored over 9000!!! Can you do better?"
}
native.showPopup("sms", options)

각 파라미터에 대해 자세히 살펴보면 아래와 같습니다.

Parameters:

name
"mail"과 "sms" 둘 중 하나가 올 수 있습니다.

options
옵션들 입니다. 이 옵션들 중 사용하고 싶은 것만 사용하시면 됩니다.


메일과 관련해서는 아래의 프로퍼트들이 지원됩니다.

    options.to : 받는이의 이메일 주소. 한 개 이상 올 수 있습니다.
    options.cc : 참조인 이메일 주소. 한 개 이상 올 수 있습니다.
    options.bcc : 숨은 참조인 이메일 주소. 한 개 이상 올 수 있습니다.
    options.attachment : 첨부. 형식은 { baseDir=, filename= [, type=] }입니다. type 프로퍼티에는 "image" 같은 MIME type을 사용합니다. 여러 첨부 파일을 보내려면 이 테이블의 배열을 만들어야 합니다.
    options.body : 이메일의 내용입니다.
    options.isBodyHtml : true 나 false 값이 올 수 있으며 이메일 내용에 html 태그를 사용할 수 있는지 여부를 정해 줍니다. 디폴트로는 일반 텍스트(html을 사용하지 않는 경우)를 지원합니다.
    options.subject : 이메일 제목 입니다.

sms 와 관련해서는 아래와 같은 property들이 지원 됩니다.

    options.to : 받는이의 전화번호 입니다. 한 개 이상 사용할 수 있습니다.
    options.body : 문자메세지 내용입니다.

Returns:

result
result 값이 false 이면 popup 사용이 불가능하다는 것이거나 해당 기계에서 해당 기능을 사용할 수 없다는 것 입니다.

===== o ===== o ===== o ===== o ====== o ===== o=====
이메일과 sms 를 native.showPopup 을 사용해서 call 을 하면 전화기에서 지원하는 이메일과 문자보내기 화면이 뜰 겁니다. 옵션을 넣으면 그 옵션들이 해당 칸에 들어가게 됩니다.
옵션이 들어가도 유저들이 이를 수정할 수 있습니다. 그 부분은 coronasdk 앱이 아니라 해당 전화기의 앱이니까요.

sms 는 140문자 까지 됩니다. 모바일을 보면 sms 이외에 mms라는 기능도 있던데 이 기능은 아직 따로 지원이 안 되는 것 같습니다.

한번 더 언급하는데요. 이 기능은 CoronaSDK 2011.715 버전 이후에서 사용 가능합니다. 그리고 이 버전은 유료 가입자만 다운 받을 수 있구요.
그래서 아직까지는 유료 가입자만 이 기능을 이용할 수 있습니다.
무료로 SDK를 다운 받아서 연습하시는 분들은 이 버전이 공개될 때까지 기다리셔야 합니다.
아마 조만간 공개 되겠죠.
반응형

화면 전환 Storyboard API 개요

2011. 12. 10. 00:33 | Posted by 솔웅


반응형
오늘은 이번에 새로 릴리즈된 코로나 버전에서 선보인 Corona SDK Storyboard API에 대해 공부하겠습니다.

이 API가 나오기 전에 코로나에서는 화면전환을 위한 방법으로 우선 모든 display object들을 그룹화하고 그 그룹을 없앤 후 다음 화면의 새 display 그룹을 불러오고 하는 방식으로 사용하도록 제시했었습니다.
그런데 이 방법은 한계가 있었습니다. 예를 들어 한 화면(Scene)의 display 객체들이 별도의 모듈에 있다던지 할 때는 콘트롤 하기가 아주 힘듭니다.

얼마전에 다룬 주제 중에 director.lua 클래스를 이용해서 화면 전환하는 것을 다뤘습니다.
이것은 코로나 SDK를 만든 Ansca 에서 제공한 것이 아니라 Ricardo Rauber 라는 개발자가 개발해서 공유한 3rd party 클래스 입니다. 사실 저도 그렇고 이 리카르도가 배포한 director 클래스를 화면전환할 때 많이 썼습니다.  아마 한 1년 정도는 이 director 클래스가 화면전환에 주로 사용 되어졌을 겁니다.

이제 Ansca에서도 공식적인 화면 전환 API인 Storyboard API 를  Corona build 2011.678 버전에 선보였고 Corona build 2011.703 버전이 무료 사용자에게도 공개 되면서 누구나 사용할 수 있게 됐습니다.

Getting Started

일단 시작해 볼까요?
File-New Project 를 누릅니다.

이런 화면이 나올겁니다.
제가 지금 맥에서 하고 있는데요. 아까 집에서 윈도우즈로 코로나 세버전 다운 받아서 해 보니까 Choose a template에 좀 더 다양한 내용들이 있더라구요.
이번에 코로나가 새로 업그레이드 되면서 많이 변했습니다.
나중에 이 부분은 따로 다룰 기회가 있겠죠?
오늘은 Storyboard API에 대해 공부할 거니까요. 일단  Scene을 체크하고 Next를 누릅니다.
그럼 App Name에 넣은 이름으로 폴더가 만들어지고 그 안에 아래와 같이 4개의 파일이 자동적으로 생길 겁니다.

기본적으로 필요한 파일들을 자동으로 생성해 주네요.
개발하기 훨씬 편해 졌습니다.
여기서 main.lua를 볼까요?
-----------------------------------------------------------------------------------------
-- main.lua
-----------------------------------------------------------------------------------------
local storyboard = require "storyboard"
-- load scenetemplate.lua
storyboard.gotoScene( "scenetemplate" )
-- Add any objects that should appear on all scenes below (e.g. tab bar, hud, etc.):

storyboard를 require하고 storyboard.gotoScene("scenetemplate") 했습니다.

자 이제 이 내용들을 공부해 보겠습니다.

The Basics

우선 Storyboard API를 보세요.
처음에 있는 Scene Template이 아까 만들어진 파일 중에 있는 scenetemplate.lua 입니다.
이것은 조금 후에 공부하겠습니다.
그 다음엔 어떤 함수(메소드)들이 있는지 한번 이름만이라도 보고 갈까요?
getPrevious()는 이전 Scene 정보를 얻는 걸테고 getScene은 현재 정보 그리고 gotoScene()은 다음에 넘어갈 Scene(화면)으로 갈 때 사용하는 함수 일 겁니다.
newScene()은 새로운 화면을 만드는 것 같고 purgeAll(), purgeScene(),removeAll(), removeScene() 함수들은 화면을 없애는 것과 관련이 있을 겁니다.

자세한 내용과 사용법은 나중에 다뤄 보겠습니다. 오늘은 간단히 개요만 볼께요.

Loading Scenes

가장 기본적으로 Scene을 로드 하는 방법은 아래와 같습니다.
local storyboard = require "storyboard"
    storyboard.gotoScene( "scene1" )
여기서 scene1은 scene1.lua 파일의 내용으로 가라는 겁니다.
여기서 알아두어야 할 것은 이 scene1.lua는 scenetemplate.lua에 있는 규칙에 맞게 작성 되어져야 합니다.
gotoScene()함수는 다음 화면으로 가도록 하는 함수입니다.

Scene Events

각 화면(Scene)에는 여러 이벤트가 있어서 이 이벤트 별로 콘트롤이 가능합니다.
즉 scene1.lua에서 이 이벤트 가지고 콘트롤 하게 된다는 얘기 입니다.
storyboard API에는 4가지 이벤트가 있습니다.
이 이벤트를 핸들링 하려면 해당 리스너를 셋업 해야 하겠죠?
이 예제는 아까 생성됐던 scenetemplate.lua 파일에 소개 돼 있습니다.
조금 후에 볼께요.

createScene
storyboard.gotoScene()이 call 되고 새 화면이 display 될 때 동작을 합니다.
enterScene
storyboard.gotoScene()이 call되고 transition이 완료된 직후에 동작합니다.
exitScene
gotoScene()이 call되고 transition이 막 일어나기 전에 동작합니다.
이것은 현재의 Scene에 해당 되는 거겠죠? 이전에 두개는 그 다음 Scene에 해당하는 거구요.
destroyScene
display group이 removed 되기 바로 전에 동작합니다. 그리고 storyboard.purgeScene()이나 storyboard.removeScene()을 call 했을 때도 동작합니다.

지 이제 Scene Template를 보겠습니다.
----------------------------------------------------------------------------------
-- scenetemplate.lua
----------------------------------------------------------------------------------
local storyboard = require( "storyboard" )
local scene = storyboard.newScene()
----------------------------------------------------------------------------------
--    NOTE:
--    Code outside of listener functions (below) will only be executed once,
--    unless storyboard.removeScene() is called.
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
-- BEGINNING OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------
-- Called when the scene's view does not exist:
function scene:createScene( event )
    local group = self.view
 -----------------------------------------------------------------------------
    --    CREATE display objects and add them to 'group' here.
    --    Example use-case: Restore 'group' from previously saved state.
----------------------------------------------------------------------------
end
-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
    local group = self.view
-----------------------------------------------------------------------------
    --    INSERT code here (e.g. start timers, load audio, start listeners, etc.)
----------------------------------------------------------------------------
end
-- Called when scene is about to move offscreen:
function scene:exitScene( event )
    local group = self.view
-----------------------------------------------------------------------------
    --    INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.)
-----------------------------------------------------------------------------
end
-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
    local group = self.view
----------------------------------------------------------------------------
    --    INSERT code here (e.g. remove listeners, widgets, save state, etc.)
-----------------------------------------------------------------------------
end
---------------------------------------------------------------------------------
-- END OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------
-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )
-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )
-- "exitScene" event is dispatched before next scene's transition begins
scene:addEventListener( "exitScene", scene )
-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )
---------------------------------------------------------------------------------

return scene

공간을 절약하기 위해 필요없는 라인은 지웠는데요.
여러분은 scenetemplate.lua 파일을 열어 보시면 됩니다.

주석이 아주 자세히 달려 있어서 보시면 아실 겁니다.
쭉 훑어 보면
storyboard를 require하고 storyboard.newScene()을 합니다.
그 밑에 보면 좀 전에 다뤘던 createScene,enterScene,exitScene,destroyScene 함수가 차례대로 나옵니다.
createScene에 display object들하고 이 객체들을 그룹화 하는 것을 구현해야 합니다.
그리고 enterScene에 이 화면에서 동작할 내용들을 코딩해야 합니다.
주로 타이머 시작이나 오디오 로드, 리스너 등록 등의 작업을 이곳에서 합니다.
exitScene 함수 안에서는 타이머 멈춤, 리스너 제거, unload sounds 등의 작업을 합니다.
destroyScene 함수에서는 리스너 제거나 게임같은 경우에 저장해야 할 데이터(점수,레벨 등등) 저장 등을 이곳에서 합니다.

그리고 그 밑에는 이 함수들에 대한 리스너를 다는 방법이 설명 돼 있습니다.
이건 보시면 아실 겁니다.

각 scene들은 마지막에 반드시 return scene을 해야 합니다.

Scene Purging and Removal

이전 화면에서 다음 화면으로 넘어갔을 때 이전 화면은 스크린에서만 안 보이는 것이지 실제로 없어진 것은 아닙니다. 그 얘기는 이전 신은 여전히 메모리를 차지하고 있다는 얘기 입니다. 이전화면에 빨리 돌아가야 하면 이렇게 메모리에 올라 있는것이 더 좋을 겁니다.
하지만 때때로 절대 불려지지 않을 화면인데 계속 메모리만 차지하고 있으면 메모리 낭비이고 퍼포먼스에도 영향을 줍니다.
이럴때 purge나 remove 함수를 씁니다.

storyboard.purgeScene(sceneName)

이 함수는 해당(seneName) scene을 purge시킵니다. 즉 보이지 않게 없어진다는 겁니다. 하지만 이 scene은 메모리에 남아 있게 됩니다. 그래서 다른 화면에서 이 화면을 storyboard.gotoScene()하게 되면 빠르게 불려지게 됩니다.
purge되기 전에 destroyScene 함수가 실행 될 겁니다.

storyboard.removeScene(sceneName)

이 함수는 우선 scene을 purge하고 나서 메모리에서 이 scene을 unload 합니다.
이후에 이 scene이 gotoScene()으로 call되면 메모리에 reload되게 됩니다.

Automatic Scene Purging

개발자가 위 purge나 remove를 하지 않은 상태에서 OS의 메모리가 부족하게 되면 Corona는 자동적으로 이전 Scene을 제거합니다.

Excluding objects from Transitions

scenetemplate.lua에서 보시면 local group=self.view 가 있을 겁니다.
view 는 그 Scene의 display 객체들입니다. 만약 화면전환 효과에 특정 display를 적용시키지 않으려면 두가지 방법이 있습니다.
1. 객체를 main.lua에서 생성한다.
2. scene 모듈에서 객체를 생성했으면 해당 객체를 view 그룹에 insert 하지 않는다.

이 방법을 사용할 때는 잘 생각해서 해야 겠죠? 2번의 방법을 사용할 때 그 객체를 새로 생성되는 로직이 있다면 이전의 객체는 남아있고 새로운 객체는 계속 생기고 하는 현상이 발생합니다.

제가 director.lua 클래스 사용할 때도 group 에 객체를 insert 시키지 않아서 에러가 생기는 바람에 그 에러 잡느라고 시간을 좀 보냈거든요.

오늘은 이렇게 storyboard API에 대해서 개요를 살펴 봤구요.
다음엔 코로나에서 제공한 샘플 코드를 분석해 볼까 합니다.

오늘 글은 아래 코로나 홈페이지에서 제공한 글을 바탕으로 작성했습니다.
http://blog.anscamobile.com/2011/11/introducing-the-storyboard-api/

반응형


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

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이 되지는 않습니다.

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



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

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