使用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;  //当前方块的size
  int size_w;

  int next_size_h;  //下一个方块的size
  int next_size_w;

  int box_shape[4][4]; //当前方块的shpe数组 4x4
  int next_box_shape[4][4];  //下一个方块的shpe数组 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种初始形状并设置它们的size*/
 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);
    }
   }
 }
}

三、实验总结

到这里几个关键函数的介绍也就完成了,搞明白这些函数的功能并实现,再参考源码补全其他函数以及main函数就可以运行啦!当然俄罗斯方块的实现方法还有很多,每个人的思路和方法可能会不一样,或许你写出来的俄罗斯方块更简洁、更流畅! Enjoy it !:)

(0)

相关推荐

  • Linux下用C++实现俄罗斯方块

    本文实例为大家分享了C++实现俄罗斯方块游戏代码,供大家参考,具体内容如下 1.block.c #include <stdio.h> #include <termios.h> #include <unistd.h> #include <stdlib.h> #include <setjmp.h> #include <sys/time.h> #include <string.h> #include "block.h&

  • 使用C++一步步实现俄罗斯方块

    一.实验介绍 1.1 实验内容 本节实验我们进行设计俄罗斯方块前的思路分析,以及介绍ncurses 库的使用方法. 1.2 实验知识点 C++ 编程基础 ncurses 库的使用 俄罗斯方块逻辑设计 1.3 实验环境 xface 终端 g++ 编译器 ncurses 库 1.4 适合人群 本课程难度一般,适合有 C++ 编程基础,对游戏设计.逻辑分析感兴趣的同学. 1.5 代码获取 git clone https://github.com/Gamerchen/game_zero.git 二.开发

  • C++制作俄罗斯方块

    缘起: 在玩Codeblocks自带的俄罗斯方块时觉得不错,然而有时间限制.所以想自己再写一个. 程序效果: 主要内容: 程序中有一个board数组,其中有要显示的部分,也有不显示的部分,不显示的部分都存储1. 如下图: shape采用4*4数组(shape)保存.如: 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 另外用变量row和column保存shape数组左上角在board中的位置. 每次下落或左右移动,先对row和column做出改变,然后检测当前row和column

  • C++俄罗斯方块游戏 无需图形库的俄罗斯方块

    本文实例为大家分享了C++俄罗斯方块游戏的具体实现代码,供大家参考,具体内容如下. #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<time.h> #include<conio.h> #define MOD 28 #define SIZE_N 19 #define SIZE_M 12 int cur_x,cur_y; int score,mark,next,map

  • 使用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 头文件 首先包含头文件以及定义一个交换函数和随机数函数,后面用到(交

  • JS+Canvas实现的俄罗斯方块游戏完整实例

    本文实例讲述了JS+Canvas实现的俄罗斯方块游戏.分享给大家供大家参考,具体如下: 试玩(没有考虑兼容低版本浏览器): ********************************************************************** 9月3日更新: 修复了隐藏的比较深的BUG 加上暂停.再来一次功能 速度随分数增高而递减 添加log日志 ****************************************************************

  • 一步步打造简单的MVC电商网站BooksStore(1)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore <一步步打造一个简单的 MVC 电商网站 - BooksStore(一)> <一步步打造一个简单的 MVC 电商网站 - BooksStore(二)> <一步步打造一个简单的 MVC 电商网站 - BooksStore(三)> <一步步打造一个简单的 MVC 电商网站 -

  • python实现简单俄罗斯方块

    本文实例为大家分享了python实现俄罗斯方块的具体代码,供大家参考,具体内容如下 # teris.py # A module for game teris. # By programmer FYJ from tkinter import * from time import sleep from random import * from tkinter import messagebox class Teris: def __init__(self): #方块颜色列表 self.color =

  • Java实现简易俄罗斯方块

    本文实例为大家分享了Java实现简易俄罗斯方块的具体代码,供大家参考,具体内容如下 一.将对象抽象为类 首先考虑俄罗斯方块游戏中含有哪些具体的对象,对象中含有哪些具体属性和方法,然后用代码来实现. 建立如下类: Cell类:代表最小的方格单位,构成7种图形的最基本图形. 含有row(行号),col(列号),image(对应的图片)属性, 含有left(左移),right(右移),drop(下落)方法. Tetromino类:代表由4个最小方格构成的7种图形的合集. 含有cells(四个方块)属性

  • CocosCreator Typescript制作俄罗斯方块游戏

    目录 1.引言 2.需要解决的几个关键问题 3.写在最后 1.引言 最近开始学cocos,学完Typescript语法之后,跑去看cocos的官方文档,捣鼓了几天,写了一个非常简单的贪吃蛇,甚至连像样的碰撞检测也没有,自觉无趣,就荒废了一段时间.这几个星期我又重拾了cocos,就有了实现俄罗斯方块的想法.一开始我想着上网找找资料,发现关于cocos开发俄罗斯方块的文章几乎寥寥无几(也有可能是我找的方法不对),更头痛的是,我找到的仅有几个分享文章的代码注释比较少,也可能是我的理解能力不行,后来花了

  • 利用Jetpack Compose实现经典俄罗斯方块游戏

    目录 可组合函数 游戏机身 - TetrisBody 游戏按钮 - TetrisButton 游戏屏幕 - TetrisScreen 调度器 - TetrisViewModel 项目地址 你的童年是否有俄罗斯方块呢,本文就来介绍如何通过 Jetpack Compose 实现一个俄罗斯方块 ~~ 先看下效果图,功能还是挺完善的 就我自己的体验来说,使用 Compose 开发的应用我感受不到和 Android 原生开发之间有什么性能差异,但 Compose 在开发难度上会低很多 Google 官网上

  • JavaScript实现简洁的俄罗斯方块完整实例

    本文实例讲述了JavaScript实现简洁的俄罗斯方块.分享给大家供大家参考,具体如下: 先来看看运行效果图: 完整实例代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>俄罗斯方块</title> <style ty

  • 用Python编写一个简单的俄罗斯方块游戏的教程

    俄罗斯方块游戏,使用Python实现,总共有350+行代码,实现了俄罗斯方块游戏的基本功能,同时会记录所花费时间,消去的总行数,所得的总分,还包括一个排行榜,可以查看最高记录. 排行榜中包含一系列的统计功能,如单位时间消去的行数,单位时间得分等. 附源码: from Tkinter import * from tkMessageBox import * import random import time #俄罗斯方块界面的高度 HEIGHT = 18 #俄罗斯方块界面的宽度 WIDTH = 10

随机推荐