[GpGiki 대문으로]

지피껨 만들기 05


지피껨 만들기.. (5)

샬롬~

우움.... 예전에 썼던 지피껨 만들기를 읽어보니.. 틀린 부분도 많고.. 뻔한 얘기는 길게 쓰고, 중요한 건 대충 넘어가고.. 그런 부분이 많더군요.

역시, 질보다 속도....라는 측면에서 쓰인 글인가 -_- ( 글 쓴 놈이 상당히 귀찮은 걸 싫어한다는 게 더 중요한 요소였나... )

하여튼... 그러네요... ^^

뭐, 요새 하도 글을 안 써서.. 뭘 써야 할지도 모르겠네요. 원래 글에서 GP32용 gcc 사용법이라든가 쓸라구 했는데... 이미 퍼트려 버려서... 굳이 그럴 것도 없고.. ( 아, 제 홈피-아시죠?-에 완전 무장된 GP32용 gcc가 있습니다. 혹시 다른 곳에서 gcc 받으시고 "이거 어케 쓰란 거냐!"라는 분... 이거 받아서 써보세요... 쿨럭.. 아, 글구... 잘 되면 방명록에 글 좀 남겨주세요... T_T 접속 통계와 방명록의 글이 왠지 안 맞는 느낌이 드는 건 저의 착각일까요 -_-;;;;; 홈페이지 주소 - http://www.ahastudio.com ) 아, 괄호가 상당히 길었네요. 하여튼... 소재의 빈곤 속에 있습니다.

..........

사실 전에 뭐 집에서 그래픽 리소스를 가져와서 뭐 한다고 했는데.. 절대 안 가져왔습니다 -_- 죄...죄송합니다... 쿨럭....... 어짜피 집에서 뭔가 가져와도 스프라이트 에디터 등을 만들어야 하는데.. 그런 것도 하나도 안 만들었고.. ( 설마 도스용 툴을 쓸까요. 13h 모드여 부활하라! 털썩.. ) 그럼... 대체 뭘 쓰려는 걸까요... -_-

7. 청기백기 일케 만드러따!!!

탕................

퍽퍽퍽...

드르륵..........

아, 예... 죄송합니다. 이런 걸 쓰게 되서 -_- 하지만 얼마나 쓸 게 없으면 이런 걸 우려먹겠습니까....... 쿨럭... 아시는 분이 계실지 모르겠습니다만... 허접스러운 청기백기 게임을 만든 적이 있습니다. 특별히 그래픽이 들어간 것도 아니고.. 소스도 뭐, 소프트웨어 공학적으로 설계를 해서 작성한 게 아니라.. 그냥 닥치는데로 코딩을 해서.... -_- ( 아, 뭐.. 도스 시절에는 이렇게 한 적이 꽤 됩니다만... 쿨럭... ) 그렇지만... 누군가가 5편 압력을 넣고 있고... 또한 개인적인 슬럼프를 벗어나기 위해.. 이런 짓이라도 해봅니다 -_-

자자, 이 놈의 소스 구조를 알아보죠. gpmain.c는 게임을 가지고 있는 놈이고.. 그래픽 처리 및 기타 등등을 따로 빼서 nalim.c, nalim.h에 넣었습니다.

Nalim이라는 것은.. 예전에 Cho-Her-Jup Soft에서 Maku-Nalim RPG Engine이란 걸 만들었는데... 그때부터 심심하면 Nalim이란 이름을 붙이고 있죠. 한때 하려다 그냥 바로 접은 Seal 엔진 카피 프로젝트(?)에서도 아마 Maku-Nalim이란 이름을 썼던 것 같습니다 -_- 하여튼.. 이름의 유래 따위는 던져 버리고... nalim.c/nalim.h를 알아봅시다.

먼저 nalim.c입니다~ +_+

#include "nalim.h"

GPDRAWSURFACE gpDraw[2];
int nflip = 0;

#define LCD_WIDTH	GPC_LCD_WIDTH
#define LCD_HEIGHT	GPC_LCD_HEIGHT

#define MAX_PCM	100

typedef struct
{
	ubyte *data;
	int size;
}	pcm_t;

pcm_t pcm[MAX_PCM];

int nalim_init()
{
	GpLcdSurfaceGet( &gpDraw[0], 0 );
	GpLcdSurfaceGet( &gpDraw[1], 1 );

	GpSurfaceSet( &gpDraw[0] );

	if ( !( GPC_LCD_ON_BIT & GpLcdStatusGet() ) )	GpLcdEnable();

	GpFatInit();

	GpPcmInit( PCM_M11, PCM_8BIT );

	return 0;
}

ubyte *read_file( char *name, int *size )
{
	ubyte *data = NULL;
	ulong data_size, rcount;
	F_HANDLE h_file;

	GpFileGetSize( name, &data_size );

	GpFileOpen( name, OPEN_R, &h_file );
	data = gp_mem_func.malloc( data_size );
	GpFileRead( h_file, data, data_size, &rcount );
	GpFileClose( h_file );

	if( size )	*size = data_size;

	return data;
}

void set_palette( ubyte *palette )
{
	GP_HPALETTE h_pal;
	GP_PALETTEENTRY entry[256];
	ubyte r, g, b;
	int i;

	for( i = 0 ; i < 256 ; i++ )
	{
		r = palette[i * 3    ] >> 1;
		g = palette[i * 3 + 1] >> 1;
		b = palette[i * 3 + 2] >> 1;
		entry[i] = ( r << 11 ) | ( g << 6 ) | ( b << 1 );
	}

	h_pal = GpPaletteCreate( 256, entry );
	GpPaletteSelect( h_pal );
	GpPaletteRealize();
}

void draw_text( int x, int y, char *s, ubyte color, int flag )
{
	if( flag )	GpTextOut( NULL, &gpDraw[nflip], x + 1, y + 1, s, 0 );

	GpTextOut( NULL, &gpDraw[nflip], x, y, s, color );
}

void put_image( int x, int y, ubyte *image, int flip )
{
	if( image )
	{
		int width = *(word*)image;
		int height = *(word*)( image + 2 );
		if( !flip )	GpBitBlt( NULL, &gpDraw[nflip], x, y, width,
			height, image + 4, 0, 0, width, height );
		else		GpBitLRBlt( NULL, &gpDraw[nflip], x, y, width,
			height, image + 4, 0, 0, width, height );
	}
}

void put_sprite( int x, int y, ubyte *image, int flip )
{
	if( image )
	{
		int width = *(word*)image;
		int height = *(word*)( image + 2 );
		if( !flip )	GpTransBlt( NULL, &gpDraw[nflip], x, y, width,
			height, image + 4, 0, 0, width, height, 0xEF );
		else		GpTransLRBlt( NULL, &gpDraw[nflip], x, y, width,
			height, image + 4, 0, 0, width, height, 0xEF );
	}
}

void show()
{
	GpSurfaceFlip( &gpDraw[nflip++] );
	nflip %= 2;
}

void load_pcm( int num, char *name )
{
	pcm[num].data = read_file( name, &pcm[num].size );
}

int play_pcm( int num )
{
	GpPcmPlay( (uword*)pcm[num].data, pcm[num].size, 0 );
	return pcm[num].size * 1000 / 11160;
}

자, 이것으로 이번 글은 줄이도록.. (탕)

.....

(벌떡 일어난다)

예전의 소스를 유심히 보셨던 분들은 특이할 게 없을 것 같습니다. 일단 nalim.h를 보고 다시 설명하도록 하죠^^

#ifndef _NALIM_H_
#define _NALIM_H_

#include "gpdef.h"
#include "gpstdio.h"
#include "gpstdlib.h"
#include "gpgraphic.h"
#include "gpfont.h"
#include "gpmm.h"

#define get_time()	(int)GpTickCountGet()
#define randomize()	GpSrand( get_time() )
#define random( X )	GpRandN( (X) - 1 )
#define get_key()	GpKeyGet()

int nalim_init();

ubyte *read_file( char *name, int *size );

void set_palette( ubyte *palette );

void draw_text( int x, int y, char *s, ubyte color, int flag );

void put_image( int x, int y, ubyte *image, int flip );
void put_sprite( int x, int y, ubyte *image, int flip );

void show();

void load_pcm( int num, char *name );
int play_pcm( int num );

#endif

자, 몇가지 가닥이 보이시죠? nalim_init()란 놈으로 초기화를 시키고... read_file은 파일을 읽을 수 있으며.. set_palette는 팔레트 세팅이 가능하고.. draw_text는 문자열을 출력하고.. put_image는 이미지 출력을 하고.. put_sprite는 스프라이트 출력을 하고.. show는 LCD Surface Flip을 해서 화면을 보여주며.. load_pcm은 PCM 사운드를 불러오고.. play_pcm은 PCM 사운드를 연주하는 겁니다............ 전과 크게 다를 건 없는데요. 사운드 출력이 추가된 게 관건이죠. 자, 사운드 초기화 부분을 볼까요?

	GpPcmInit( PCM_M11, PCM_8BIT );

예, 간단합니다. 샘플링이 11kHz이고, 8bit로 녹음된 사운드를 쓴다는 거죠. 혹시 가지고 계신 웨이브 파일이 있다면 여러가지 툴로 11kHz, 8bit로 변환하시기 바랍니다 +_+

그리고 나서 웨이브 파일을 PCM 파일로 변환해야 하는데.... 저는 귀찮아서.. 변환 안 하고 있습니다. 즉, 잡음이 들어가고 음이 이상하게 나죠 -_- 이번 꺼의 경우에는 다행이도 도스 시절의 VOC 파일을 구해서 썼는데.. 이놈은 앞에 살짝 헤더가 붙은 형태라 그냥 PCM 데이터로 써도 무관하더군요. ( 그래도 잡음은 있긴 합니다 -_- )

여기서 잠깐!!!!!!!!!1

아까부터 PCM, PCM하는데 PCM이 뭔가!!!!!!!!!!

PCM의... 뜻은... 까먹었습니다.

예전에 정영덕씨가 쓴 SF2 제작자가 쓴 게임 만들기..였나.. 그 책에 자세히 나와있는데.... 지금 책을 찾기 귀찮으니... 뜻은 몰라도 될 듯 합니다 -_- P가 아마 Pulse인가 할텐데..

소리에 대해서 아시죠? 공기을 떨리게 하는 거.. 그 값은 -1~+1 같은 식으로 표현이 가능하겠죠? 이걸 8bit로 표현하면 -128~127로 되겠죠. 그게 8bit 사운드입니다.

당연히 음질을 높이려면 16bit로 함이... 쿨럭... 샘플링은 아날로그 데이터를 디지털로 바꿔야 하니까.. 얼마나 자주 그 Pulse를 잡겠냐는거죠. 11kHz면 1초에 11k(k가 1024였나.. 그럼 11 * 1024)번 잡아주는 거죠. CD의 경우엔 44kHz에 16bit인가.. 그렇습니다. 자, 우리가 쓰는 걸로 1초 동안 녹음하면 용량은 얼마나 될까요?

11k * 8bit = 88kbit겠죠?

11KB 정도 하는 겁니다. ............제가 쓰고도 무슨 소린지 모르겠습니다. WAV->PCM은 나중에 시간이 나면 유틸을 만들던지 해서 소스까지 공개할 예정이니..... 이번에는 살려주세요... -_-

하여튼..... 연주하는 부분을 알아볼까요?

int play_pcm( int num )
{
	GpPcmPlay( (uword*)pcm[num].data, pcm[num].size, 0 );
	return pcm[num].size * 1000 / 11160;
}

예, 간단하죠? 여기서 리턴 값이 중요한데... 아, 11kHz가 11160...이라는군요. GP32 SDK 도큐멘트 참조한 겁니다 -_- 하여튼... 리턴 값이 현재 연주하는 사운드의 플레이 시간입니다. 단위는 msec죠. ( 즉, 1000이 1초입니다. ) 이제 이해가 되시죠? ( 날림이다. -_- )

그럼 이제 게임을 알아보죠.............

#include "nalim.h"

#define BLUE_UP		1
#define BLUE_DN		2
#define WHITE_UP	4
#define WHITE_DN	8

ubyte *palette = NULL;
ubyte *img_back = NULL;

int score, hp;
int draw_flag;

int input;

void draw()
{
	int blue_y, white_y;
	char temp_str[256];

	put_image( 0, 0, img_back, 0 );

	switch( draw_flag )
	{
		case 0:
			blue_y = 110;
			white_y = 110;
			if( input & BLUE_UP )	blue_y = 80;
			if( input & BLUE_DN )	blue_y = 140;
			if( input & WHITE_UP )	white_y = 80;
			if( input & WHITE_DN )	white_y = 140;
			draw_text( 100, blue_y,  "청", 1, 1 );
			draw_text( 200, white_y, "백", 2, 1 );
			break;
		case 1:
			draw_text( 140, 100, "게임 오버", 3, 1 );
			if( (int)( GpTickCountGet() / 500 ) % 2 )
			{
				draw_text( 100, 120, "PRESS START BUTTON", 3, 1 );
			}
			break;
	}

	gp_str_func.sprintf( temp_str, "점수 : %d", score );
	draw_text( 10, 10, temp_str, 3, 1 );

	gp_str_func.sprintf( temp_str, "남은 에너지-_- : %d", hp );
	draw_text( 10, 30, temp_str, 3, 1 );

	show();
}

void game()
{
	int cmd, check;
	int time, snd_time, wait = 1000;
	int temp;

	img_back = read_file( "gp:\gpmm\flag\back.img", NULL );

	randomize();

	score = 0;
	hp = 3;

	draw_flag = 0;

	play_pcm( 1 );

	input = 0;

	time = get_time();
	while( get_time() < time + 3000 )
	{
		draw();
	}

	while( 1 )
	{
		input = 0;

		cmd = random( 13 );
		switch( cmd )
		{
			case 0:		// blue up
				temp = random( 4 );
				snd_time = play_pcm( 5 + temp );
				check = BLUE_UP;
				break;
			case 1:		// blue down
				temp = random( 4 );
				snd_time = play_pcm( 9 + temp );
				check = BLUE_DN;
				break;
			case 2:		// blue not up
				snd_time = play_pcm( 13 );
				check = 0;
				break;
			case 3:		// blue not down
				snd_time = play_pcm( 14 );
				check = 0;
				break;
			case 4:		// white up
				temp = random( 4 );
				snd_time = play_pcm( 15 + temp );
				check = WHITE_UP;
				break;
			case 5:		// white down
				temp = random( 4 );
				snd_time = play_pcm( 19 + temp );
				check = WHITE_DN;
				break;
			case 6:		// white not up
				snd_time = play_pcm( 23 );
				check = 0;
				break;
			case 7:		// white not down
				snd_time = play_pcm( 24 );
				check = 0;
				break;
			case 8:		// both up
				snd_time = play_pcm( 25 );
				check = BLUE_UP | WHITE_UP;
				break;
			case 9:		// both down
				snd_time = play_pcm( 26 );
				check = BLUE_DN | WHITE_DN;
				break;
			case 10:	// both not up
				temp = random( 2 );
				snd_time = play_pcm( 27 + temp );
				check = 0;
				break;
			case 11:	// both not down
				temp = random( 2 );
				snd_time = play_pcm( 29 + temp );
				check = 0;
				break;
			case 12:	// no move
				temp = random( 2 );
				snd_time = play_pcm( 31 + temp );
				check = 0;
				break;
		}

		time = get_time();
		while( get_time() < time + snd_time + wait )
		{
			if( get_key() & GPC_VK_UP )	input |= BLUE_UP;
			if( get_key() & GPC_VK_DOWN )	input |= BLUE_DN;
			if( get_key() & GPC_VK_FA )	input |= WHITE_UP;
			if( get_key() & GPC_VK_FB )	input |= WHITE_DN;

			draw();
		}

		wait--;
		if( wait < 500 )	wait = 500;

		if( check == input )
		{
			score += 10;

			snd_time = play_pcm( 3 );
		}
		else
		{
			hp--;

			snd_time = play_pcm( 4 );
		}

		time = get_time();
		while( get_time() < time + snd_time )
		{
			draw();
		}

		if( hp <= 0 )
		{
			draw_flag = 1;

			play_pcm( 2 );

			while( 1 )
			{
				if( get_key() == GPC_VK_START )	break;

				draw();
			}
			break;
		}
	}

	gp_mem_func.free( img_back );
}

void title()
{
	int start = 0;

	img_back = read_file( "gp:\gpmm\flag\back.img", NULL );

	play_pcm( 0 );

	if( get_key() & GPC_VK_START )	start = 1;

	while( 1 )
	{
		if( get_key() & GPC_VK_START )
		{
			if( !start )	break;
		}
		else
		{
			start = 0;
		}

		put_image( 0, 0, img_back, 0 );

		draw_text( 120, 90, "청기백기 게임", 3, 1 );

		if( (int)( GpTickCountGet() / 500 ) % 2 )
		{
			draw_text( 30, 200, "PRESS START BUTTON", 2, 1 );
		}

		show();
	}

	gp_mem_func.free( img_back );
}

void init()
{
	nalim_init();

	palette = read_file( "gp:\gpmm\flag\flag.pal", NULL );
	set_palette( palette );
	gp_mem_func.free( palette );

	load_pcm( 0,  "gp:\gpmm\flag\title.pcm" );
	load_pcm( 1,  "gp:\gpmm\flag\start.pcm" );
	load_pcm( 2,  "gp:\gpmm\flag\gameover.pcm" );
	load_pcm( 3,  "gp:\gpmm\flag\ok.pcm" );
	load_pcm( 4,  "gp:\gpmm\flag\bad.pcm" );
	load_pcm( 5,  "gp:\gpmm\flag\b_up1.pcm" );
	load_pcm( 6,  "gp:\gpmm\flag\b_up2.pcm" );
	load_pcm( 7,  "gp:\gpmm\flag\b_up3.pcm" );
	load_pcm( 8,  "gp:\gpmm\flag\b_up4.pcm" );
	load_pcm( 9,  "gp:\gpmm\flag\b_dn1.pcm" );
	load_pcm( 10, "gp:\gpmm\flag\b_dn2.pcm" );
	load_pcm( 11, "gp:\gpmm\flag\b_dn3.pcm" );
	load_pcm( 12, "gp:\gpmm\flag\b_dn4.pcm" );
	load_pcm( 13, "gp:\gpmm\flag\b_n_up.pcm" );
	load_pcm( 14, "gp:\gpmm\flag\b_n_dn.pcm" );
	load_pcm( 15, "gp:\gpmm\flag\w_up1.pcm" );
	load_pcm( 16, "gp:\gpmm\flag\w_up2.pcm" );
	load_pcm( 17, "gp:\gpmm\flag\w_up3.pcm" );
	load_pcm( 18, "gp:\gpmm\flag\w_up4.pcm" );
	load_pcm( 19, "gp:\gpmm\flag\w_dn1.pcm" );
	load_pcm( 20, "gp:\gpmm\flag\w_dn2.pcm" );
	load_pcm( 21, "gp:\gpmm\flag\w_dn3.pcm" );
	load_pcm( 22, "gp:\gpmm\flag\w_dn4.pcm" );
	load_pcm( 23, "gp:\gpmm\flag\w_n_up.pcm" );
	load_pcm( 24, "gp:\gpmm\flag\w_n_dn.pcm" );
	load_pcm( 25, "gp:\gpmm\flag\2_up.pcm" );
	load_pcm( 26, "gp:\gpmm\flag\2_dn.pcm" );
	load_pcm( 27, "gp:\gpmm\flag\2_n_up1.pcm" );
	load_pcm( 28, "gp:\gpmm\flag\2_n_up2.pcm" );
	load_pcm( 29, "gp:\gpmm\flag\2_n_dn1.pcm" );
	load_pcm( 30, "gp:\gpmm\flag\2_n_dn2.pcm" );
	load_pcm( 31, "gp:\gpmm\flag\no_move1.pcm" );
	load_pcm( 32, "gp:\gpmm\flag\no_move2.pcm" );
}

void GpMain( void *arg )
{
	init();

	while( 1 )
	{
		title();

		game();
	}
}

자, 모두 외칩시다.

"존나 지저분하다!"

죄송합니다 -_- 하여튼... 뭐, 어쩌겠습니까.......

?GpMain부터 알아보죠.

void GpMain( void *arg )
{
	init();

	while( 1 )
	{
		title();

		game();
	}
}

예전에는 ?GpMain에 모든 걸 넣었었죠? 이번엔 다릅니다. 실제로 게임을 분리한 거죠. init는 이 게임 전반적인 Initialize 작업을 해줍니다. 그리고 타이틀과 게임을 무한 루프 돌리는 거죠. 만약 while( 1 )이 없다면 게임이 끝나면 바로 프로그램이 종료되죠. 하지만, 우리는 계속 반복을 할 겁니다. 게임 끝나면 타이틀이 다시 나오게... +_+ 그럼, 초기화 부분을 알아보죠~

void init()
{
	nalim_init();

	palette = read_file( "gp:\gpmm\flag\flag.pal", NULL );
	set_palette( palette );
	gp_mem_func.free( palette );

	load_pcm( 0,  "gp:\gpmm\flag\title.pcm" );
	...(중략)...
	load_pcm( 32, "gp:\gpmm\flag\no_move2.pcm" );
}

일단 nalim_init를 호출해서 GP32를 깨웁시다. 그 담에는 우리가 256 컬러를 쓰니까, 팔레트를 불러와서 세팅을 해주죠. 그 담에는 사운드를 불러옵니다. 여기서는 상당히 무식한 방법이 쓰였는데... 리스트 파일을 만들거나 리소스를 묶어서 쓰는 게 좋겠죠? 하여튼... 그렇다고 치고... ( 엄청 대충 넘어갑니다 -_- ) 타이틀 부분을 알아보죠.

void title()
{
	int start = 0;

	img_back = read_file( "gp:\gpmm\flag\back.img", NULL );

	play_pcm( 0 );

	if( get_key() & GPC_VK_START )	start = 1;

	while( 1 )
	{
		if( get_key() & GPC_VK_START )
		{
			if( !start )	break;
		}
		else
		{
			start = 0;
		}

		put_image( 0, 0, img_back, 0 );

		draw_text( 120, 90, "청기백기 게임", 3, 1 );

		if( (int)( GpTickCountGet() / 500 ) % 2 )
		{
			draw_text( 30, 200, "PRESS START BUTTON", 2, 1 );
		}

		show();
	}

	gp_mem_func.free( img_back );
}

먼저 배경을 읽어옵니다. 배경은 '초야'라는 일본의 H 애니에서 짤라왔습니다. 내용은 H가 분명한데... 자막이 없어서 내용은 잘 모르겠지만.. 나름대로 그림의 느낌이 좋아서.. 써봤습니다 -_-

자, 그리고 나서 0번 사운드를 출력하죠?

START가 처음 눌릴 수 있게 start 변수를 체크합니다. ( 전에 다룬 슈팅 게임에서 버튼 누르고 있기 방지 기법을 쓴 거죠 ) START가 눌리면 게임으로 넘어갈 수 있게 하고....

"PRESS START BUTTON"이 깜빡이는 부분에 대해서 설명을 드리면.. ?GpTickCountGet이란 함수....... 헉, 왜 이걸 썼지 -_- nalim.h에 보면 get_time이라고 선언이 되어있습니다. 뭐, 날림으로 작성하다 보니.. 그럴 수도 있는 거죠.

하여튼.. Tick Count를 얻어서... 이놈이 msec 단위니까... 500으로 나눠주죠. 그럼 0.5초 단위로 되겠죠? 그걸 %2 해주면 0.5초마다 0, 1로 값이 떨어집니다. 자, 그럼 그걸 가지고 글이 깜빡이게 하는 거죠. 쉽죠?

자자, 대충 대충 게임 쪽으로 넘어갑시다 -_-

게임을 구성할 요소를 알아보죠. 일단, 점수하고 에너지가 있으면 플레이어가 표현 되겠죠?

int score, hp;

이렇게 전역으로 때려줬습니다. 뭐, 전역으로 하기 싫으면... 다른 방법을 쓸 수도 있지만.. C++ 쓰는 것도 아니고.. 어짜피 막 짜는 건데.. 대충 하자...라고 생각해서 이렇게 막 했습니다. energy로 할까.. 뭐로 할까..하다가 무식하게 HP로 작성한 저 변수명 보십시오! +_+

게임에서 화면에 그려주는 건 따로 떼어냈습니다. 잘 떼어내고 싶으면 REFACTORING 같은 책을 봄이 좋겠지만... 뭐, 여담이었고.. 대충 했습니다. 예, 이 소스야말로 진정한 REFACTORING의 대상이죠 -_-

하여튼....

게임 중 화면에 뜨는 종류를 저는 크게 둘로 잡았습니다. 하나는 게임 중.. 하나는 게임 오버....

대충대충 넘어갑시다 -_-

 int draw_flag;

자, 이게 현재 그 상태(?)를 넣는 거죠. 0이면 게임 중... 1이면 게임 오버...

그리고 화면에 청기, 백기의 상태를 그려주려면.. 현재 사용자의 입력 상태를 알아야겠죠?

 int input;

으음.. 왜 달랑 변수 하나냐...라고 하실 수도 있을 텐데.. 그냥 비트를 켜는 방식으로 체크를 쓰려고 하기 때문입니다. #define BLUE_UP 1 #define BLUE_DN 2 #define WHITE_UP 4 #define WHITE_DN 8

잘 보시면 2진수로 1, 10, 100, 1000인 숫자들이죠? 이걸 input에 OR 해주면 바로 켜지죠.

blue_y = 110; white_y = 110; if( input & BLUE_UP ) blue_y = 80; if( input & BLUE_DN ) blue_y = 140; if( input & WHITE_UP ) white_y = 80; if( input & WHITE_DN ) white_y = 140; draw_text( 100, blue_y, "청", 1, 1 ); draw_text( 200, white_y, "백", 2, 1 );

그걸 이렇게 활용하는 겁니다. input에 따라서 Y 좌표를 바꿔주죠? ^^

자, 게임은 맨 처음에 점수는 0, 에너지는 3으로 시작합니다.

 score = 0;
 hp = 3;

게임 상태는 당연히 게임 중...이겠죠?

 draw_flag = 0;

게임 시작을 알리는 소리를 내고~

 play_pcm( 1 );

입력 상태를 초기화합니다. ( 아래에서 draw 함수를 쓰기 때문에 초기화를 해주죠 ^^ )

 input = 0;

그리고 나서 3초 동안 기다립니다. ( 소리가 끝나길 기다리며 유저들에게 여유를 주는 거겠죠? ^^ )

 time = get_time();
 while( get_time() < time + 3000 )
 {

draw();

 }

게임 메인 루프를 돌립니다.

 while( 1 )
 {

매번 입력 상태를 초기화해줘야 겠죠? ^^

input = 0;

어떤 명령을 내릴지 랜덤하게 고릅니다. 청기 올려, 내려, 올리지마, 내리지마... 등등... 총 13가지 패턴이 나오죠.

switch문을 보시면 뭐뭐인지 알 수 있을 겁니다. 사운드도 거기에 맞춰서 연주를 해주죠.

cmd = random( 13 ); switch( cmd ) { case 0: // blue up temp = random( 4 ); snd_time = play_pcm( 5 + temp ); check = BLUE_UP; break; case 1: // blue down temp = random( 4 ); snd_time = play_pcm( 9 + temp ); check = BLUE_DN; break; case 2: // blue not up snd_time = play_pcm( 13 ); check = 0; break; case 3: // blue not down snd_time = play_pcm( 14 ); check = 0; break; case 4: // white up temp = random( 4 ); snd_time = play_pcm( 15 + temp ); check = WHITE_UP; break; case 5: // white down temp = random( 4 ); snd_time = play_pcm( 19 + temp ); check = WHITE_DN; break; case 6: // white not up snd_time = play_pcm( 23 ); check = 0; break; case 7: // white not down snd_time = play_pcm( 24 ); check = 0; break; case 8: // both up snd_time = play_pcm( 25 ); check = BLUE_UP | WHITE_UP; break; case 9: // both down snd_time = play_pcm( 26 ); check = BLUE_DN | WHITE_DN; break; case 10: // both not up temp = random( 2 ); snd_time = play_pcm( 27 + temp ); check = 0; break; case 11: // both not down temp = random( 2 ); snd_time = play_pcm( 29 + temp ); check = 0; break; case 12: // no move temp = random( 2 ); snd_time = play_pcm( 31 + temp ); check = 0; break; }

자, 위에서 보면 snd_time에 연주 시간을 얻죠? 그럼 우리는 snd_time + wait만큼 입력할 기회를 줍니다. 즉, 소리가 나오는 도중에 해도 되고, 소리가 다 나오고 나서 wait만큼 더 여유가 있는 거죠. wait의 초기값은 1000, 즉 1초입니다.

		time = get_time();
		while( get_time() < time + snd_time + wait )
		{
			if( get_key() & GPC_VK_UP )	input |= BLUE_UP;
			if( get_key() & GPC_VK_DOWN )	input |= BLUE_DN;
			if( get_key() & GPC_VK_FA )	input |= WHITE_UP;
			if( get_key() & GPC_VK_FB )	input |= WHITE_DN;

			draw();
		}

자, 입력하는 거에 따라서 그림까지 보여주는 걸 보셨구요~ 이렇게 입력이 끝나면... wait를 줄여줍니다. 즉, 점점 여유식간이 줄어드는 거죠.

일단, 게임을 제가 제대로 하지 못해서 wait는 500까지만 내려가게 했습니다. 극악으로 하고 싶으시면 0으로 하셔도 됩니다. 즉, 소리가 끝나면 기회 박탈이라는 식으로... 미리 예언해서 하거나 끝남과 동시에 하거나.. 그렇게 하라는 거죠. 만약 -으로 세팅을 하신다면.. 예언해서 해야 하는 겁니다 -_-

wait--; if( wait < 500 ) wait = 500;

아까 위에서 명령에 따라 check를 세팅했죠? 간단히 check와 input이 같으면 정답...이라고 할 수 있습니다. 맞으면 10점 추가하고 축하해줍시다. 틀리면 에너지 깎고 틀렸다고 놀려주구요~

		if( check == input )
		{
			score += 10;

			snd_time = play_pcm( 3 );
		}
		else
		{
			hp--;

			snd_time = play_pcm( 4 );
		}

소리가 다 나올 때까지 기다리는 거죠~

time = get_time(); while( get_time() < time + snd_time ) { draw(); }

만약 에너지가 다 달았으면 끝냅니다. START 버튼을 누르면 종료를 해서 title로 돌아가게 하죠...

		if( hp <= 0 )
		{
			draw_flag = 1;

			play_pcm( 2 );

			while( 1 )
			{
				if( get_key() == GPC_VK_START )	break;

				draw();
			}
			break;
		}

모든 설명이 끝났습니다. 아주 날림이죠 -_- 소스를 몇번씩 반복해서 길이는 무진장 길지만요.. 쿨럭...

청기백기의 소스하고 FPK(GP32 프리런처용)는 제 홈피에 올려놨습니다. ( DATA 메뉴를 누르시면 "청기백기라고 우기는 녀석"이라고 있죠 -_- )

잘 만들어진 건 아닙니다만.. 그래도 점수하고 에너지가 존재하는 게임일 수도 있는 녀석이니.... 여러분도 게임이라고 우기는 녀석들을 만드시면서 자신감을 얻으시는게.. 쿨럭...

그냥.. 그런 겁니다.

아아, 정신 없어 -_-;;;;

다음에는 또 뭐를 써야 하지... 쿨럭..

이걸 꼭 써달라...라는 분은 제 홈피 방명록에 글 남겨주세요~ 홈피 주소는 http://www.ahastudio.com 잊지마세여 -_-

예, 그런 거죠....

그럼 이만..... (탕탕)

2002.05.28 아샬

지피껨 만들기로 돌아가기


제일 위로
최종 수정 일시: 06월 18일(2003년) 08:29 PM 편집 | 정보 | 차이 | 비슷한 페이지 DebugInfo
유용한 페이지들: 분류 분류 | 자유로운 연습장 SandBox | 무작위 페이지들 RandomPages | 인기있는 페이지들 MostPopular