English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

C 사용++로시아方块를 단계별로 구현(계속)

이一章, 실험 소개

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에 신고를 위해 #을 @으로 변경하고, 관련 증거를 제공해 주세요. 신고가 확인되면, 사이트는 즉시 저작권 침해 내용을 삭제합니다.

추천 합니다