잠시.. 일본에 갔다와서 연재 시기가 늦었습니다. >_<..

음.. 저번에 했던건 배경 그림 컨트롤 하는것 인데, 이번엔 게임 플레이어가 직접 뭔가 어떻게 할 수 있는 대상
즉, 오브젝트 그림들... 통칭 스프라이트를 띄우는 방법에 대해서 이야기 하겠습니다.

일단 스프라이트 개념은.. 대충 저런 느낌이구요.. 슈퍼 마리오 게임을 예로 들면,
마리오, 필드의 몹들, 부실 수 있는 벽돌, 장애물... 등을 말 할 수 있겟죠...

일단... 스프라이트 제약 사양입니다.

아래표의 스프라이트 사이즈만 사용가능

사용자 삽입 이미지

각각 가로 세로 길이이며 256색 이하 bmp 그림

단위는 당연히 픽셀이며, 딱 저것만 가능합니다. 각각 가로 세로 8의 배수로 생각하시면 될꺼 같네요.
이 이상 크기는.. 확대 하는 함수를 사용해서 최대 128x128 크기까지 뽑아 낼 수 있습니다.

하지만 저 정도 크기이면 NDS 화면 해상도가... 256x192 크기임을 가만하면... 저런 케릭 2개만 있어도
화면 가득 메워 버리므로... 적절한 크기는 64x64 정도로 잡은거 같습니다.

여튼... 예제 스프라이트 이미지 입니다.

사용자 삽입 이미지
<Sprite_Init_Menu.bmp>

사용자 삽입 이미지
<Sprite_Point.bmp>
파일은 여길 누르세요

한가지 주의점은.. 스프라이트는 위와 같이 세로 방향으로 넣어 주셔야 합니다.
왜 저렇게 해야 하면, NDS 자체가 성능이 많이 딸려서 라고 추정됩니다.
좀더 빠른 메모리 접근 이유가 타당하겠네요.

예를들어 가로로 그림을 배치 한다면..

  000111222333444555666777888999
  000111222333444555666777888999
  000111222333444555666777888999

와 같이 메모리 배열이 있고, 2번으만 되어있는 프레임을 긁어올려면 첫 배열에서 x*3(넓이)*2(프레임)에서 프레임 넓이, 다음 줄 그림 읽기 위해 넓이를 더하고, 다시 메모리에 붙이고 ......

하지만 그림을 세로로 할 경우
000
000
000
111
111
111
222
222
222
....
즉...
000000000111111111222222222 ..... 식으로 메모리에 배열이 되겠죠.
그럼 이미지를 가져올때 필요한 정보는 스프라이트 넓이와, 프레임 번호만 있으면 이후 별도 연산은 안해도 되는것이죠.

음.. 뭐 요지는... 세로로 해야 제대로 스프라이트가 나옵니다.
스프라이트를 추가시키기 위해선 배경 그림 컨버팅 한것 처럼 PAGfx안의 프로그램을 사용해서 컨버팅 합니다.

사용자 삽입 이미지
그림 설명대로, sprite 탭에서 스프라이트 그림을 로드 한뒤에 컨버팅 합니다.
이때 주의점... 배경 그림도 같이 변환해야 합니다.

사용자 삽입 이미지

왜냐면.. 결과 파일로 all_gfx.c/h 파일이 만들어 지는대 여기에서 모든 그림 리소스 변수명을 정의하기 때문입니다.
스프라이트만 해놓으면, 배경 그림 변수가 빠진 all_gfx.h 파일이 만들어지므로, 배경 그림을 못찾는다거나 하는 문제가 발생할 수 있습니다.

여튼... 만들어진 파일을, 저번과 같이 프로젝트/source/gfx 안에 덮어 씌웁니다.
사용자 삽입 이미지


이후... 소스는 아래와 같이 수정 합니다.


//////////////////////////////////////////////////////
// Includes
#include <PA9.h>       // Include for PA_Lib
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"

#define UP_SCREEN 1
#define DOWN_SCREEN 0

#define BG0   0
#define BG1   1
#define BG2   2
#define BG3   3

#define SPRITE_MENU_PAL  0
#define SPRITE_POINT_PAL 1

#define SPRITE_MENU   0
#define SPRITE_POINT  1

#define TRUE 1
#define FALSE 0

// Video ram 의 모든 그림을 제거 하는 함수
void UnLoad_Screen()
{
 int i;

 for(i=0; i<2; ++i)
 {
  PA_ResetBgSysScreen(i);
  PA_ResetSpriteSysScreen(i);
 }
}

int main()
{
 PA_Init();    // Initializes PA_Lib
 PA_InitVBL(); // Initializes a standard VBL

 PA_InitText(UP_SCREEN, BG2);
 PA_OutputSimpleText(UP_SCREEN, 1, 2, "Hello World!");

 // 배경 화면 로드
 PA_EasyBgLoad(UP_SCREEN, BG3, bg2);
 PA_EasyBgLoad(DOWN_SCREEN, BG2, bg_charactor);
 PA_EasyBgLoad(DOWN_SCREEN, BG3, bg1);

 // 스프라이트 준비 1. 팔레트 설정
 PA_LoadSpritePal(DOWN_SCREEN, SPRITE_MENU_PAL, (void*)Sprite_Init_Menu_Pal);
 PA_LoadSpritePal(DOWN_SCREEN, SPRITE_POINT_PAL, (void*)Sprite_Point_Pal);

 // 스프라이트 준비 2. 실제 그림 생성
 PA_CreateSprite(DOWN_SCREEN, SPRITE_MENU,
    (void*)Sprite_Init_Menu_Sprite, OBJ_SIZE_64X64,
    TRUE, SPRITE_MENU_PAL, 10, 10);

 PA_CreateSprite(DOWN_SCREEN, SPRITE_POINT,
    (void*)Sprite_Point_Sprite, OBJ_SIZE_32X32,
    TRUE, SPRITE_POINT_PAL, 80, 10);

 PA_CreateSprite(DOWN_SCREEN, SPRITE_POINT+1,
    (void*)Sprite_Point_Sprite, OBJ_SIZE_32X32,
    TRUE, SPRITE_POINT_PAL, 80, 40);

 // 스프라이트 준비 3. 몇몇 변경
 PA_SetSpriteAnim(DOWN_SCREEN, SPRITE_MENU, 2);
 PA_SetSpriteHflip(DOWN_SCREEN, SPRITE_POINT, TRUE);

 s16 x =0, y = 0;

 // Infinite loop to keep the program running
 while (1)
 {
  // 배경 움직이기
  PA_EasyBgScrollXY(UP_SCREEN, BG3, --x, ++y);
  PA_EasyBgScrollX(DOWN_SCREEN, BG3, x);

  // 스타일러스 팬 위치에 스프라이트를 이동 시키기.
  PA_SetSpriteXY(DOWN_SCREEN, SPRITE_MENU, Stylus.X, Stylus.Y);
  PA_WaitForVBL();
 }

 UnLoad_Screen();  // 리소스 정리
 return 0;
}

//////////////////////////////////////////////////////


바뀐곳만 빨간색을 칠했습니다.
일단 정의 부분
#define SPRITE_MENU_PAL  0
#define SPRITE_POINT_PAL 1

#define SPRITE_MENU   0
#define SPRITE_POINT  1

#define TRUE 1
#define FALSE 0


각 스프라이트의 팔레트 번호와, 스프라이트 번호 그리고 true, false 값의 정의입니다.
왜 저짓을 하냐면... 숫자로 그냥 둘 경우 나중에 스프라이트 더 추가 하거나 할때 상당히 햇갈립니다.

다음
 // 스프라이트 준비 1. 팔레트 설정
 PA_LoadSpritePal(DOWN_SCREEN, SPRITE_MENU_PAL, (void*)Sprite_Init_Menu_Pal);
 PA_LoadSpritePal(DOWN_SCREEN, SPRITE_POINT_PAL, (void*)Sprite_Point_Pal);

이건 뭐냐면... 팔레트 생성 입니다. 위의 define 한거에 따라 팔레트 번호를 배당하고,
뒤의 (void*)Sprite_Init_Menu_Pal 은 팔레트 변수명 입니다.
팔레트 변수는 [그림파일_Pal] 로 컨버팅시 정의 됩니다.

다음
 // 스프라이트 준비 2. 실제 그림 생성
 PA_CreateSprite(DOWN_SCREEN, SPRITE_MENU,
    (void*)Sprite_Init_Menu_Sprite, OBJ_SIZE_64X64,
    TRUE, SPRITE_MENU_PAL, 10, 10);

 PA_CreateSprite(DOWN_SCREEN, SPRITE_POINT,
    (void*)Sprite_Point_Sprite, OBJ_SIZE_32X32,
    TRUE, SPRITE_POINT_PAL, 80, 10);

 PA_CreateSprite(DOWN_SCREEN, SPRITE_POINT+1,
    (void*)Sprite_Point_Sprite, OBJ_SIZE_32X32,
    TRUE, SPRITE_POINT_PAL, 80, 40);

스프라이트를 생성하는 부분 입니다.
총 3개의 스프라이트가 나옵니다.
각각 인자값은,
생성할 위치 스크린 번호,
할당시킬 스프라이트 번호 (중복없이 한 화면 최대 128개)
스프라이트 그림 변수로서.. [그림파일_Sprite]로 자동 할당,
스프라이트 크기
이 스프라이트가 256 색인가?
이 스프라이트가 사용할 팔레트 번호
처음 생성될때의 x, y 위치

입니다.

다음..
 // 스프라이트 준비 3. 몇몇 변경
 PA_SetSpriteAnim(DOWN_SCREEN, SPRITE_MENU, 2);
 PA_SetSpriteHflip(DOWN_SCREEN, SPRITE_POINT, TRUE);

이 함수는
각각, SPRITE_MENU 번호의 스프라이트의 2번 프레임을 표시하라와
SPRITE_POINT 를 좌우대칭(Hflip) 시켜라 라는 의미 입니다.

다음 while 루프 안에
// 스타일러스 팬 위치에 스프라이트를 이동 시키기.
  PA_SetSpriteXY(DOWN_SCREEN, SPRITE_MENU, Stylus.X, Stylus.Y);


는 스프라이트 x,y 이동시키는 함수로서 (이 말은 x만, y 만 전용 움직이게 하는 함수도 있습니다)
스프라이트 있는 곳의 화면 번호
스프라이트 번호
Stylus 는 스타일러스 상태를 가져오는 매크로 같은 것이라.. 현재 정의는 X,Y 값을 가져오는것 입니다.
 

이후 컴파일 하면 아래와 같이 나오며
아래 스크린을 마우스로 클릭해서 끌어 보면, 카드 스프라이트가 마우스로 끄는 위치로
쫏아감을 느낄 수 있습니다.

사용자 삽입 이미지

음.. 대략 이런 느낌으로 하면 됩니다.
좀더 자세한건 C:\devkitPro\PAlibExamples\Sprites 안의 여러 예제 소스들과 (상당히 간단합니다)
http://www.palib.info/wiki/doku.php?id=day3ko 부분을 병행해서 보시면 될꺼 같습니다.

음.. 이것으로 2D 이미지를 띄우는 방법은.. 다 됬네요.
게임은 솔직히 이것 만으로도 만들어 지고, 남은건 알고리즘 재량 정도가 아닐까 싶네요 >_<..

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소