코로나 SDK - 복합형 스프라이트
* 복합형 스프라이트란
각각 다른 크기의 이미지를 사용하여 여백의 데이터를 절약하는 비균일 스프라이트에 이어, 그림의 각 부분을 더 작은 그림으로 쪼개서 그걸 조합하는 형식으로 만들어지는 스프라이트이다. 그림을 바꾸어가며 보여주는 일반적인 애니메이션 방식에 더해, 부분의 작은 그림만을 조금씩 이동시키거나 회전시키는 등으로 애니메이션을 줄 수 있다. 작은 동작은 그림 리소스를 바꾸지 않은 채 이런 식의 좌표 이동만으로도 표현할 수 있으므로 용량을 최대한으로 아낄 수 있으며, 무기를 든 인간 캐릭터의 부분별로 이미지를 바꾼다거나 하는 복잡한 처리도 가능하므로 2D 스프라이트 처리에서는 가장 진보된 방식이자 가장 어려운 방식이기도 하다.
* sequenceData 문으로 애니메이션 동작을 관리
복합 스프라이트를 만들려면 부분별로 나누어진 각각의 파츠에 애니메이션이 있어야 한다. 결론적으로 여러 개의 단일 애니메이션들을 조합하는 것이기 때문이다. 이를 위해 우선 sequenceData 문으로 애니메이션 데이터(이미지 시트)를 다루는 방법에 대해 알아두어야 한다. 시퀀스의 종류는 다음과 같이 4가지가 있다.
1. 단일 시퀀스 (순차 프레임)
이 형식의 시퀀스는 이미지 시트에 있는 애니메이션 프레임들을 순서에 맞게 차례대로 보여준다. 모든 예제들은 graphics.newImageSheet() 항목에서 만들었던 "imageSheet"를 사용한다. (참고 : http://docs.coronalabs.com/api/library/display/newSprite.html#format-for-sequencedata)
local sequenceData =
{
name="walking",
start=3,
count=6,
time=100, -- 선택옵션. 단위는 ms. 만약 이 옵션이 주어지지 않으면 스프라이트는 프레임 기반이 된다.
loopCount = 0 -- 선택옵션. 디폴트는 0. 0으로 세팅되면 무한히 반복된다.
loopDirection = "bounce" -- 선택옵션. 값은 "forward"나 "bounce" 중의 하나가 된다.
}
local character = display.newSprite( imageSheet, sequenceData )
2. 단일 시퀀스 (비순차 프레임)
이 형식은 위의 순차 프레임 샘플과 같지만, 대신 애니메이션에 사용할 프레임 인덱스를 열거함으로써 사용하고 싶지 않은 프레임을 빼고 재생할 수가 있다.
local sequenceData =
{
name="walking",
frames= { 3, 4, 5, 6, 7, 8 }, -- 애니메이션의 이미지 시트에서의 프레임 인덱스
time = 50, -- 선택옵션. 단위는 ms. 만약 이 옵션이 주어지지 않으면 스프라이트는 프레임 기반이 된다.
loopCount = 0 -- 선택옵션. 디폴트는 0. 0으로 세팅되면 무한히 반복된다.
}
local character = display.newSprite( imageSheet, sequenceData )
3. 다중 시퀀스
이 형식은 하나의 스프라이트에 여러 개의 애니메이션 시퀀스를 넣는다. sequenceData 배열 안에 순차 시퀀스와 비순차 시퀀스를 다 넣을 수 있다는 사실에 주목하라.
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 )
4. 다중이미지 시퀀스
이 형식은 하나의 스프라이트에 여러 개의 이미지 시트를 사용한다. 각각의 시퀀스 설정에 이미지 시트의 파라미터(정보)를 넣어서, 그 프레임들이 어느 이미지 시트에서 오는 것인지 알 수 있게 한다.
-- 1번 이미지 시트
local sheetData1 = { width=64, height=64, numFrames=6, sheetContentWidth=384, sheetContentHeight=64 }
local sheet1 = graphics.newImageSheet( "mySheet1.png",sheetData1 )
-- 2번 이미지 시트
local sheetData2 = { width=64, height=64, numFrames=6, sheetContentWidth=384, sheetContentHeight=64 }
local sheet2 = graphics.newImageSheet( "mySheet2.png", sheetData2 )
-- 시퀀스에서, 사용되어야 하는 이미지 시트를 파라미터 'sheet='로 참조하고, 각각의 이미지 시트에 이름을 부여해 놓는다.
-- 처음에는 1번, 즉 seq1 이미지 시트의 애니메이션만 재생된다.
local sequenceData = { --> 즉 여기는 초기에 sequenceData로 설정하는 부분
{ name="seq1", sheet=sheet1, start=1, count=6, time=220, loopCount=0 },
{ name="seq2", sheet=sheet2, start=1, count=6, time=220, loopCount=0 }
}
local myAnimation = display.newSprite( sheet1, sequenceData )
myAnimation.x = display.contentWidth/2 ; myAnimation.y = display.contentHeight/2
myAnimation:play() --> 애니메이션을 재생시킨다
-- 잠시 딜레이를 둔 뒤, 시퀀스를 'seq2'로 교체한다.
local function swapSheet()
myAnimation:setSequence( "seq2" ) --> setSequence("이미지시트명") 으로 시퀀스를 교체하는 부분
myAnimation:play() --> 애니메이션을 다시 재생시킨다
end
timer.performWithDelay( 2000, swapSheet ) --> 타이머로 시간을 잰 뒤 2000ms가 지나면 swapSheet 함수를 실행한다
* 스프라이트들을 묶어서 큰 스프라이트로 만들기
sequenceData 문으로 각각의 애니메이션 동작을 다루는 방법을 알았다면, 이제 다음 예제 소스를 보자. 이 소스는 시퀀스 데이터들을 그룹으로 묶어서 오브젝트화 시키고 조합된 애니메이션을 만든다.
(원본 출처는 http://www.coronalabs.com/blog/2012/10/09/dynamically-optimized-sprite-sheets/)
-- 스프라이트 파일로부터 데이터를 읽어온다.
local 변수1 =
{
frames = require("스프라이트파일명").frames --> *.lua 스프라이트 파일로부터 파라미터를 읽어온다.
}
local sheetData1 = { width=가로크기, height=세로크기, numFrames=6, sheetContentWidth=384, sheetContentHeight=64 }
local sheet = graphics.newImageSheet( "이미지파일",sheetData1 )
-- 시퀀스데이터 내에 이름과 프레임을 분류해서 설정해 넣는다.
local sequenceData = {
{ name="beachboy_hat_yellow", frames={1} }, --> 단일 비순차 시퀀스지만. 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" },
--> 단일 비순차 시퀀스. frames={} 안에 설정된 번호의 그림들을 사용. loopDirecrion으로 왔다갔다 재생을 하게 한다.
{ name="beachboy_foot_dark", frames={47} }
}
-- 읽어온 시퀀스데이터 역시도 테이블(배열)로 저장되므로, 내부의 어떤 요소든지 읽어내 참조하거나 값을 변경해 넣을 수 있다.
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 }
}
sequenceData[1].name = "test" --> 시퀀스데이터 안의 3개의 애니메이션 데이터 중 첫번째 데이터의 name 값을 walking
에서 test로 바꿔넣는다
print(sequenceData[1].name) --> name값을 출력해본다. 방금 변경해넣은 test가 출력된다.
print(sequenceData[1].start) --> start 값을 출력해본다. 1이 출력된다.
print(sequenceData[2].frames[2]) --> 2번째 애니메이션의 frames 데이터 중 2번째를 출력한다. 2번째인 4가 출력된다.
sequenceData[1].start = 25 --> start값을 25로 변경한다.
sequenceData[2].frames = { 22, 33, 44 } --> 2번째 애니메이션의 frames 데이터 배열을 아예 바꿔버렸다.
print(sequenceData[1].start) --> 다시 한번 start값을 출력한다. 이번엔 1이 아닌 25가 출력된다.
print(sequenceData[2].frames[2]) --> 2번째 애니메이션의 frames 데이터 중 2번째를 다시 출력해본다. 4가 아니라 33이
출력된다.
-- 캐릭터를 위한 디스플레이 그룹을 만든다. 보통의 display.newSprite 함수로 만들어지는 하나의 스프라이트들을 이 그룹으로 묶게 된다.
local beachboy = display.newGroup()
-- 스프라이트로 각 몸의 파츠를 만든다.
local hat = display.newSprite( sheet, sequenceData )
hat : setSequence( "beachboy_hat_yellow" )
--> 보통 단일 스프라이트를 만들때 쓰는 것과 같은 구문이다. 다만 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" )
-- 각 파츠의 위치 설정 및 왼쪽/오른쪽 팔의 위치, 확대축소 비율. 팔은 하나의 그림을 뒤집어 사용하기 때문에 .xScale = -1 을 준다.
shorts.x, shorts.y = 0, 15
leftArm.x, leftArm.y = -20, -18
leftArm.xScale = -1 --> 애니메이션 leftArm을 가로로 뒤집는 부분
-- 각 파츠들을 아래에서부터 위의 순서대로 디스플레이 그룹에 넣는다.
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...