U E D R , A S I H C RSS

데블스캠프2005/게임만들기/제작과정예제

데블스캠프2005/게임만들기/제작과정예제


화면에 블럭 출력하기


화면에 블럭을 출력해보자.
start()안에보면, 다양한 색상들이 적혀있는color라는 이름의 변수가 보일 것이다.
그앞의 주석이 가르키는데로, 1~10까지의 색상을 지정할 수 있다.
그밑에 보이는 block변수가 블럭을 기억하게 된다.
~cpp 
block[0][0]=1;
이라고 적고 실행해보자. 아래와 같은 결과가 나올것이다.

test01.jpg

~cpp 
block[x좌표][y좌표]=블럭의 종류;
와 같은 형식으로 출력할 블럭을 지정하며, 0은 블럭이 없는 것이고 그 이외의 값은 해당하는 색상의 블록이 들어가게 된다.
block_next변수는, 메인창 옆의 next아래에 블럭을 출력하며 가로3세로4의 크기를 출력할 수 있다.
지정형식은 위와 동일하다.
~cpp 
block_next[x좌표][y좌표]=블럭의 종류;


할일 정하기

보다 쉽게 일을 진행하기 위해서 할일을 정해두어야 할 필요가 있다.
테트리스를 떠올려보고, 어떤 과정을 거쳐서만들지 생각해보자.

실습예제
~cpp 
1.출력할 블럭의 모양을 결정한다.
2.랜덤으로 블럭을 생성한 후 next창에 출력한다.
3.next창에 출력된 블럭을 가져온뒤 메인창에 출력한다.
4.블럭을 움직인다.
5.블럭을 회전시킨다.
6.한줄이 다 찼을때, 블럭을 삭제한다.
7.점수와 스테이지를 변화시킨다.

위의 예제 순서를 따라 진행하면 테트리스를 만들 수 있다.
하지만, 막히는 부분이 생기게 되면 아래를 참조하길 바란다.


출력할 블럭의 모양 정하기


모양을 정하는 방법에는 여러가지가 있다.
n칸 띄우고, n칸만큼 블럭을 채우는 식으로,
예를 들어 T는(0,3)(1,1)(1,1)이렇게 표시할 수 있다. 방향에 따라, 가로가 아닌 세로부터 시작하거나 해서 바꿔줄 수 있다.
그리고 다른 방법으로는 그냥 편하게 블럭의 크기만큼 배열을 지정해서 있는곳에는 숫자를 넣고 아닌곳에는 0을 넣는 방식이 있다.

예제) 일자모양을 배열으로 표시하는 방법. 소스가 길어지므로, 헤더파일에 넣기를 권장한다.
~cpp 
	{{
	{0,0,0,0},
	{2,2,2,2},
	{0,0,0,0},
	{0,0,0,0}}
	,{
	{0,0,2,0},
	{0,0,2,0},
	{0,0,2,0},
	{0,0,2,0}}
	,{
	{0,0,0,0},
	{2,2,2,2},
	{0,0,0,0},
	{0,0,0,0}}
	,{
	{0,0,2,0},
	{0,0,2,0},
	{0,0,2,0},
	{0,0,2,0}}
},

랜덤으로 블럭을 생성한 후 next창에 출력하기


우선 랜덤하게 블럭을 생성하기 위해서 start()함수에 srand((int)time(NULL));라고 적어서, 시간을 이용해서 랜덤하게 나오게 한 다음,
블럭을 랜덤하게 호출하는 함수를 만들자. 그리고 그 안에 rand()함수를 사용해서, 임의의 숫자를 얻은후 그 숫자에 맞추어 블럭을 지정하고
next변수에 출력되게 하면 된다.
그리고 출력될 변수를 기억하게 하기 위해서 다음에 나올 블럭을 기억하는 전역변수를 만들고, 거기에 값을 저장시키자.

테스트를 위해서는 key_Right()와 같은 변수안에 넣고, 해당하는 키를 눌러보면 실행이 된다.

test02.jpg

예제)
~cpp 
	int block_id;
	block_id=rand()%7;
	for (register int i=0;i<3;++i)
		for (register int j=0; j<4; ++j)
			block_next[i][j]=block_type[block_id][0][i][j];
	prv_block=block_id; //전역변수에 저장.



생성된 블럭을 메인창에 소환하기


정해진 블럭을 메인창에 소환해 보자. 메인창의 맨 위의 가운데쯤이 가장 적당할 것이다.
또 소환되는 순간, 그 위치에 블럭이 있다면, 게임은 종료될 것이다.
소환된 후, 진행을 위해 소환된 위치의 x,y값을 기억하는 전역변수를 만들고 소환하면 된다.
그리고, 현제 블럭의 아이디를 기억할 전역변수도 만들어서 저장하자.
소환하는 알고리즘은 위에서 next창에 불러오는것과 같은 알고리즘 이다.

테스트를 위해서는 key_Left()와 같은 변수안에 넣고, 랜덤으로 만드는 함수를 호출한후, 호출해 보면 된다.

예제)
~cpp 
	for (register int i=0;i<3; ++i)
	{
		for (register int j=0; j<4; ++j)
		{
			if (0!=block_type[prv_block][0][i][j])
			{
				if (0==block[3+i][16+j])
					block[3+i][16+j]=block_type[prv_block][0][i][j];
				else
					game_end();
			}
		}
	}
	x=3;  //전역변수 x
	y=16; //전역변수 y
	shape=0; //전역변수 현재 블록의 회전정도
	now_block=prv_block; //전역변수 현재 블럭의 ID

test03.jpg


생성된 블럭을 움직이게 하기


소환된 블럭을 움직이게 해보자. 움직이게 하는데는 여러가지 알고리즘이 있지만, 쉬운 방법은 블럭이 내려가면 색깔을 바꾸고, 색깔이 있는 블럭만 움직이게 하는 방법이 있다. 두번째는, 블럭의 끝을 계산해서 그 부분이 닿게되면 멈추게 하는 방법이 있다. 후자가 연산이 더 복잡하나, 좀더 다양한 색상을 볼 수 있다. 움직일 때에는, 좌우의 벽과 다른 블럭을 고려해서 움직여야 한다.

다 만들어 진 후, 왼쪽, 오른쪽 방향키에 알맞은 인자를 넣어서 함수를 넣고, now_time()에 블럭을 아래로 내리는 함수를 호출하도록 하자.

test04.jpg


예제) 두번째 방법을 이용한 모습중 하나. -1은 왼쪽 0은 아래, 1은 오른쪽으로 움직인다는 뜻으로 인자를 넘겨받는다.
리팩토링이 많이 필요한 모습이다.


~cpp 
int move_block(int where)// -1좌 0아래  1우
{
	int temp_x=x+where;
	int temp_y=y-(where+1)%2;
	int temp_block[4][4]={0,};
	int ground[4]={0,};
	int side[2][4]={0,};
	int swit[2][4]={0,};
         
         // 블럭의 끝을 체크한다.         

	for (register int i=0;i<4; ++i)
	{
		for (register int j=0; j<4; ++j)
		{
			if (0!=block_type[now_block][shape][i][j])
			{
				if (i>=side[0][j] && 0==swit[1][j])
				{
					side[0][j]=i;
					swit[1][j]=1;
				}
				side[1][j]=i;
				if (j>=ground[i] && 0==swit[0][i])
				{
					ground[i]=j;
					swit[0][i]=1;
				}
			}
		}
	}

         // 블럭이 움직일 수 있는가를 판단한다. 못 움직일 경우에는 1을 리턴하고 종료한다.

	for (i=0;i<4; ++i)
	{
		for (register int j=0; j<4; ++j)
		{
			if (0!=block_type[now_block][shape][i][j])
			{
				if (temp_x+i<0 || temp_x+i>9 )
					return 1;
				else if (temp_y+j<0 || temp_y+j>19 )
					return 1;
				else if (0!=block[temp_x+i][temp_y+j])
				{
					if (ground[i]+1>j && where==0)
						return 1;
					else if (side[0][j]+1>i && where==-1)
						return 1;
					else if (side[1][j]-1<i && where==1)
						return 1;
				}
			}
		}
	}

         // 이전의 블럭의 이미지를 제거한다.

	for (i=0;i<4; ++i)
		for (register int j=0; j<4; ++j)
			if (0!=block_type[now_block][shape][i][j])
				block[x+i][y+j]=0;

         // 새로운 블럭을 그린다.

	for (i=0;i<4; ++i)
		for (register int j=0; j<4; ++j)
			if (0!=block_type[now_block][shape][i][j])
				block[temp_x+i][temp_y+j]=block_type[now_block][shape][i][j];
	x=temp_x;y=temp_y;
	return 0;
}


블럭을 회전하게 하기


블럭을 회전하기 위해서, 해야할 일은, 좌 우측으로 충분한 공간이 있는지를 보고, 벽이 막고 있으면 움직여준 뒤에 공간을 확보해서 회전하게 하는 것이다. 이동보다는 간편한 편이며, 전역변수에 움직인 정도를 기억시켜서, 움직일 수 있도록 해야 한다.

예제)

~cpp 
	int temp_x=x;
	int temp_y=y;
	int temp_shape=(shape+1)%4;
	int crash=2;
	int temp_block[4][4]={0,};

         //블럭이 벽에 닿는지, 충분한 공간이 있는지를 확인한다.

	while (2==crash)
	{
		for (register int i=0;i<4; ++i)
			for (register int j=0; j<4; ++j)
				temp_block[i][j]=0;
		crash=0;
		for (i=0;i<4; ++i)
			for (register int j=0; j<4; ++j)
				if (0!=block_type[now_block][shape][i][j]&& 0<=i+x-temp_x && 3>=i+x-temp_x )
					temp_block[i+x-temp_x][j]=1;
		for (i=0;i<4; ++i)
		{
			for (register int j=0; j<4; ++j)
			{
				if (0!=block_type[now_block][temp_shape][i][j])
				{
					if (temp_x+i<0)
					{
						temp_x+=1;crash=2;
					}
					else if (temp_x+i>9 )
					{
						temp_x-=1;crash=2;
					}
					else if (0!=block[temp_x+i][temp_y+j] && 1!=temp_block[i][j])
						crash=1;
				}
			}
		}
	}

         //블럭이 회전할 공간이 있으면, 이전의 블럭을 삭제한다.

	if (1==crash)
		return 0;
	for (register int i=0;i<4; ++i)
		for (register int j=0; j<4; ++j)
			if (0!=block_type[now_block][shape][i][j])
				block[x+i][y+j]=0;

         //새로운 블럭을 그려넣는다.

	for (i=0;i<4; ++i)
		for (register int j=0; j<4; ++j)
			if (0!=block_type[now_block][temp_shape][i][j])
				block[temp_x+i][temp_y+j]=block_type[now_block][temp_shape][i][j];

         //변화된 값을 저장한다.

	x=temp_x;y=temp_y;shape=temp_shape;
	return 0;


블럭을 삭제하기


블럭이 내려오게 되어, 한줄이 완성되면 삭제되어야 한다. 또한, 삭제된후 그 윗줄부터는 한칸씩 아래로 내려져야 한다.
일반적으로 for문을 이용해서 한줄을 검색할 수 있으나, 같은 내려온 블럭이 같은색으로 처리되는 경우는, 더해서 숫자가 일치하는지를 볼 수도 있다.

예제)
~cpp 
void delete_block()
{
	for (register int line=y; line<y+4; ++line)
		for (register int i=0; i<10; ++i)
			if (0==block[i][line])
				i=10;   //빈 블럭이 있으면, 그줄의 검사를 끝낸다.
			else
				if (9==i) //블럭이 다 차있을 경우에는 한줄을 삭제한다.
				{
					for (register int k=0; k<10; ++k)
						block[k][line]=0;
					for (k=line; k<19; ++k) //블럭을 한줄씩 내린다.
						for (register int l=0; l<10; ++l)
							block[l][k]=block[l][k+1];
					for (k=0; k<10; ++k)
						block[k][19]=0;
					--line;
				}
}


점수와 스테이지 변환


점수는 블럭이 내려올때, 삭제될때 등으로 증가되는 경우를 정하고, 그 경우마다 알맞은 값을 증가시켜 주면된다.
그리고 그 결과 점수가 일정치에 달하면 스테이지를 바꾸고, time_delay 변수의 값을 감소시켜 난이도를 증가시키면 된다.

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:28:59
Processing time 0.0220 sec