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

최근에 받은 트랙백

글 보관함


이번주 튜토리얼은 Performance Optimizations 에 관한 내용입니다.

개발 할 때 항상 중요하게 생각해야 할 내용들이죠.


Coronasdk 에서 제공하는 글에서 여러번 강조된 내용이기도 합니다.

계속 강조하는 이유는 그만큼 중요하기 때문이겠죠.


10가지의 팁을 제공하고 있는데요.

시간 나는대로 정리해서 올릴께요.


몇번에 나눠서 글이 정리될지 모르겠네요.

제가 참여하는 프로젝트가 이번달 말로 끝나거든요.

다른 position 을 찾아봐야 되서 그렇게 여유가 없네요.



Posted on . Written by




오늘의 튜토리얼은 퍼포먼스 최적화와 관련된 내용입니다. 모든 개발자들이 항상 신경써야 할 주제죠. 이 중 어떤 팁들은 뻔한 내용일 수 있습니다. 프로그래밍을 하면서 코드를 최적화 하는 작업은 소요되는 시간그로 인해 얻는 이익을 저울질 해서 처리해야 할 문제 입니다.

어떤 최적화가 구식 디바이스에서 2%의 퍼포먼스 개선을 불러올 수 있는데 그 일을 하려면 50시간을 투자해야 한다면 그 작업을 해야 될 필요가 없을 수도 있습니다. 그리고 어떤 최적화는 10시간 정도 소요되는데 아주 많은 디바이스들에서 큰 성능 개선이 일어날 수 있다면 그 최적화는 10시간을 들이더라도 반드시 해야 되겠죠.

새로운 프로젝트들을 시작할 때 퍼포먼스가 크게 개선되고 앱 코드와 동작이 깨끗해지고 그래서 모든 디바이스들에서 user experience 가 아주 개선 된다면 그런 결과를 유도할 수 있는 performance trick들은 많이 사용하면 사용할 수록 좋을 겁니다.



“Time-Critical” Routines


대부분의 performance trick 들은 주로 “time-critical” routines 들을 위한 방법들이 제공될 겁니다. 앱이 버벅거려서 유저들이 사용하는데 불편을 느끼거나 하면 안되니까요. 예를 들어 액션게임을 하는데 new scene 이 로딩되는 시간이 너무 길거나 하면 불편해서 유저들이 잘 사용하지 않을 겁니다.


1. Localize, Localize


이것은 몇번을 강조해도 부족하지 않은 내용입니다. 글로벌 변수나 글로벌 함수를 가급적 사용하지 않는 것이 성능 향상에 아주 도움이 됩니다. 로컬 변수와 로컬 함수에 접근하는 것이 훨씬 빠르니까요. 특히 time-critical routines 에서는요.



이 이미지는 이 글 내용과 거의 관계가 없네요. ;; 왜 이 이미지가눈에 띄었을까?


Non-Local — Discouraged

CCX = display.contentCenterX  --global variable
for i = 1,100 do
   local image = display.newImage( "myImage" )
   image.x = CCX
end


Local — Recommended

local CCX = display.contentCenterX  --local variable
for i = 1,100 do
   local image = display.newImage( "myImage" )
   image.x = CCX
end


또한 이것은 math library 같은 core Lua 라이브러리에 적용됩니다. time-critical routines 에서 여러분은 항상 라이브러리 함수들을 localize 해야 합니다.


Non-Local — Discouraged

local function foo( x )
   for i = 1,100 do
      x = x + math.sin(i)
   end
   return x
end


“External” Local — Recommended

local sin = math.sin  --local reference to math.sin
local function foo(x)
   for i = 1,100 do
      x = x + sin(i)
   end
   return x
end

마지막으로 함수들은 가능하면 항상 localize 되어야 한다는 것을 기억하고 계세요. 물론 scoping 을 제대로 해야 하겠죠. 만약 Lua 초보자이시라면 링크 Understanding Scope for Beginners 를 참조하세요.


Non-Local — Discouraged

function func1()
   func2( "myValue" )
end

function func2( y )
   print( y )
end

func1()


Local — Recommended

--"func2" properly scoped above "func1" 
local function func2( y )
   print( y )
end

local function func1()
   func2( "myValue" ) 
end

func1()


오늘은 시간 관계상 1번 팁만 소개해 드립니다.

다음 글에선 좀 더 많은 팁들을 올릴께요.

원문은 저 위의 제목을 클릭하면 보실 수 있으니까 관심 있는 분들은 저 위의 제목을 클릭해서 보세요.


반응형

Comment


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 라는 곳에서 공유한 팁 입니다. 원문은 저 위의 제목을 클릭하면 보실 수 있습니다.

반응형

Comment

런타임 에러 처리하기

2013. 3. 7. 05: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 이후부터 적용될 겁니다.


반응형

Comment


Posted on . Written by



build 2013.1043 버전부터 iOS, Android, Mac 그리고 윈도우즈에 모두 적용된 건데요. network api에 새로운 기능을 추가했습니다. 다운로드나 request 같은 네트워크 function들이 이 새로운 기능으로 좀 더 개선 됐습니다. 여기 daily API docs 의 networkRequest에 있는 network와 event 부분을 읽어 보세요. single call에서 텍스트 파일을 업로드하고 bytes transferred 를 보고 하는 것들을 어떻게 하는지 확인해 보세요.


또한 이전 버전과 호환이 되지 않은 새로운 기능 변화가 몇개가 있는데요. 아래 예제에서 보시듯이 event response들은 이제 fileName과 baseDirectory가 포함된 테이블이 됐습니다. 이제 event.response를 string 이 아니라 table로 취급해야 되는 것을 잊지 마세요.

아래에 새로운 event.response table 정보를 어떻게 사용하는지에 대한 예제가 있습니다.


local function networkListener( event )
        if ( event.isError ) then
                print( "Network error - download failed" )
        elseif ( event.phase == "ended" )
                print( "displaying response image file" )
                myImage = display.newImage( event.response.filename, event.response.baseDirectory, 60, 40 )
                myImage.alpha = 0
                transition.to( myImage, { alpha = 1.0 } )
        end
end
 
network.download(
        "http://developer.anscamobile.com/demo/hello.png",
        "GET",
        networkListener,
        "helloCopy.png",
        system.TemporaryDirectory
        )


그리고 코로나에서 지난주와 이번주에 새로운 위젯들을 선보였습니다.

관련 글들은 따로 번역해서 올리지는 않고 아래 링크로 대신 할 께요.

이 글들에 있는 샘플코드를 실행하시던가 새로운 Daily Build 에 예제들을 보시면 도움이 되실 겁니다.




반응형

Comment

  1. 저도 이래저래 프로그램을 하고 있지만 항상 새로운
    소스코딩을 하려면 힘들고 거기에 익숙해 지기까지는 참으로
    많은 노력과 시간이 필요한것 같습니다.


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입니다. 도움이 되셨기를 바랍니다.

반응형

Comment


Posted on . Written by



오늘 업데이트 된 Corona Cloud 와 현재 분석 시스템을 이후 몇주간 어떻게 적용할 것인지에 대해 간단히 말씀 드리겠습니다.

우선 Corona Cloud 에 대해서인데요. 이제 거의 완료 됐습니다. 베타 버전에 대한 response 는 경이적이었습니다. 그래서 여러분에게 모바일 개발이 더 빠르고 더 쉽게 될 거라는 이 사실들을 공개하지 않을 수가 없습니다. 다음주에 더 개선된 베타 버전을 공개할 예정입니다. 그리고 launch date는 3월 초순이나 3월 중순이 될 겁니다. beta 버전을 사용하겠다고 신청하신 분들은 조금만 기다려 주시면 됩니다. 우리는 여러분들 특히 indie 개발 그룹들이 좋아할 만한 Corona twist 를 가지고 있습니다.

이제 분석과 관련해서 얘기해 보죠. Corona Cloud launch 의 일환으로 우리의 analytics infrastructure를 overhauling 하고 있습니다. 2월말까지 우리의 분석들을 마칠예정입니다. 그리고 새로운 analytics system 을 만들고 이를 3월 중순내로 빠르게 공개할 예정입니다. 시스템이 바뀌게 되면 초기에는 일정정도 불편한 점이 있으실 겁니다. 하지만 새로 제공되는 analytics 는 훨씬 좋고 유용할 겁니다.

월요일 올린 글에서 말씀 드렸듯이 현재 저희들은 아주 많은 일들을 준비하고 있습니다. 그리고 많은 것들이 조만간 여러분들에게 제공될 겁니다.
잠시만 기다려 주세요. :)


반응형

Comment


Posted on . Written by




Wind Tunnel / Vents


Runtime 이벤트에서 Physics sensor와 :applyForce() function 을 사용하게 되면 wind tunnel 지역이나 객체들을 날려버리는 통풍구들을 만들기가 쉽습니다. 이 작업을 하기 위해 통풍구(vent)들과 각 객체들에 모두 두가지 프로퍼티들을 할당할 겁니다. 이 프로퍼티들을 xFyF로 명명하겠습니다. 객체들과 관련된 xy 의 force를 나타낼 겁니다.

실제 vent들은 사각형의 센서들이 될 겁니다. 이 센서들은 원하는 각도로 rotate 될거구요. 이 각도에 맞게 바람이 뿜어져 나와서 객체들을 위로 보내서 땅에 떨어지지 않도록 할 겁니다. 이 예제에서 떠 다니는 물체들은 나뭇잎들로 할겁니다.


각각의 잎들도 Runtime 이벤트 동안 적용될 force 를 나타내는 xFyF force 프로퍼티를 갖고 있습니다. 이 값들은 이 나뭇잎이 vent 지역을 들어오고 나갈때마다 새로 계산될 겁니다. 그리고 vector force 값들이 두개 이상의 통풍구 바람이 다른 방향으로 영향을 미칠 때 적용될 요소가 될 겁니다. 여러분이 원하신다면 휘어진 통풍구도 만들 수 있습니다.



Configuring Vents


vent 는 여느 physics body와 마찬가지로 셋업 하시면 됩니다. 센서로 세팅할 것이고 isVent 프로퍼티도 넣어서 collision handler 에서 다른 객체들이 vent로 처리되지 않도록 할 겁니다. 네번째 줄에서는 xFyF 값을 얻게 됩니다. 여기서 getVentVals() 함수는 바로 다음에서 다루게 될 겁니다.


local vent1 = display.newRect( 0, 0, 80, 300 )
physics.addBody(vent1, "kinematic", { isSensor=true } )
vent1.isVent = true ; vent1.rotation = 14 ; vent1.x = 432 ; vent1.y = 660
vent1.xF, vent1.yF = getVentVals( vent1.rotation, 160 )



Retrieve Vent Force Values


아래 함수는 두개의 파라미터를 전달 받습니다. vent의 각도와 power 입니다. 이 값들로 계산해서 xFyF force 값을 리턴하게 되죠. 이 값들이 떠다니는 객체들이 영역으로 들어왔을 때 불어 올리는 동작을 할 수 있도록 해 줍니다.


function getVentVals( angle, power )
   local xF = math.cos( (angle-90)*(math.pi/180) ) * power
   local yF = math.sin( (angle-90)*(math.pi/180) ) * power
   return xF,yF
end



Vent Collision Handler


collision handler 는 collision의 beganended phases 에 어떤 처리를 하게 됩니다. began phase에서는 vent의 xFyF 요소들을 떠다니는 객체들의 해당 프로퍼티 값에 더해 주게 됩니다. 그리고 ended phase에서는 그 값을 빼주게 되죠.

예를 들어 한 vent에는 xFyF 값이 02 이고 위로 불어 올리고 있고  다른 vent는 이보다 약간 더 센 power를 가지고 아래로 내리고 있다고 칩시다. (0하고 3.5 정도). 이 두 vent의 영향을 한꺼번에 받는다면 떠다니는 객체는 1.5(-2+3.5)만큼 아래로 가게 되겠죠. 


function ventCollide( self,event )

   local vent = event.other
   if ( event.phase == "began" and vent.isVent == true ) then
      self.xF = self.xF+vent.xF ; self.yF = self.yF+vent.yF
   elseif ( event.phase == "ended" and vent.isVent == true ) then
      self.xF = self.xF-vent.xF ; self.yF = self.yF-vent.yF
   end
end
leaf1.collision = ventCollide ; leaf1:addEventListener( "collision", leaf1 )



Runtime Force Application


마지막으로 각 나뭇잎들에 실제 force를 적용할 필요가 있습니다. 이 작업은 Runtime event 동안에 하게 됩니다. (이 force value들은 계속 누적 될 겁니다.) 그러면 나뭇잎이 오래 떠 다닐수록 더 큰 force 가 적용된다는 얘기가 되겠죠.

실제 함수는 아주 간단합니다. 특정 잎의 xFyF 값이 0이 아니면 특정 force가 게임 중에 적용이 되게 됩니다.


function constantForce()
   if not ( leaf1.xF == 0 and leaf1.yF == 0 ) then
      leaf1:applyForce( leaf1.xF, leaf1.yF, leaf1.x, leaf1.y )
   end
end

Runtime:addEventListener( "enterFrame", constantForce )



눈치 빠르신 분들은 알아차리셨겠죠? 객체가 통풍구에서 멀어질 수록 적은 영향을 받게 됩니다. 이 값이 true 일 경우에 객체들은 vent 의 범위 안에 있는 것이고 그러면 그 force 의 영향을 받게 됩니다.

windtunnel.zip


여기까지가 기본 사항입니다. 예제에 있는 여러 값들을 바꿔가면서 테스트 해 보세요.


In Summary…


세 가지 예제를 모두 보시려면 여기 sample projects 에서 다운 받아서 보세요. 그리고 궁금하신 점이나 의견이 있으시면 댓글에 남겨 주세요.

반응형

Comment


Posted on . Written by



Sticky Projectiles


끈적한 포탄들을 코로나로 구현하는 방법은 아주 간단합니다. 이 장에서는 아래와 같은 내용들을 다룰 겁니다.



1. 포탄이 벽에 붙을 수 있는 충분한 속도가 됐는지 여부를 체크하기 위한 directional velocity 를 어떻게 detect 하는지에 대한 방법
2. weld joint 를 사용해서 포탄을 다른 객체에 붙이는 방법


포탄을 만들고 launching하는 것은 큰 주제 입니다. 그래서 그에 대해서는 깊게 들어가지 않겠습니다. 이 게임을 만들 때 생각해야 할 것은 위 두 경우를 다룰 collision handler 함수 입니다.


우리가 할 첫번째 일은 포탄의 방향과 속도를 감지하는 것입니다. 우선 피타고라스 이론(Pythagorean theorem)을 사용해서 속도를 계산해야 합니다. 그리고 나서 그 값으로 다음의 둘 중 하나를 구현할 수 있습니다.  만약 속도가 충분하다면 포탄과 벽에 weld joint 를 생성할 겁니다. 속도가 충분하지 않으면 아무 joint 도 만들지 않을 겁니다. 그러면 포탄은 땅에 떨어지겠죠.


Detect Directional Velocity


우선 포탄의 vx vy linear velocities 를 가져옵니다. 그리고나서 그 값을 공식에 대입시키죠. 그러면 우리가 원하는 속도를 얻을 수 있습니다.


local vx,vy = self:getLinearVelocity()
local dirVel = math.sqrt( (vx*vx)+(vy*vy) )


Make Joint — or Don’t!

포탄의 속도에 따라 위에서 언급한 둘 중 한가지를 적용하게 될 겁니다. 속도가 여러분이 원하는 만큼 충분하다면 포탄이 다른곳에 붙을 수 있도록 만듭니다.


if ( dirVel > 330 ) then
   self:setLinearVelocity( 0,0 )
   timer.performWithDelay( 10, resolveColl, 1 )
end


Box2D 에서는 어떤 action들은 같은 time step 에 한꺼번에 실행되지 않는 것들이 있습니다. 왜냐하면 Box2D 엔진이 내부적으로 계산을 하고 있는 상황이기 때문이죠. 우리는 10 밀리세컨드의 timer 가 작동된 후에 joint 를 생성할 겁니다.


function resolveColl()
   local weldJoint = physics.newJoint( "weld", self, event.other, self.x, self.y )
end


여기까지 하시면 sticky 포탄 시나리오의 아주 기본적인 사항들은 완료 된 겁니다. 예제에 있는 여러 setting 들을 바꿔가면서 이것저것 많이 시도해 보세요.

StickyProjectiles.zip


참고로 전체 소스는 아래와 같습니다.


local physics = require("physics") ; physics.start() ; physics.setGravity( 0.0,9.8 ) ; physics.setDrawMode( "normal" )
physics.setPositionIterations( 16 ) ; physics.setVelocityIterations( 6 )
display.setStatusBar( display.HiddenStatusBar )

--set up some references and other variables
local ox, oy = math.abs(display.screenOriginX), math.abs(display.screenOriginY)
local cw, ch = display.contentWidth, display.contentHeight
local stage = display.getCurrentStage()

--set up terrain and background
local back = display.newImageRect( "sky.jpg", 1024, 768 ) ; back.x = cw/2 ; back.y = ch/2
local wallL = display.newRect( -ox, -oy, 40, ch+oy+oy )
physics.addBody(wallL, "static", { bounce=0.6, friction=1.0 } )
local wallR = display.newRect( cw-40+ox, -oy, 40, ch+oy+oy )
physics.addBody(wallR, "static", { bounce=0.6, friction=1.0 } )
local wallB = display.newRect( -ox, ch-40+oy, cw+ox+ox, 40 )
physics.addBody(wallB, "static", { bounce=0.6, friction=1.0 } )
local wallT = display.newRect( -ox, -oy, cw+ox+ox, 40 )
physics.addBody(wallT, "static", { bounce=0.6, friction=1.0 } )
local text = display.newText( "Tap screen to launch projectiles", 0, 0, "Times", 44 ) ; text:setTextColor(0,0,0,160) ; text.y = 140 ; text.x = cw/2

--set up boolean for projectile firing
local projFiring = false

--set up projectile sheet
local proj
local options = { width=40, height=40, numFrames=2, sheetContentWidth=80, sheetContentHeight=40 }
local projSheet = graphics.newImageSheet( "projspike.png", options )
local seq = { name = "main", frames = {1,2} }

--function to create new projectiles
local function newProj()

    proj = display.newSprite( projSheet, seq ) ; proj.x = 150 ; proj.y = 600
    physics.addBody( proj, "dynamic", { density=15.0, friction=0.8, bounce=0.3, radius=16 } )
    proj.gravityScale = 0
    projFiring = false
    proj.isBodyActive = false
end

--collision handler
local function projCollide( self,event )

    if ( event.phase == "began" ) then

        --get world coordinates of projectile for joint reference   
        self:removeEventListener( "collision", self ) ; self.collision = nil
       
        --delay function to resolve collision
        local function resolveColl( timerRef )
            if ( timerRef.source.action == "makeJoint" ) then
                local weldJoint = physics.newJoint( "weld", self, event.other, self.x, self.y )
            end
            newProj()
        end

        --check if velocity of projectile is sufficient to "stick"
        local vx,vy = self:getLinearVelocity()
        local dirVel = math.sqrt( (vx*vx)+(vy*vy) )

        if ( dirVel > 330 ) then  --if sufficient, stop velocity and trigger joint creation
            self:setLinearVelocity( 0,0 )
            local t = timer.performWithDelay( 10, resolveColl, 1 ) ; t.action = "makeJoint"
        else  --if not sufficient, "break" projectile and create new
            self:setFrame(2)
            local t = timer.performWithDelay( 10, resolveColl, 1 ) ; t.action = "none"
        end

    end
end

--screen touch handler
local function touchAction(event)

    if ( event.phase == "began" and projFiring == false ) then
       
        projFiring = true
        proj.isBodyActive = true
        local px,py = event.x-proj.x, event.y-proj.y

        proj:applyLinearImpulse( px/2, py/2, proj.x, proj.y )
        proj:applyTorque( 1200 )
        proj.gravityScale = 1.0
        proj.collision = projCollide ; proj:addEventListener( "collision", proj )
    end
end
stage:addEventListener( "touch", touchAction )

newProj()



반응형

Comment


이번에 다룰 튜토리얼은 아주 유용한 예제 3개 입니다.

이 3개를 따로 따로 정리해서 글을 올릴 계획입니다.


Posted on . Written by



오늘의 튜토리얼에서는 몇가지 유용한 physics methods 를 소개해 드리겠습니다. 이 튜토리얼에서 우리는 "내가 점프할 수 있을까" 를 2D side-view 게임으로 만드는 과제를 풀어갈 겁니다. 그리고 sticky projectiles와 기본 wind tunnel befavior 를 다루는 방법도 다룰께요.

이 모든 method들은 다운로드 받을 수 있는 프로젝트 안에 포함돼 있습니다. 이 튜토리얼 마지막 부분에 있는 링크를 클릭해서 다운받아 보실 수 있습니다.


Can I Jump?


등장 캐릭터가 점프할 수 있는 2D 게임을 만들 때 아마 어떤 물체가 점프를 할 경우 ground 에서 점프해서 ground 로 떨어지도록 만들어야 할 겁니다. 이걸 구현하라고 하면 아마 개발자마다 자기만의 방법으로 구현할 수 있을 겁니다. 방법은 여러가지가 있을 수 있지만 두가지 정도는 지켜야 합니다. 일단 물체가 ground 에 있을 때는 verticla 속도는 없을 겁니다. 그리고 점프를 한 다음에는 짧은 timer를 사용하고 또 boolean flag를 사용해서 그 물체가 땅에 떨어지기 전에는 다시 점프를 할 수 없도록 해야 할 겁니다. 이것을 구현해 놓으면 그 다음에 또 다른 구현해야할 기능들이 눈에 보이겠죠.


일단 이 물체의 physics body 에 두번째 element로 “foot sensor”를 달면 쉽게 접근할 수 있습니다. 이 센서는 그 물체의 base에서 약간 의 곤간을 차지하게 될 겁니다. 이 센서가 ground와 겹쳐지면 우리는 이 물체가 ground 위에 붙어 있다고 가정할 수 있습니다. 그러면 jump를 할 수 있는 상태가 되는 거죠. 그리고 이 센서는 그 물체의 width 보다 약간 좁게 만들겁니다. 화면 끝에 가서 부딪힌 다음 다시 돌아와야 되는데 그 물체보다 넓으면 벽에 부딪히기도 전에 튀어나올 수 있으니까요.




Constructing Terrain


코로나에서 지형을 만드는 방법은 아주 간단합니다. 아래에 우리는 모든 ground 객체들에 objType 프로퍼티를 추가할 겁니다. 그래서 물체가 이 ground에서만 반응해서 점프할 수 있도록 할 겁니다. 


local cw, ch = display.contentWidth, display.contentHeight local ground = display.newRect( 0, ch-64, cw, 64 ) ground.objType = "ground" physics.addBody( ground, "static", { bounce=0.0, friction=0.3 } )


Declaring Character Body


이 물체는 두개의 body elements로 구성될 수 있습니다. 두번째 body element 는 sensor 라는 것을 기억해 두세요. 이것은 polygonal shape 를 사용해서 정의 될 겁니다. 그리고 센서는 두번째에 정의 된다는 것도 기억해 두시구요. 이 센서가 ground 에 닺았는지의 여부를 체크할 때 두번째 element를 사용해서 체크할 거거든요.

그리고 이 물체가 점프할 수 있는 상황인지를 알기 위해 canJump counter 도 추가합니다. 이 counter 는 다음과 같은 이유에서 아주 유용합니다. : 두개의 ground 객체가 있고 이 두 ground 사이를 이 물체가 지나갈 때 이 foot sensor 는 두 ground 객체에 모두 닿아있게 됩니다. counter 대신에 true/false boolean 프로퍼티를 사용한다면 자칫 원하지 않는 상황이 연출 될 수 있습니다. 그래서 센서가 ground 객체에 접촉할 때 이 counter 를 사용해서 숫자를 증감시켜서 현재의 상태를 보다 정확하게 파악할 수 있도록 합니다.


local character = display.newRect( 100, 300, 120, 120 ) ; character:setFillColor(0)
physics.addBody( character, "dynamic",
 { density=1.0, friction=0.0, bounce=0.0 },
 { shape={20,0,20,65,-20,65,-20,0}, isSensor=true }
 )
character.isFixedRotation = true
character.canJump = 0


Jump Handler

점프하도록 만드는 것도 아주 간단합니다. 그냥 canJump counter를 체크해서 0보다 크면 점프 시키면 됩니다.


function touchAction(event)
   if ( event.phase == "began" and character.canJump > 0 ) then
      --jump procedure here
   end
end
stage:addEventListener( "touch", touchAction )


Collision Handler


마지막으로 다룰 것은 collision 핸들러 입니다. 이 핸들러를 구현하기 위해 아래 사항들을 체크해야 합니다.

    1. colliding body element index 가 2 이다 - 이것으로 그 물체가 아니라 foot sensor 가 collid 된 것을 알 수 있습니다.
    2. ground 객체의 지형 element - 만약 지형 element이면 이 물체는 안전하게 점프할 수 있습니다.


이 conditional clause 안에서 우리는 아래 둘 중 하나를 실행시키게 됩니다.


    1. began phase 에서 (foot sensor 가 ground 객체에 들어 섰을때), canJump counter를 증가시킨다.
    2. ended phase 에서 (foot sensor 가 ground object 를 나갔을 때), canJump counter 를 감소시킨다.


function charCollide( self,event )

   if ( event.selfElement == 2 and event.other.objType == "ground" ) then
      if ( event.phase == "began" ) then
         self.canJump = self.canJump+1
      elseif ( event.phase == "ended" ) then
         self.canJump = self.canJump-1
      end
   end
end
character.collision = charCollide ; character:addEventListener( "collision", character )


여기까지가 jump를 구현하기 위해 가장 기본적으로 필요한 것들 입니다.


전체 소스와 이미지들은 아래 파일을 다운 받아서 사용하세요.

canJump.zip



반응형

Comment


Posted on . Written by


지난 몇달간 저희들은 자잘한 여러 이슈들과 소위 보이지 않는 특정 문제들을 해결하는데 Corona daily builds 에 적용하는데 포커스를 뒀었습니다. 그것들은 짧은 시간안에 해결할만해 보이지 않는 그런 이슈들이었습니다. 동시에 이 플랫폼을 제대로 유지하기 위해서는 필수적으로 해결해야 할 이슈들이었죠.

그런 보이지 않는 이슈들 중에는 안드로이드 퍼미션 특히 default 퍼미션들과 관련 된 것들이 있었습니다. 이전에 저희들은 몇가지 퍼미션들을 디폴트 퍼미션으로 사용했었습니다.


  • INTERNET
  • READ_PHONE_STATE
  • ACCESS_NETWORK_STATE


한편으로는 그 앱에 필요하지 않은 퍼미션을 적용하게 될 수도 있었고 많은 앱 스토어에서 그런 경우 페널티를 매기기도 했었습니다.


이 문제를 해결하기 위해 이 디폴트들을 없애버리고 싶었습니다. 사실 더 정확하게 얘기하자면은 잠시동안 없애 버리고 싶었죠. 그런데 이 문제를 좀 더 깊게 들여다 보니 이 작업을 제대로 못하면 큰 문제가 발생할 수도 있겠더라구요.



우선 저희들이 이 문제를 잘 해결했다는 것을 알려드리며 이 기능은 daily build 1030 서부터 포함 됐다는 것을 참고하시기 바랍니다.
이제 디폴트 퍼미션들은 없습니다.


이제 naive (나이브) 하게 코딩하시면 됩니다. 즉 필요한 퍼미션만 추가하시면 됩니다. 그런데 여기에는 심각한 단점이 있습니다. 예를 들어 여러분 앱이 wep API call 을 사용하는데 INTERNET 퍼미션이 세팅되지 않았다면 crash 가 일어날 겁니다. 그리고 여러분은 그 원인을 찾기가 쉽지 않을 겁니다.



이런 문제를 코로나 식으로 해결하도록 했습니다. 해결할 수 있는 방법을 API 를 통해 제공합니다. 퍼미션이 바뀔 떄 어떤 API 가 영향을 받는지 보실 수 있습니다.

여러분이 API 를 사용하는데 퍼미션을 세팅하는 걸 깜빡하셨다면 OS 가 앱을 kill 하기 전에 코로나에서 그 exception 을 가로 채게 됩니다. 그리고 alert 창을 띄우죠. 아래 그림처럼요.




이 alert 창에서는 어떤 퍼미션이 missing 됐는지 알려드립니다. 이렇게 하면 이 에러의 원인이 무엇인지 여러분에게 정확하게 알려드릴 수 있겠죠? 그러면 에러를 디버깅하고 fix 하는데 훨씬 더 쉬운 환경이 될 겁니다.


일단 그 전에 처음 퍼미션을 줄 때 신경을 쓰셔야겠죠. build.settings 에 퍼미션을 세팅할 때 이 앱에서 필요한 모든 퍼미션을 잘 세팅해 주셔야 합니다. 만약 여러분이 어떤 3rd party services 를 사용한다면 암시적으로 퍼미션을 시용하는 경우도 있을 겁니다. 그런 경우 build.settings 에 모두 세팅해 주셔야 합니다.


예를 들어 web API call 을 사용하신다면 INTERNET 퍼미션을 아래와 같이 세팅하실 수 있습니다.


settings =
{
    android =
    {
        usesPermissions =
        {
            "android.permission.INTERNET",
        },
    },
}

모든 샘플 코드에 있는 build.settings 파일들을 이에 맞게 모두 업데이트 했습니다. 그리고 daily build API documentation도 업데이트 했구요.
아래 나오는 events, libraries, library functions, and object methods 들이 이 영향을 받습니다.


  • heading events
  • location events
  • ads library
  • analytics library
  • display.capture()
  • display.captureBounds()
  • display.captureScreen()
  • facebook library
  • gameNetwork library
  • media.newRecording()
  • media.playVideo()
  • media.save()
  • media.show()
  • native.newMapView()
  • native.newWebView()
  • native.showWebPopup()
  • network.download()
  • network.request()
  • socket library
  • store.init()
  • system.getInfo()
  • system.scheduleNotification()
  • system.vibrate()
  • mapView:getUserLocation()
  • mapView.isLocationVisible()
  • recording:startRecording()
  • webView:request()


반응형

Comment

  1. 짤랑이 2013.02.21 19:09

    잘보고 추천 꾸 ~ 욱 누르고 갑니다. 좋은 하루되세요 ^ ^