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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

이번에 다룰 튜토리얼은 아주 유용한 예제 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



반응형


반응형
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()


반응형


반응형
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


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


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

반응형