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

최근에 받은 트랙백

글 보관함

Sprite Sheet 활용하기 (Advanced)

2012. 10. 11. 13:54 | Posted by 솔웅


Posted on . Written by


오늘의 게스트 Tutorial은 Omid Abourai 입니다. 인디 게임 개발자 이며 ArdenKid 라는 별명을 가지고 있습니다. 그는 2년여간 코로나 SDK 를 가지고 개발을 했으며 곧 그가 주도한 첫 게임인 "Balloon Bazooka"를 릴리즈 할 예정입니다. 그의 블로그는 www.ardentkid.com 입니다.  



Basic Sprite Sheets


만약 지난주의 를 animation tutorial 놓치셨다면 그것 부터 보세요. 이 튜토리얼은 지난주 튜토리얼의 basic sprite methods 의 연장선에 있는 겁니다.


Sprite sheet는 CPU 의 부담을 덜어준다는 면에서 아주 훌륭한 기술입니다. 그런데 그냥 vector animation 과 비교해서 단점이 있는 것도 사실입니다. 예를 들어 메모리를 많이 잡아먹는다든지 앱 크기가 커진다든지 하는 것들이요. (애플의 경우 20메가가 넘으면 wifi로 다운로드 받으라고 하는데 저희는 한 앱이 20메가를 넘지 않도록 작업을 하고 있습니다.)

그리고 예를 들어 한 캐릭터가 옷을 갈아입고 악세사리를 바꾸고 할 경우는 어떨까요? 우리의 캐릭터를 각 옷별로 또 악세사리별로 중복해서 만든다면 sprite sheet 를 쓰는 잇점이 줄어들겠죠. 이럴 경우 처음부터 부분 부분 별로 만들어서 활용한다면 적은 이미지 용량으로 훨씬 많은 효과를 낼 수 있을 겁니다.



이 튜토리얼은 그 이슈와 관련해서 가능한 해결법을 다룰 겁니다. 비밀은 코딩과 애니메이션 간에 좀 더 긴밀한 관계를 만드는 겁니다.

조만간 발매될 Balloon Bazooka 앱에서 사용했던 kid 관련 스프라이트 쉬트를 샘플로 시작해 보겠습니다.






이 sheet 는 코로나의 basic sprite animation에 맞게 디자인 됐습니다. 이미지를 로드한 다음 sequence들을 셋업하고 원하는 sequence들을 play 하는 방식이죠. 좀 더 진보된 테크닉을 원하신다면 아래 내용을 말씀해 드리고 싶습니다. 그런데 일단 우선은 이 basic methods가 익숙해지셔야 합니다.


Splitting the Animation Elements


우리는 화면에서 동시에 같은 sprite 표현을 사용하는 여러 kids 를 만들겁니다. 우리는 그들에게 다양한 옷과 살 색 등등을 적용할 겁니다. 다행히도 프로그램적으로 비쥬얼 property들을 tweening 함으로서 비슷한 애니메이션들을 만들 수 있습니다. 프로그램으로 scale, rotation, translation 등을 다야하게 적용할 수 있습니다. (우선 편의상 이번에는 팔은 가만히 있고 발만 움직이는 걸로 한번 만들어 보겠습니다.)


먼저 이 kid를 토막을 내겠습니다. color 별로 따로 만들고 나중에 이것들을 합칠께요. 이 작업을 하기 위한 새로운 image sheet 는 아래와 같을 겁니다.





이 new sheet 의 file size 가 225KB 에서 109KB로 줄어들었습니다. 그러면서도 다양하게 의상과 피부색을 조합할 수 있게 됐죠. 50%가 넘게 파일 사이즈를 줄였고 우리고 표현하고 싶은 다양한 kid 들을 표현할 수 있게 됐습니다.


이제 이 각각의 element들을 코드안에 individual sprite 로 정의 하겠습니다.


-- DECLARE CHARACTER SEQUENCES (MOST ARE JUST STATIC CHERRY-PICKED FRAMES!)
local sequenceData = {
  { name="beachboy_hat_yellow", frames={1} },
  { name="beachboy_body_dark", frames={34} },
  { name="beachboy_shorts_red", frames={42} },
  { name="beachboy_arm_dark", frames={ 5,12,15,19,23,26 }, loopDirection="bounce" },
  { name="beachboy_foot_dark", frames={47} }
}

-- CREATE A DISPLAY GROUP FOR THE CHARACTER
local beachboy = display.newGroup()

-- CREATE BODY PARTS AS SPRITES
local hat = display.newSprite( sheet, sequenceData )
      hat:setSequence( "beachboy_hat_yellow" )
local body = display.newSprite( sheet, sequenceData )
      body:setSequence( "beachboy_body_dark" )
local shorts = display.newSprite( sheet, sequenceData )
      shorts:setSequence( "beachboy_shorts_red" )
local rightArm = display.newSprite( sheet, sequenceData )
      rightArm:setSequence( "beachboy_arm_dark" )
local leftArm = display.newSprite( sheet, sequenceData )
      leftArm:setSequence( "beachboy_arm_dark" )
local rightFoot = display.newSprite( sheet, sequenceData )
      rightFoot:setSequence( "beachboy_foot_dark" )
local leftFoot = display.newSprite( sheet, sequenceData )
      leftFoot:setSequence( "beachboy_foot_dark" )

-- POSITION PARTS & ORIENT L/R SIDES WITH SCALING
shorts.x, shorts.y = 0, 15
leftArm.x, leftArm.y = -20, -18
leftArm.xScale = -1 --flip 'leftArm' horizontally
-- etc...

-- INSERT ELEMENTS INTO THE DISPLAY GROUP, ORDERED BOTTOM TO TOP
beachboy:insert(leftFoot)
beachboy:insert(rightFoot)
beachboy:insert(shorts)
-- etc...

-- STORE REFERENCES TO EACH ELEMENT
beachboy["feet"] = {leftFoot, rightFoot}
beachboy["shorts"] = shorts
beachboy["body"] = body
-- etc...



Animating the Elements


이 모든 element들이 제자리를 잡고 여러분의 캐릭터가 show up 하면 이제 그 캐릭터의 발 움직임을 줘서 애니메이션을 만들 수가 있습니다.


--WALK SEQUENCE
function beachboy:walk()

  local feet = self.feet

  --CLEAR EXISTING TRANSITIONS IF RUNNING
  if not ( self.currentTransitions ) then self.currentTransitions = {} end --add container table if not present
  local currTrans = self.currentTransitions
  local tot = #currTrans
  for i = tot,1,-1 do --loop backwards through transitions and clear each one
    if (currTrans[i]) then transition.cancel(currTrans[i]) ; currTrans[i] = nil end
  end

  local t = 500 --WALK CYCLE TIMING
  local dist = 160 --FOOT MOVEMENT DISTANCE
  local ease = easing.inOutQuad --FOR A NATURAL SWINGING MOTION

  --RECURSIVE ANIMATION FUNCTION
  local function anim()
    for i = 1,#feet do
      currTrans[i] = transition.to(feet[i], {y=dist, time=t, transition=ease, onComplete=function()
        currTrans[i] = transition.to(feet[i], {y=0, time=t, transition=ease, onComplete=function()
          anim()
        end})
      end})
    end
  end

  --START THE ANIMATION
  anim()

end


 

이제 우리만의 Runtime animation code 를 call 하고 있죠? 우리는 이 튜토리얼에서 계속 반복되는 transition들을 사용하고 있습니다. 여기서 팔도 발하고 같이 어울리게 애니메이션을 주고 싶다면 여기서 조금 더 작업을 해야 합니다. (이 부분은 따로 다루지는 않겠습니다.)

이제 같은 메모리로 여러 color들을 표현할 수 있게 됐습니다.


“Tinting” the Elements



여기서 각 파트별로 programatic tinting을 사용해서 color 까지도 코딩으로 넣을 수가 있습니다. 이것은 코로나의 setFillColor() method를 사용하면 됩니다. 우선 모든 부분을 흰색으로 만듭니다. 아래 샘플 이미지가 있습니다.







아래에 코딩으로 어떻게 sprite object들에 tint를 하는지에 대한 예제가 있습니다.

-- ACCESS EACH ELEMENT FROM THE CONTAINER TABLE BY INDEX NAME
beachboy.hat:setFillColor(252, 206, 0)
beachboy.body:setFillColor(126, 79, 33)
beachboy.shorts:setFillColor(220, 0, 0)
beachboy.feet[1]:setFillColor(126, 79, 33) --access 'leftFoot' by position within 'feet' table
beachboy.feet[2]:setFillColor(126, 79, 33) --access 'rightFoot' by position within 'feet' table
-- etc...

  

이제 원하는 색을 넣을 수도 있고 랜덤하게 색을 넣을 수도 있습니다. 여러분 마음대로 하시면 됩니다. 단지 다른 animation sequence 될 때마다 색을 넣어야 하는 작업을 해야 되겠죠. 그리고 캐릭터가 움직이면서 좀 더 CPU 를 잡아 먹을 겁니다. 그리고 한 객체에 한가지 색만 넣을 수 있겠죠. 줄무늬 옷이나 뭐 그런거는 좀 힘듭니다. 흰색과 회색을 사용해서 조금 sprite 한 느낌은 줄 수 있겠죠.


In Summary



해 보니까 이 방법은 요즘 대부분의 모바일 디바이스에서 사용하는 예외적인 방법이더라구요. 이 방법을 사용하면 texture memory를 훨씬 줄일 수 있습니다. 우리는 이 beachboy 캐릭터의 용량을 225 KB에서 109 KB로 줄였었습니다. 그리고 마지막 방법까지 하면 12KB 로 95%나 줄일 수 있었습니다.


그리고 또 다른 효과는 각 파트별로 다른 애니메이션 효과를 줄 수 있게 된 겁니다. 팔 하나만 움직이게 할 수도 있고 두 팔을 다 움직이게 할 수도 있구요 또 더 나아가서는 펀치나 킥하는 장면도 넣을 수 있겠죠.


그리고 각 character element 별로 tint/color 를 할 수가 있어서요 아주 다양한 캐릭터를 만들어 낼 수 있게 됐습니다.

이렇게 dynamically-optimized sprite sheet 를 사용하면 아주 다양하고 훌륭한 효과를 낼 수가 있습니다. 그리고 여러분의 코딩에 따라서 한정된 이미지로 개성있는 캐릭터를 계속 만들어 낼 수 있구요.


반응형

Comment