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

최근에 받은 트랙백

글 보관함

calendar

    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    


Posted on . Written by



수요일 입니다. 다시 FAQ 시간이 돌아왔습니다. 오늘의 주제는 런타임 에러 처리와 안드로이드 퍼미션에 관한 내용들을 다루겠습니다.



1. What about the Runtime Error Message Popup on current Daily Builds?


안드로이드용은 Build 1030부터 그리고 다른 플랫폼들은 Build 1047 부터 적용됐는데요. 런타임 에러가 나면 팝업 메세지 박스가 뜨고 파일과 line number 와 함께 해당 에러를 보여 줍니다. (debug build 로 세팅 됐을 경우에요).  그리고 물론 콘솔에도 에러 정보가 뜨구요. 그러니까 이제는 콘솔 없이도 런타임 에러를 볼 수 있는 기능이 추가 된거죠. Build 1047 부터 여러분 코드에 런타임 리스너를 include 하실 수 있습니다. 이 리스너는 에러를 trap 하고 런타임 팝업 메세지 박스를 뜨지 않도록 할 수 있습니다. 이 때 다른 동작을 행하도록 분기해 버리면 런타임 에러가 발생해도 앱이 멈추지않고 계속 작동하게 할 수 있습니다.

모든 플랫폼과 맥/윈도우 시뮬레이터에서 런타임 메세지 박스는 디폴트로 보이도록 설정돼 있습니다.





2. How do I implement a Lua Runtime Error Listener?


아래 예제를 보시면 런타임 리스너를 어떻게 implement 하는지 보실 수 있습니다. ("unhandledError") 코로나 시뮬레이터나 디바이스에서 이 코드를 실행하면 print statement 의 string 과 함께 concatenating a nil value 에 의해 발생한 에러를 보실 수 있을 겁니다.



local releaseBuild = true   -- Set to true to suppress popup message

-- Error handler
local function myUnhandledErrorListener( event )

    if releaseBuild then
        print( "Handling the unhandled error >>>\n", event.errorMessage )
        display.newText( ">>> ERROR OCCURRED <<<", 30, 1, native.systemFont, 18 )
    else
        print( "Not handling the unhandled error >>>\n", event.errorMessage )
    end
    
    return releaseBuild
end

Runtime:addEventListener("unhandledError", myUnhandledErrorListener)

-- Displays text message in center of screen
txtMsg1 = display.newText( "Runtime Error Test Code", 55, 200, "Verdana-Bold", 14 )

print( "ABC" .. nil )  -- <<<< Lua error here


이 리스너에서 false (default value)가 return 되면 이 리스너 함수를 나갈 때 팝업 메세지 박스가 뜹니다. true 가 return 되면 리스너 함수가 런타임 에러를 처리한다는 의미 입니다. 그래서 팝업 메세지가 뜨지 않습니다. 위 코드에서 releaseBuild 변수는 팝업 메세지가 표시 될지 안될지를 결정하는 변수 입니다.


3. What is the best practices for the Runtime Error Listener?


런타임 팝업 메세지 와 런타임 에러 리스너 기능을 추가한 이유는 진행되는 코드의 상황과 에러를 보여줄 수 있는 툴을 개발자들에게 제공하기 위해서 입니다. 만약에 디버그를 위해서 빌드를 한다면 (iOS에서는 developer mode가 되겠고 안드로이드에서는 debug key 를 사용한 빌드가 되겠죠.) 에러가 발생한 파일과 그 line number 가 포함된 런터임 에러 정보를 보실 수 있을 겁니다. 이 정보들은 팝업 메세지 박스를 통해서 보게 되죠. 만약에 런타임 리스너를 추가하고 이 팝업 박스를 띄우지 않는다고 하더라도 그 에러 정보들을 얻을 수 있습니다. (리스너에 전달된 event table 이나 콘솔창등에서요.)

production release 라면 error type 만 가능합니다. 런타임 리스너를 implement 하시고 에러 팝업을 띄우지 않도록 세팅한 다음 내부적으로 그 에러를 log 하게 되죠. 이 에러 로그로 무엇을 할 지는 여러분이 하기 나름입니다. 그냥 무시할 수도 있고 문제점들을 track 하기 위해 서버로 보낼 로그 파일을 만들수도 있구요. 팝업 에러가 뜨도록 하는 것의 장점은 이 에러 정보가 Google Play 에 전달 될거라는 겁니다. 개발자들은 그 에러 정보를 Google Play 에서 보실 수 있게 되는 거죠. 만약 팝업 창을 띄우지 않게 되면 여러분이 에러 정보를 log 해서 여러분의 서버에 전달하지 않는한 그 에러 정보는 lost 되게 되죠.

디버그 할 때는 대부분 런타임 팝업이 뜨도록 하는 경우가 많을 겁니다. 그리고 앱 스토어에 올릴 때는 그 팝업을 띄우지 않도록 바꾸는 경우가 많을 거구요.


이전 FAQ 에서 언급한 건데요. 에러를 그냥 무시해 버리는 것은 그다지 좋은 방법은 아닙니다. 런타임 에러가 발생하면 앱이 안정적이지 않게 될 수 있습니다. 가능하면 많은 디바이스에서 앱을 테스트 해 보고 에러가 발생하면 이 에러를 없애거나 pcall 을 이용해서 trap 하셔야 합니다.



4. I'm using the Runtime Listener but I'm still getting the Runtime Error popup.


여러분 코드에서 런타임 에러 리스너를 implement 해서 에러 팝업창을 띄우지 않도록 했는데도 계속 팝업이 뜬다면 런타임 리스너가 시작하기 전에 런타임 에러가 일어났을 가능성이 큽니다. 위 예제에서 보여드렸듯이 런타임 리스너 함수는 코드 내에서 정의를 하셔야 합니다. 그리고 그 함수를 가능하면 빨리 enabling 하셔야 합니다. 그래야 에러를 trap 하실 수 있습니다. 런타임 에러 리스너가 enable 되기 전에 일어난 에러들은 당연히 팝업 메세지를 발생시킬겁니다. 일반적으로 startup 시 발생하는 에러는 fix 하기가 쉽습니다. 그리고 trap 작업도 필요 없구요. 터치 이벤트, 충돌 등이 일어날 때 발생하는 런타임 에러들이 trap 해야할 그런 에러들 입니다.



5. What about Android Permissions and runtime errors?

Build 1030에서 안드로이드 Manifest 에서 디폴트 퍼미션을 없앴습니다. 여러분이 필요한 퍼미션들을 추가해야 된다는 얘기죠. Daily Build 샘플 코드 프로젝트와 API 페이지들을 보시면 build.settings 파일 안에 필요한 퍼미션들이 있읍니다.

대개 퍼미션을 빠뜨리면 런타임 팝업과 함께 런타임 에러가 발생합니다. (혹은 unHandledError 리스너가 call 되기도 하죠.) 퍼미션을 빠뜨리는 것은 앱을 릴리즈 하기 전에 뭔가가 테스트 되고 또 fix 되어야 한다는 얘기 입니다. (6번 질문을 보세요.)



6. Which Android Permissions won't generate a runtime error?

어떤 API call 들은 build.settings 파일에 특정 퍼미션이 세팅되어 있지 않으면 조용히 fail 해 버립니다.
아래와 같은 것들인데요.


  • display.capture
  • display.captureBoard
  • display.captureScreen
  • media.newRecording
  • media.newVideo
  • media.save
  • media.show
  • native.webView
  • native.newWebPopup
  • heading( Compass) event
  • location (GPS) event


위의 API들을 사용할 때는 그 코드가 원하는대로 제대로 작동하는지 꼭 확인하셔야 됩니다. build.settings 파일에 이런 퍼미션들이 세팅되어 있는지도 한번 더 확인하는 습관도 좋은 거 같습니다.


각 API 별로 필요한 안드로이드 퍼미션이 무엇인지 보시려면 Daily Build Documents 를 확인해 보세요.

That's it for today's answers. I hope you enjoyed them and even learned a few things!



저작자 표시 비영리 동일 조건 변경 허락
신고


loadSoundLibrary: a quick way of structuring and loading your sound effects



Posted by BeyondtheTech, Posted on March 8, 2013




여러분 코드가 여러분이 로드해야 할 사운드 효과들로 조금 어지러워져 있다면 보다 적은 코딩으로 훨씬 더 쉽게 사운드를 로드 할 수 있는 정보를 이 글에서 보실 수 있으실 겁니다.

개발을 하다 보면 이미지 파일이나 lua 파일들과 더불어 사운드 파일들도 시간이 지날수록 숫자가 많이지게 될 겁니다. 그리고 그러한 파일들을 구분하려면 긴 디렉토리 이름을 찾아가느라고 힘들게 되구요. 이 글에서 보여드리는 예제는 그러한 부분들도 고려 된 겁니다. 좀 더 쉬운 방법으로 각각의 하위폴더를 관리할 수 있도록 해 드릴 겁니다.

예를 들어 여러분 코드가 이렇게 돼 있을 때...


sound_fx[ "meow" ] = audio.loadSound( "meow.caf" )
sound_fx[ "bark" ] = audio.loadSound( "bark.caf" )
sound_fx[ "explosion" ] = audio.loadSound( "explosion.caf" )
sound_fx[ "laugh" ] = audio.loadSound( "laugh.caf" )
sound_fx[ "cry" ] = audio.loadSound( "cry.caf" )
sound_fx[ "fart" ] = audio.loadSound( "fart.caf" )
sound_fx[ "belch" ] = audio.loadSound( "belch.caf" )
sound_fx[ "kiss" ] = audio.loadSound( "kiss.caf" )
sound_fx[ "slap" ] = audio.loadSound( "slap.caf" )
sound_fx[ "punch" ] = audio.loadSound( "punch.caf" )
sound_fx[ "boing" ] = audio.loadSound( "boing.caf" )
...


보시면 아시겠지만 라인별로 많은 부분이 반복됩니다.





저의 loadSoundLibrary 를 사용하시면 프로그래밍 하기 훨씬 더 쉬워지실 겁니다.


local function loadSoundLibrary( ... )
        -- quick-load a bunch of audio files, by Raphael Salgado aka BeyondtheTech, 2013
        if #arg < 2 then
                print( "Invalid number of arguments for loadSoundLibrary" )
        else
                local loadList, t, soundName
                local t2 = 0
                for t = 2, #arg do
                        soundName = arg [ t ]
                        if sound_fx[ soundName ] ~= nil then
                                print( "Warning - Sound name element " .. soundName .. " already exists, skipping" )
                        else
                                sound_fx[ soundName ] = audio.loadSound( arg[ 1 ] .. "/" .. soundName .. ".caf" )
                                -- check, did it load successfully?
                                if sound_fx[ soundName ] ~= nil then t2 = t2 + 1 end
                        end
                end
                print( "Loaded " .. t2 .. " out of " .. #arg - 1 .. " " .. arg[ 1 ] .. " sounds" )
        end
end


이제 같은 오디오 파일 세트를 불러오기 위해서 아래 한줄만 추가하시면 됩니다.

loadSoundLibrary( "SFXstuff", "meow","bark","explosion","laugh","cry","fart","belch","kiss","slap","punch","boing" )


물론 SFXstuff라는 서브폴더를 생성해서 .caf 파일들을 그곳에 넣어야 겠죠.

이 함수의 첫번째 인수는 폴더 이름 입니다.  그 다음은 모두 테이블에 들어갈 sound element 들이구요. 즉 확장자가 없는 sound 파일들이죠.
일단 정확한 argument들의 숫자를 전달했는지 체크하구요. sound 이름을 잘 못 사용했다면 warning 도 해 줍니다.


이 정보가 여러분 코딩을 좀 더 쉽고 빠르게 하는데 도움이 되기를 바랍니다.


==> 이번 글은 BeyondtheTech 라는 곳에서 공유한 팁 입니다. 원문은 저 위의 제목을 클릭하면 보실 수 있습니다.

저작자 표시 비영리 동일 조건 변경 허락
신고

런타임 에러 처리하기

2013.03.07 22:29 | Posted by 솔웅


Posted on . Written by



안드로이드용으로 빌드하시는 분들은 에러를 report 하는 방법이 바뀐것을 공지해 드렸었습니다. 이 기능은 안드로이드쪽에 먼저 작업이 됐는데요 그 이유는 custom 안드로이드 퍼미션 기능을 implement 하는 부분을 수정하면서 런타임 에러를 처리하는 기능이 개선될 필요가 있었기 때문입니다.

이제 다른 플랫폼에도 이 기능이 지원돼야 겠죠. iOS 와 시뮬레이터 등에도 런타임에러를 캐치하고 처리하는 기능이 지원될 차례입니다. 에러를 trap 해서 런타임 에러 팝업을 보여줄지 안 보여줄 지 하는  기능도 지원되게 됩니다. 이 기능을 통해서 런타임 에러를 fix 하는 것은 아니지만 그 런타임 에러를 감출수는 있을 겁니다.

런타임 에러를 처리하는 예제입니다.

local function myUnhandledErrorListener( event )

    local iHandledTheError = true

    if iHandledTheError then
        print( "Handling the unhandled error", event.errorMessage )
    else
        print( "Not handling the unhandled error", event.errorMessage )
    end
    
    return iHandledTheError
end

Runtime:addEventListener("unhandledError", myUnhandledErrorListener)



이 리스너에 pass 된 이벤트 테이블은 errorMessagestackTrace 파라미터를 가지고 있습니다. 이 에러 메세지는 에러의 result 가 되겠죠 그리고 stack trace 는 에러의 마지막 부분이 될 겁니다. 리스너에서 true 가 리턴되면 팝업 alert 메세지가 뜨는 걸 방지하고 앱이 계속 진행하도록 합니다. false가 return 되면 팝업이 보여지고 앱이 멈추게 되겠죠. (이 경우는 unhandledError 리스너가 적용되지 않은 상황에서도 일어나는 현상입니다.)

다음 daily build 에서 여러분이 보게 될 가장 큰 변화 내용은 런타임 에러가 났을 때의 팝업 메세지 입니다. 런타임 에러를 trap 하고 팝업창이 뜨는 것을 막고 앱이 중지하는 것을 막으시려면 위에서 처럼  unhandledError 리스너를 추가하시면 됩니다.

위의 기능은 daily build 1044 이후부터 적용될 겁니다.


저작자 표시 비영리 동일 조건 변경 허락
신고


Posted on . Written by




수요일의 FAQ 시간입니다. 오늘은 코로나 런타임 에러에 대한 질문들을 다뤄 보겠습니다.




1. My app was working fine and now I’m seeing a pop-up saying an error was found and my app quits. What changed?



안드로이드에서는 build 2013.1030 부터 적용된 건데요, run-time error 가 일어나면 경고창이 뜨고 그 에러에 대한 정보를 보여주게 됩니다. 이전에는 런타임에러가 adb logcat 화면에만 표시되던가 아니면 아무런 표시 없이 지나갔었거든요. 이 기능은 현재 iOS build 에서는 지원되지 않습니다.



2. I don’t remember getting these errors before. Are these real errors?



예 만약 런타임 에러가 났다면 exception이 일어났기 때문입니다. 그러면 그 프로그램은 그 에러 이후의 코드를 실행하지 않습니다. 여러 에러가 있어도 그 에러가 한번에 다 표시되는 게 아니거든요. 혹은 어떤 특정 디바이스에서만 에러가 일어날 수도 있구요.


일반적으로 이런 에러들은 API 에 의해 nil 값이 return 되서 일어나는 경우가 많습니다. 예를 들어 객체의 포인터가 nil일 때 Corona 객체를 remove 하는 경우를 들어보죠. 그리고 nil인 값을 return 한 것을 받아서 그 스트링을 연결하고 display 하는 경우도 예를 들 수 있습니다. 일반적으로 그 값을 사용하려는 함수의 out of scope 인 경우가 많습니다.



Here is one example:


local touchRect = display.newRoundedRect( 0, 50, 70, 30, 10 )
touchRect.x = display.contentCenterX

local count1 = 0
local count2 = 0
local count3 = 0
        
function touchRect:touch( event )
    if event.phase == "ended" then
        count1 = count1 + 1
        count2 = count2 + 1
        count3 = count3 + 1

        text1.text = "Count1 = " .. count1
        text2.text = "Count2 = " .. count2    -- << error here
        text3.text = "Count3 = " .. count3        
    end
    
    return true
end

text1 = display.newText( "Count1 = 0", 10, 100, native.systemFont, 20 )
local text2 = display.newText( "Count2 = 0", 10, 140, native.systemFont, 20 )
text3 = display.newText( "Count3 = 0", 10, 170, native.systemFont, 20 )

touchRect:addEventListener( "touch" )


하얀 사각형을 두드리면 한번에 3개의 count 가 증가하고 그 증가된 값이 display 됩니다. text2.text 가 세팅될 때 버그가 있는데요. 그 에러는 "attempt to index global 'text2' (a nil value)" 입니다. 이유는 text2가 로컬 변수로 정의 됐고 또 touch 함수 이후에 정의 됐기 때문이죠. 이 경우가 바로 함수의 out of scope (범위 밖) 에서 정의 된 것으로 그 값이 nil 이 되는 경우입니다.

시뮬레이터에서 위 코드를 실행하고 사각형을 두드리면 첫번째 counter 가 증가되는 것까지 보실 수 있을 겁니다. 왜냐하면 text2.text 부분에서 에러가 났기 때문이죠. 그 이후의 코드는 실행이 되지 않습니다. 그 사각형을 계속 두드릴 수 있습니다. 그러면 첫번째 counter 만 계속 증가할 겁니다. 사용자 입장에서는 프로그램에 에러없이 잘 실행된다고 느낄 수 있죠.


안드로이드 코드에 저희들이 준 변화는 이 경우 런타임 에러를 감지하고 팝업창에 에러가 난 파일 이름과 line number 를 display 하도록 했습니다. 그러면 그 라인 이후의 코드는 아직 실행이 안 됐다는 것을 알 수 있죠. 이 메세지를 보시려면 콘솔 윈도우를 열어 놓으셔야 합니다. (iOS 에서는 Xcode 가 될 테고 안드로이드 에서는 adb logcat 이 되겠죠.)

위 에러는 text2를 함수가 정의되기 이전에 정의해 버리면 쉽게 고칠 수 있습니다. 혹은 text2에서 local을 없애서 정의 해도 되죠.


text2 = display.newText( "Count2 = 0", 10, 140, native.systemFont, 20 )



3. How do I keep the pop-up from happening when I release my app to Google Play?



이 팝업을 피하는 방법은 두가지가 있습니다.


1) 모든 버그를 찾아서 고치기
2) 문제가 발생할 수 있는 코드 부근에 pcall 구문을 추가하기

앱에서 모든 버그를 찾아서 고치는건 불가능에 가깝습니다. 그래서 에러를 trap 하고 이 에러를 어떻게 할 것인지 선택할 수 있는 런타임 리스너를 추가할 계획을 가지고 있습니다. 앱 스토어용으로 앱을 빌드하고 유저가 에러가 있는 앱을 실행하면 로그가 기록되서 여러분 서버로 이것을 보낼지 아니면 그냥 넘어갈지를 선택해서 실행할 수 있게 됩니다. 이 기능은 현재는 지원되지 않구요 조만간 업데이트 될 Daily Build 에 적용 될 예정입니다.


4. Is there any way to trap the errors myself and not have it quit the app or display a pop-up to the user?



새로운 런타임 에러 리스너가 포함되게 될 Daily Build 를 기다리기 전에 여러분이 하실 수 있는 방법이 있습니다. Lua pcall 구문으로 여러분 코드 블럭을 감싸서 에러를 trap 할 수 있습니다. pcall (protected call) 은 execution status 를 return 하는 call 로 어떤 함수나 메소드든지 call 할 수 있습니다. call 이 제대로 실행 됐다면 true 를 반환할 것이고 런타임 에러 때문에 실패 했다면 false를 반환할 겁니다. 일단 런타임 에러를 trap 하면 pop-up을 띄우거나 app을 quit 하지 않을 겁니다. pcall을 사용하면 약간의 overhead 가 있을 수 있습니다. 그러니까 꼭 에러를 감지해서 어떤 일을 해야 할 경우에만 사용하시기 바랍니다.


The syntax of pcall is:

pcall( f [, ...] )



where f is the function to be called,
and ... is the arguments for the function.

이 구문을 연습해 보기 위해 함수 안의 text2 세팅 부분을 감싸봤습니다. 그리고 이것을 pcall 을 사용해서 call 했습니다. 이 call 의 결과는 status 에 저장이 되서 display 될 겁니다.


local touchRect = display.newRoundedRect( 0, 50, 70, 30, 10 )
touchRect.x = display.contentCenterX

local count1 = 0
local count2 = 0
local count3 = 0

local function setText2( value )
    text2.text = "Count2 = " .. value
end
        
function touchRect:touch( event )
    if event.phase == "ended" then
        count1 = count1 + 1
        count2 = count2 + 1
        count3 = count3 + 1

        text1.text = "Count1 = " .. count1
        
        local status = pcall( setText2, count2 )
        print( "pcall status is ", status )
        
        text3.text = "Count3 = " .. count3        
    end
    
    return true
end

text1 = display.newText( "Count1 = 0", 10, 100, native.systemFont, 20 )
local text2 = display.newText( "Count2 = 0", 10, 140, native.systemFont, 20 )
text3 = display.newText( "Count3 = 0", 10, 170, native.systemFont, 20 )

touchRect:addEventListener( "touch" )


위 코드를 실행시키면 pcall status 가 false가 될 겁니다. 즉 런타임 에러가 났다는 것이죠. counter 1하고 3은 증가할 겁니다. 즉 pcall 이 런타임 에러를 trap 하고 그 안에 런타임 에러가 있더라도 이후의 코드가 실행된다는 것을 알 수 있을 겁니다.

pcall 은 여러분 코드 중에 fail 일 것 같은 구문이 있고 그 부분을 테스트하거나 guard 하고 싶을 때 사용하실 수 있을 겁니다.



5. Why is the error pop-up only in Android builds?



일단 안드로이드쪽을 작업했습니다. 그 이유는 안드로이드 빌드와 관련해서 default permission들을 없애면서 이 에러들을 다뤄야 할 필요성을 느꼈었거든요. 개발자들에게 이 에러들을 감지하고 trap 하는 기능이 제공되면 아주 유용할 거라고 생각했습니다. 그리고 이 기능을 iOS에도 적용할 거고 Mac 과 윈도우즈 시뮬레이터에도 적용할 계획입니다.

현재 릴리즈 버전과 Daily build 에서는 여러분 앱의 버그를 찾을 수 있도록 하기 위해 런타임 에러 정보를 제공합니다. developer build 를 할 때 파일이름과 line number 그리고 에러의 type 을 콘솔을 통해 보실 수 있을 겁니다. Release build (앱 스토어용)에서는 에러 type 만 보실 수 있습니다.


추가적으로 말씀 드린다면 맥 시뮬레이터를 사용하신다면 Corona Simulator가 아니라  Corona Terminal을 start 하는 걸 잊지 마세요. Corona Terminal 은 console 창을 띄우고 나서 Corona Simulator를 실행할 겁니다. 이 콘솔창에는 print, warning 그리고 에러 메세지 등이 display 될 겁니다.


여기까지가 오늘의 FAQ입니다. 도움이 되셨기를 바랍니다.

저작자 표시 비영리 동일 조건 변경 허락
신고


Posted on . Written by



수요일 FAQ 시간이 돌아왔습니다. 오늘은 서브 폴더와 파일들에 접근하는 것에 대한 FAQ 입니다.


1. How do you create a new sub-folder within the Documents or Temporary directory?



루아 파일 시스템(LFS)을 통해서 디렉토리에 서브 폴더를 추가할 수 있습니다. Resource 디렉토리는 modify 될 수 없는 read-only 입니다.

아래에 Documents 디렉토리에 어떻게 Images 폴더를 생성할 수 있는지에 대한 예제가 있습니다.


local lfs = require "lfs"

-- get raw path to app's Documents directory
local docs_path = system.pathForFile( "", system.DocumentsDirectory )

-- change current working directory
local success = lfs.chdir( docs_path ) -- returns true on success
local new_folder_path
local dname = "Images"
if success then
    lfs.mkdir( dname )
    new_folder_path = lfs.currentdir() .. "/" .. dname
end



여기에서 LFS 에 대한 좀 더 자세한 정보를 얻으실 수 있습니다.




2. How do you access (read or write) a file that has been placed in a sub-folder?


여러분은 두가지 방법으로 서브 폴더에 있는 파일에 access 하실 수 있습니다. 그 파일로 무엇을 할 것인지에 따라 접근하는 방법이 다른데요. 이미지를 display 하거나 sound 를 play 하신다면 서브 폴더 이름에 파일이름을 연결해서 거기에 base 디렉토리를 지정해 주시면 됩니다. 예를 들어 Document 디렉토리의 Images 서브폴더에 있는  cat.png 파일을 display 하고 싶다면 아래와 같이 하시면 됩니다.


local catImage = display.newImage( "Images/cat.png", system.DocumentsDirectory, 0, 0 )


Note : baseDirectory 파라미터가 필요한 system.pathForFile 을 API call 에서 사용하지 않은 점을 유의하세요. (e.g., display.newImage, display.newImageRect, audio.loadSound, etc.)


만약 같은 디렉토리의 readme.txt 파일을 열어보고 싶으시면 system.pathForFile 을 사용해서 아래와 같이 하세요.


local path = system.pathForFile( "Images/readme.txt", system.DocumentsDirectory )
local fileHandle = io.open( path )
-- You can now use fileHandle:read or fileHandle:write to read or write the file.


해당 파일이 있다면 fileHandle 은 nil 이 아니겠죠. 그 다음은  아래 질문과 연결 되게 됩니다.



3. How do you test that a file exists in a folder or sub-folder?



해당 폴더나 서브폴더 안에 특정 파일이 존재하는지를 보기 위해 아래와 같이 코딩 하실 수 있습니다. 이 함수를 call 하기 전에 서브폴더 이름을 file 이름에 append 하는 것을 잊지 마세요.


----------------------------------------------------------------------------------
-- doesFileExist
--
-- Checks to see if a file exists in the path.
--
-- Enter:   name = file name
--  path = path to file (directory)
--  defaults to ResourceDirectory if "path" is missing.
--
-- Returns: true = file exists, false = file not found
----------------------------------------------------------------------------------
--
function doesFileExist( fname, path )

    local results = false

    local filePath = system.pathForFile( fname, path )

    -- filePath will be nil if file doesn't exist and the path is ResourceDirectory
    --
    if filePath then
        filePath = io.open( filePath, "r" )
    end

    if  filePath then
        print( "File found -> " .. fname )
        -- Clean up our file handles
        filePath:close()
        results = true
    else
        print( "File does not exist -> " .. fname )
    end

    print()

    return results
end


위의 함수가 어떻게 call 되는지 보겠습니다.


-- Checking for file in Documents directory
local results = doesFileExist( "Images/cat.png", system.DocumentsDirectory )
    
-- or checking in Resource directory
local results = doesFileExist( "Images/cat.png" )


4. How do you copy a file to a sub-folder?

아래 코드는 파일을 A 폴더에서 B 폴더로 copy 하는 예제 입니다. 흔히 사용하는 방법은 파일을 카피하기 위해 Resource 디렉토리에서 Document 디렉토리로 옮기는 겁니다.
이 함수를 사용하기 전에 해당 서브 폴더들이 있어야 합니다.


----------------------------------------------------------------------------------
-- copyFile( src_name, src_path, dst_name, dst_path, overwrite )
--
-- Copies the source name/path to destination name/path
--
-- Enter:   src_name = source file name
--      src_path = source path to file (directory), nil for ResourceDirectory
--      dst_name = destination file name
--      overwrite = true to overwrite file, false to not overwrite
--
-- Returns: false = error creating/copying file
--      nil = source file not found
--      1 = file already exists (not copied)
--      2 = file copied successfully
----------------------------------------------------------------------------------
--
function copyFile( srcName, srcPath, dstName, dstPath, overwrite )

    local results = false

    local srcPath = doesFileExist( srcName, srcPath )

    if srcPath == false then
        -- Source file doesn't exist
        return nil
    end

    -- Check to see if destination file already exists
    if not overwrite then
        if fileLib.doesFileExist( dstName, dstPath ) then
            -- Don't overwrite the file
            return 1
        end
    end

    -- Copy the source file to the destination file
    --
    local rfilePath = system.pathForFile( srcName, srcPath )
    local wfilePath = system.pathForFile( dstName, dstPath )

    local rfh = io.open( rfilePath, "rb" )

    local wfh = io.open( wfilePath, "wb" )

    if  not wfh then
        print( "writeFileName open error!" )
        return false            -- error
    else
        -- Read the file from the Resource directory and write it to the destination directory
        local data = rfh:read( "*a" )
        if not data then
            print( "read error!" )
            return false    -- error
        else
            if not wfh:write( data ) then
                print( "write error!" )
                return false    -- error
            end
        end
    end

    results = 2     -- file copied

    -- Clean up our file handles
    rfh:close()
    wfh:close()

    return results
end


아래에 readme.txt 파일을 Resource 에서 Documents 디렉토리에 복사하는 방법이 있습니다.


copyFile( "readme.txt", nil, "readme.txt", system.DocumentsDirectory, true )
local catImage = display.newImage( "cat.png", system.DocumentsDirectory, 0, 0 )



5. What are the Android restrictions concerning files?


코로나에서의 File access 는 해당 운영체제(Operating System) 에 기반해서 이루어 집니다. 즉 플랫폼에 의존적인 거죠. iOS 디바이스에서는 Resource 디렉토리(main.lua 가 있는 디렉토리) 에 access 하실 수 있습니다. 그외에 Documents 그리고 Temporary 디렉토리들에 access 하실 수 있죠. 안드로이드에서는 Resource 디렉토리에 제한이 있습니다. 왜냐하면 그 디렉토리는 실제 디렉토리가 아니기 때문이죠. 파일들은 zip 파일로 압축돼 있습니다. 코로나는 audio와 image API들을 사용해서 직접 이미지와 오디오를 로딩할 수 있도록 해 줍니다. 하지만 I/O API를 사용해서 Resource 파일들에 접근하는 것은 한계가 있습니다.


안드로이드의 이러한 제한사항 때문에 다른 디렉토리에 카피할 Resouce 디렉토리 내의 파일을 옮긴다면 resource 디렉토리에 있는 파일 이름을 바꾸어야 합니다. 그래야 file I/O API에 의해 접근 될 수 있습니다. 예를 들어 이미지 파일을 Resource 에서 documents 디렉토리로 옮기고 싶다면 다른 확장자를 가진 파일로 이름을 바꾸셔야 합니다. 그래야 접근할 수 있습니다. cat.png cat.png.txt 로 바꾼 후 카피할 수 있습니다.


아래에 안드로이드에서 어떻게 cat.png 파일을 Document 디렉토리로 복사하는지 알려주는 예제가 있습니다. (assuming it was stored as cat.png.txt)


copyFile( "cat.png.txt", nil, "cat.png", system.DocumentsDirectory, true )
local catImage = display.newImage( "cat.png", system.DocumentsDirectory, 0, 100 )


안드로이드에서 Resource 디렉토리 안에 있는 것 중 읽을 수 없는 확장자들 : html, htm, 3gp, m4v, mp4, png, jpg, and rtf


위 방법은 모든 플랫폼에서 사용 가능합니다. 그러니 안드로이드에서 제대로 돌아가면 어디서든지 돌아갑니다.


오늘은 여기까지 입니다. 유익한 시간이었기를 바랍니다.

저작자 표시 비영리 동일 조건 변경 허락
신고


by Corona Labs on Wednesday, January 30, 2013 at 7:32pm ·



When leaving a storyboard scene, you are required to clean up:

 

Runtime listeners    

Timers    

Transition's that have an onComplete    

Audio that has an onComplete    

Network requests that have not completed yet and will call back to a listener.

 

Also, if you load sounds in your storyboard scene in either createScene, willEnterScene or enterScene, it's your responsibility to dispose of them in exitScene() or destroyScene().



스토리보드 장면을 떠날때는 깨끗하게 정리하고 가셔야 합니다.


런타임 리스너

타이머

onComplete 된 Transition

onComplete 된 오디오

아직 complete 되지 않았고 리스너로 call back 될 네트워크 요청


그리고 createScene, willEnterScene or enterScene 안에서 스토리보드에 사운드를 로드했다면 exitScene() or destroyScene() 에서 그것들을 dispose 시켜주셔야 합니다.



댓글에는 이미지나 위젯들도 clean up 해 줘야 된다고 써있네요.

그리고 transition이나 network request 는 어떻게 cancel 해야 하냐는 질문도 있구요.

아직 답글은 안 달렸고.....

그거 보니까 작년 12월에 보낸 제 질문 메일이 생각납니다.

이 블로그에 올라온 질문들 정리해서 메일로 보냈었는데... 한달째 감감무소식....

이젠 메일로 보내지 말고 포럼에 올려봐야겠어요.

저작자 표시 비영리 동일 조건 변경 허락
신고



Posted on . Written by



수요일의 FAQ 시간입니다. 이번주는 정확하게 FAQ라고는 할 수 없는데요. Daily Builds 에 최근 적용된 맥 시뮬레이터와 관련된 업데이트들을 다루겠습니다.


1. Loading the last project on simulator startup


Daily Build #996에서 새로 Preferences menu에 새로운 preference 가 추가됐습니다. “Automatically open last project” "바로 전 project 자동적으로 open 하기" 입니다. 이 기능은 이전에 작업했던 프로젝트를 계속 작업해야 될 때 아주 유용하겠죠.




2. Reloading the last project


맥과 윈도우 시뮬레이터에서 Cmd R (Ctrl R on Windows) 를 사용해서 지난번 프로젝트를 Relaunch 할 수 있었습니다. 그런데 맥 시뮬레이터에서는 이 시뮬레이터가 처음 시작했을 경우는 이 기능을 사용할 수 없었는데요. build #1017 에서 이 기능이 가능하게 됐습니다. 그리고 위 1번에서 소개했던 relaunch 를 항상 실행되게 할 수도 있습니다.



3. Simulator now continues to run after a build


맥 시뮬레이터로 프로젝트를 빌드했다면 빌드가 진행될 때 디바이스 skin 윗부분에 giant “iOS spinner”가 나오는 것을 보셨을 겁니다. 이게 빌드가 다 끝나고 난 다음에도 계속 나타났었죠. 그리고 시뮬레이터가 suspend 됐었습니다. 이제는 suspend 되지 않고 시뮬레이터가 계속 실행됩니다. 여러분 스스로 시뮬레이터를 suspend 시키시려면 Hardware menu에서 Suspend 를 선택하거나 Cmd downarrow를 누르시면 됩니다.

4. Unicode characters in asset file names


build #1017에서 맥 시뮬레이터는 asset file들의 이름에서 unicode를 지원합니다. 이렇게 함으로서 이미지나 사운드 파일과 하위 디렉토리까지 사용할 수 있게 됐습니다. 윈도우에서는 아직 파일이름에 유니코드를 사용하는데 약간의 이슈가 있습니다.



5. Supporting iOS 6.1 builds


1월 28일 애플이 공식적으로 iOS 6.1을 release 했습니다. 저희 build server에는 최신버전으로 인스톨할 계획입니다. 그리고 며칠 후에 여러분들이 그 최신 버전을 사용하실 수 있을 겁니다. (check the Daily Build summary page) 이후에 나올 몇개의 Daily Builds 에서는 현재 버전인 iOS 6.0 SDK와 새 버전인 6.1 SDK를 모두 지원하게 될 겁니다. build window에서 pull-down menu 를 선택해서 원하시는 iOS SDK 버전을 선택하시면 됩니다.

시뮬레이터를 좀 더 개선하거나 추가했으면 하는 기능이 있으면 Corona Feedback page 에 올려 주세요.


오늘은 여기까지 입니다. 여러분에게 도움이 되었기를 바랍니다.






저작자 표시 비영리 동일 조건 변경 허락
신고


Facebook Corona TIP



Want to turn off your debug print statements for your production build?


production build에서는 디버그용 print 구문이 실행되지 않도록 하고 싶으세요?


 

Add this little block of code to your main.lua:


main.lua 파일에 아래 코드 블럭을 추가하세요.


 

debugMode = true

cachePrint = print

function print(...)   

if debugMode then       

cachePrint(unpack(arg))   

end

end

 


Then to turn it off, change "debugMode" to false.


print 구문을 출력하고 싶지 않으시면 간단하게 debugMode를 false로 바꿔주시기만 하면 됩니다.




저작자 표시 비영리 동일 조건 변경 허락
신고


Posted on

. Written by


수요일 FAQ 시간입니다. 코로나에서 custom fonts 를 사용하는것과 관련해서 자주 질문되는 것들입니다.

1. How do I load custom fonts?

어떻게 사용하는 platform 에 따라서 폰트들을 load 할 수 있나요?


iOS (iPhone, iPad)


iOS (아이폰, 아이패드) 는 build.settings 파일에 font 파일 이름을 추가하면 됩니다.


    iphone =
    {
        plist =
        {
            UIAppFonts =
            {
                "PTF55F.ttf",
                "AvenirLTStd-Black.otf",
                "SourceCodePro-Black.ttf"
            },
            UIApplicationExitsOnSuspend = true
        },
    }


폰트파일들은 resource 디렉토리에 있어야 합니다. 그래야지 iOS app bundle 로 compile 될 수 있습니다.


Mac


맥은 이 폰트들을 여러분의 시스템 안에 인스톨 해야 합니다. 폰트파일을 더블클릭하시면 Font Book app 이 그 폰트를 인스톨 할 겁니다. Mac 에서는 build.settings 파일에 추가할 것은 없습니다.


Android


폰트 파일이 resource 디렉토리에 있어야 합니다. 그래야 안드로이드 앱 bundle 로 컴파일 될 수 있습니다. 안드로이드에서는 build.settings file에 추가될 것은 없습니다.


Windows


윈도우에 custom font를 인스톨하는 것은 맥에서 인스톨하는 거랑 비슷합니다. 그 폰트를 사용하려면 시스템에 폰트를 인스톨 해야 합니다. 윈도우에서는 build.settings file에 추가될 것은 없습니다.



2. How do I use the custom fonts in my app?


Mac and iOS


맥과 iOS 에서 폰트를 사용하려면 폰트 이름을 명시해야 합니다. (파일이름이 아닙니다.) 해당 폰트의 이름이 무엇인지 알려주는 툴이 있을 겁니다. 제가 찾은 가장 쉬운 방법은 폰트를 load 하고 코로나에게 폰트 이름을 표시하도록 하는 겁니다. 시스템에 해당 폰트를 인스톨 했다면 맥 시뮬레이터에서 아래 코드를 실행해 보세요.


-- Code to have Corona display the font names found
--
local fonts = native.getFontNames()

count = 0

-- Count the number of total fonts
for i,fontname in ipairs(fonts) do
    count = count+1
end

print( "\rFont count = " .. count )

local name = "pt"     -- part of the Font name we are looking for

name = string.lower( name )

-- Display each font in the terminal console
for i, fontname in ipairs(fonts) do
    j, k = string.find( string.lower( fontname ), name )

    if( j ~= nil ) then

        print( "fontname = " .. tostring( fontname ) )
    
    end
end
---------------------------------------------------------


예를 들어 PTF55F는 “PTSerif-Regular” 가 될 겁니다. 바로 이 이름이 해당 폰트를 display 하고 싶을 때 명시해야 할 이름입니다.


CustFont = display.newText( "PTSerif-Regular", 40, 20, "PTSerif-Regular", 24 )

Android


안드로이드에서는 파일 이름이 폰트 이름입니다. (파일의 확장자를 제외한 이름이죠)


CustFont = display.newText( "PTSerif-Regular", 40, 20, "PTF55F", 24 )


안드로이드와 iOS 그리고 맥에서 같은 폰트 이름을 사용할 수 있는 팁이 있는데요. 폰트 파일 이름을 폰트 이름과 같이 하는 겁니다. 그려면 iOS/Mac 그리고 안드로이드에서 같은 이름을 사용할 수 있습니다.


Windows


The font name may be slightly different from Mac/iOS. In come cases spaces are inserted between words in the font name. In the PT Serif example, the font name is “PT Serif”. You can run the code snippet listed under the Mac and iOS section to get the exact name.

폰트 이름은 MAC/iOS 와는 약간 다릅니다. PT Serif 를 예를 들면 폰트 이름은 "PT Serif" 입니다.


CustFont = display.newText( "PTSerif-Regular", 40, 20, "PT Serif", 24 )


아래 예제는 각 플랫폼에 대해 폰트를 사용하는 예제입니다.


if "Win" == system.getInfo( "platformName" ) then PTSERIF = "PT Serif" elseif "Android" == system.getInfo( "platformName" ) then PTSERIF = "PTF55F" else -- Mac and iOS PTSERIF = "PTSerif-Regular" end CustFont = display.newText( "PTSerif-Regular", 40, 20, PTSERIF, 24 )


3. I’ve loaded the fonts but the fonts didn’t load. What’s wrong?


안드로이드와 윈도우즈에서는 폰트를 로드하지 못할 때는 터미널에 경고 메세지가 뜰겁니다.“WARNING: Could not load font xyz. Using default.”. iOS 와 맥에서는 로드를 하지 못했을 때 아무런 메세지도 뜨지 않습니다.

폰트를 로드하지 못하는 두가지 흔한 이유는 시스템이 로드되지 못한경우라던가 (윈도우나 맥) display.newText api에 잘못된 폰트 이름을 사용했을 경우 입니다. 윈도우와 관련해서 추가적으로 알아야할 것이 있는데 그 내용은 아래 질문 4에서 확인하세요.


4. Are OTF (Open Type Font) fonts supported in Corona?


OTF fonts는 맥과 iOS 에서 지원됩니다. 안드로이드는 build 984 후부터 지원하기 시작했습니다. 윈도우에서는 지원하지 않습니다. True Type Fonts (TTF) 는 모든 플랫폼에서 지원합니다.


5. What are the advantages of using Custom Fonts?


일단 custom font를 사용하면 더 이쁘거나 아니면 더 눈에 띄거나 앱에 더 잘 어울리거나 하겠죠. 그리고 또 다른 장점은 모든 플랫폼에서 text placement 의 일관성을 유지할 수 있습니다. 디폴트 폰트를 사용하면 각 플랫폼들마다 폰트가 다를수 있습니다. 그러면 폰트들 크기가 달라서 폰트들의 위치가 약간씩 다를 수 있습니다. custom font 를 사용하면 좀 더 쉽게 font placement 의 일관성을 지킬수가 있죠. 


여기까지가 오늘의 questions 입니다. 좋은 시간 되셨길 바랍니다.



저작자 표시 비영리 동일 조건 변경 허락
신고

Corona tip: Shuffle it!

2013.01.10 19:26 | Posted by 솔웅


Corona tip: Shuffle it!


랜덤하게 어떤 것을 뽑아내고 싶으세요? 그러면 아래처럼 Shuffle 함수를 만들어서 사용하세요.


 

local function shuffle(t)    

local rand = math.random    

assert(t, "shuffle() expected a table, got nil")    

local iterations = #t     local j        

 

for i = iterations, 2, -1 do        

j = rand(i)    

  t[i], t[j] = t[j], t[i]    

end

end

 

numbers = {1,2,3,4,5,6,7,8,9,10,11,12} -- say up to 52 if your're doing a card game   

 

shuffle(numbers)

 


간단하게 1에서 #numbers 까지의 테이블을 루프 돌리고 그 entry들의 배열을 랜덤하게 정렬하세요. 그 값들이 숫자가 아니라 문자라도 상관없습니다. 그리고 이미지라도 상관 없구요. 노래가 담긴 오디오 파일들일 수도 있겠죠. 그러면 많은 노래들을 shuffle 해서 play 시킬 수 있겠죠.






저작자 표시 비영리 동일 조건 변경 허락
신고

티스토리 툴바