메가드라이브에서 이미지와 텍스트를 표시하기 위해서는 이미지의 형식에 대해 이해할 필요가 있습니다. SGDK에는 ResComp라고 하는, 이미지나 음악 파일을 다루기 위한 유용한 도구가 포함되어 있어, 비교적 쉽게 이미지를 다룰 수 있도록 되어 있습니다. 여기서는 그 사용법을 정리해 보겠습니다.

 

<메가드라이브의 이미지 형식>

 

 이것은 메가드라이브에서 표시가 가능한 512색의 컬러 차트입니다. 각 채널이 3비트인 RGB 색상으로 되어 있습니다.

 

 일반적으로 메가드라이브라고 하면 발색 수가 적다는 인상이 있지만, 사실 사용 가능한 색상은 이 정도로 많습니다. 인간의 눈은 인접한 색상의 차이와 색의 조합은 섬세하게 식별할 수 있지만, 하나하나의 색상의 인식은 상당히 모호합니다. 그렇다는 것은, 결국 궁리하기 나름이라는 것으로, 512색으로도 상당한 표현을 할 수 있다고 생각합니다.

 

 다만 이 512색 중에서, 한 장의 그림에는 임의의 15색을 골라 사용해야 합니다(16색 중 1색은 배경색이 되므로).

 

 EDGE 등의 이미지 편집 소프트웨어는 8비트의 RGB 색상( 빨강, 초록, 파랑 각 채널이 각각 256단계)에서 편집하는 것이 표준적인 사양입니다. 그래서 필자는 각 채널의 값을 0, 36, 73, 109, 146, 182, 219, 255 8단계로 분류하여 이 값에 한정한 이미지 편집을 하고 있습니다.

 

 EDGE에서 팩맨 스타일의 도트 그림을 그려 보았습니다(여전히 센스가 없습니다).

 

 색상 파레트의 맨 위쪽 16색만을 사용하고 있고, 가장 왼쪽의 0번째의 색은 투명색(첫번째 파레트 : PAL 0의 경우 배경색)입니다.

 

 파레트의 빨강, 초록, 파랑 채널의 각 값을 0, 36, 73, 109, 146, 182, 219, 255 중의 하나로 하면 SGDK에 가져와도 감색되지 않고 원한 색이 그대로 출력되는 것입니다. 

 

 '16색으로 저장한다'를 체크하고, PNG 형식으로 저장합니다. 이 파일은 스프라이트로 사용할 예정입니다.

 

gr_char.png

 

 마찬가지로 배경 이미지를 만들고 16색 포맷으로 저장합니다.

 

gr_map.png

 

 SGDK에는 폰트도 준비가 되어 있지만, 이번에는 ATARI FONT로 변경했습니다. ASCII 코드로, 0x20 ~ 0x7F(32 ~ 127)까지의 문자입니다.

 

gr_font.png

 

<이미지와 문자를 표시해 본다>

 

 실제로 메가드라이브에서 이미지와 문자를 표시해 보겠습니다.

 

 그림을 넣을 res 폴더를 프로젝트 안에 만들고, 방금 전의 이미지 파일(gr_font.png, gr_map.png)를 만든 res 폴더에 넣습니다. 또한 ResComp에서 이미지를 로드하게 해 주는 파일 gfx.res를 생성한 뒤, 안에는 다음 두 줄의 코드를 넣습니다. gfx.h는 자동으로 생성되는 것 같습니다.

 

IMAGE image_font "gr_font.png"-1

IMAGE image_map "gr_map.png"-1

 

 main.c의 코드는 다음과 같이 합니다.

 

#include <genesis.h>

#include "gfx.h"

#define TILE_MAP  TILE_USERINDEX  /* gr_map.png의 타일 셋의 VRAM 상의 위치 */

int main()
{

    /* 폰트 디자인의 설정 */
    VDP_loadFont(image_font.tileset, TRUE);

    /* 타일 셋을 읽어들인다 */
    VDP_loadTileSet(image_map.tileset, TILE_MAP, FALSE);

    /* 파레트 데이터를 읽어들인다 */
    VDP_setPalette(PAL0, image_map.palette->data);

    /* 폰트의 파레트 설정 */
    VDP_setTextPalette(PAL0);

    /* 배경맵을 표시 */
    VDP_setMap(PLAN_A, image_map.map, TILE_ATTR_FULL(PAL0, 0, 0, 0, TILE_MAP), 1, 0);

    /* 문자를 표시 */
    VDP_drawTextBG (PLAN_A, "HIGH SCORE", 30,1);
    VDP_drawTextBG (PLAN_A, "   000000", 30,3);
    VDP_drawTextBG (PLAN_A, "1UP", 30,5);
    VDP_drawTextBG (PLAN_A, "   000000", 30,7);
    VDP_drawTextBG (PLAN_A, "CREDIT 00", 30,26);

    while(1)
    {
        
// program
    }
    return (0);
}

 

 실행하면 이렇게 됩니다.

 

 소스 파일 : CookieMan.zip

 

<ResComp에서 사용하는 Image 구조체에 대해>

 

 SGDK에는 이미지를 다루는 구조체인 Image가 준비되어 있어, 여기에는 Palette, TileSet, Map 구조체의 포인터가 멤버로 포함되어 있습니다. ResComp에서 PNG 파일을 가져오면 Image 구조로 로드됩니다.

 

typedef struct
{
    Palette *palette;
    TileSet *tileset;
    Map *map;
} Image;

 

 이런 구조체입니다.

 

 Palette 구조체는, 512 색상 중 어떤 16색을 사용할 것인지에 대한 색상의 지정 데이터입니다.

 

typedef struct
{
    u16 index;
    u16 length;
    u16 *data;
} Palette;

 

 TileSet에는 이미지에 사용되는 타일 데이터(패턴)이 포함되어 있습니다.

 

typedef struct
{
    u16 compression;
    u16 numTile;
    u32 *tiles;
} TileSet;

 

Map에는 이미지의 크기, 타일 맵(이미지의 어떤 위치에서 어떤 패턴이 사용되고 있는지 알려주는 데이터)이 포함되어 있습니다.

 

typedef struct
{
    u16 compression;
    u16 w;
    u16 h;
    u16 *tilemap;
} Map;

 

 

 

Posted by windship

 이번에는, 메가드라이브의 화면 표시에 대해, 화면 모드와 VRAM의 매핑에 대해서 정리해 봤습니다. 자신이 생각하는 대로 표시를 하기 위해서는, 메가드라이브의 이미지 표시 방식에 대해 어느 정도 이해를 할 필요가 있습니다.

 

<메가드라이브의 화면 모드>

 

1. GRAPHIC IV 모드 : 마스터 시스템과의 호환 모드

2. GRAPHIC V 모드 : 메가드라이브의 16비트 모드

 

이렇게 2종류가 있습니다만, 기본적으로는 GRAPHIC V 모드를 사용합니다.

 

 

 위 그림과 같이 (1) 스프라이트 레이어(SPRITE), (2) 윈도우 레이어(WINDOW), (3) 스크롤 A 레이어(SCROLL A), (4) 스크롤 B 레이어(SCROLL B)의 4개 레이어가 존재합니다. 각각의 레이어(스프라이트 레이어는 제외)를 플랜(PLAN) 이라 부릅니다.

 PRIORITY CONTROLLER가 표시 순서를 관리하고 있어, 스프라이트 레이어는 스프라이트 단위로, 그 외의 배경 레이어는 CELL(셀) 단위(8x8 도트를 1 CELL이라고 부릅니다)로 표시 순서를 임의로 설정 가능합니다. PRIORITY의 설정이 같은 경우, 위 그림의 순서대로 표시됩니다.

 다만, 윈도우 레이어(WINDOW)와 스크롤 A 레이어(SCROLL A)는, 공통의 플랜에 표시되고 있기 때문에 겹쳐서 표시하는 것은 불가능합니다.

 

<화면의 해상도 설정>

 

(1) 32x28 CELL(256x224 픽셀)과 (2) 40x28 CELL(320x224 픽셀)의 2종류 모드가 있습니다. 특징으로는 40x28셀 모드일 경우 스프라이트는 80장을 사용할 수 있고, 32x28셀의 경우에는 스프라이트 64장을 사용할 수 있습니다.

 

 SGDK에서는 다음과 같이 하여 해상도를 설정 가능합니다.

 

VDP_setScreenWidth320(); // 40x28셀 모드로 설정

VDP_setScreenWidth256(); // 32x28셀 모드로 설정

 

<스크롤 레이어의 사이즈 설정>

 

 스크롤 A 레이어(SCROLL A)와 스크롤 B 레이어(SCROLL B)는 사이즈 변경이 가능해서, 이하의 사이즈 중에서 선택이 가능합니다. 이 범위의 사이즈라면 한번에 이미지를 읽어들이는 것이 가능합니다. 다만, 판타지 존과 같이 배경 레이어가 넓은 경우(가로 256 CELL)는 한번에 읽어들일 수가 없으므로, 스크롤에 맞춰서 이미지를 다시 읽어줘야 할 필요가 있습니다. 범위가 넓어지면 당연히 VRAM의 사용량이 많아집니다. 보통은 64x32 셀로 설정하는 것으로 문제없지 않을까 라고 생각합니다.

 

(1) 32x32 셀 2Kbytes (32x32x2bytes = 2048 bytes 사용)

(2) 32x64 셀 4Kbytes 

(3) 32x128 셀 8Kbytes

(4) 64x32 셀 4Kbytes

(5) 64x64 셀 8Kbytes

(6) 128x32 셀 8Kbytes

 

 SGDK에서는 다음과 같이 하여 설정합니다.

 

VDP_setPlanSize(64, 32) // 64x32 셀로 설정

 

 

<윈도우 레이어의 설정>

 예를 들어, 판티지 존의 위쪽에 있는 스코어 패널의 표시 등에 사용합니다. 스크롤이 되지 않습니다. 설정 가능한 것은 4각형의 범위로, 한 군데 뿐입니다.

 화면에 대해서, 상단, 하단, 우측단, 좌측단, 좌상단, 우상단 같은 설정도 가능합니다. 최대 40x28 셀 범위에서 사용합니다. VRAM을 가로 한 줄로 40 셀이 아니라 64 셀을 사용하기 위해서, 세로 28 셀을 전부 사용하는 설정이라면, 윈도우 레이어에서 3584(64x28x2) bytes의 VRAM을 확보할 필요가 있습니다.

 

SGDK의 설정

 

VDP_setWindowVPos(FALSE, 5); // 상단의 5셀 부분을 윈도우 레이어로 사용

 

<VRAM의 매핑 설정>

 

 메가드라이브의 VRAM에는 64Kbytes의 용량이 있습니다. 매핑은 자유롭게 변경 가능하고, 이해의 내용이 VRAM에 사용됩니다.

 

(1) 스크롤 A 레이어의 패턴 네임 테이블(SCROLL A PATTERN NAME TABLE)

    각 플랜의 셀의 사용패턴, 파레트, 셀의 반전, 표시 우선순위 등을 정의한 데이터를 패턴 네임 테이블이라고 부르고, 1 셀로 2Bytes를 사용합니다. 최대 8Kbytes(128x32x2Btytes)

(2) 스크롤 B 레이어의 패턴 네임 테이블(SCROLL B PATTERN NAME TABLE). 최대 8k를 사용 가능.

(3) 스크롤 A 레이어와 스크롤 B의 수평 스크롤 양(H SCROLL DATA TABLE) 화면 전체를 한꺼번에 스크롤하는 모드, 셀 단위로 스크롤 시키는 모드, 그리고 라인 단위로 스크롤 시키는 모드가 있다.

(4) 라인 단위라면 1Kbytes(265x2x2화면) 사용. 전 화면이라면 4bytes 뿐입니다.

(5) 스프라이트 어트리뷰트 테이블(SPRITE ATTRIBUTE TABLE)

    스프라이트의 좌표, 사용 패턴, 파레트, 표시 우선순위, 다음에 묘화될 스프라이트의 번호를 정의한 데이터를 스프라이트 어트리뷰트 테이블이라고 부르고, 1 스프라이트에 8bytes 를 사용합니다. 80개의 스프라이트라면 640bytes를 사용하게 됩니다.

(6) 패턴 제네레이터(PATTERN & SPRITE GENERATOR TABLE

    스프라이트, 스크롤 A 레이어, 스크롤 B의 표시에 필요한 비트맵 이미지를 일반적으로 패턴(patterns)라고 부릅니다. 스프라이트에 사용되는 것을 스프라이트 제네레이터, 스크롤 레이어에 사용되는 것을 패턴 제네레이터라고 부릅니다. 패턴 데이터는 양쪽에서 공유 됩니다. 가장 첫 어드레스는 0x0000으로 고정입니다만 데이터 사이즈는 임의입니다.

    데이터는 셀 단위의 배치가 됩니다. 1 셀은 16tor(4비트)의 8x8 픽셀로, 1 셀은 4x8x8= 32bytes의 데이터가 됩니다.

 

SGDK의 설정

 

/* VRAM의 주소 설정 예 */

VDP_setWindowAddress(0xD000);
VDP_setAPlanAddress(0xC000);
VDP_setBPlanAddress(0xE000);

VDP_setHScrollTableAddress(0xF000);
VDP_setSpriteListAddress(0xFC00);

 

/* 스크롤 모드 설정의 예 *'

 

VDP_setScrollingMode(HSCROLL_PLANE, VSCROLL_PLANE);  

 

가로, 세로 양쪽 다 PLANE 전체로 스크롤하는 설정입니다.

Posted by windship

출처 : https://ameblo.jp/arcade-cabinet/entry-12268974254.html

 

『メガドライブのゲームの作り方 その1 - SGDKの環境設定』

SGDKを用いて、メガドライブのプログラミングを始めて約10か月。今年で48歳になる劣化した脳細胞は、一週間もすれば覚えたことを忘れてしまいます。そこで、自分…

ameblo.jp

 

 SGDK를 이용해서, 메가드라이브 프로그래밍을 시작한 지 약 10개월. 올해로 48세가 되어 늙어가는 뇌세포는, 1주일 정도면 기억한 것을 잊어 버립니다. 그래서, 제 나름대로 시행착오 끝에 체득한 메가드라이브 게임 만드는 방법을 기록으로 남겨 두기로 했습니다. 팩맨같은 도트 먹기 게임을 실제로 만들어 보려고 합니다.

 

<프로그래밍에 필요한 소프트웨어>

* SGDK : 메가 드라이브의 개발 키트. (Stephane씨에 의해 개발됨)

* Code::Blocks : 오픈 소스 C/C++ 통합개발환경

* EDGE : 256색 도트 그래픽을 위해 개발된 윈도우용 그래픽 에디터

* Fusion : 메가드라이브 에뮬레이터 (이외에도 Gens나 Regen 같은 것들도 있습니다)

 

<SGDK의 인스톨>

 

1. 여기에서 SGDK를 다운로드 한다

  (2017년 4월 25일 최신 버전은 SGDK 1.22)

 

2. 다운로드한 파일을 기억하기 쉬운 곳에 저장

  (여기에서는 C:\MEGADRIVE\sgdk122 로 합니다)

 

3. 윈도우의 환경변수를 설정합니다.

  윈도우 10의 경우, 데스크탑의 '컴퓨터'를 우클릭하고 속성 > 왼쪽 시스템에서 상세설정을 엽니다. 거기에서 뜨는 윈도우 아래쪽의 "환경변수(N)..."을 클릭하고, 유저 환경변수의 "신규(N)..."을 클릭합니다.

  "GDK"와 "GDK_WIN"의 두개의 환경 변수를 신규 등록합니다.

 

  * GDK의 등록 ('\'가 아니라 '/'로 해야합니다)

  * GDK_WIN의 등록

 

<Code::Blocks의 인스톨>

 

1. Code::Blocks 사이트에서 셋업 파일을 다운로드 합니다. codeblocks-16.01mingw-setup.exe 파일을 다운 받습니다(2017. 4. 25 현재 최신 버전).

 

2. 셋업 파일을 실행하여 Code::Blocks를 인스톨합니다. 설정은 전부 디폴트 값으로 해도 문제 없습니다.

 

3. 셋업이 완료되면 Code::Blocks가 실행됩니다.

처음에 컴파일러를 고르는 화면이 나오므로, GNU GCC Compiler를 선택하고 OK를 클릭합니다.

 

4. 다음으로 Code::Blocks의 확장자를 연결하는 과정이 있는데, 확장자 연결이 필요없다면 제일 위의 옵션을 선택합니다.

 

5. Code::Blocks의 메뉴에서 Setting -> Compiler...를 선택합니다.

6. 'Copy'를 클릭합니다.

7. 새로운 컴파일러의 이름을 물어보므로, 'Sega Genesis Compiler'라고 입력하고 OK를 클릭합니다.

 

8. OK를 클릭합니다.

 

9. Toolchain executables의 탭으로 가서 Compiler's installation directory에 있는 '...' 버튼을 클릭하고 SGDK를 설치한 폴더를 선택합니다.

10. Program Files를 아래와 같이 각각 '...' 버튼을 클릭해 선택해 줍니다.

모두 선택했다면 OK를 눌러줍니다. 이것으로 설정은 완료입니다.

 

<프로젝트의 신규 작성>

 

1. Code::Blocks의 메뉴에서 File > New > Project... 를 선택

2. Empty project 를 선택하고 'Go'를 클릭

3. 'Next >'를 클릭

4. 프로젝트 이름과 저장될 폴더를 설정합니다.

  여기에서는 프로젝트 이름을 'CookieMan', 프로젝트 폴더를 'C:\MEGADRIVE\sgdk122\projects'로 했습니다.

5. 아까 위에서 설정한 컴파일러 'Sega Genesis Compiler'를 선택하고, 아래와 같이 설정하고 'Finish'를 클릭하면 새 프로젝트가 만들어집니다.

6. 다음으로 작성한 프로젝트를 우클릭하여 Properties... 를 선택

7. Makefile을 SGDK용의 커스텀 Makefile로 설정하고 OK를 클릭하여 윈도우를 닫습니다.

8. 다시 프로젝트의 Properties...를 열고, 같은 방식으로 윈도우를 닫고, 'Project's build options...'를 클릭

9. 'defaults'에서 "Make" commands 탭을 열고, 아래와 같이 설정한 뒤 OK를 클릭

10. 프로젝트가 저장된 폴더에 아래외 같이 폴더를 추가로 만들어 줍니다.

src : 소스파일(*.c, *.s)

inc : 헤더 파일(*.h, *.inc)

res : 이미지/사운드 리소스 파일(*.wav, *.png)

가 저장될 폴더입니다.

11. 프로그램 소스(main.c)를 만듭니다.

  메뉴의 File > New > File... 을 선택

12. C/C++ source 를 선택하고 'Go'를 클릭

13. 'Next >' 를 클릭

14. C언어를 선택하고, 'Next >' 를 클릭

15. 파일명과 파일 경로를 물어오므로, 'Filename with full path:' 에 있는 '...' 버튼을 클릭

16. 파일명은 main.c로, 경로는 아까 만든 src 폴더를 지정합니다.

17. default에 체크

이것으로, main.c를 Code::Blocks에서 편집할 수 있게 되었습니다.

아래와 같이 코드를 짭니다. SGDK 사이트에 있던 Hello World의 코드입니다.

 

#include <genesis.h>

int main()
{
VDP_drawText("Hello Genny World!", 10, 13);

while(1)
{
//read input
//move sprite
//update score
//draw current screen (logo, start screen, settings, game, gameover, credits...)
}
return (0);
}

 

17. 프로젝트를 빌드합니다.

18. 문제없이 완료된다면, 프로젝트 경로에 있는 out 폴더에 rom.bin이 만들어질 것입니다.

  이 파일을 에뮬레이터로 실행시키면 아래와 같은 화면을 볼 수 있습니다.

  메가드라이브의 롬파일이 완성되었습니다!

 

* 내용은 SGDK의 공식 사이트에 자세하게 설명되어 있습니다.

* 내용에 잘못된 부분이 있을 수도 있습니다. 혹시 틀린 부분을 발견하셨다면, 코멘트로 지적해 주시면 감사하겠습니다.

Posted by windship

1. SGDK 다운로드


   - https://github.com/Stephane-D/SGDK/wiki/Download

   - 임의의 폴더에 압축을 푼다



2. 이클립스 다운로드


   - http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/oxygen3a

   - 다운로드 후 설치(Applications 폴더에 등록)



3. 환경변수 설정


   - http://huskdoll.tistory.com/97

   - 윈도우의 경우에는 내 PC의 속성에서 설정해주지만, 맥OS의 경우는 명령 프롬프트를 사용한다.

   - 명령 프롬프트에서 printenv 라고 입력하면 설정된 변수를 확인할 수 있음

   - GDK라는 환경변수를 세팅하고 그 값을 SGDK 압축 푼 폴더 경로로 지정해야 함

   - nano ~/.bash_profile 입력해서 편집함. bash_profile 파일이 없는 경우는 새로 만드는 것으로 OK

   - export GDK=/Applications/sgdk134 입력하고 저장 후 nano 종료

   - source ~/.bash_profile 입력

   - echo $GDK 입력하여 경로가 잘 지정되었는지 확인



4. 이클립스 세팅


   - https://github.com/Stephane-D/SGDK/wiki/Setup-SGDK-with-Eclipse 


Posted by windship
프로그래밍/Unity2018. 5. 6. 23:01
출처 : https://qiita.com/kuuki_yomenaio/items/00d74762e930d037ad27

Spine 오브젝트는?

Spine 개체는 Unity에서 말하는 MeshRenderer로 렌더링되고 있습니다. 
(spine-unity의 SkeletonRenderer.cs가 해당 소스입니다) 
즉, MeshRenderer의 Mask 처리를 만들 수 있다면, Mask를 사용할 수 있게 됩니다. 

이름이 설정되지 않았습니다 ..mov.gif
(왜 이런 엄한 부분을 가지고 예제를 만든 걸까...)

Mask 처리에 대해

요는 Renderer를 Mask하면 되는 것이지만, 
SpriteRenderer 자체를 Mask하는 처리는 Unity에는 없는 것 같습니다. 
그래서, 간단히 자작하기로 했습니다.

Shader로 실제 제작

간단한 것은 역시 스텐실 테스트를 사용하는 것이군요. 
아주 간단하게 설명하면, 스텐실이란 모양을 도려낸다는 의미로 
픽셀 렌더링을 할 때 이 점을 찍는가 찍지 않는다인지를 판정하는 테스트입니다. 
이것을 이용합니다.

Spine 쪽에 적용하는 shader

SpineShader.shader
Shader "Custom/SpineShader"{
Properties
{
        _MainTex ("Base (RGB)", 2D) = "white" {}
}

SubShader
{
        Tags {"Queue"="Transparent+2" "IgnoreProjector"="True" "RenderType"="Transparent"}
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Stencil {
                                Ref 1
                                Comp Equal
        }

        Pass
        {
                CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag

                        #include "UnityCG.cginc"

                        struct appdata_t
                        {
                                float4 vertex : POSITION;
                                float2 texcoord : TEXCOORD0;
                        };

                        struct v2f
                        {
                                float4 vertex : SV_POSITION;
                                half2 texcoord : TEXCOORD0;
                        };

                        sampler2D _MainTex;
                        float4 _MainTex_ST;

                        v2f vert (appdata_t v)
                        {
                                v2f o;
                                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                                return o;
                        }

                        fixed4 frag (v2f i) : COLOR
                        {
                                fixed4 col = tex2D(_MainTex, i.texcoord);
                                return col;
                        }
                ENDCG
        }
}

}

Mask할 Sprite에 설정하는 Shader

SpineSoriteMask.shader
Shader "Custom/SpineSpriteMask"{
Properties
{
        _MainTex ("Base (RGB)", 2D) = "white" {}
}

SubShader
{
        Tags {"Queue"="Transparent+1" "IgnoreProjector"="True"}
        ZWrite Off
        AlphaTest Greater 0.5
        ColorMask 0
        ZTest Always


        Stencil {
                                Ref 1
                                Comp always
                                Pass replace
                        }


        Pass
        {
                CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag

                        #include "UnityCG.cginc"

                        struct appdata_t
                        {
                                float4 vertex : POSITION;
                                float2 texcoord : TEXCOORD0;
                        };

                        struct v2f
                        {
                                float4 vertex : SV_POSITION;
                                half2 texcoord : TEXCOORD0;
                        };

                        sampler2D _MainTex;
                        float4 _MainTex_ST;

                        v2f vert (appdata_t v)
                        {
                                v2f o;
                                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                                return o;
                        }

                        fixed4 frag (v2f i) : COLOR
                        {
                                fixed4 col = tex2D(_MainTex, i.texcoord);
                                if(col.a<0.1)discard;
                                return col;
                        }
                ENDCG
        }
}

}

해설

SpineShader.shader
   Stencil {
             Ref 1
             Comp Equal
   }
SpineSoriteMask.shader
   ColorMask 0
   Stencil {
             Ref 1
             Comp always
             Pass replace
   }

Stencil

해설에 대해서는 
edo_m18 씨의 [Unity] Unity의 Shader 스텐실 버퍼를 시도
를 참고하십시오.

먼저, SpineSpriteMask 입니다만, 
"참조 값 1의 것과 비교하고, 모두 OK로 해서,
참조 값을 버퍼에 기록하고, ColorMask 0 으로 묘화는 하지 않음" 
이 됩니다.

그리고 SpineShader. 
"참조 값은 1, 값의 일치를 체크" 
가 됩니다.

이 설정을 함으로써 
SpineShader 측의 묘화는  StencilTest 를 받게 되고, 
SpineSpriteMask 측에서 설정한 모양대로 도려내지도록 그려집니다.

Unity에서 사용할 때에는 Material화 시킬 필요가 있습니다만, 
그에 대해서는
github
여기를 참고하십시오.

---

* 스파인측 설정


* 마스크이미지측 설정

* 매터리얼 설정은 이렇게(특별히 이미지는 지정하지 않고 쉐이더만 넣음)



Posted by windship
프로그래밍/Spine2017. 1. 30. 13:43

(출처 : http://ko.esotericsoftware.com/spine-unity#Controlling-Animation)



Controlling Animation

애니메이션 제어하기




SkeletonAnimation


 Normally, Spine.AnimationState manages keeping track of time, updating the skeleton, queueing, layering and mixing/crossfading animation. If you're used to "Mecanim" as having State Machines, Spine.AnimationState is a simpler, non-programmable kind, but flexible enough to serve as the base for most animation logic.

 일반적으로 Spine.AnimationState 는 타임트랙 유지, 스켈레톤 갱신, 큐, 레이어링, 애니메이션의 믹싱/크로스페이딩을 관리하게 된다. 만약 당신이 상태 관리를 위해서 "메카님"을 사용하는 데에 익숙하다면, Spine.AnimationState 가 더 간단하고 프로그램을 잘 모르는 사람도 사용하기 쉬우면서도 대부분의 애니메이션 로직에 대응하기에 충분한 유연성을 제공할 수 있다.


 But Spine.AnimationState is a C# class. To make it usable in Unity, the Spine.AnimationState object is wrapped in a MonoBehaviour called SkeletonAnimation. You will find that SkeletonAnimation has a member field called state, a reference to the Spine.AnimationState object.

 하지만 Spine.AnimationState 는 C# 클래스이므로, 유니티에서 사용하려면 Spine.AnimationState 를 MonoBehaviour에서 SkeletonAnimation 이라는 이름으로 사용해야 한다. 


 SkeletonAnimation both manages the timing of the updates (through Update) and generates the Mesh object since it derives from the SkeletonRenderer class. This is the main component that's added to the GameObject when you Instantiate a Skeleton Data Asset into a "Spine GameObject". You could say that SkeletonAnimation is the Spine component.

 SkeletonAnimation 은 SkeletonRenderer 클래스에서 받아와 메쉬 오브젝트를 생성하고 Update를 통해 타이밍을 관리한다. 이것은 유니티에서 Spine GameObject를 사용해 Skeleton Data Asset 을 생성했을 때 자동으로 만들어진다. SkeletonAnimation 은 곧 스파인 컴포넌트라고 생각해도 좋다.


How to use SkeletonAnimation

SkeletonAnimation 사용하기


For Beginners

당신이 초심자라면


 “Out of the box”, an instantiated GameObject with a SkeletonAnimation component on it is ready to go. Changing the Animation dropdown in the Inspector allows you to set the initial animation for that SkeletonAnimation object which starts playing immediately when the scene starts.

 SkeletonAnimation 컴포넌트로 만들어진 GameObject만 있으면 준비는 된 것이다. 인스펙터에서 애니메이션을 변경하면 SkeletonAnimation 에 초기 애니메이션을 설정해줄 수 있다. 프로그램이 실행되고 씬이 동작하면 즉시 이 애니메이션이 돌아가게 된다.


 Looping can be enabled and disabled, and the Time Scale can be changed. These Inspector properties map to .AnimationName, .loop and .timeScale.

 계속 반복되는가의 여부와 타임스케일(빠르기)도 설정해줄 수 있다. 이것은 코드에서 .AnimationName / .loop / .timeScale 을 통해 읽어들여 사용할 수 있다.


 For beginner code, you can use skeletonAnimation.AnimationName property to change the playing animation with its name. It will start playing and loop if the skeletonAnimation.loop was set to true at the time it was changed.

 초심자 코드라면, skeletonAnimation.AnimationName 프로퍼티를 사용해 재생될 애니메이션을 변경할 수 있다. 재생이 시작되고 skeletonAnimation.loop가 true로 설정되면 계속 반복 재생될 것이다.


skeletonAnimation.timeScale = 1.5f;

skeletonAnimation.loop = true;

skeletonAnimation.AnimationName = "run";



Left and Right Animations

좌우 이동 애니메이션


 It is not necessary for you to animate left-facing and right-facing animations in Spine editor. SkeletonAnimation's Skeleton object has a FlipX property (and FlipY) you can set which horizontally flips the rendering and logical positions of bones of that skeleton.

 당신이 스파인 애니메이터에서 좌우 각각의 애니메이션을 따로 만들어야 할 필요는 없다. SkeletonAnimation 의 Skeleton 오브젝트는 FlipX와 FlipY 프로퍼티를 가지고 있어서 본의 위치와 렌더링 포지션을 뒤집어 출력시킬 수 있다.


// If your character was facing right, this will cause it to face left.

// 만약 캐릭터가 오른쪽을 보고 있다면, 이 코드는 왼쪽을 보게 만들어준다.


skeletonAnimation.skeleton.FlipX = true;


 However, if you opted to design your character asymmetrically but the movement is essentially the same in both directions, you can use Spine's Skins feature to have two skins— one facing right, and one facing left— and switch between those two as your character faces left and right.

 하지만, 당신이 캐릭터의 좌우 그래픽을 다른 형태로 디자인했다면, 스파인의 스킨 기능을 써서 좌우 두가지 모습을 만들고 캐릭터가 좌/우로 움직일 때 각각 다른 스킨을 바꿔가며 보이게 할 수 있다.


bool facingLeft = (facing != "right"); 

skeletonAnimation.skeleton.FlipX = facingLeft;

skeletonAnimation.skeleton.SetSkin(facingLeft ? "leftSkin" : "rightSkin");


For Typical Use

전형적인 사용법


 The class that actually controls the animation of the skeleton is Spine.AnimationState. SkeletonAnimation is built around AnimationState. It stores a reference to its instance of Spine.AnimationState in SkeletonAnimation.state. This is instantiated on Awake so make sure to only access it on Start or any time after.

 스켈레톤의 애니메이션을 실질적으로 컨트롤하는 클래스는 Spine.AnimationState 이다. SkeletonAnimation 은 AnimationState를 포괄하도록 만들어졌으며, SkeletonAnimation.state 안에 Spine.AnimationState의 인스턴스를 참조하여 저장한다. 이것은 Awake를 사용해서 생성할 수 있고 Start로 시작시에 부르거나 이후 언제든지 불러올 수 있다.


 AnimationState is Spine’s common-case implementation of a Spine Animation State Machine. It is lower-level in a sense that it only manages updating, queuing, layering and mixing/crossfading animations. (It is not a "State Machine" in the Mecanim sense).

 AnimationState 는 스파인의 일반적인 삽입 방식이다. 메카님을 작업할 때의 "스테이트 머신"이 아니다. 좀 더 로우레벨이고 업데이트, 큐잉, 레이어링, 믹싱, 크로스페이딩만을 관리한다.


 In code, you can play animations through AnimationState methods:

 코드에서는 AnimationState 메소드로 애니메이션을 플레이할 수 있다.


// Plays the animation named “stand” once on Track 0.

// 애니메이션 "stand"를 트랙 0에 할당하고 한번만 재생한다.


skeletonAnimation.state.SetAnimation(0, “stand”, false);


// Queues the animation named “run” to loop on Track 0 after the last animation is done.

// 애니메이션 "run"을 마지막 애니메이션 뒤에 트랙 0으로 반복 재생하도록 큐잉한다.


skeletonAnimation.state.AddAnimation(0, “run”, true, 0f);


 Mixing/crossfading options can be found on your SkeletonDataAsset's inspector. There you'll find a field for "Default Mix" which is the default duration of a mix between two animations on the same track. You may also define specific mix durations for specific pairs of animations there.

 믹싱/크로스페이딩 옵션은 SkeletonDataAsset 의 인스펙터에 있다. Default Mix 라는 필드가 있는데 이것은 하나의 트랙을 사용하는 두 개의 애니메이션 간의 믹스 수치를 결정하는 것이다. 특정 애니메이션 간에 임의의 믹스 타이밍을 주고 싶다면 이 수치를 조절해야 할 수 있다.



TRACKS

트랙


 Tracks are animation layers, for playing more than one Spine animation at once.

 트랙이란 동시에 재생되는 1개 이상의 스파인 애니메이션을 위한 레이어와 같은 개념이다.


 This is useful for when, for example, you have an animation for a character's body running, but also have an  animation for the arm shooting a gun.

 이것은 예를 들면 하나의 캐릭터가 다리는 달리고 팔은 총을 쏘는 두 개의 애니메이션을 가지고 있을 때에 유용하다.


 If you play an animation on track 0, and another animation on track 1, both animations will play at the same time and end or loop according to their own durations independently.

 만약 한 애니메이션을 트랙 0에서 재생하고 다른 애니메이션을 트랙 1에서 재생한다면, 각각의 애니메이션은 함께 재생되면서 각각 애니에 설정된 대로 끝나거나 반복된다.


 The animations keys of the animation playing on the higher track will override the keys of the lower track: higher track number, higher priority.

 두 애니메이션이 같은 부분의 키를 가지고 있을 경우, 더 높은 트랙의 키가 낮은 트랙의 키를 덮어버린다. 즉 높은 트랙이 높은 우선권을 가진다.


// Play a running animation on track 0, and a shooting animation on track 1.

// 트랙 0에서 달리는 애니, 트랙 1에서 총을 쏘는 애니메이션을 재생


skeletonAnimation.state.SetAnimation(0, "run", true);

skeletonAnimation.state.SetAnimation(1, "shoot", false);



TRACKENTRY

트랙 엔트리


 When you call SetAnimation or AddAnimation, it returns a TrackEntry object.

 SetAnimation이나 AddAnimation을 호출했을 경우, TrackEntry 오브젝트가 리턴된다.


 This object represents the instance of an animation playback for the animation you just played or queued. It holds information about what animation it is, its elapsed time, its own timescale and several other properties.

 이 오브젝트는 방금 플레이했거나 큐된 애니메이션의 인스턴스를 되돌려준다. 여기에는 어떤 애니메이션인가, 걸린 시간은 얼마인가, 타임스케일 크기, 그리고 몇 가지 다른 프로퍼티가 들어있다.


 TrackEntry lets you control the state of a playing or queued animation, and playback parameters not included in SetAnimation and AddAnimation.

 TrackEntry는 재생중이거나 큐된 애니메이션의 상태를 컨트롤할 수 있게 해주고, SetAnimation과 AddAnimation에 포함되지 않은 파라미터를 플레이백한다.


 If you choose not to keep the reference to the TrackEntry object when you called SetAnimation or AddAnimation, you can get the currently active TrackEntry by calling

 당신이 SetAnimation이나 AddAnimation을 호출했을 때 TrackEntry 오브젝트에의 참조를 보관하지 않기로 했다면, 다음을 호출해서 현재 활성화된 TrackEntry를 얻어올 수 있다.


   var trackEntry = skeletonAnimation.state.GetCurrent(myTrackNumber); 

   // null if no animation is playing.

   // 만약 아무 애니도 재생되지 않고 있다면 null이 리턴된다.   


   

CURRENT TIME (OR START TIME)

현재 시간(혹은 시작 시간)


 The time the Animation is currently playing at can be changed at any point while it's playing.

 현재 재생중인 애니메이션의 시간은 재생되고 있는 중에 언제든지 바뀔 수 있다.


 For example, you can change the .Time immediately after SetAnimation so the animation starts in the middle instead of the beginning. Note that time is in seconds. To convert Spine editor frames to seconds, you must divide by the dopesheet’s 30 fps tick marks: time = (frameNumber/30f).

 예를 들어, SetAnimation 이후 .Time을 즉시 바꿀 수 있고 그러면 시작지점 대신에 중간에서 애니메이션을 스타트시킬 수 있다. 이 시간이 초 단위라는 것을 기억하자. 스파인 에디터의 프레임을 초로 환산하려면, 타임 도프시트의 수치를 30으로 나눠야 한다.


    // Play a "dance" animation starting from frame 10

    // "dance" 애니메이션을 10프레임부터 재생

    var trackEntry = skeletonAnimation.state.SetAnimation(0, "dance", false);

    trackEntry.Time = 10f/30f;


    // You can shorten the above code this way if you only need

    // to change one field, and don't need to store the trackEntry.

    // 만약 필드 하나만 바꿀 것이고 트랙 엔트리를 보존할 필요가 없다면 코드를 아래와 같이 더 줄일 수 있다.

    skeletonAnimation.state.SetAnimation(0, "dance", false).Time = 10f/30f;


 If you are doing this to Animations with animation events, make sure you also set lastTime to the same value as well. If lastTime is left as 0, all events between time 0 and time will be captured and raised/fired in the next Update.

 만약 이것을 애니메이션 이벤트와 함께 하려면, lastTime을 같은 값으로 해 줘야 한다. 만약 lastTime이 0이 되어버리면, 0에서부터 time까지 사이에 있는 모든 이벤트가 캡처되고 다음 Update 때 나와버릴 것이다.


 You can also change the point where the animation ends by setting .EndTime.

 .EndTime을 설정해서 애니메이션이 언제 끝날 것인지도 정해줄 수 있다.



TIMESCALE

타임스케일


 You can change the playback speed by setting that TrackEntry’s .TimeScale. This gets multiplied by the SkeletonAnimation.timeScale and AnimationState.timeScale to get the final timescale.

 트랙엔트리의 .TimeScale을 설정해서 애니메이션의 재생 속도를 바꿀 수 있다. 


 You can set timeScale to 0 so it pauses. Note that even if timeScale = 0 results in the skeleton not moving, the animation is still be applied to the skeleton every frame. Any changes you make to the skeleton will still be overridden in the normal updates. Here is a sample helper method if you want to jump to a certain point in time.

 timeScale 을 0으로 설정해서 애니메이션을 일시 정지시킬 수 있다. timeScale을 0으로 하는 순간 스켈레톤은 움직이지 않지만, 매 프레임 애니메이션은 계속 적용되고 있다는 점을 기억하자.  스켈레톤에 가해지는 변형은 계속 업데이트되고 있을 것이다.


static public Spine.TrackEntry JumpToTime (SkeletonAnimation skeletonAnimation, int trackNumber, float time, bool skipEvents, bool stop) {

    if (skeletonAnimation == null) return null;

    return JumpToTime(skeletonAnimation.state.GetCurrent(trackNumber), time, skipEvents, stop);

}


static public Spine.TrackEntry JumpToTime (Spine.TrackEntry trackEntry, float time, bool skipEvents, bool stop) {

   if (trackEntry != null) {

      trackEntry.time = time;

      if (skipEvents)

         trackEntry.lastTime = time;   // in pre-3.0. This ignores attachment keys too.

         // 3.0 이전 버전에서는 이 부분이 어태치먼트 키도 무시하게 된다.


      if (stop)

         trackEntry.timeScale = 0;

   }

   return trackEntry;

}



TRACKENTRY-SPECIFIC EVENTS.

트랙 엔트리 - 특정 이벤트


 You can subscribe to events just for that specific animation playback instance. See the list of events here: Events Documentation.

 특정한 애니메이션 재생을 위해서 이벤트를 사용할 수 있다. Events Documentation 에서 어떤 이벤트들이 있는지 확인해보자.




Spine.Animation


 A Spine.Animation object corresponds to an individual "animation clip" animated in Spine. If a run animation called "run" was animated in Spine, you'll get a Spine.Animation object named "run".

 Spine.Animation 오브젝트는 스파인에서 재생되는 개별 "애니메이션 클립"에 대응한다. "run"이라는 애니가 스파인에서 재생되었다면, "run"이라는 이름의 Spine.Animation 오브젝트를 얻을 수 있다.


 Each Spine.Animation is a collection of Timeline objects. Each Timeline object is a collection of keys, which defines how a certain animatable value (scale, rotation, position, color, attachment, mesh vertices, IK, events, draw order) changes over time. Each Timeline has its own target: a Bone for scale, rotation and position; a slot for attachments and colors; a MeshAttachment for mesh deformation, and the appropriate parts of the skeleton for draw order and events.

 각각의 Spine.Animation 은 타임라인 오브젝트의 집합이다. 각각의 타임라인 오브젝트는 매 시간 변화하는 애니메이션 값들(스케일, 회전, 위치, 컬러, 어태치먼트, 메쉬 버텍스, IK, 이벤트, 드로우오더)를 정의하는 키 값들의 집합이다. 각각의 타임라인은 대상 타겟을 가지고 있다. 스케일과 회전, 위치는 Bone, 컬러는 Slot, 메쉬 변형치와 드로우 오더, 이벤트는 MeshAttachment이다.


 In Spine runtimes, a Spine.Animation is applied to a skeleton with Animation.Apply(...). This poses the Skeleton's various parts only according to that Animation's Timelines and keys. If a Timeline isn't there, the animatable value won't change. If a Timeline is there, it will overwrite whatever value is there with a new value.

 스파인 런타임에서 Spine.Animation은 Animation.Apply(...) 와 함께 스켈레톤에 적용된다. 이것은 스켈레톤의 여러 가지 파츠들을 애니메이션의 타임라인과 키에 따라 움직여서 애니메이션을 만들어 낸다. 


 This means that if you play two Animations at the same time using Spine.AnimationState's track system, the Timelines of the higher track will overwrite any changes that the lower track applied, but will not modify anything else.

 이는 Spine.AnimationState의 트랙 시스템을 사용해 2개의 애니메이션을 함께 재생할 때, 높은 트랙의 타임라인이 낮은 트랙에 덮어씌워진다는 것을 의미한다. 


 This system allows the user to combine different animations for different parts of the skeleton and play them back independently of each other.

 이것은 스켈레톤의 각각 다른 파츠를 움직이는 별개의 애니메이션을 유저가 조합할 수 있도록 해 준다.



Posing the Skeleton according to an animation frame/time.

애니메이션 프레임 / 시간에 의해 스켈레톤 움직이기


 You can call Animation.Apply directly if you want to pose a skeleton according to a certain point in time in that Animation. The Spine-Unity runtime also has a Skeleton.PoseWithAnimation extension method that allows you to do this according to an animation's name.

 애니메이션의 특정 지점의 스켈레톤 포즈를 바로 취하는 데에 Animation.Apply 를 호출할 수 있다. 스파인-유니티 런타임도 Skeleton.PoseWithAnimation 확장 메소드를 가지고 있어서 애니메이션 이름을 이용해 원하는 포즈를 찾을 수 있다.


 One of the parameters is always time. This is time in seconds. If you want to pose the skeleton according to what you see in the editor, you may need to call skeleton.SetToSetupPose() before calling Animation.Apply or Skeleton.PoseWithAnimation

 파라미터 중의 하나는 항상 초 단위의 시간 값을 가진다. 에디터에서 보이는 대로 스켈레톤을 움직이고 싶다면, Animation.Apply 나 Skeleton.PoseWithAnimation을 호출하기 전에 skeleton.SetToSetupPose 를 호출해야 한다.



One Animation After Another (pre-3.0)

하나의 애니메이션 직후에 다른 애니메이션 재생 (3.0 이전 버전)


 This also means that without auto-reset logic, playing animations one after another will not necessarily cause it to look like what the animations like in Spine editor. Instead, playing a sequence of animations will result in later animations inheriting the values and bone poses of the previous animation.

 이것은 자동 리셋 로직 없이, 하나의 애니메이션이 끝난 직후 다른 애니메이션을 재생할 때 스파인 에디터에서 보이는 것처럼 바로 리셋이 되도록 하는 것을 말한다. 이것이 없으면 이전 애니메이션의 키 값이 남아서 다음 애니메이션에 영향을 끼쳐 버리기 때문이다.


 In 3.0, Spine.AnimationState will handle auto-reset logic itself and give you the option to use it so it can behave like it does in Spine editor, or if you want the original free-rein mode for advanced uses.

 3.0에서는, Spine.AnimationState 가 오토 리셋 로직을 자체적으로 관리하고 스파인 에디터에서처럼 자동으로 초기화될지, 아니면 보다 고급 사용을 위해 이전 애니메이션 키값을 남길지 선택할 수 있게 해준다. 



Animation Callbacks + Spine Events

애니메이션 콜백 + 스파인 이벤트


 Spine.AnimationState provides animation callbacks in the form of C# events. You can use these to handle some basic points of animation playback.

 Spine.AnimationState 는 C#의 이벤트 형식으로 애니메이션 콜백을 제공한다. 이것을 이용해 애니메이션 재생의 기본적인 포인트를 제어할 수 있다.


 Spine.AnimationState raises events:

 Spine.AnimationState 가 발생시키는 이벤트는 다음과 같다:


Start when an animation starts playing, 

Start : 애니메이션이 언제 재생을 시작하는가

End when an animation is cleared or interrupted,

End : 애니메이션이 언제 중단 혹은 취소되는가

Complete when an animation completes its full duration,

Complete : 애니메이션이 언제 완료되는가

Event when a user-defined event is detected.

Event : 사용자가 정의한 이벤트가 감지되었을 때



WARNING:

주의 : 


 NEVER subscribe to End with a method that calls SetAnimation. Since End is raised when an animation is interrupted, and SetAnimation interrupts any existing animation, this will cause an infinite recursion of End->Handle>SetAnimation->End->Handle->SetAnimation, causing Unity to freeze until a stack overflow happens.

To learn more about Spine Events and AnimationState callbacks, see the Events documentation.

 End 이벤트는 SetAnimation을 호출하는 메소드로 사용하지 말 것. 애니메이션이 중단됐을 때 End가 걸리고, SetAnimation 이 모든 존재하는 애니메이션을 멈춰 버리며, End->Handle>SetAnimation->End->Handle->SetAnimation 식의 무한 루프가 걸리게 된다. 이렇게 되면 유니티 자체가 뻗어서 스택 오버플로우 에러가 날 때까지 멈춰버리게 된다.



Advanced Animation Control

고급 애니메이션 컨트롤


 Tracks and TrackEntry are just part of Spine.AnimationState, and AnimationState is included with the Spine runtimes just to get you going immediately. It's usable in most cases and performant for production. However, Spine-Unity and Spine-C# can function even without AnimationState. For example, SkeletonAnimator uses UnityEngine.Animator instead of Spine.AnimationState and uses Mecanim to manage numbers and data for mixing, layering and queueing.

 트랙과 트랙엔트리는 Spine.AnimationState 의 일부일 뿐이며, AnimationState 는 바로 사용할 수 있도록 스파인 런타임 안에 포함되어 있다.  이것은 대부분의 경우에 유용하지만, 스파인-유니티와 스파인-C#은 AnimationState 없이도 애니메이션을 제어할 수 있다. 예를 들어, SkeletonAnimator는 Spine.AnimationState 대신에 UnityEngine.Animator 를 사용하고 데이터의 믹싱, 레이어링, 큐잉과 애니메이션 번호 관리를 위해서 메카님을 사용한다.


 If you wanted to do bookkeeping of your animations a different way, whether to be optimized based on how you plan to use it, or to handle special cases just the way you want them, you can build a different system.

 만약 애니메이션을 다른 방식으로 다루고 싶다면 다른 방식으로 시스템을 만들어야 한다.


 In this case, studying the Official Spine Documentation will come in handy.

 이런 경우라면, 공식 스파인 문서를 좀 더 자세히 연구하는 것이 좋을 것이다.

Posted by windship
프로그래밍/Spine2017. 1. 29. 18:29

(출처 : http://blog.naver.com/ateliersera/220663775981)


* GroundConstraint *

스파인 캐릭터의 특정 본에 접근해서 제어를 하고싶다면, 해당 본 데이터를 꺼내야 합니다.


일단 모델이 있어야 하니, 뷰어 상에 Skelecton Animation 을 하나 생성 해서 제어 하고자 하는 Skelecton Data Asset을 연결시켜 줍니다.

여기에서는 기본적으로 제공하는 raptor_SkeletonData 를 이용하였습니다.



약간의 손질이 필요합니다. 포함중인 본 데이터를 제어할 수 있게 밖으로 꺼내야 하기에, Skelecton Utility Bone 스크립트를 사용합니다. 

Add Skelecton Utility를 눌러주면 생성되는 Skeleton Utility 스크립트의 BoneRoot에 위에서 만들어 놓은 testroot를 끌어다가 놓으면 사용할 수 있게 됩니다.


여기에는 바닥에 발을 붙이기 위한 스크립트를 사용 하였지만, 벽에 손을 붙인다거나 , 사다리를 팔 다리로 잡는 등의 액션을 한다면(어쌔신 크리드!?) 위의 스크립트와는 좀 더 다른 스크립트를 만들어서 써 줘야 합니다. 



* Skin 변경 방법 * 

튜토리얼에서는 Gobline이 남/녀 고블린으로의 스킨 변경 예제로 사용되어 있습니다.  

goblins 를 보면, 내부적으로 이미지 폴더 아래에 goblin 과 goblingirl 이라는 폴더가 있는데, 해당 폴더 내에는 동일한 이름의 이미지들이 들어가 있습니다.

실제로 root에서 바인딩 된 이미지를 보면 스킨 플레이스 홀더 라는 조각퍼즐 같은 부분 아래에 이미지가 위치해 있음을 알 수 있습니다.

 

이런 구조 입니다.  위에 이미지에서 right hand 부분에서 +새로만들기 버튼을 누른 후, 플레이스 홀더를 생성 하고, 그 아래쪽에 사용할 이미지를 넣으면 됩니다.

공용으로 사용하는 무기나 악세사리의 경우, 폴더 내부에 두지 말고, 그냥 이미지 아래에 둡니다. 공용 이미지는 플레이스 홀더를 사용하지 않습니다.



public void OnMouseDown () { // 마우스 클릭으로 스킨이 변경됩니다.

skeletonAnimation.skeleton.SetSkin(girlSkin ? "goblin" : "goblingirl"); //SetSkin이 스킨 변경 함수 입니다.  여기에 이름이 곧 이미지 폴더!

skeletonAnimation.skeleton.SetSlotsToSetupPose(); 

girlSkin = !girlSkin; //bool부호 값입니다. 

if (girlSkin) { 

skeletonAnimation.skeleton.SetAttachment("right hand item", null);

skeletonAnimation.skeleton.SetAttachment("left hand item", "spear");   //왼손의 아이템을 창으로 바꿉니다.

} else

skeletonAnimation.skeleton.SetAttachment("left hand item", "dagger"); // 왼손의 아이템을 단검으로 바꿉니다. 

}


skeletonAnimation.skeleton.SetSkin()을 통해 변경 합니다. Setskin("스킨 이름")으로 들어가는데, 여기에서는 간단하게 아닌지 맞는지로만 선택되게 되어있습니다.

일반 사용방법은 그냥 이름 스트링을 넣어주면 됩니다.



*지원하는 쉐이더 *

 


SPine 라는 카테고리 내의 쉐이더들 입니다. 

Bones : 가장 기본적인 Unlit 쉐이더 입니다. 라이팅을 받지 않습니다. 바리에이션이 2종류로 가장 가벼운 쉐이더중 하나 입니다. OpenGL과 Dx9 를 지원합니다.

HiddenPass : 그냥 렌더링이 안됩니다;;보이지 않아요...


Skeleton : 그림자를 적용 시킵니다. 디렉션 라이트에 영향을 받아서 그림자를 생성 합니다. 

Skeleton Lit : 그림자 적용도 되고, 스프라이트 이미지도 라이트에 영향을 받게 됩니다.

SkeletonGhost : 유령 만들때 쓰라는 걸까요??? 좀 애매한 쉐이더이긴 한데, MainColor 의 알파 값이 적용 됩니다. 그리고 Texture Fade Out값에 따라 Main Color의 색상 값이 씌워집니다. 정작 알파값이 적용되면 관절의 겹치는 부분이 보이는 불편한 진실이...

SkeletonGraphic(Premultiply Alpha) : 스탠실 지정 기능이 있습니다. 마스킹도 가능합니다. 예제중에 SkeletonGraphic부분에서 활용 되어있습니다. 캐릭터에서 쓸 일은 잘 모르겠지만, 어딘가 쓰이겠지요.


 그림자 생성의 경우,카메라에 비춰질 그림자 형태를 잡아다가 스프라이트에다가 그려주는 방식입니다. (3D 표면에 그리는 방식이 아닙니다!?)  2D 스프라이트랑 함께 그림자가 한 면에 붙어서 움직이는걸 볼 수 있습니다.  



* 애니메이션 컨트롤 샘플 코드 *


    [SpineEvent] public string footstepEventName = "footstep"; 


    void Start () {

        var skeletonAnimation = GetComponent<SkeletonAnimation>();

        if (skeletonAnimation == null) return;    // told you to add this to SkeletonAnimation's GameObject.


        // This is how you subscribe via a declared method. The method needs the correct signature.

        skeletonAnimation.state.Event += HandleEvent;


        skeletonAnimation.state.Start += delegate (Spine.AnimationState state, int trackIndex) {

            // You can also use an anonymous delegate.

            Debug.Log(string.Format("track {0} started a new animation.", trackIndex));

        };


        skeletonAnimation.state.End += delegate {

            // ... or choose to ignore its parameters.

            Debug.Log("An animation ended!");

        };

    }


    void HandleEvent (Spine.AnimationState state, int trackIndex, Spine.Event e) {

        // Play some sound if the event named "footstep" fired.

        if (e.Data.Name == footstepEventName) {            

            Debug.Log("Play a footstep sound!");

        }

    }

}


Posted by windship
프로그래밍/Spine2017. 1. 29. 18:27

(출처 : http://blog.naver.com/ateliersera/220663774610)



* 초기 셋팅법 *


#region Inspector

[SpineAnimation]

public string runAnimationName;  //스파인은 String으로 동작을 찾게 되어있습니다. 스파인 프리펩에서 동작을 연결해 줍니다.

.

.

public AudioSource footstepAudioSource; //오디오 소스

#endregion


위와같이 사용할 애니메이션을 참조합니다. 이동이나 점프,공격..피격 등 필요한 만큼 작성해 줍니다. 


spineAnimationState.SetAnimation(0, runAnimationName, true);


등으로 사용이 가능합니다. spineAnimationState는 SkeletonAnimation 의 state 에 있는  정보 입니다. 그리고 skeleton 이라는 정보도 있습니다.

skeleton정보는 뼈대에 대한 정보로, X축으로 회전을 시키지 않고, skeleton.FlipX = true; 등을 사용하면 반전해서 사용할 수 있습니다. 


로테이션으로 스파인 유닛을 반전 시킬 경우, 쉐이더에 따라 라이트가 안받는 뒷면이 나오는 등의 문제가 발생 할 수 있습니다.


skeletonAnimation.state.SetAnimation(트랙(번호), 애니메이션이름(스트링), 루프(true/false) 로 3가지 속성이 있습니다. 보통 Idle이나, 이동 의 경우 루프가 true겠지만, 공격 등의 한번만 나와야 하는 모션의 경우에는 false 시켜야 합니다.


*스파인의 애니메이션 섞기 *

유닛이 이동 하면서, 눈을 깜빡이고 싶은데, 눈의 깜빡임은 좀 랜덤하게 나오고 싶다면?

유닛의 이동을 담당하는 애니메이션을 만들고, 이동 애니메이션에는 눈 깜빡임 애니메이션에대한 키값을 만들지 않습니다.  

다른 본들의 움직임에는 애니메이션을 만들지 않고, 눈깜빡이는 애니메이션만을 만듭니다.


위처럼 필요한 동작만을 만든 후, 두 트랙을 동시에 skeletonAnimation.state.SetAnimation 을 통해 플레이 할 경우, 자동으로 섞여서 나오게 됩니다.  매우 간단하지만, 애니메이션 제작 시 염두해 두고 만들어야 할 문제 입니다. 키값이 이미 존재한다면 무언가 하나의 애니메이션은 존재가 사라지는 현상이 발생합니다.




* 스파인 캐릭터의 기본 설정하기 * 

외부 키에 따라 액션을 설정하기 이전에, 모델에 대한 셋팅을 만들어 봅시다.

기본형은 예제에 있는 Spineboy입니다. 

.

.

public class SpineboyBeginnerModel: MonoBehaviour {


#region Inspector

[Header("Current State")]

public SpineBeginnerBodyState state;  //캐릭터의 상태를 위해 만들어진 헤더 입니다. 

public bool facingLeft;   // 좌우 반전을 위해 존재 합니다. 

[Range(-1f, 1f)]

public float currentSpeed; 


[Header("Balance")]

public float shootInterval = 0.12f;  //총 발사의 딜레이를 위한 값 입니다. 

#endregion


float lastShootTime;

public event System.Action ShootEvent; 



// 여기 아래부분은 점프, 발사, 이동을 시도하는 루틴 부분 입니다.

#region API

public void TryJump () {

 StartCoroutine(JumpRoutine());  //코루틴으로 점프 루틴을 돌렸습니다.

}


public void TryShoot () {

 float currentTime = Time.time;

      // 일정 시간이 지나야 슈팅 이벤트가 발생합니다.

 if (currentTime - lastShootTime > shootInterval) {

  lastShootTime = currentTime;

  if (ShootEvent != null) ShootEvent(); // Fire the "ShootEvent" event.

 }

}


public void TryMove (float speed) {

 currentSpeed = speed; // show the "speed" in the Inspector.


 if (speed != 0) {

  bool speedIsNegative = (speed < 0f);

  facingLeft = speedIsNegative; 

 }

 //점프중이 아닐때만 이동을 시도합니다. 

 if (state != SpineBeginnerBodyState.Jumping) {

 state = (speed == 0) ? SpineBeginnerBodyState.Idle : SpineBeginnerBodyState.Running;

 }


}

#endregion

//여기까지가 시도 하는 루틴 부분


IEnumerator JumpRoutine () {

 if (state == SpineBeginnerBodyState.Jumping) yield break; // 점프중일때는 점프를 시도하지 않습니다. 


 state = SpineBeginnerBodyState.Jumping;

 yield return new WaitForSeconds(1.2f); 

 state = SpineBeginnerBodyState.Idle;

 }


}


//state 를 나눠 줍니다. 현재는 세가지.  대기 상태와 뛰기, 점프 상태 입니다. 

public enum SpineBeginnerBodyState {

 Idle,

 Running,

 Jumping

}


//여기까지가 스파인 유닛에 붙는 스크립트 입니다. 간단하게 대기/이동/점/슈팅 으로 되어 있습니다. 이동이나 점프중에 슈팅이 가능한 구조 입니다.

-------------------------------------------------------------------------------------------

* 컨트롤 부분의 스크립트 예제 * 

public class SpineboyBeginnerInput : MonoBehaviour {


 #region Inspector

 public string horizontalAxis = "Horizontal";

 public string attackButton = "Fire1";

 public string jumpButton = "Jump";


 public SpineboyBeginnerModel model;



//OnValidate()가 사용되었는데, 값의 유효성을 검사할때 추가로 사용자 지정 프로세스를 수행하는 메소드 입니다. CollectionBase.OnValidate메소드 입니다.

//Start() 라거나 Awake()에 넣지 않고 값의 유효성 검사를 위해 OnValidate()를 사용했습니다.

 void OnValidate () {

  if (model == null)

   model = GetComponent<SpineboyBeginnerModel>();

 }

 #endregion


 void Update () {

  if (model == null) return;


  float currentHorizontal = Input.GetAxisRaw(horizontalAxis);

  model.TryMove(currentHorizontal);


  if (Input.GetButton(attackButton))

   model.TryShoot();


  if (Input.GetButtonDown(jumpButton))

   model.TryJump();

 }

}


//유닛의 컨트롤 부분에 있어서는 키를 받게 되면 해당 오브젝트에게 이동이나 슈팅, 점프를 시도하도록 명령만 보내는 구조 입니다.  흔히 말하는MVC구조

[출처] Spine2D 튜토리얼 03. 스파인을 가지고 C#에서 사용해 보자.|작성자 세라프


Posted by windship
프로그래밍/Spine2017. 1. 29. 18:25

(출처 : http://ateliersera.blog.me/220653281955)


이전까지만 해도 스파인 프리버젼을 사용해 보다가, 이번에 정품 프로 버젼으로 구매 하였습니다.

약 30만원 가량.. 개발자 분께 투.척...  (개발 하신 분께 수고와 감사의 마음을 담아 *--*)


기본 사용법에 대한 툴팁은 이후에 따로 게시하겠습니다. 일단 성격이 급하니, 돌아가나 확인해 봐야겠지요.

여기까지만 알아도 거의 다 안것과 다름 없습니다. 제작이야 어떻게든 하기 때문입니다. 


스파인의 기본 사용법은 본 구조 타입이고, 애니메이션 제작하기에도 매우 쉽습니다.



위 사진은 테스트 용으로 제작해 보고 있는 횡스크롤 액션 게임에 들어가는 캐릭터 입니다.

동작이 많습니다. 피격의 경우, 방향별로 있고,  애니메이션 보간을 위해, 중간 모션들도 제작해 놓았습니다. 

공격 타격은 5연타 타격이며, 각각 타격과 특별한 스매시 공격 모션을 연결하기 위해 제작된 애니메이션들 입니다.


유니티에서 사용할 데이터를 뽑기 위해, 내보내기를 클릭 해서 JSon 을 선택합니다.


확장자가 json 입니다. 어차피 텍스트 데이터 이지만, 파일의 내부 형식이 그렇습니다. 원하는 출력 폴더를 선택하면, 이름은 프로젝트 이름으로 뽑히게 됩니다.

 

설정 버튼을 누르면..여기서 중요한 부분이 있습니다. 아틀라스 확장자 뒤에 꼭!! .txt 를 붙여 주세요

안그러면 유니티에서 못 읽습니다. . . . . 왜 이걸 아직도 안고쳤는지 모르겠지만.. 안고쳤네요;




이렇게 내보내기 버튼을 누르면, atlas.txt 파일과 json파일, PNG파일이 만들어 집니다. PNG의 경우에 256 컬러 압축 하면 더 작아지겠지요?

Json파일의 경우에도 무압축 순수 텍스트여서 저렇게 용량이 큽니다. 실제로 패킹 할땐 매우 작아집니다.




다음은 유니티쪽 셋업 방법 입니다.



스파인 홈페이지에 있느 유 니티 런타임

위의 주소에 해당 유니티 패키지가 있습니다.


또는 깃 허브에서도 구할 수 있습니다.

https://github.com/EsotericSoftware/spine-runtimes



 



필요한 파일은 이렇게 두개 입니다. 스파인 CSharp과 Unity 입니다. 이 두개를 유니티 에셋 어딘가에 잘 넣어둡니다. 


(웬만한 튜토리얼은 Example\Getting Start를 참고하시면 대부분 해결 됩니다. 만.. 그래도 귀찮으신 분들을 위해, 이후에 제작해서 올리도록 하겠습니다.)




이제 스켈렉톤 데이터를 제작해야 합니다.


앞에서 Export한 파일들을 저장한 폴더로 가서 Create -> Spine Skeleton Data 를 클릭해서 제작해 줍니다.



 

위에 처럼 생겨 있습니다.  JSON 파일에 아까 뽑은 JSON파일을 연결해 준 다음에 Attempt Reimport 를 누 릅니다.


그러면 SkeletonData 파일이 하나 생성이 됩니다.  이로서 사용할 준비는 모두 끝났습니다.


 


이제 씬으로 가서, Spine -> SkeletonAnimation 을 생성 합니다. 


 


Skelecton Data Asset에다가 사용할 스켈렉톤 데이터를 연결해 주면 끝입니다. 이제 나머지는 코드에서 제어하기 나름입니다.

애니메이션을 루핑 시키거나, 타임 스케일링을 주거나, 어떠한 애니메이션을 틀어 줄지, 레이어 소팅은 어디로 할지..등등 의 정보들이 있습니다. 


아래의 영상은 가벼운 이동 테스트 애니메이션 입니다.


Posted by windship
프로그래밍/Spine2015. 6. 19. 14:23