homebrew 개발에 관심있는 분들을 위해 버들 맞고의 제작 과정을 간단하게 소개해 보고자 합니다.
homebrew DS는 처음 제작해 본거라 부족한 점도 많겠지만 이런 방법도 있구나 하고 보시면 될 것 같습니다.

1. 화면 배치 설계
먼저 화투패 48장을 배열할 공간을 정합니다.
sprite는 8x8의 배수의 크기로 만들어야 하므로 최적의 크기를 찾다가 21x32 크기로 정하였습니다. 먹은패를 배열할 공간을 고려해 보면 광, 열긋, 띠, 피 이렇게 배열을 하는데 두줄을 배열하도록 설정했고 패와 패의 간격은 6 pixel로 정했습니다.
광 3개: 간격 6X2 + 화투패 폭 21 = 33
열끗 5 개: 간격 6X4 + 화투패 폭 21 = 45
띠 5 개: 간격 6X4 + 화투패 폭 21 = 45
피 10개: 간격 6X9  + 화투패 폭 21 = 75
 

그리고 메세지박스, 메인 메뉴도 sprite 최대크기보다 크므로 배경으로 설정했습니다.
256색이 PAGfx 로 이미지변환이 쉬워서 gif로 변환해서 사용했습니다.

2. 백그라운드 구성
백그라운 구성은 다음과 같이 했습니다. 여기서 중요한 것은 텍스트도 배경으로 인식이 되어서 다른 배경과 겹치면 글자가 보이지 않게됩니다. 그리고 다른 배경이나 sprite 위에 보여야 하므로 우선 순위가 높은 1로 설정했습니다.
#define SCREEN_TOP 1
#define SCREEN_BTM 0
//top screen
#define BG_START  0
#define BG_TOP_TXT 1
#define BG_SCORE  3
//bottom screen
#define BG_MENU   0
#define BG_BTM_TXT 1
#define BG_MSGBOX 2
#define BG_GOSTOP 3

PA_InitText(SCREEN_TOP, BG_TOP_TXT); 
PA_InitText(SCREEN_BTM, BG_BTM_TXT);

PA_EasyBgLoad(SCREEN_TOP, // screen
     BG_START, // background number (0-3)
     start); // Background name, used by PAGfx...
 PA_EasyBgLoad(SCREEN_TOP, BG_SCORE, score);
 PA_EasyBgLoad(SCREEN_BTM, BG_GOSTOP, gostop);
 PA_EasyBgLoad(SCREEN_BTM, BG_MENU, menu);
 PA_EasyBgLoad(SCREEN_BTM, BG_MSGBOX, msgbox);

3. sprite 구성
한 화면에 128개의 sprite와 16개 팔레트가 가능한데.. 화투패가 48장이니깐 sprite 패의 갯수에 제한은 없는데.. 화투패를 하나 하나 분리해서 만들면 각각의 팔레트가 필요하게 되어 16개 팔레트를 넘기때문에 간단한 트릭으로 제어가 쉽게 하기위해 12개씩 패를 묶어서 sprite의 프레임 기능을 사용하였습니다.
 
//pae
 for(i=0;i<12;i++)
 {
  PA_CreateSprite(SCREEN_BTM,i+1,(void*)PAE0_Sprite,OBJ_SIZE_32X32,1,0,POS_BADAK_1ST_X,POS_BADAK_1ST_Y);
  PA_SetSpritePrio(SCREEN_BTM,i+1, 3);
  PA_SetSpriteAnim(SCREEN_BTM,i+1, i+1);
 }
 for(i=0;i<12;i++)
 {
  PA_CreateSprite(SCREEN_BTM,i+13,(void*)PAE1_Sprite,OBJ_SIZE_32X32,1,1,POS_BADAK_1ST_X,POS_BADAK_1ST_Y);
  PA_SetSpritePrio(SCREEN_BTM,i+13, 3);
  PA_SetSpriteAnim(SCREEN_BTM,i+13, i+1);
 }
 for(i=0;i<12;i++)
 {
  PA_CreateSprite(SCREEN_BTM,i+25,(void*)PAE2_Sprite,OBJ_SIZE_32X32,1,2,POS_BADAK_1ST_X,POS_BADAK_1ST_Y);
  PA_SetSpritePrio(SCREEN_BTM,i+25, 3);
  PA_SetSpriteAnim(SCREEN_BTM,i+25, i+1);
 }
 for(i=0;i<12;i++)
 {
  PA_CreateSprite(SCREEN_BTM,i+37,(void*)PAE3_Sprite,OBJ_SIZE_32X32,1,3,POS_BADAK_1ST_X,POS_BADAK_1ST_Y);
  PA_SetSpritePrio(SCREEN_BTM,i+37, 3);
  PA_SetSpriteAnim(SCREEN_BTM,i+37, i+1);
 }


4. 기능 설정
-. 패 특성 설정
화투패의 각각의 설정을 다음과 같이 정했습니다.
//nType
#define GWANG 1
#define YUL      3
#define DDI       5
#define PI         11
//nChar
#define SSANGPI     10
#define HONGDAN   6
#define CHODAN     7
#define CHUNGDAN 8
#define GODORI      4
#define YUL9          9
#define BIGWANG    2

typedef struct _PAE
{
 u8 nIdx;     //sort index - open, hand arrange
 u8 nType;  //광,열끗,띠,피
 u8 nChar;  //쌍피, 홍단,초단,청단,고도리,국화열끗,비광
 u8 nSprite; //sprite number
 u8 nSet;    //먹을 패 비교시 사용 1~12
} PAE;

-. 패이동 보이기
핸드패에서 바닥에 헤딩하던지 먹을때, 데크에서 까서 먹던지 바닥에 깔때, 싹쓸이나 설사먹어서 피빼어 올때, 바닥에서 먹은패로 가져올때 필요합니다.
다음은 핸드패에서 바닥패로 이동할때 코드입니다.
  
//move : hand -> badak
    nFromX=PA_GetSpriteX(SCREEN_BTM,stHandPae.nSprite);
    nFromY=PA_GetSpriteY(SCREEN_BTM,stHandPae.nSprite);
    nToX=nDropPaeX;
    nToY=nDropPaeY;
    nScrollX=(u8)((nFromX-nToX)>>4);
    if(nFromY>nToY)
      nScrollY=(u8)((nFromY-nToY)>>4);
    else
      nScrollY=(u8)((nToY-nFromY)>>4);
    while(nCnt<16)
    {
     nFromX=nFromX-nScrollX;
     if(nFromY>nToY)
        nFromY=nFromY-nScrollY;
     else
        nFromY=nFromY+nScrollY;
     PA_SetSpriteXY(SCREEN_BTM,stHandPae.nSprite,nFromX,nFromY);
     nCnt++;
     PA_WaitForVBL();
    }
    PA_SetSpriteXY(SCREEN_BTM,stHandPae.nSprite,nToX,nToY);
    PA_WaitForVBL();


-. 메세지박스 이동
해보셨는지 모르지만 간단한 메세지를 보여주는 메세지박스를 클릭해서 이동이 가능합니다. 메세지박스는 커서 sprite 로 구현이 불가능하여 background로 처리했는데.. 백그라운드 스크롤 기능으로 가능하더군요. 그런데 이동하면 숨어있는 화면쪽이 스크롤되면서 깨져서 보여서 사용을 추천하진 않지만 밑에 패가 궁금할때 사용하면 될 것 같습니다.

while(1)
 {
  if(Stylus.Released)
  {
   bHold=false;
   scrollx=0;
   scrolly=0;
   PA_EasyBgScrollXY(SCREEN_BTM,BG_MSGBOX,0,0);
  }
  if(Stylus.Held)
  {
   if(bHold)
    PA_EasyBgScrollXY(SCREEN_BTM,BG_MSGBOX,scrollx-Stylus.X,scrolly-Stylus.Y);
  }

  if(Stylus.Newpress)
  {
   if((POS_MSGBOX_X<<3 < Stylus.X) && (POS_MSGBOX_MAXX<<3 > Stylus.X) \
    && (POS_MSGBOX_Y<<3 < Stylus.Y) && (POS_MSGBOX_MAXY<<3 > Stylus.Y))
   {
    bHold=true;
    scrollx=Stylus.X;
    scrolly=Stylus.Y;
   }

  //버튼 처리 코드 생략 ...
  }
  PA_WaitForVBL();
 }


-. 먹은 패 정렬
메세지창이나 메뉴의 뒤로 보이게 하기위해 패가 제일 뒤로 우선순위를 보냈는데.. 우선 순위가 같다보니 먹은패와 같이 패가 많이 모이니깐 서로 겹쳐서 중간패가 안보이기도 하더군요. 그래서 좋은 함수가 있나 찾아 보다가 역시 좋은 라이브러리는 예제가 잘 되어 있더군요. 예제를 뒤져보니 PA_SetSpriteExtPrio 함수가 있더군요. 사용하니 정렬이 잘되더군요. 단 이 함수를 사용하기 위해서 PA_InitSpriteExtPrio(1); 로 앞에서 먼저 초기화를 해줘야 합니다.
PA_InitSpriteExtPrio(1);

//중간 생략...

u8  nPrio=125;

//중간 생략...

case PI:
    if(stUserPae[bMePlay].nPiCnt < 10)
    {
     PA_SetSpriteXY(SCREEN_BTM,stUserPae[bMePlay].stOpenPae[i].nSprite,POS_ME_4TH_X+stUserPae[bMePlay].nPiCnt*POS_PI_SPACE,nOpenPaeY2);
     PA_SetSpriteExtPrio(SCREEN_BTM,stUserPae[bMePlay].stOpenPae[i].nSprite,nPrio);
     --nPrio;
    }
    else
    {
     PA_SetSpriteXY(SCREEN_BTM,stUserPae[bMePlay].stOpenPae[i].nSprite,POS_ME_4TH_X+(stUserPae[bMePlay].nPiCnt-10)*POS_PI_SPACE,nOpenPaeY1);
     PA_SetSpriteExtPrio(SCREEN_BTM,stUserPae[bMePlay].stOpenPae[i].nSprite,nPrio);
     --nPrio;
    }
    if((stUserPae[bMePlay].stOpenPae[i].nChar==SSANGPI)||(stUserPae[bMePlay].stOpenPae[i].nChar==YUL9))
     stUserPae[bMePlay].nSsangpiCnt++;
    stUserPae[bMePlay].nPiCnt++;
    break;

이상으로 간단하게 제작과장을 정리해 봤습니다.
신고
Posted by 버들피리불며

BLOG main image
닌텐도 DS 관련해서 Palib를 소개하고 제가 개발한 홈브류와 다른 개발자의 홈브류를 소개하고자 합니다. NDS 자체 제작(Homebrew)에 관심있는 다른 분들의 길잡이가 되고 싶습니다. by 버들피리불며

공지사항

카테고리

분류 전체보기 (39)
따라하기 (5)
PA_lib 소개 (10)
Homebrew 소개 (7)
나의 Homebrew (15)
기부하기(Donate) (1)
Wii (1)
Total : 105,370
Today : 0 Yesterday : 6