음... 아마 이번만 하면.. 솔직히 왠만한 간단한 게임 만들면서 NDS 에 관한 내부 구조 및 코딩방법은 파악이 될듯 싶네요

게임 사운드... 게임을 구성하는데 중요한 요소이지만, 게임 프로그래밍을 배우는 사람들에게 있어서 무시하기 쉬운 부분이기도 합니다.

일단 사운드는.. 함수 자체가 출력.. (방법 정의 ex ) loop 로 돌릴꺼야(배경음), 한번 출력하고 말꺼냐(효과음)) 정도이다 보니, 그래픽을 다루는것에 비해 상당히 간단하죠..
그래서 인지 PAlib 에서도 sound 예제는 상당히 대충 대충.. orz

사운드 삽입하는건 어렵지 않지만 그 방법이 좀 만만치 않습니다. (이거 삽질에 2일 걸렸던..)
일단 기존에 하던거에서 사운드를 출력해서 뭔가... 뭔가.. 좀 있는 느낌을 줘봅시다.

일단 switch 라는 프로그램을 다운로드 합시다.
http://www.palib.info/wiki/doku.php?id=day7ko 에 소개되있는.. 이 라이브러리 제작자가 추천하는 프로그램입니다.
실제 다운로드는 http://www.nch.com.au/switch/ 여기서 합니다.
일단 설치하면.... 아래와 같은게 뜹니다..

사용자 삽입 이미지
자기네 회사꺼 다른 프로그램 더 설치할꺼냐 하는데.. wavepad 는 사운드 편집기로 쓸만합니다.. 나머지는;;; 선택적..

설치가 끝나면 아래와 같은 switch 프로그램이 뜹니다.
사용자 삽입 이미지

이제 NDS에 넣을 음악을 고릅니다.
저는... 몇일전 일본에 가서 겨우 구한 미즈키 나나의 Starcamp 앨범안의 3번 트랙 Dancing in the velvet moon 를 넣어 보겠습니다.
사용자 삽입 이미지

mp3 파일이든 wav 파일이든 만든뒤 switch에 Add Files 버튼으로 추가합니다.
그리고 아래의 Output Format 을 .raw 로, Encoder options... 를 그림과 같이 설정하고 컨버팅 합니다.
사용자 삽입 이미지


컨버팅을 한 뒤 결과물을 project 폴더 내의 data 폴더에 넣습니다.
사용자 삽입 이미지

주의점으로.. 파일을 있는데로 다 끌어 쓰면 안됩니다.
이 NDS 프로그래밍에서는 ROM 파일의 용량이 4MB로 한정 되 있어서 (아무래도 롬파일을 NDS 메모리에 모두 적재 하고 실행시키는 구조인가 봅니다... 참고로 원래 NDS 칩은 128MB 의 용량을 가지고 있습니다)

사운드 파일 크기든, 다 합해서 4MB가 초과 되면 컴파일이 안됩니다.
위의 WavePad 로 적절히 끊어서 저장해서 raw 파일 용량을 줄이던지 해야 합니다.


경험상..  여기 밑에 포스트 되있는 제 졸업작품경우... 그림 + 알고리즘 소스가 1MB, 사운드 3MB 됩니다.
(소스야.. 원래 모바일 WIPI 소스다 보니 최적화를 잘 시켜놔서 별로 용량이 안됩니다)

여하튼...
그 뒤 Visual Stdio 상에서 아래와 같이 사운드 파일을 추가해주고
#include "사운드파일 이름.h" 로 선언해 줍니다.
사용자 삽입 이미지

이후 소스 입니다. 역시 새로 추가한 부분만 빨간색입니다.
//////////////////////////////////////////////////////
// Includes
#include <PA9.h>       // Include for PA_Lib
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"
#include "velvet_moon.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);
 }
}

// 현재 출력중인 사운드를 모두 끈다
void UnLoad_Sound()
{
 int i;
 
 for(i=0; i<16; ++i)
  PA_StopSound(i);
}

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

 // Init AS_Lib for normal sound playback only
 PA_InitASLibForSounds(AS_MODE_SURROUND | AS_MODE_16CH);

 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;

 AS_SoundDefaultPlay((u8*)velvet_moon, (u32)velvet_moon_size, 127, 64, true, 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();  // 리소스 정리
 UnLoad_Sound();
 return 0;
}

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

음.. 각각 설명입니다..
#include "velvet_moon.h"

위에서 설명되있듯, 사운드 파일이 있음을 컴파일러에게 알려주는 역활을 합니다.
밑의 Makefile 을 분석하시면 대략 아실꺼라 생각합니다.

요약하면, 컴파일러가 저 velvet_moon.h 가 있는지 보고, 당연히 선언이 안되어있으니
raw, jpg, gif ... 등 확장자를 바꿔가면서 /data/ 디렉토리에 해당 파일이 있는지 봅니다.
그리고 관련된 파일 (raw 파일 있으니 이거겠죠..)이 있으면 파일이름.h 헤더를 생성하고
거기에 대략 맞는(선언 위주의) 헤더를 생성합니다.

// 현재 출력중인 사운드를 모두 끈다
void UnLoad_Sound()
{
 int i;
 
 for(i=0; i<16; ++i)
  PA_StopSound(i);
}
위 소스는 제가 만든겁니다.
현 게임 상황에서 다음 게임 상황 (Menu state 에서 Game state 로 넘어갈때) 사운드가 바뀌지 않아
아예 다 꺼버리는 매크로 같은겁니다.
(즉.. 사운드는 0~15번까지 각각 재생이 된다는거라 생각합니다만.. 확실치는... orz
다만 0~7번이 효과음, 8~15번이 배경음 으로 지정되 있는거 같습니다)

 PA_InitASLibForSounds(AS_MODE_SURROUND | AS_MODE_16CH);
이 소스는 사운드를 출력할태니 준비하라는 초기화 API 입니다.
당연히 이걸 선언 안하면 사운드가 출력이 안되겠죠.

 AS_SoundDefaultPlay((u8*)velvet_moon, (u32)velvet_moon_size, 127, 64, true, 0);
위 소스로 사운드를 실행 시킵니다.
대략 AS_SoundDefaultPlay( (u8*) 사운드 파일 이름,
                                       (u32) 사운드 파일 이름_size,
                                       127, 64  (사운드 화음 같은거 같네요..)
                                        true, (이건 루프를 돌릴껀지 선택하는겁니다),
                                        0);
음... 사운드 파일 이름은.. 위에서 컴파일러가 자동으로 만들어 주는 "사운드파일.h" 에 선언되 있습니다.
그런이유로 컴파일 에러가 나진 않죠..

여튼 컴파일 해서 이상이 없으면 아래와 같은 전에 했던 작업에
사운드 파일이 재생되는것을 들으실 수 있습니다.

사용자 삽입 이미지

음.. 대략.. 이걸로 환경조성하고, 배경띄우고, 스프라이트 조작하고, 사운드를 넣어보았습니다.
기본적으로 NDS 게임 프로그래밍은 이거를 조금더 풀어쓴다고 생각하시면 될꺼 같네요
(여기 있는게, MFC 같은 캡슐화된 메소드들이고, 실제는 여기서 좀 풀어쓴 API 급 정도..)

게임의 기본 루프는

fn_game1()
{
     // 메모리 청소 및 초기화
     ...
 
     // 화면에 띄울것 메모리에 Load
     ...

     // 게임 알고리즘 셋팅
     ...

     while( 탈출 변수 )
     {
           // 화면 움직임 처리
           ...
           //  입력 감지 및 처리
           switch(입력?)
           {
                 case 1 :  fn_game_1();  break;
                 case 2 :  fn_game_2();  break;
                 .....
            }
      }
       // 메모리 청소
}

등으로 구성 됩니다.
이 정도면.. 간단한 2D 정도는 문제 없을꺼라 생각되네요.

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

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

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

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

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

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

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

사용자 삽입 이미지

각각 가로 세로 길이이며 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

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

그림은 2종류가 있습니다.
배경그림과 같은 것과 스프라이트 그림(케릭터 그림)이 있습니다

보통 배경그림을 뒤에 분위기를 깔아주고
스프라이트 그림들의 움직임 조작으로 게임은 돌아가죠
(슈퍼마리오 경우.. 파이프 배경이 배경그림이고, 마리오 케릭 자체를 스프라이트 그림입니다)

배경 그림은 총 4개를 메모리에 올릴 수 있습니다.
NDS의 그림의 구조가 BMP와 다릅니다
자세한건 http://rosagigantea.tistory.com/77 여기를 봐주시기 바랍니다.

뭐 요약하면
NDS에선 그림의 크기를 줄이기 위해 꽁트를 쓰고
단순한 그림일 수록 이 꽁트가 잘 먹히며,
256색을 사용하는게 좋다는 것 입니다.

그림 크기에 따라선
256x192    :   x축 배경 스크롤만 할 경우....
256x256    :   x,y축 배경 스크롤을 할 경우...
512x512    :   x,y축 좀더 버라이어티한 스크롤 하고 싶은 경우.
로 하는것이 좋습니다.

우선 예제 그림으로
사용자 삽입 이미지
 <bg1>
사용자 삽입 이미지
<bg_charactor>

사용자 삽입 이미지
<bg2>



등을 사용해 봅시다.


위 링크를 다운로드해서 사용하시기 바랍니다.

음.. 대충 읽지 않으셨다면, 뭔짓으로 변환을 한뒤에 그걸 사용함을 느끼셨을 껍니다.
그러므로... 툴을 사용합니다. (시간이 되시면 만드셔도 됩니다 http://rosagigantea.tistory.com/79 식으로요)

PAlib 안에는 변환툴이 있으니 이것을 사용합니다.
C:\devkitPro\PAlib\Tools\PAGfx 경로로 들어가시면 PAGC Frontend 프로그램이 있습니다. 이걸 실행 시킵니다  (참고로 .net framework 2.0 이 설치가 안되어 있으면 실행이 안됩니다.  c#으로 짜여진건지;;;)

1. 아래 경로의 프로그램을 실행합니다.
사용자 삽입 이미지

2. 화살표 순서대로.. 변환할 Background 에서, Add file로 그림을 넣은뒤, Save and Convert 로 컨버팅 합니다.
사용자 삽입 이미지
옆에 RGB(255,0,255) 색을 투명색으로 지정되었음을 알 수 있습니다. 다른 색으로도 변경가능합니다.

3. 성공하면 아래와 같이 C언어로 변환된 파일이 보입니다. 만약 안된다면 .net framework 2.0을 다시 설치 하셔야 합니다.
사용자 삽입 이미지

4. 변환된 파일을, 작업디렉토리의 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

// 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);

 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_WaitForVBL();
 }
 
 UnLoad_Screen();  // 리소스 정리
 return 0;
}
//////////////////////////////////////////////////////
이걸 컴파일 하면 아래와 같습니다.

사용자 삽입 이미지
윗 스크린은 x,y 스크롤 되어가고
아래 스크린은 x축 방향만 스크롤 됩니다.
그리고 케릭터 스크린도 옆에 표시 됩니다.
투명색을 255,0,255 색으로 해서 투명색이 아닌 부분만 나온걸 확인 할 수 있습니다.

소스를 분석해 봅니다.
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"

을 선언해야, 변환된 그림을 제대로 사용할 수 있습니다.
// Video ram 의 모든 그림을 제거 하는 함수
void UnLoad_Screen()
{
 int i;
 
 for(i=0; i<2; ++i)
 {
  PA_ResetBgSysScreen(i);
  PA_ResetSpriteSysScreen(i);
 }
}

의 경우 일단 리소스 정리 차원에서 만들었습니다.
나중에 게임 장면 변환 할때,(배경, 스프라이트 완전히 다른걸로 뿌릴때)
이런 함수를 사용해야 메모리 충돌이 일어나지 않습니다.

 PA_EasyBgLoad(UP_SCREEN, BG3, bg2);
 PA_EasyBgLoad(DOWN_SCREEN, BG2, bg_charactor);
 PA_EasyBgLoad(DOWN_SCREEN, BG3, bg1);

각각 배경그림을 띄우는 함수 입니다.
다른 함수도 있지만.. 일단 간단하게 저 함수를 주로 사용해도 상관없을꺼 같습니다.
각각... 윗 스크린에 BG3부분에 bg2 그림을 띄우고
         아랫스크린에 BG2 부분에 bg케릭터 그림을 띄우고
         아랫스크린에 BG3 부분에 bg1 그림을 띄우는 것 입니다..
그림의 출력은
       
---------------------- LCD 표면 ----------------------------
스프라이트 0 ~ 127  (숫자가 작을수록 위)
----
BG0
----
BG1
----
BG2
----
BG3         (가장 아래)

식으로 보이게 됩니다.
즉, 케릭터 그림을 BG2로 하고 배경그림을 BG3으로 하는 이유도 다 저런 이유 때문이죠. 반대로 바꿔서 컴파일 해보시면 더 이해가 빠를실겁니다..

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_WaitForVBL();
 }

배경 스크롤 하는 함수 입니다.    PA_EasyBgScrollX , Y, XY.. 식의 함수가 있으며, 위의 방법처럼 사용합니다.

이정도면 대부분 배경은 다룰수 있으실꺼 같습니다. 더욱 다양한 배경 효과를 원하시면
C:\devkitPro\PAlibExamples\Backgrounds 에 다양한 효과의 소스가 있습니다
각각 해보시면서 원하는 효과를 적용하시는것이 낳으실 껍니다.

다음은 스프라이트에 대해 얘기해 보겠습니다.

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

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

우선
http://rosagigantea.tistory.com/280 로 인해 생성되는 소스입을 밝힘니다.

소스는 아래와 같이 되어있습니다.
///////////////////////////////////////////////////////////////
// PALib Template Application

// Includes
#include <PA9.h>       // Include for PA_Lib

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

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

 // Infinite loop to keep the program running
 while (1)
 {
  PA_WaitForVBL();
 }
 
 return 0;
}
///////////////////////////////////////////////////////////////

음.. C언어의 HelloWorld 보다 많이 어렵네요..
뭔가 함수 안에 숫자가 얽겨있고...

참고로 C언어에선

#include <stdio.h>

int main()
{
   printf("Hello World\n");

   return 0;
}


인것에 반해서 많이 복잡하단 느낌이 드네요.

어쨋든 저 위의 소스를 좀더 쉽게 바꿔봅시다.
///////////////////////////////////////////////////////////////
#include <PA9.h>       // Include for PA_Lib

#define UP_SCREEN 1
#define DOWN_SCREEN 0

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

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!");

 // Infinite loop to keep the program running
 while (1)
 {
  PA_WaitForVBL();
 }
 
 return 0;
}

///////////////////////////////////////////////////////////////
음... 단지 define 좀 해놓으니 보기가 편해지네요

우선...
PA_Init();    // Initializes PA_Lib
PA_InitVBL(); // Initializes a standard VBL

이 부분....
NDS 는... OS 가 없습니다. 불행히도....
소프트웨어는 하드웨어와 직접 컨트롤 하지 않고 운영체제에 중계 하여 컨트롤 하는것이 이 세계의 상식입니다.
하지만.. 임베이드 장비는.. 리눅스만 올려도 감지덕지죠.
하지만, 리눅스 올리면 당연히 여러 어플리케이션을 돌릴 수 있고, 속도도 느려지므로, 대부분은 OS가 없습니다.

그럼... 메모리 컨트롤 이나.. 자원 관리는 어떻게 할까요?
이런 임베이드 장비는 대부분 자원관리 API를 제공하고 있으며, 그런 걸 먼저 실행시키고 나서 프로그램을 돌려야
이상없이 프로그램이 실행이 됩니다.
그 API 가.. 저 2줄로 보시면 됩니다.

다음 줄을 봅시다.
 PA_InitText(UP_SCREEN, BG2);
 PA_OutputSimpleText(UP_SCREEN, 1, 2, "Hello World!");


음.... #define 으로 UP_SCREEN, DOWN_SCREEN을 해두는것이 편합니다. 가독성이 좋으니까요
NDS는 말 그대로 스크린이 2개 입니다. 위에 출력할지 아래에 출력할지 알려줘야 하죠
우선. InitText 로 글꼴을 메모리에 올립니다.
NDS의 비디오 램은 총 640kb 이고, 이 메모리로 두 스크린을 표현합니다.
비디오 렘 자체에 숫자를 넣으면 그것에 해당되는 색이 lcd에 표현되는 방식이죠....
이건.. 좀 복잡하니 http://rosagigantea.tistory.com/75 를 참고해 주시기 바랍니다.

어쨋든 PA_InitText(UP_SCREEN, BG2)는 텍스트 폰트 그림을 BG2 영역 (BG는 각각 4개 띄울수 있습니다.)에 업로드 하고
PA_OutputSimpleText(UP_SCREEN, 1, 2, "Hello World!"); 명령어로 (1,2) 그림이 폰트 크기 기준으로
Hello World 를 찍습니다.

다음...
 // Infinite loop to keep the program running
 while (1)
 {
  PA_WaitForVBL();
 }

여기는 게임 루프 입니다.
PA_WaitForVBL() 중요한 함수 입니다. 이 함수는 스크린 프레임을 60fps 로 맞춰주고, 인터럽트 처리를 담당하는
함수 입니다. 이걸 써주지 않으면 그림이 이상하게 출력되는등 이상현상을 보실 수 있습니다.


음.. 이렇게 Hello World 를 분석했습니다. 다음엔 게임기 답게 그림을 띄어 보겠습니다.

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

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


닌텐도 DS 게임은... 원래 닌텐도에서 발급하는 라이센스를 취득한뒤에 게임 제작을 할 수 있습니다.
(물론.. PSP나.. XBOX 다 그런 시스템입니다)

허나.. 개 개인이 그런 라이센스를 받는다는거 자체가 불가능한 일이고.. 하지만 뭔가 만들고 싶고..
그런 분들을 위한 툴입니다

해외에선 많이 알려진, 널리 사용하는 devkitPro를 사용하여 NDS 프로그래밍 하는 방법을 알려드리겠습니다.

1. 우선 http://www.devkitpro.org/downloads/ 사이트로 들어가셔서 최신 devkitPro를 다운로드 합니다.

사용자 삽입 이미지

1-1. 클릭하면 sourceforge 로 들어가서 최신 버젼을 다운로드 할 수 있음.
사용자 삽입 이미지

2. 이제 다운로드 한 최신 프로그램을 설치 합니다.
사용자 삽입 이미지

2-1. 첫번째것을 선택해서 next
사용자 삽입 이미지

2-2. 그냥 next
사용자 삽입 이미지

2-3. 뭘 설치할지 묻는 화면입니다.
사용자 삽입 이미지
각각..
 minmal system : 리눅스와 같이 ls, gcc 뭐 이런 명령어들을 명령프롬프트창(dos창)에서 사용할 수 있게 합니다.
 devkitARM : 닌텐도 DS는 ARM프로세서를 사용합니다. 당연히 설치 합니다. 이외 국산 게임기인 gp32 등을 만질수 있는 gcc 도 제공합니다.
 devkitPPC : PocketPC ... 즉.. PDA 같은 시스템입니다. 의외로.. 닌텐도 게임큐브나.. 닌텐도 wil 컴파일도 있습니다
 devkitPSP : 말그대로 PSP 컴파일 입니다. 이걸로 PSP 컴파일 할 수 있습니다.
 Programmer's Notepad : IDE 툴입니다. 우리는 Visual Studio를 사용할 것 이므로 패스..
 Insight : 음... 디버깅 툴이랍니다.. 설치 합시다.

2-4. 설치 디렉토리를 선택합니다. 그냥 디폴트값을 두고 next
사용자 삽입 이미지

2-5. 인터넷으로 설치 파일을 다운로드 하고 있습니다...
사용자 삽입 이미지

2-6. 다 다운로드하면 자동 설치 합니다. 끝나면 next..
사용자 삽입 이미지

2-7. 설치가 완료되었습니다.
사용자 삽입 이미지


3. 그냥 저 상태에서 컴파일 해도 되지만. 좀더 쉽게 프로그래밍 하기 위해 라이브러리를 다운로드 합시다.
  가장 유명한 라이브러리는 PAlib 입니다.
  http://forum.palib.info/ 으로 들어가셔서 라이브러리를 다운합니다.
  참고로 저 사이트엔 라이브러리에 관한 튜토리얼도 제공되니 필히 읽어 보시기 바랍니다.
  (한국어 버젼도 있습니다)
사용자 삽입 이미지

4. 다운로드는 c:\devitpro 즉, 아까 devkitpro를 설치한 디렉토리에 다운로드 합니다.
사용자 삽입 이미지

5. 다운로드 한 파일을 알집을 통해 '현재 폴더에 풀기'로 압축 풉니다.
사용자 삽입 이미지

6. visual studio 에서 컴파일 할 수 있게 http://www.thechip.net/images/PALibAppWizard.zip 의 파일을 c:\devkitpro 에 다운로드 합니다. 그리고 이건 해당 파일명 폴더로 압축풉니다.
사용자 삽입 이미지

7. 압축을 풀면 아래와 같은 파일이 있는데, 각각 Visual Studio 2005 express 버젼과 Visual Studio 2005 버젼입니다. 자신의 환경에 맞게 설치 합니다. VSExpress 경우 MSDN 사이트에서 무료로 다운로드 할 수 있습니다.
이 파일은.. 오우거 엔진의 VS 연동 파일을 개조 했다고 합니다 :)
사용자 삽입 이미지


8. 설치가 완료 됬습니다. 이제 Visual Studio 를 실행시켜 봅니다.
사용자 삽입 이미지

9. 새 프로젝트 생성을 보면 NintendoDS 가 있습니다 >_<
 경로명 주의 사항을 잘 읽어보시고 셋팅해줍니다.
사용자 삽입 이미지

10. 별 내용 없습니다. Finsh 누릅니다.
사용자 삽입 이미지

11. 원할한 디버깅을 위해 예뮬레이터를 연결합니다.
   Tool -> External Tools (도구 -> 외부도구) 를 클릭합니다.
사용자 삽입 이미지

12. 가장 아랫줄... (9번째 줄 일껍니다) 를 아래와 같이 셋팅합니다.
사용자 삽입 이미지

Command : C:\devkitPro\PAlib\Emulators\no$gba\NO$GBA.EXE
Arguments : $(TargetDir)\$(TargetName).sc.nds
Inital directory : $(TargetDir)

C:\devkitPro\PAlib\Emulators에 들어가면 NDS 예뮬 프로그램이 많습니다.
하지만 가장 괜찮은건 No$GBA 라는군요. 다른걸로 사용해도 무방합니다.
단, No$GBA 경우 생성되는 파일의 *.sc.nds 파일을 업로드 해야 제대로 실행됩니다.

13. 계속.. 외부도구에 쉽게 접근하기 위해 단축키를 설정합니다.
Tool -> Option (도구 -> 옵션) 으로 들어갑니다.
사용자 삽입 이미지

14. 키보드 설정에 Tools (도구) 쪽의 외부도구 9번 (NDS 설정이 9번째 라인이므로 9번입니다. 다른 라인에 하셨으면 그거 에 맞는 번호를 찾으셔야 합니다)에 빨간 박스 해놓은곳에 키를 직접 눌러 셋팅합니다.
사용자 삽입 이미지

15. 이제 디버깅을 해봅시다. 에러가 생겼네요 >_<
사용자 삽입 이미지

16. 뭐가 문제인지 찾아가 봅시다.

extern inline u16 PA_GetBmpWidth(void *bmpdata){
 BMP_Headers *Bmpinfo = (BMP_Headers*)(bmpdata+14);
 return Bmpinfo->Width;
}
에서 void *bmpdata 가 갑자기 BMP_Header * 로 붙어서 문제가 생긴거 같습니다.
PA_GetBmpWidth(void *bmpdata) -> PA_GetBmpWidth(BMP_Headers*bmpdata) 로 변경합니다.

한군데 더 그런곳이 있는데, 거기도 똑같이 변경합니다.
사용자 삽입 이미지
사용자 삽입 이미지

17. 또 문제가 생겼네요.. 이번엔 arm7 라이브러리가 없답니다. 그러면.. 복사해 주면 간단해지겠죠
아까 devkitpro 설치한 디렉토리로 가셔서 palib 의 lib 디렉토리를 devkitpro/msys 에 복사합니다.

사용자 삽입 이미지

당연히 합칩니다. >_<
사용자 삽입 이미지

18. 다시 디버깅 해봅시다. 파일이 3개나 나왔습니다. 이러면 성공입니다.
사용자 삽입 이미지

19. 이제 아까 설정한 키를 눌러서 예뮬을 돌려 봅니다. Hello world 가 나오면 성공입니다
사용자 삽입 이미지

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

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