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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

새로 추가된 이미지 캡쳐 기능

2012. 3. 21. 11:05 | Posted by 솔웅


반응형
Taking Snapshots of Objects and Groups

Corona Daily Build 2012.768 에서는 display.capture() 함수를 call 함으로서 간단히 display object (혹은 group) 를 snapshot 하는 새로운 기능이 추가 됐습니다.

이전에는 display.save() 함수를 사용해서 화면의 display 객체들을 저장했었습니다. 이번 display.capture()에서는 save to disk 부분을 거치지 않아도 됩니다. 그리고 snapshot의 새로운 display object로서 사용할 수도 있습니다.

이것은 기본적으로 display.captureScreen()과 같습니다. (display object를 return 한다는 점에서) 하지만 display.captureScreen()에서는 특정 display object나 display group을 따로 캡쳐할 수는 없습니다. display.captureScreen()은 화면 전체의 snapshot을 가져올 뿐입니다.

이번에 새로 추가된 display.capture()함수는 특정 객체나 그룹에 대해서만 snapshot을 할 수가 있습니다. 이 기능을 이용하면 아주 많은 부분에서 활용이 가능 하실 겁니다.

그리고 이 새로운 기능에서는 캡쳐된 스냅샷의 배경을 투명 처리 할 수도 있습니다. (display.captureScreen()과 display.save() 모두 백그라운드를 투명처리하지 못했습니다.)

NOTE : 안드로이드에서는 display.save() 기능이 업데이트 되서 백그라운드를 투명처리할 수 있게 됐습니다. 이것은 daily build 2012.768 부터 가능합니다. 아직까지 iOS에서는 지원이 안 됩니다.



Wait, display.save() can do the same thing!

display.capture() 가 아주 좋은 기능이라고 생각 되시죠? 그런데 display.save() 만 사용해서 저장된 이미지를 새로운 display 객체로 display.newImage()를 사용해서 로드할 수 있을까요?

배경 화면을 투명처리하는 것만 빼고는 가능합니다. display.capture() 가 하는 일과 똑 같이 display.save()를 사용해서도 할 수 있습니다. 그런데 여기에는 좀 큰 단점이 있습니다. 이미지를 디스크에 저장하고 이것을 다시 로드하는 과정에서 퍼포먼스에 큰 영향을 미치게 될 겁니다. 그러므로 display.save를 사용해서 하는 것보다 display.capture()를 사용하는 편이 훨씬 이로울 겁니다.

file I/O 과정을 생략하고 새로운 display 객체로 바로 이미지를 렌더링하는 것, display.capture()는 이런 점에서 display.save()를 사용하는 것 보다 훨씬 빠를 겁니다. 물론 display.save()도 아주 유용한 부분이 있죠. 이미지를 특정 위치에 save 해야 할 때는 display.save()를 사용 하셔야 합니다.

display.capture() Usage

신택스는 아래와 같습니다.
display.capture( displayObject [, saveToPhotoLibraryFlag ] )

아주 간단하죠? 아래 파라미터에 대한 설명이 있습니다.

displayObject — snapshot을 하고 싶은 display 객체나 그룹 입니다.

saveToPhotoLibraryFlag — 이 부분은 필수사항은 아닌데요. true로 설정하면 (디폴트는 false입니다.) 캡쳐된 객체는 display.captureScreen()의 같은 파라미터가 하듯이 디바이스의 photo library로 저장이 될 겁니다.

아래에 display.capture()를 사용하는 예제가 있습니다.

  local group = display.newGroup()

    local image1 = display.newImage( group, "image1.png" )
    local image2 = display.newImage( group, "image2.png" )

    -- take snapshot of the entire group
    local snapshot = display.capture( group )

    -- the object 'snapshot' is now another display object
    snapshot:translate( 100, 100 )
   
주의하셔야 할 부분은 display object가 return 됐을 때 이것은 캡쳐한 object 처럼 캡쳐되기 전과 같은 위치에 잊지 않을 거라는 겁니다. 디폴트로 return 된 display object는 0,0 에 위치합니다. 그러니까 좌상단에 위치하게 되죠. (디폴트로 이미지의 기준점은 이미지의 중앙이니까 실제로는 좌 상단을 더 벗어 나겠네요.)

반응형


반응형
Image Groups

Image Groups는 이번에 새로 소개되는 Corona의 기능입니다. 조금 전에 다뤘던 image sheets 와 이름이 비슷하긴 하지만 완전 다른 기능입니다. Image Groups는 일반 display groups의 sub class 입니다.

Here are the primary differences between Image Groups and normal Display Groups:
아래에 Image Groups가 일반 Display Groups 와 다른 점을 적어놓았습니다.

    Image sheet 객체로부터 생성된다.
    children은 반드시 같은 image sheet에서 나온 것이어야 한다.
    image group내의 모든 child 객체들에대해 제한을 갖는다.
    Image Groups는 nested 될 수 없다. (다른 image groups에 insert 될 수 없다.)

한개의 image sheet에서 나와야 한다? child 객체들에 제한이 있다? 여러분들은 이렇게 제한이 있으면 왜 일반적인 groups를 사용하지 않고 이 Image Groups를 사용해야 하는지 의아해 할 수 있습니다. 정말 그렇습니다. 하지만 여기 이 image groups를 사용함으로서 얻는 장점들도 있습니다.

객체들이 image group에 insert 될 때 일반 display object 들은 할 수 없는 몇가지 이미지 최적화 기능들을 사용할 수 있습니다. 여러분 앱이 최대의 퍼포먼스를 필요로 할 때 그리고 내부적인 그래픽 최적화를 필요로 할 때 바로 이 Image Groups 라는 것을 사용하시면 됩니다. 다음의 제한 사항이 여러분의 앱에 별 상관을 미치지 않을 때 사용하시면 되겠죠?

    image group에 insert 된 객체들은 개별적으로 Mask를 적용할 수 없다.
    nested image groups는 없다. 그러므로 같은 image sheet 안에 있는 다른 곳에 group화 되지 않은 객체만이 같은 image group에 insert 될 수 있다.
    per-object blend mode 가 없다. (image group에 insert 된 객체들에 대해)


How to Use Image Groups

Image Groups는 Image Sheets와 직접적으로 연결이 돼 있습니다. (그러니까 image sheet 을 제대로 보지 못했다면 Image Groups를 보기 전에 이전 글인 image sheet를 보셔야 합니다.)

Image Group Syntax:
display.newImageGroup( imageSheet )

imageSheet 파라미터는 graphics.newImageSheet()를 사용해서 생성한 image sheet 객체를 참고합니다. 한번 Image Group을 만들면 해당 imageSheet에 있는 객체들을 child 객체들로 image Group에 insert 할 수 있습니다.

아래 예제를 참고하세요.

-- assumes an image sheet has already been created
local imageGroup = display.newImageGroup( imageSheet )

local object1 = display.newImage( imageSheet, 1 )
local object2 = display.newImage( imageSheet, 2 )
local object3 = display.newImage( otherImageSheet, 2 )
local object4 = display.newImage( "image.png" )

-- insert objects into image group
imageGroup:insert( object1 )
imageGroup:insert( object2 )

-- lines below result in errors because 'object3' and 'object4'
-- do not belong to same image sheet as imageGroup
imageGroup:insert( object3 )
imageGroup:insert( object4 )

위 예제에서 보듯이 imageGroup은 약간 까다롭습니다. object3,object4 같이 다른 imageSheet나 imageSheet를 사용하지 않은 일반 이미지 객체는 위에서처럼 imageGroup에 insert 될 수 없습니다. 하지만 이렇게 사용하기 조금 불편하고 제약이 있음에도 불구하고 이 image group을 잘 사용하면 그렇지 않았을 경우에 나왔던 단점들을 훌륭하게 극복할 수 있습니다.

코로나는 일부 device에서 퍼포먼스의 문제가 있었습니다. 하지만 이 image groups를 사용함으로서 이러한 퍼포먼스 문제가 깨끗이 해결 됐습니다.
(저도 fireman -iPhone,iPad-, firefighter -Android- 앱을 개발 한 후에 퍼포먼스 문제로 아주 고민을 많이 했었거든요. 이 문제가 해결 됐다니 아주 반갑네요.)

Removing Image Groups

다른 display 객체들 처럼 image groups도 간단히 remove 할 수 있습니다. object:removeSelf()와 display.remove(object) 로 image groups도 remove 합니다. 이 작업을 한 다음에 nil을 대입해 주시는 걸 잊지 마세요.

image group이 child 객체들을 가지고 있다면 그 child들을 개별적으로 remove 한 다음에 image group을 remove 하시기를 권장해 드립니다.

Sprites

새로운 API인 image sheets, image groups 가 나오면서 기존에 있던 애니메이션을 위한 Corona Sprite API도 좀 더 쉽게 사용할 수 있도록 바뀌었습니다. 아래에 바뀐 sprite API에 대한 summary 가 있습니다.

    이전의 sprite sheets나 sprite sets 같은 혼란스러운 개념들 대신 그냥 image sheets를 사용해서 sprite animation 기능을 사용하시면 됩니다.
    변경된 API는 좀 더 직관적이고 이해하기 쉽습니다.
    image sheets를 사용함으로서 dynamic 하게 이미지 해상도에 맞게 작업을 해 줍니다. 다른 말로 이제 sprite를 사용함으로서 retina graphics를 아주 쉽게 사용할 수 있습니다.
    좀 더 파워풀하고 유연한 기능을 위해 새로운 프로퍼티들과 메소드들이 제공됩니다.
   
   
또한 sprite 객체들이 같은 image sheet에 있다면 image groups에 insert 할 수 있습니다. 이것을 사용하면 이전에 image groups에서 얘기했던 제한들이 sprite 객체에도 적용되게 됩니다.

Syntax
display.newSprite( [parent, ] imageSheet, sequenceData )
[Editors Note: Multisprites are not available yet. Sorry for the confusion]
[멀티 스프라이트는 아직 지원되지 않습니다. 혼돈을 드려서 죄송합니다.]

display.newSprite는 single image sheet에서 애니메이션 효과를 주는 객체를 생성하기 위해 사용하는 함수 입니다.
display.newMultiSprite()는 여러 image sheet으로부터 애니메이션 효과를 주기 위한 frame들을 불러와서 애니메이션 효과를 줄 때 사용합니다. 하지만 아직까지는 이 multi sprites는 지원되지 않습니다.

sequenceData는 특적 sequence에 대한 데이터를 가지고 있는 테이블 입니다. 혹은 single object에 대해 1개의 애니메이션 sequence 이상의 sequence를 가지고 있다면 sequenceData는 각각의 sequence에 대한 데이터를 가지고 있는 테이블 배열이 될 겁니다. 예를 들어 charactor 객체를 가지고 있다고 상상해 보면, 걷기, 점프하기, 수영하기 등의 각기 다른 애니메이션 효과를 가지고 있을 수 있습니다.
sequence는 해당 imagesheet의 순서대로 1,2,3,4 가 될 수 있고 1,3,4,6,9 처럼 순서대로 안 돼 있을 수 있습니다. 이 두 경우에 대한 예제를 모두 보시겠습니다.

Single Sequence; Consecutive Frames

아래의 경우는 1,2,3,4 처럼 한개의 image sheet에 있는 frame 순서대로 sequence가 이뤄지는 경우 입니다.

-- Example assumes 'imageSheet' already created from graphics.newImageSheet()

-- consecutive frames
local sequenceData = {
    name="walking",
    start=3,
    count=6,
    time=100, -- Optional. In ms. If not supplied, then sprite is frame-based.
    loopCount = 0 -- Optional. Default is 0 (loop indefinitely)
    loopDirection = "bounce" -- Optional. Values include: "forward","bounce"
}

local character = display.newSprite( imageSheet, sequenceData )

Single Sequence; Non-Consecutive Frames

아래 예제는 frame의 순서와 상관없이 sequence가 이뤄지는 경우인데요. 기본적으로 위의 예제와 같습니다. 다만 frames라는 배열을 넣어야 하는데 이것은 image sheet 안의 frame들의 index들을 넣은 배열입니다.

-- Example assumes 'imageSheet' already created using graphics.newImageSheet()

-- non-consecutive frames
local sequenceData = {
    name="walking",
    frames= { 3, 4, 5, 6, 7, 8 }, -- frame indexes of animation, in image sheet
    time = 50, -- Optional. In ms. If not supplied, then sprite is frame-based.
    loopCount = 0 -- Optional. Default is 0.
}

local character = display.newSprite( imageSheet, sequenceData )

Multiple Sequences

아래 예제는 하나의 sprite 객체에 어떻게 여러개의 애니메이션 sequence들을 가질 수 있는지를 보여 줍니다.
보시면 각 애니메이션 별로 1,2,3,4 처럼 순서대로 index를 사용할 수 있고 비 순서적으로 frame 인덱스를 넣어서 애니메이션을 만드실 수도 있습니다.

-- Example assumes 'imageSheet' already created using graphics.newImageSheet()

local sequenceData = {
    { name="walking", start=1, count=3 },
    { name="running", frames={ 3, 4, 5, 6, 7, 8 }, time=50, loopCount=4 },
    { name="jumping", start=9, count=13, time=300 }
}

local character = display.newSprite( imageSheet, sequenceData )

start/count, frame 파라미터이외에 다른 sequenceData 파라미터들이 있습니다.
아래 내용을 참조하세요.
   
    name - sequence에 대한 unique한 이름 애니메이션이 실행 될 때 이 name을 사용해서 sequence를 load 합니다.
    start & count - consecutive-frame sequences (순차적인 frame 시퀀스)를 위한 파라미터 입니다. start는 시작하는 frame이고 count는 start 부터 끝나는 frame 까지의 index 갯수를 말합니다.
    frames - non-consecutive-frame sequence (비 순차적인 frame 시퀀스)를 위한 파라미터 입니다. 애니메이션을 위해 play 되는 frame 들의 순차적이지 않은 index 데이터를 말합니다.
    time - 각 frame들 사이의 시간 (밀리세컨드)을 말합니다. 이 숫자를 지정하지 않으면 여러분 앱의 기본 framerate을 사용하게 됩니다.
    loopCount - 애니메이션이 몇번 일어나는지 그 숫자를 써 넣으시면 됩니다. 1을 넣으면 애니메이션이 1번 일어나고 멈추게 됩니다. 디폴트는 0이구요 애니메이션이 계속 반복되는 겁니다.
    loopDirection - forward 와 bounce가 있습니다. 둘 중에 아무것도 지정하지 않으면 디폴트인 forward가 지정됩니다. bounce는 forward로 진행했다가 다시 start로 가지 않고 backward로 진행하는 겁니다.
    imageSheet - 이 파라미터는 multi-sprite를 사용할 경우에 필요한 겁니다. (display.newMultiSprite). 해당 시퀀스가 속한 image sheet를 표시합니다. 하지만 아직까지는 multi sprite를 지원하지 않기 때문에 이 파라미터를 사용할 일은 없습니다.

Using “Old” Spritesheet Data

많은 third-party 툴들이 old spritesheet 데이터 포맷을 사용해서 작업을 합니다. 그래서 image sheet 객체를 생성할 때 이 데이터 포맷을을 사용할 수 있도록 했습니다. 아래 그 방법을 보시죠.

local oldStyleSpriteSheetData = require("uma").getSpriteSheetData()

local options =
{
    spriteSheetFrames = oldStyleSpriteSheetData.frames
}

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

Sprite Properties

일반적인 display object 프로퍼티 이외에 sprite에서 사용하는 프로퍼티들이 있습니다.

    spriteObject.frame – Read-only – 로드된 시퀀드싀 frame index를 보여 줌
    spriteObject.numFrames – Read-only – 로드된 시퀀스의 frame 갯수를 보여 줌
    spriteObject.isPlaying – Read-only – 이름 그대로 현재 플레이되고 있는지 여부를 알려 줌.

Sprite Methods

아래는 display.newSprite()로 생성된 모든 object들과 연관된 sprite만의 메소드들입니다.

    spriteObject:setSequence( name ) – name을 가지고 애니메이션 시퀀스를 로드함. name 파라미터가 없을 경우 현재 로드된 시퀀스의 첫번째 프레임이 보여지게 됩니다.
    spriteObject:play() – 현재 로드된 시퀀스를 플레이 합니다.
    spriteObject:pause() – 시퀀스내의 현재 보여지는 프레임에서 pause 합니다.

Sprite Listener

old API를 보시면 sprite 객체들은 리스너 함수를 사용해 sprite event를 listen 할 수 있습니다. 이벤트 프로퍼티가 변경됐는데요, 지금은 began과 ended event phase 가 있습니다.

Here’s an example:

local function spriteListener( event )
    if event.phase == "began" then
        -- sequence has began playing; do something

    elseif event.phase == "ended" then
        -- sequence has reached the last frame; do something
    end
end
 
-- Add sprite listener
spriteObject:addEventListener( "sprite", spriteListener )

우리는 이 스프라이트 리스너 함수를 좀 더 유용하게 사용할 수 있도록 작업을 하고 있습니다. 조만간에 공개 될 겁니다.

Removing Sprite Objects

sprite 객체들은 다른 일반 display 객체들을 remove 하듯이 remove 하시면 됩니다. object:removeSelf()를 사용하시고 display.remove(object) 도 사용하세요. 이 작업을 한 다음에 nil값을 대입시키는 것도 잊지 마시구요.

Frame Trimming Not Yet Supported

Image Sheets 나 새로운 Sprite API에서 아직 지원하지 못하는 부분이 있는데 그것은 tril/crop frames 기능입니다. 이 기능이 가능할 수 있도록 지금 현재 작업중에 있습니다. 완료되면 공개 하겠습니다.



Wrapping it all up…

여러 이미지를 하나의 이미지 파일에 넣어서 각각의 이미지를 image sheet나 sprite API를 사용한 애니메이션으로 사용하는 방법을 다뤘습니다.
그런데 저는 개발자로서 그래픽 작업하는게 많이 부담 됩니다.
여러 이미지를 하나의 파일로 만들어야 되고 각각의 이미지들 규격도 알아내야 되고...
이러한 작업을 해 주는 third-party 제품들이 있습니다.
코로나에서는 SpriteLoqTexture Packer 를 추천하고 있습니다.
필요하신 분 잘 활용하세요..
저도 필요하긴 합니다.
요즘 시간적인 여유가 좀 생겨서 개인 프로젝트를 시작하려고 하는데요.
그래픽 부분이 부담 되서....
하여간 새로운 기술들을 잘 활용해서 개성있는 앱을 하나 만들어 보려고 합니다.
반응형


반응형
지난번에 올린 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 등에 대해 다룰 겁니다.
반응형