본문 바로가기
프로그래밍/PSP

[펌]PSP 프로그래밍 104 - Controller

by 베리베리 2008. 7. 28.
디버그용 기능을 이용해서 화면에 Hello World를 출력하는 것을 해 보았으니, 이젠 입력을 해 보기로 한다. PSP의 입력 도구는 방향키, 아날로그 스틱, 기호키, 트리거키와 그 외 HOME, SELECT, START 키가 있고, 볼륨과 화면 조정 키가 있다. 이 키들에서 받은 입력을 인식하고 적절한 처리를 해 주는 것이 목표이다.

PSPSDK의 샘플을 보면 적당한 샘플이 있다. cygwin 쉘을 실행해서 다음 위치로 가보도록 한다.

C:\cygwin\usr\local\pspdev\psp\sdk\samples\controller\basic

역시 main.c와 Makefile이 존재한다. main.c의 내용을 살펴 보면 Hello World와 비슷하지만 뭔가 헤더 파일도 많이 include 하고, main 함수 내에 커다란 while 문이 있고, done이라는 전역 변수도 보이고, exit_callback 함수에 있었던 sceKernelExitGame 함수가 main 함수 안으로 옮겨져 있는 변화가 보인다.

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <stdlib.h>
#include <string.h>

/* Define the module info section */
PSP_MODULE_INFO("CONTROLTEST", 0, 1, 1);

/* Define the main thread's attribute value (optional) */
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);

/* Define printf, just to make typing easier */
#define printf pspDebugScreenPrintf

int done = 0;

/* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
  done = 1;
   return 0;
}

/* Callback thread */
int CallbackThread(SceSize args, void *argp)
{
    int cbid;

    cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
  sceKernelRegisterExitCallback(cbid);
  sceKernelSleepThreadCB();

    return 0;
}

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void)
{
  int thid = 0;

  thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
    if(thid >= 0)
  {
         sceKernelStartThread(thid, 0, 0);
  }

  return thid;
}

int main(void)
{
  SceCtrlData pad;

  pspDebugScreenInit();
  SetupCallbacks();

  sceCtrlSetSamplingCycle(0);
  sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);

   while(!done){
       pspDebugScreenSetXY(0, 2);
       sceCtrlReadBufferPositive(&pad, 1);
       printf("Analog X = %d ", pad.Lx);
       printf("Analog Y = %d \n", pad.Ly);
       if (pad.Buttons != 0){
           if (pad.Buttons & PSP_CTRL_SQUARE){
               printf("Square pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_TRIANGLE){
               printf("Triangle pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_CIRCLE){
               printf("Cicle pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_CROSS){
               printf("Cross pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_UP){
               printf("Up pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_DOWN){
               printf("Down pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_LEFT){
               printf("Left pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_RIGHT){
               printf("Right pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_START){
               printf("Start pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_SELECT){
               printf("Select pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_LTRIGGER){
               printf("L-trigger pressed \n");
           }
           if (pad.Buttons & PSP_CTRL_RTRIGGER){
               printf("R-trigger pressed \n");
           }
        }
  }
  sceKernelExitGame();

    return 0;
}

SceCtrlData라는 구조체가 main 함수에 있는데 이 구조체는 controller에 대한 정보를 반환받기 위한 구조체이다. 다음과 같이 정의되어 있다. 이 정보의 timestamp와 버튼 상태 정보, 아날로그 스틱의 X, Y 좌표값들로 이루어져 있다. 위의 소스 코드에서는 sceCtrlReadBufferPositive 함수로 값을 받아서 처리하고 있다.

/** Returned controller data */
typedef struct SceCtrlData {
  /** The current read frame. */
  unsigned int  TimeStamp;
  /** Bit mask containing zero or more of ::PspCtrlButtons. */
  unsigned int  Buttons;
  /** Analogue stick, X axis. */
  unsigned char  Lx;
  /** Analogue stick, Y axis. */
  unsigned char  Ly;
  /** Reserved. */
  unsigned char  Rsrv[6];
} SceCtrlData;

버튼 상태 정보를 담고 있는 Buttons라는 변수에는 PspCtrlButtons라는 형식의 값들이 있을 수 있는데, PspCtrlButtons는 다음과 같이 정의되어 있다. 모든 버튼의 상태를 알아낼 수 있으나, 아래 주석에 있는 것과 같이 볼륨 조절 키, 화면 조정 키, 무선랜 키 등과 같은 것들은 커널 모드에서만 사용할 수 있다. controller 샘플은 유저 모드로 돌아가기 때문에 이와 같은 키들은 사용하지 않고 있다.

/**
* Enumeration for the digital controller buttons.
*
* @note PSP_CTRL_NOTE, PSP_CTRL_SCREEN, PSP_CTRL_VOLUP, PSP_CTRL_VOLDOWN, PSP_CTRL_DISC, PSP_CTRL_WLAN_UP, PSP_CTRL_REMOTE, PSP_CTRL_MS can only be read in kernel mode
*/
enum PspCtrlButtons
{
  /** Select button. */
  PSP_CTRL_SELECT     = 0x000001,
  /** Start button. */
  PSP_CTRL_START      = 0x000008,
  /** Up D-Pad button. */
  PSP_CTRL_UP         = 0x000010,
  /** Right D-Pad button. */
  PSP_CTRL_RIGHT      = 0x000020,
  /** Down D-Pad button. */
  PSP_CTRL_DOWN       = 0x000040,
  /** Left D-Pad button. */
  PSP_CTRL_LEFT       = 0x000080,
  /** Left trigger. */
  PSP_CTRL_LTRIGGER   = 0x000100,
  /** Right trigger. */
  PSP_CTRL_RTRIGGER   = 0x000200,
  /** Triangle button. */
  PSP_CTRL_TRIANGLE   = 0x001000,
  /** Circle button. */
  PSP_CTRL_CIRCLE     = 0x002000,
  /** Cross button. */
  PSP_CTRL_CROSS      = 0x004000,
  /** Square button. */
  PSP_CTRL_SQUARE     = 0x008000,
  /** Home button. */
  PSP_CTRL_HOME       = 0x010000,
  /** Hold button. */
  PSP_CTRL_HOLD       = 0x020000,
  /** Music Note button. */
  PSP_CTRL_NOTE       = 0x800000,
  /** Screen button. */
  PSP_CTRL_SCREEN     = 0x400000,
  /** Volume up button. */
  PSP_CTRL_VOLUP      = 0x100000,
  /** Volume down button. */
  PSP_CTRL_VOLDOWN    = 0x200000,
  /** Wlan switch up. */
  PSP_CTRL_WLAN_UP    = 0x040000,
  /** Remote hold position. */
  PSP_CTRL_REMOTE     = 0x080000,
  /** Disc present. */
  PSP_CTRL_DISC       = 0x1000000,
  /** Memory stick present. */
  PSP_CTRL_MS         = 0x2000000,
};

실제로 각 controller의 값을 알아내는 sceCtrlReadBufferPositive 함수는 SceCtrlData 구조체의 주소와 count 값을 인자로 받는데, count 값은 대부분의 경우 1로 주면 된다. 그리고, sceCtrlReadBufferPositive 함수가 있으면 sceCtrlReadBufferNegative 함수가 있을 것이라는 것도 유추할 수 있을텐데, sceCtrlReadBufferNegative 함수는 SceCtrlData 구조체의 값이 반전되어 들어가게 된다.

만약에 HOME 키를 눌러서 게임을 끝내겠다고 하면 exit_callback 함수에서 바로 sceKernelExitGame 함수를 부르지 않고 main 함수의 while 루프가 끝나도록 done 전역 변수의 값을 1로 설정하게 된다. 그러면 main 함수의 thread에서 while 루프를 빠져 나온 다음에 sceKernelExitGame 함수를 호출하고 종료하게 된다.

빌드를 하고 PSP에 넣은 후 실행을 해 보면 화면에 아날로그 스틱의 좌표가 나오고, 키를 누르면 어떤 키가 눌려졌다고 나오게 될 것이다. 물론 키를 복수개 동시에 눌러도 모두 검출해 낼 수 있다.

마지막으로, 이 샘플 코드에는 오자가 하나 있는데, 멋지게 고쳐주자. "Cicle pressed"라고 되어 있는 부분이 있다. 올바른 표현은 "Circle pressed".


이제 키 입력까지 받아서 처리할 수 있게 되었다, 축!

출처 : http://www.onlinegamer.co.kr/ 지금은 사라져서;; 공개해두 괜찮을지;;;

댓글