我罗斯方块最终篇(Player类、Game类)

|--------------------项目GitHub地址--------------------|

我负责的部分

  • player类的完成
  • game类的完成
  • player类的调试
  • game类的调试

代码要点

Player类

  • 玩家地图map

    • 碰撞检测:

      bool Player::detectCollision(Block block, int x, int y) {
      //发生碰撞返回false
      //未发生返回true
      for(int i = 0; i < 4; i++) {
      for(int j = 0; j < 4; j++) {
      // 如果检测到block中该位置为1的话
      // 先检测这个位置是否越界
      // 再检测这个位置对应的map位置是否也为1
      if(block.block[i][j]==1) {
      if(x + i >= 20 || y + j < 0 || y + j >= 10) {
      return false;
      }
      else if(map[x+i][y+j]==1 && x + i >=0) {
      return false;
      }
      }
      }
      }
      return true;
      }
  • 玩家方块管理

    • 旋转:利用tempBlock标记,如果旋转后不会碰撞,则保持不变,如果旋转后碰撞则将tempBlock赋值回去

    • 左移、右移:利用碰撞检测去检测移动后pos定位点,实现同旋转。

    • 下移:检测同左移右移,如果发生碰撞就执行map的更新操作,如果发生碰撞需返回true,代表发生碰撞

      for(int i = 0; i < 4; i++) {
      for(int j = 0; j < 4; j++) {
      if(posX + i >= 0 && posY + j >= 0 && posX + i < 20 && posY + j < 10 && blockNow.block[i][j] == 1) {
      map[posX+i][posY+j]=1;
      }
      }
      }
  • 管理用户信息如名字,得分

  • 检测消行:检测每一行,如果满足消行,则将该行上面方块向下移动一格,在结尾返回消去的行数,方便双人模式下此消彼长

    int Player::detectReductsion() {
    // 记录消去的行数
    int count = 0;
    for(int i = 0; i < 20; i++) {
    // 记录单行的方块个数
    int tempcount = 0;
    for(int j = 0; j < 10; j++) {
    if(map[i][j] == 1) {
    tempcount++;
    }
    }
    if(tempcount >= 10) {
    // 增加分数
    point+=10;
    // 增加消除行数
    count++;
    // 移动上面的行
    for(int k=i;k>=1;k--) {
    for(int j=0;j<10;j++) {
    map[k][j]=map[k-1][j];
    }
    }
    }
    }
    return count;
    }
  • 方块增加:接受一个int类型传参,利用for循环每次在底部增加一行,在增加前先检测map顶部,是否游戏结束

    bool Player::addBlock(int num) {
    for(int i=0;i<num;i++) {
    for(int t=0;t<10;t++) {
    if(map[0][t]>0) {
    fail = true;
    return false;
    }
    }
    // 移动下面行
    for(int m=0; m<19; m++) {
    for(int n=0; n<10; n++) {
    map[m][n] = map[m+1][n];
    }
    }
    // 底部清零
    for(int j=0; j<10; j++) {
    map[19][j] = 0;
    }
    // 底部随机增加 for(int j=0; j<10; j++) {
    map[19][rand()%10]++;
    }
    for(int j=0; j<10; j++) {
    if(map[19][j]) {
    map[19][j]=1;
    }
    }
    }
    return true;
    }
  • 玩家状态更新

    • 生成新方块
    • 玩家恢复数据到游戏开始前

Game类

  • 私有成员:itfs -> Interface类的实例化,用于Game的渲染

  • 游戏控制:控制游戏进程,包括模式选择,名字输入,游戏结束是否重新开始

    • 游戏模式选择

      int Game::selectGameType() {
      // 渲染
      itfs.selectPart();
      itfs.selectKey1();
      int type=1;
      while(1) {
      if(_kbhit()){
      int key=_getch();
      if(key==72&&type!=1){
      itfs.selectKey1();
      type=1;
      }
      if(key==80&&type!=2){
      itfs.selectKey2();
      type=2;
      }
      if(key==13){
      return type;
      }
      }
      }
      }
  • 单人模式:速度控制,每秒检测50次键盘输入

    // speed = 50
    void Game::onePlayer(Player player) {
    // 页面渲染
    while(1) {
    if(_kbhit()) {
    int key=_getch();
    if(key==115) {
    // S键
    if(方块下落) {
    if(检测消行) {
    // 渲染失败页面
    // 返回
    }
    // 更新方块和map
    }
    }
    else if(key==119) {
    // W键
    // 旋转
    }
    else if(key==97) {
    // A键
    // 左移
    }
    else if(key==100) {
    // D键
    // 右移
    }
    else if(key == 32) {
    // 暂停处理
    }
    if(有消行) {
    // 清除map区域
    // 更新方块
    // 更新map
    // 更新分数
    }
    else {
    // 更新方块
    }
    }
    Sleep(20);
    if(--temptime == 0) {
    if(方块下落) {
    if(检测消行) {
    // 渲染失败页面
    // 返回
    }
    // 更新方块和map
    }
    if(有消行) {
    // 清除map区域
    // 更新方块
    // 更新map
    // 更新分数
    }
    else {
    // 更新方块
    }
    temptime = speed;
    }
    }
    }
  • 双人模式:参考单人模式代码,增加在检测消行处增加方块增加功能

    int add1 = player1.detectReductsion();
    int add2 = player2.detectReductsion();
    // 如果有消行,此消彼长,检测玩家2是否失败
    if(add1) {
    if(!player2.addBlock(add1)) {
    // 失败渲染
    itfs.gameResult(player1.getName(), player1.getPoint(), 1);
    return;
    }
    itfs.printPointPlayer1(player1.getPoint());
    }
    // 如果没有,正常渲染玩家1的方块
    else {
    itfs.deleteBlock1();
    itfs.drawNowBlock1(player1.getNowBlock(), player1.getX(), player1.getY());
    itfs.refreshBlock1(player1.getX(), player1.getY(), player1.getNowBlock());
    }
    // 如果有消行,此消彼长,检测玩家1是否失败
    if(add2) {
    if(!player1.addBlock(add2)) {
    // 失败渲染
    itfs.gameResult(player2.getName(), player2.getPoint(), 1);
    return;
    }
    itfs.printPointPlayer2(player2.getPoint());
    }
    // 如果没有,正常渲染玩家2的方块
    else {
    itfs.deleteBlock2();
    itfs.drawNowBlock2(player2.getNowBlock(), player2.getX(), player2.getY());
    itfs.refreshBlock2(player2.getX(), player2.getY(), player2.getNowBlock());
    }
    // 如果任何一个人有消行就重新渲染map
    if(add1 != 0 || add2 != 0) {
    itfs.clearMap1(player1.map);
    itfs.printMap1(player1.map);
    itfs.drawNowBlock1(player1.getNowBlock(), player1.getX(), player1.getY());
    itfs.clearMap2(player2.map);
    itfs.printMap2(player2.map);
    itfs.drawNowBlock2(player2.getNowBlock(), player2.getX(), player2.getY());
    }

遇到的问题

头文件包含问题

由图可见,block被重复包含,所以在编译时会报错,需要用到预编译知识

在Block处定义_BLOCK,用于判断是否已经包含Block类

/*-----------block.h-----------*/
#define _BLOCK /*-----------player.h----------*/
#ifndef _BLOCK
#include "block.h"
#endif /*----------interface.h--------*/
#ifndef _BLOCK
#include"block.h"
#endif

这样不会导致Block类在多处被定义

数组越界问题

在完成检测碰撞的测试时,在一个不起眼的地方数组发生了越界。我请求了团队的另外两个人一起查看,由于其中一个人使用的是32位虚拟机,所以在编译时需加上-m32参数。然而在我这运行程序会出现问题,但是在32位虚拟机上却可以正常运行。这一结果导致思考方向偏离了原先正确的思路,开始相信自己代码没有存在问题,经过一段时间后,最终发现问题在于数组越界,但是这也值得思考32位和64位电脑对于数组越界的检查上的差异。

功能重复实现

在两个多星期之前,尝试过完成检测游戏是否失败的功能,但是在中间一段时间没有去处理该文件,后来对于功能的实现上的思路转变,导致渐渐淡忘这段不起眼的代码,再后来,将代码拼凑起来后,出现问题,却迟迟找不到错误,花费大量时间,最后通过不断测试输出,找到bug。

关于渲染页面闪烁的问题

在每次方块移动,消行,方块碰撞之后都需要渲染新的页面,开始采用的较为简单的,将页面全部清除,再重新渲染,但是会造成页面闪烁的问题,原因是由于清除的打印中间短暂的间隔,使得看上去好像屏幕在闪烁。为了解决这个问题,和负责渲染部分的队友讨论过后,认为只有在消行情况下需要重新渲染整个页面,其他情况下则可部分渲染,只对原先页面的的一小部分改动的地方进行清除和重新渲染,这样可以解决屏幕闪烁的问题,提高了用户的体验。

总结收获

  1. 这次作业的代码量较大,文件多,需要测试的地方也多。在完成这次作业的过程中,感觉自己对出现的bug的处理能力得到提高,在多文件,代码量巨大的情况下,对功能的实现需要构造清晰的框架,否则容易乱,我觉我在此方面得到了一定的锻炼。
  2. 这次作业适合另外两位小伙伴一起完成的,虽然先前有过合作开发的经验,到时在这次作业中是首次利用到GitHub这个平台来实现代码的综合管理,学习到了很多先前没有接触过的关于git的知识。在完成作业的过程中,和两位小伙伴之间的交流沟通是必不可少的,在对待问题的讨论和思考上,我见证了我们团队从开始的少言少语到后来的滔滔不绝,这中间的过程是渐渐磨合出来的,对于开发正需要这样的气氛。我认为这锻炼了我和团队沟通能力,对于整个团队来说,我们之间的默契都得到了提升。
  3. 对于编程工具vscode的使用更加熟练,在完成作业的过程中尝试了使用vscode自带的终端以及git工具,极大的方便了在完成作业的过程中的效率,其中充分的利用了vscode结合git的文档对比功能,查看另外两位小伙伴他们在解决问题时的思路和以及他们的实现代码,从中学习他人的优点。
  4. 对象面向对象的理解加深了,如果采用面向过程的方式,那么这个程序最后的代码一定会很长且不好理解,拆分成类之后,既方便了分工合作,也使得整体思路清晰,方向确定。

仍然存在的问题

  1. 关于双人模式下,键盘输入采用_kbhit()函数监听会导致按键发生冲突,当一个人按住键盘不动时,两个人的按键输入都会受到影响,目前还没有较好的解决办法。

    思路:多线程、更好的输入函数

  2. 代码还不够简洁,关于game类还可以将一些功能进行拆分;在游戏重新开始的实现上采用了goto语句,不是很好。

  3. 在解决屏闪的问题时为了图方便,和队友商量后在Interface类中放入两个记录当前渲染的Block的变量,破坏了类的封装。

我罗斯方块最终篇(Player类、Game类)的更多相关文章

  1. 我罗斯方块最终篇(Interface类)

    负责的任务 游戏过场及界面设计 Interface类的基础实现 根据队友需求完善Interface类功能 Interface类的本地测试 辅助队友改良游戏操作 代码要点 我们主要是通过控制台进行界面渲 ...

  2. 我罗斯方块最终篇(Block类)

    负责的任务 完善Block类的相关函数及变量: 对Block类中函数进行调整改进,并于其他人负责的类相互配合: 对Block类的函数功能进行调试: github项目地址. 效果图 具体可见总篇,一下仅 ...

  3. 我罗斯方块第二次作业(Block类)

    负责任务 完善Block类的相关函数及变量: 对Block类的函数功能进行调试: github项目地址. 开发日记 2020.5.11 今天和朋友们讨论了如何分工的工作,我负责的部分是Block类的完 ...

  4. 我罗斯方块第二次作业(Player类)

    我罗斯方块第二次作业 我的任务 完成player类的编写 player类的测试 我的计划 类的设计: Player类作为一个玩家类,需要处理和玩家有关的所有信息,以及维护玩家的游戏页面map.关于玩家 ...

  5. 图解Python 【第六篇】:面向对象-类-进阶篇

    由于类的内容比较多,分为类-初级基础篇和类-进阶篇 本节内容一览图: 一.类成员修饰符 每一个类的成员都有两种形式: 公有成员,在任何地方都能访问 私有成员,只能在类的内部才能访问 1.1.私有成员和 ...

  6. 图解Python 【第五篇】:面向对象-类-初级基础篇

    由于类的内容比较多,分为类-初级基础篇和类-进阶篇 类的内容总览图: 本节主要讲基础和面向对象的特性 本节内容一览图: 前言总结介绍: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 ...

  7. #企业项目实战 .Net Core + Vue/Angular 分库分表日志系统六 | 最终篇-通过AOP自动连接数据库-完成日志业务

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 04 | 强化设计方案 05 | 完善业务自动创建数据库 06 | 最终篇-通过AOP自动连接数据库-完成日志业 ...

  8. Java入门篇(六)——类和对象

    写到这里终于写到了入门篇的最后一个知识点了.类和对象是Java中经常被提到的两个词汇,实际上可以将类看作对象的载体,它定义了对象所具有的功能.Java是面向对象的语言,因此掌握类与对象是学习Java语 ...

  9. JavaScript 学习笔记之最终篇

      JavaScript 的学习算是最后一篇了...把JS基础已经核心的部分彻底的进行了学习... 唯一的缺陷就是自己在运用上并不是特别的熟练...今天是JS的最后一章内容了..也是JS 中最常用的内 ...

随机推荐

  1. P7115-[NOIP2020]移球游戏【构造】

    正题 题目链接:https://www.luogu.com.cn/problem/P7115 题目大意 \(n+1\)个柱子,前面\(n\)个上面各有\(m\)个球,球有\(n\)种颜色,每种\(m\ ...

  2. Monte-carlo-simulation

    https://towardsdatascience.com/how-to-use-monte-carlo-simulation-to-help-decision-making-a0a164bc861 ...

  3. 从commons-beanutils反序列化到shiro无依赖的漏洞利用

    目录 0 前言 1 环境 2 commons-beanutils反序列化链 2.1 TemplatesImple调用链 2.2 PriorityQueue调用链 2.3 BeanComparator ...

  4. 浅析InnoDB引擎的索引和索引原理

    浅析InnoDB引擎的索引和索引原理 什么是InnoDB的索引 InnoDB的索引就是一颗B+树.页是InnoDB引擎在内存和磁盘之间交换数据的基本单位,页的大小一般是16KB,页的大小可以在启动My ...

  5. FastAPI 学习之路(七)字符串的校验

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  6. python中冒泡排序代码实现

    1.冒泡排序代码如下图: #冒泡算法l=[12,4,56,10,6,2]for i in range(0,6): for j in range(i+1,6): if l[i]>l[j]: a=l ...

  7. 最详细的Android SDK下载安装及配置教程-------全文均为引用

    <https://www.cnblogs.com/gufengchen/p/11038029.html>

  8. 【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef

    基本概念 UE4 对 UObject 对象提供垃圾回收 UE4 对原生对象不提供垃圾回收,需要手动进行清理 方式 malloc / free new / delete new与malloc的区别在于, ...

  9. px,dp sp是像素、尺寸、尺寸

    px:即像素,1px代表屏幕上一个物理的像素点:px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同,如下图所示(图片来自android developer guide, ...

  10. 2020BUAA软工个人博客作业

    2020BUAA软工个人博客作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 我在这个课程的目标是 学 ...