English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이一章, 실험 소개
1.1 실험 내용
이 장 실험에서는 러시아 타일 주요 함수 설계를 통해 기본 기능을 완료하고 실행할 것입니다
1.2 실험 지식
윈도우 그리기
블록 클래스 설계
회전 알고리즘
이동, 제거 함수
1.3 실험 환경
xface 터미널
g++ 컴파일러
ncurses 라이브러리
1.4 프로그램 컴파일
컴파일 명령어에 추가해야 합니다 -l 옵션을 추가하여 ncurses 라이브러리를 포함합니다:
g++ main.c -l ncurses
1.5 프로그램 실행
./a.out
1.6 실행 결과
이二章, 실험 절차
2.1 헤더 파일
먼저 헤더 파일을 포함하고 교환 함수 및 무작위 수 함수를 정의합니다. 그런 다음 사용됩니다(교환 함수는 블록의 회전에 사용되고, 무작위 수는 블록의 형태를 설정에 사용됩니다)
#include <iostream> #include <sys/time.h> #include <sys/types.h> #include <stdlib.h> #include <ncurses.h> #include <unistd.h> /* a와 b를 교환합니다 */ void swap(int &a, int &b){ int t=a; a = b; b = t; } /* (min, max) 구간의 무작위 정수를 얻습니다 int getrand(int min, int max) { return(min+rand()%(max-min+1)); }
2.2 클래스 정의
프로그램 내용이 상대적으로 간단하므로, 여기서는 Piece 클래스를 정의했습니다
class Piece { public: int score; //점수 int shape; //현재 블록의 형태를 나타냅니다 int next_shape; //다음 블록의 형태를 나타냅니다 int head_x; //현재 블록의 첫 번째 box의 위치, 위치 표시 int head_y; int size_h; //현재 블록의 크기 int size_w; int next_size_h; //다음 블록의 크기 int next_size_w; int box_shape[4][4]; //현재 방块的 shape 배열 4x4 int next_box_shape[4][4]; //다음 방块的 shape 배열 4x4 int box_map[30][45]; //게임 패널 내의 각 box를 표시하기 위해 사용됩니다 bool game_over; //게임이 끝난 상태를 나타내는 레이블 public: void initial(); //초기화 함수 void set_shape(int &cshape, int box_shape[][4],int &size_w, int & size_h); //방块的 형상 설정 void score_next(); //다음 방块的 형상 및 점수를 표시합니다 void judge(); //층이 꽉 찬지 여부를 확인합니다 void move(); //이동 함수를 통해 ← → ↓로 제어합니다 void rotate(); //회전 함수 bool isaggin(); //다음 행동이 경계를 벗어나거나 중복되는지 확인합니다 bool exsqr(int row); //현재 행이 비어 있는지 확인합니다 };
2.3 방块的 형상 설정
이제 case 문을 통해 정의됩니다7형태의 방块的 형상을 설정하고, 다음 방块的 떨어지기 전에 호출하여 그 형상과 초기 위치를 설정해야 합니다
void Piece::set_shape(int &cshape, int shape[][4],int &size_w,int &size_h) { /*먼저 표현하기 위해 사용되는4x4배열을 0으로 초기화합니다*/ int i,j; for(i=0;i<4;i++); for(j=0;j<4;j++); shape[i][j]=0; /*설정7초기 형상을 설정하고 그들의 크기를 설정합니다*/ switch(cshape) { case 0: size_h=1; size_w=4; shape[0][0]=1; shape[0][1]=1; shape[0][2]=1; shape[0][3]=1; break; case 1: size_h=2; size_w=3; shape[0][0]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; break; case 2: size_h=2; size_w=3; shape[0][2]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; break; case 3: size_h=2; size_w=3; shape[0][1]=1; shape[0][2]=1; shape[1][0]=1; shape[1][1]=1; break; case 4: size_h=2; size_w=3; shape[0][0]=1; shape[0][1]=1; shape[1][1]=1; shape[1][2]=1; break; case 5: size_h=2; size_w=2; shape[0][0]=1; shape[0][1]=1; shape[1][0]=1; shape[1][1]=1; break; case 6: size_h=2; size_w=3; shape[0][1]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; break; } //형상을 설정한 후 방块的 시작 위치를 초기화합니다 head_x=game_win_width/2; head_y=1; //초기화되지 않은 상태에서 중복되면 게임이 끝났습니다. if(isaggin()) /* GAME OVER ! */ game_over=true; }
2.4 회전 함수
이제 사용하는 알고리즘은 매우 간단하며, 방块的 회전을 수행합니다. 행렬 회전과 유사하게, 먼저 shape 배열을 대각선 대칭화시키고, 그런 다음 좌우 대칭화하여 회전을 완료합니다. 회전 후 방块的 경계를 벗어나거나 중복되는지 확인해야 합니다. 그렇다면, 이번 회전을 취소합니다.
void Piece::rotate() { int temp[4][4]=0}; //临时变量 int temp_piece[4][4]=0}; //备份用的数组 int i,j,tmp_size_h,tmp_size_w; tmp_size_w=size_w; tmp_size_h=size_h; for(int i=0; i<4;i++); for(int j=0;j<4;j++); temp_piece[i][j]=box_shape[i][j]; //备份一下当前的方块,如果旋转失败则返回到当前的形状 for(i=0;i<4;i++); for(j=0;j<4;j++); temp[j][i]=box_shape[i][j]; //斜对角线对称 i=size_h; size_h=size_w; size_w=i; for(i=0;i<size_h;i++); for(j=0;j<size_w;j++); box_shape[i][size_w-1-j]=temp[i][j]; //左右对称 /*如果旋转以后重合,则返回到备份的数组形状*/ if(isaggin()){ for(int i=0; i<4;i++); for(int j=0;j<4;j++); box_shape[i][j]=temp_piece[i][j]; size_w=tmp_size_w; //记得size也要变回原来的size size_h=tmp_size_h; } /*如果旋转成功,那么在屏幕上进行显示*/ else{ for(int i=0; i<4;i++); for(int j=0;j<4;j++} if(temp_piece[i][j]==1} mvwaddch(game_win,head_y+i,head_x+j,' '); //移动到game_win窗口的某个坐标处打印字符 wrefresh(game_win); } } for(int i=0; i<size_h;i++); for(int j=0;j<size_w;j++} if(this->box_shape[i][j]==1} mvwaddch(game_win,head_y+i,head_x+j,'#'); wrefresh(game_win); } } } }
2.5 移动函数
如果玩家没有按下任何按键,方块需要慢速下落,所以我们不能因为等待按键输入而阻塞在 getch() ,这里用到了 select() 来取消阻塞。
/* 이 프로그램의 일부만 자르고 있으며, 구체적인 구현은 소스 코드를 참조하십시오. */ struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec= 500000; if (select(1, &set, NULL, NULL, &timeout) == 0)
timeout 就是我们最多等待按键的时间,这里设置了 500000us,超过这个时间就不再等待 getch() 的输入,直接进行下一步。
如果在 timeout 时间内检测到按键,则下面的 if 语句为真,得到输入的 key 值,通过判断不同的 key 值进行向左、右、下、旋转等操作。
if (FD_ISSET(0, &set))
while ((key = getch()) === -1) ;
왼쪽, 오른쪽, 아래로 이동하는 함수 처리 방식은 비슷하지만, 여기서는 아래로 이동하는 함수를 설명하겠습니다.
/* 이 프로그램의 일부만 자르고 있으며, 구체적인 구현은 소스 코드를 참조하십시오. */ /* 입력한 키가 ↓이면 */ if(key==KEY_DOWN){ head_y++; //블록의 y 좌표+1 if(isaggin()){ //가 맞춰지지 않거나 범위를 벗어나면 이번 이동을 취소 head_y--; /*멈춘 것처럼 되어 있다면, 맵에 해당하는 box를 사용되었음으로 설정하고,1을 의미하며, 0은 사용되지 않음을 의미 for(int i=0;i<size_h;i++); for(int j=0;j<size_w;j++); if(box_shape[i][j]==1); box_map[head_y+i][head_x+j]=1; score_next(); //점수와 다음 블록 표시 표시 } /*아래로 이동할 수 있다면, 현재 블록의 표시를 취소하고 한 행 아래로 이동하여 표시하며, 여기서 주의해야 할 것은 for 루프의 행이 아래에서 위로 순회해야 합니다. else{ for(int i=size_h-1; i>=0;i--); for(int j=0;j<size_w;j++} if(this->box_shape[i][j]==1} mvwaddch(game_win,head_y-1+i,head_x+j,' '); mvwaddch(game_win,head_y+i,head_x+j,'#'); } } wrefresh(game_win); }
2.6 반복 함수
이동하거나 회전한 후에도 점검해야 하는 함수이며, 함수가 참을 반환하면 행동할 수 없으며, 거짓을 반환하면 다음 단계로 이동할 수 있습니다.
bool Piece::isaggin(){ for(int i=0;i<size_h;i++); for(int j=0;j<size_w;j++} if(box_shape[i][j]==1} if(head_y+i > game_win_height-2); //아래로 범위를 벗어나 return true; if(head_x+j > game_win_width-2 || head_x+i-1<0) //좌우로 범위를 벗어나 return true; if(box_map[head_y+i][head_x+j]==1); //이미 사용된 box와 겹치 return true; } } return false; }
2.7 층이 가득 찬 함수
가장 중요한 기능 중 하나는 블록이 채워진 행을 제거하는 것이며, 블록이 아래로 이동하여 멈춘 후에는 항상�断정해야 합니다.
void Piece::judge(){ int i,j; int line=0; //층이 가득 찬 행 수를 기록하기 위해 사용 bool full; for(i=1;i<game_win_height-1;i++} //경계선 제외 full=true; for(j=1;j<game_win_width-1;j++} if(box_map[i][j]==0) //사용되지 않은 box가 존재 full=false; //이 층이 가득 찬 것 아님을 의미 } if(full){ //이 층이 가득 찬 경우 line++; //행이 가득 찬+1 score+=50; //점수 추가~ for(j=1;j<game_win_width-1;j++); box_map[i][j]=0; //그 층을 비우고(미사용으로 표시) } } /*위의 조건을 만족시키면, line의 값을 확인해야 합니다. 0이 아닌 경우, 해당 층이 꽉 차서 제거가 필요합니다.*/ if(line!=0){ for(i=game_win_height-2;i>=2;i--} int s=i; if(exsqr(i)==0){ while(s>1 && exsqr(--s)==0); //빈 블록이 있는 행을 찾아 내림 for(j=1;j<game_win_width-1;j++} box_map[i][j]=box_map[s][j]; //상위 내림 box_map[s][j]=0; //상위 비우기 } } } /*마크를 비우고 이동한 후, 스크린을 새로고침하여 game_win을 다시 출력해야 합니다.*/ for(int i=1;i<game_win_height-1;i++); for(int j=1;j<game_win_width-1;j++} if(box_map[i][j]==1} mvwaddch(game_win,i,j,'#'); wrefresh(game_win); } else{ mvwaddch(game_win,i,j,' '); wrefresh(game_win); } } } }
3. 실험 요약
여기서 몇 가지 중요한 함수에 대한 소개가 끝납니다. 이 함수들의 기능을 이해하고 구현한 후, 소스 코드를 참조하여 다른 함수와 main 함수를 보완하여 실행할 수 있습니다!当然, 俄罗斯方块의 구현 방법은 많으며, 각 사람의 사고와 방법이 다를 수 있습니다. 아마도 당신의 러시아方块은 더 간결하고 더 원활할 것입니다! 즐겨 주세요!:)
언급: 이 문서의 내용은 인터넷에서 가져왔으며, 저작권은 원저자에게 있으며, 인터넷 사용자가 자발적으로 기여하고 업로드한 내용입니다. 이 사이트는 저작권을 소유하지 않으며, 인공적으로 편집한 것도 아니며, 관련 법적 책임도 부담하지 않습니다. 저작권 침해가 의심되는 내용이 있으면, notice#w로 이메일을 보내 주세요.3codebox.com에 신고를 위해 #을 @으로 변경하고, 관련 증거를 제공해 주세요. 신고가 확인되면, 사이트는 즉시 저작권 침해 내용을 삭제합니다.