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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
지난번에 올린 Tutorial 인 In App Purchase on Android 이전에 나왔던 새로운 기능에 대한 튜토리얼이 있었습니다.
그 주에 바빠서 정리를 못했는데 너무 delay 되다 보면 다루지 못하고 넘어갈 것 같아 서둘러 정리합니다.

앱을 만들다 보면 애니메이션을 사용할 때도 있고 꼭 애니메이션을 사용하지 않아도 많은 이미지 파일을 사용하게 됩니다.
disk에서 이미지를 불러와서 rendering 하고 화면에 display 하는 과정은 device의 resource를 많이 사용하는 아주 expensive 한 작업입니다.
너무 많은 이미지 사용은 앱의 용량도 크게하고 퍼포먼스에도 안 좋은 영향을 줍니다.

이번에 Corona에서 image sheet라는 새로운 API를 제공하면서 이러한 이미지 처리를 효율적으로 할 수 있는 방법을 제공하고 있습니다.

저도 Fire Man (Fire fighter)  앱이 너무 많은 애니메이션 기능 사용으로 아이폰 3GS나 구식 안드로이드 폰에서는 퍼포먼스가 아주 느려서 고민이 많았거든요.

다음 앱을 개발할 때는 이 image sheet를 사용해서 퍼포먼스에 문제가 없도록 해야 할 것 같습니다.



Corona build 2012.759 버전에 그래픽 관련 새로운 기능이 추가 됐습니다.
오늘은 이 기능에 대해 알아보겠습니다.

아주 많은 부분이 다뤄질 예정이니 주의를 집중해서 보세요. 조금 어렵기도 합니다. 아마 중급정도 수준의 튜토리얼이라고 할까요? 오늘 다룰 부분 중에는 이전의 Corona Doc나 튜토리얼에 나왔던 개념이 아닌 전혀 새로운 개념들도 있습니다.
오늘의 세가지 주요 topic은 아래와 같습니다.
- Image Sheets (새로운 기능)
- Image Groups (새로운 기능)
- Sprite (완전히 바뀜)

아래 다뤄지는 기능은 Corona sdk build 2012.759 버전 이상에서만 작동 됩니다. 그리고 build 2012.761 버전 이상을 사용하실 것을 권장합니다. 오늘 소개할 기능과 관련돼 약간 수정된 내용이 761버전 이후 부터 적용됐거든요.

Notes on Performance

Graphics의 많은 부분이 바뀌었고 그것과 더불어 performance 향상을 제공합니다. 특히 iPhone 3GS 같은 조금 오래된 기계에서 퍼포먼스가 안좋았던 부분이 많이 개선 됐습니다. 하지만 개발자로서 항상 유념해야 될 것은 잘 구성된 코드가 퍼포먼스 향상의 가장 기본이라는 것입니다.

Image Groups 같은 경우에는 어느 부분에서는 좋은 점을 제공하지만 또한 어느 부분에서는 사용하는데 제한이 있을 수 있습니다.
이 새로운 기능들을 사용할 때 이 잇점과 제한된 점을 잘 생각해서 best 한 선택을 하셔야 합니다.

그리고 또 하나의 좋은 소식은 최근의 코로나 버전에서 다른 방법으로 퍼포먼스 향상 효과를 주었는데요. 그것은 바로 화면 밖에 있는 객체에 대해서는 렌더링이 일어나지 않도록 만들었다는 것입니다. 이 방법은 따로 코딩을 바꾸실 필요는 없습니다. 그냥 최근 버전의 코로나를 다운 받으셔서 새로 빌드 하시면 됩니다.

Image Sheets

이 API는 이번에 새로 나온 기능 입니다. 전문 용어에 익숙하신 분은 texture atlas  기능이라고 하면 이해하시기 쉬우실 겁니다.

이해를 돕기위해 좀 은유적으로 설명을 드리자면 여러분 앱의 모든 이미지들이 한장의 종이에 있고 그 종이의 부분 부분을 가져와서 한개의 이미지처럼 사용할 수 있도록 합니다. 그러면 메모리를 줄일 수 있고 이미지 렌더링 시간도 줄일 수 있어서 큰 퍼포먼스의 향상을 가지고 올 수 있습니다.

disk에서 이미지를 메모리로 불러와서 그것을 렌더링 해서 앱에 표시하는 작업은 아주 비싼(에너지 소비가 많은) 작업입니다. 그래서 각각의 이미지별로 이 작업을 하도록 하기 보다는 여러 이미지를 하나의 이미지화 해서 이런 비싼 작업을 한번만 할 수 있도록 하는게 기본 개념입니다.

Texture Size Limit

한 개의 이미지 파일에 몇개의 실제 이미지를 넣을 수 있는지에 대한 제한은 없다. 하지만 이 부분은 유념해야 한다. 각 디바이스 마다 최대 texture size의 제한이 있다. 한개의 이미지 파일의 width,height에 대한 픽셀 수에 대한 제한값이다. 그러므로 한 이미지 파일안에 몇개의 실제 이미지가 들어가는지는 제한이 없지만 이미지 파일  크기가 너무 크면 사용할 수 없다. (그러면 이론적으로는 디바이스가 허용하는 최대크기의 이미지 파일의 픽셀 수가 최대 하용할 수 있는 이미지 갯수겠네요. 1픽셀짜리 이미지를 사용한다면.. 실제 그렇지는 않겠지만요.. 그러면 그건 이미지가 아니라 그냥 color가 있는 점이겠죠?  그냥 심심해서 상상해 봤어요... ^^)

아래 코드는 해당 디바이스에서 허용하는 texture 의 최대사이즈를 구하는 방법입니다.

print( system.getInfo( "maxTextureSize" ) )

대개 코로나가 지원하는 디바이스들은 허용 사이즈가 아주 크다는 겁니다. 여러분 앱에 수백개의 이미지 객체들이 들어간다고 하더라도 몇 개의 이미지 sheet만 사용해도 충분히 해결이 될 겁니다.

Wait, I’m already doing this!

여러분들 중에는 이전의 Sprite API를 사용해서 이러한 기능들을 이미 사용해 보신 분들도 많이 계실 겁니다. 이전의 Sprite API에서는 애니메이션을 사용하기 위한 기능을 주로 지원했습니다. 그래서 애니메이션이 아닌 정적인 이미지를 만들려면 불필요한 부분들이 많이 있습니다. 메모리를 절약하기 위해 이 Sprite API를 사용한다는 건 좀 부담스러운 일이죠.

그래서 이 image sheet를 지원하게 됐습니다. 이 image sheet API를 사용하면 훨씬 더 효율적으로 그리고 쉽게 사용할 수 있습니다. 이 API를 사용하면 정적인 이미지와 애니메이션 모두에서 사용하실 수 있습니다.

보너스가 한가지 더 있는데요. image sheet는 해상도별로 이미지를 다이나믹하게 지원해주는 기능이 있습니다. (예: retina graphics) 이전의 sprite API에서는 이 같은 기능을 구현하기 위해서는 조금 복잡하게 해야 합니다. 이 의미는 이 기능을 사용하면 이번에 해상도가 아주 높아져서 나온 새 애플(Apple)의 아이패드(New iPad) 에도 자동적으로 이미지가 맞게 지원된다는 겁니다.

How to Use Image Sheets

이제 이 image sheets를 어떻게 사용하는지 알아보겠습니다. 우선 신택스부터 보겠습니다. 그리고 나서 display.newImage()와 display.newImageRect()와 함께 어떻게 image sheets가 사용되는지 알아보는 예제를 다뤄보겠습니다. 이 image sheets를 image group과 sprite animation과 함께 사용 하실 수도 있습니다.

Image Sheet Syntax:
graphics.newImageSheet( filename, [baseDir, ] options )

아주 간단하죠? 파라미터 filename은 실제 image file의 이름이구요. baseDir은 디렉토리 입니다. (system.ResourceDirectory 가 될 수도 있고 아니면 다른 곳이 될 수도 있죠.) options 파리미터는 필수로 이 image sheet의 프레임에 대한 데이터를 가지고 있는 테이블을 가리킵니다.

혼란을 방지하기 위해 image는 image sheet file로 graphics.newImageSheet()로 불러온 파일을 말하고 frame은 그 image sheet안의 각각의 이미지들을 구분해서 말하고 있습니다.

options

option 테이블에는 세가지 케이스가 있을 수 있습니다.

    Simple: image sheet의 모든 frame들이 각각 같은 width와 height를 가질 경우
    Complex: image sheet의 frame들이 각각 다른 width나 height를 가질 경우
    Old-Style:  sprite API 데이터 포맷과 같은 방법입니다. (이전에는 sprite.newSpriteSheetFromData() 함수를 사용했었습니다.)

old style은 이제는 권장하지 않습니다. simple이나 complex 케이스 중 하나만 선택하시면 됩니다. old style에 대해서는 따로 다루지 않겠습니다. 이 케이스가 있는 이유는 이것을 이용하던 다른 third-party 툴들이 있기 때문에 아직 없애지 않은 겁니다.

Simple Example

예제를 하나 소개해 드리겠습니다. 우선 image sheet를 로딩할 때 simple option을 사용하는 방법을 보죠. 다시한번 언급하면 image sheet 내의 모든 frame이 각각 같은 width와 height를 가지고 있는 경우 이 simple option을 사용합니다.

local options =
{
    -- The params below are required
   
    width = 70,
    height = 41,
    numFrames = 2,

    -- The params below are optional; used for dynamic resolution support

    sheetContentWidth = 70, -- width of original 1x size of entire sheet
    sheetContentHeight = 82 -- height of original 1x size of entire sheet
}

local imageSheet = graphics.newImageSheet( "fishies.png", options )

예제에서 보시듯이 simple case option은 진짜로 simple 합니다. options에 들어가는 파라미터는 width,height,numFrames 이렇게 3개밖에 없습니다. 남은 2개의 optional 파리미터들은 dynamic resolution images를 사용할 경우 필요한 겁니다. (예를 들어 retina graphics 를 사용하기 위한 image sheet의 @2x 버전 같은)

Complex Example

image sheets에 여러 다른 객체들을 넣어서 사용할 경우 complex-case option 테이블을 사용해야 합니다. 이 경우 각각의 frame에 대해 정해줘야겠죠.

local options =
{
    -- array of tables representing each frame (required)
    frames =
    {
        -- FRAME 1:
        {
            -- all params below are required for each frame
            x = 2,
            y = 70,
            width = 50,
            height = 50
        },
       
        -- FRAME 2:
        {
            x = 2,
            y = 242,
            width = 50,
            height = 52
        },
    },

    -- optional params; used for dynamic resolution support
    sheetContentWidth = 1024,
    sheetContentHeight = 1024
}

local imageSheet = graphics.newImageSheet( "imageframes.png", options )

위 예제를 보시면 image sheet는 두개의 frame을 가지고 있습니다. simple case와는 조금 다르죠? 왜냐하면 각각의 frame들을 array로 그 값을 지정해줘야 하기 때문입니다. image sheet의 complex-case option 테이블을 이용할 경우 이 예제를 참고로 작성하시면 됩니다.

Using with Existing Image Functions

다음으로는 image sheets로부터 얻어낸 각각의 객체들을 기존의 함수들인 display.newImage()와 display.newImageRect()을 가지고 어떻게 사용해야 하는지를 알아보겠습니다.

일단 image sheet를 이용해서 이미지 객체를 이미 만들었다고 가정한 후 아래 예제를 살펴 보세요.
-- assumes 'options' is already constructed (simple or complex cases)
local imageSheet = graphics.newImageSheet( "myimagesheet.png", options )

-- display.newImage()
--
-- SYNTAX:
-- display.newImage( [parent ,] sheet, frameIndex )

local fish = display.newImage( imageSheet, 2 )
fish.x, fish.y = 100, 100

-- display.newImageRect()
--
-- SYNTAX:
-- display.newImageRect( [parent ,] sheet, frameIndex, width, height )

local frog = display.newImageRect( imageSheet, 4, 40, 82 )
frog:setReferencePoint( display.TopLeftReferencePoint )
frog.x, frog.y = 0, 10

보시다시피 일반적으로 display.newImage()나 display.newImageRect()함수를 사용하는것과 크게 다르지 않습니다. 다른 점은 image sheet를 사용할 경우 특정 filename 대신 해당 frame index number를 사용한다는 것입니다.

여러분들이 image sheet를 로딩할 때 simple을 사용했던지 complex를 사용했던지 상관없이 위 예제처럼 사용하시면 됩니다. (old-style을 사용해도 마찬가지 입니다.)

Power of 2 Dimensions

build 2012.264를 보시면 power of 2 에 대한 제한이 있었습니다. 지금은 이 image sheet를 사용하면서 이 제한이 없어졌습니다. 그 제한들은 width와 hight가 8, 16, 32, 64, 128, 256, 512, 1024, 2048 .... 사이즈에 부합되야 한다는 것이었습니다. 하지만 image sheets를 사용하면서 이런 제약은 없어졌습니다. 하지만 texture memory 관리 등을 생각하면 이 power of 2 에 대한 규칙을 지키는 것이 좋습니다.

Removing Image Sheets

image sheet를 remove하려면 image sheet를 사용하는 객체를 그냥 remove 하시면 됩니다. (image objects, sprites, image groups 등) 그리고 나서 image sheet를 nil로 선언하세요.

아래 예제가 있습니다.

-- obj1 and obj2 are using the image sheet
obj1:removeSelf()
obj1 = nil

obj2:removeSelf()
obj2 = nil

-- remove reference to the image sheet
imageSheet = nil


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

내용이 길어서 2회로 나눠 싣습니다.
다음 글에서는 Image Groups() , Sprites 등에 대해 다룰 겁니다.
반응형