手撸俄罗斯方块(五)——游戏主题

news2024/11/15 13:36:03

手撸俄罗斯方块(五)——游戏主题

当确定游戏载体(如控制台)后,界面将呈现出来。但是游戏的背景色、方块的颜色、方框颜色都应该支持扩展。

当前游戏也是如此,引入了 Theme 的概念,支持主题的扩展。

AbstractTheme

系统抽象了一个AbstractTheme,它将一些渲染过程中的行为进行了抽象,抽象定义如下:

abstract class AbstractTheme {
  /**
   * 设置外框的样式,如外框的颜色、整体的背景等。
   * @param outer 指代外框对象的元素,通过修改其内容改变显示样式。
   */
  abstract outStyle(outer: any): void;
  /**
   * 设置内框的样式,如内框的颜色、整体的背景等。
   * @param inner 指代内框对象的元素,通过修改其内容改变显示样式。
   */
  abstract innerStyle(inner: any): void;
  /**
   * 设置分数的样式。
   * @param score 指代分数对象的元素,通过修改其内容改变显示样式。
   */
  abstract scoreStyle(score: any): void;
  /**
   * 设置状态栏的样式
   * @param status 指代状态对象的元素。
   */
  abstract statusStyle(status: any): void;
  /**
   * 分数的格式化字符串,输入一个分数的数字,将其转换为目标的样式;
   * @param score {number} 当前游戏的分数
   */
  abstract scoreTemplate(score: number): string;
  abstract nextStyle(blocks: any): void;
  abstract currentStyle(current: any): void;
  /**
   * 设置方块区域的样式
   * @param block 指代当前方块区域
   */
  abstract blockStyle(block: any): void;
  /**
   * 设置current区域和已填充区域的小方块的样式
   * @param blockItem 当前小方块,如一个IBlock会拆分成4各BlockItem。
   * @param point 当前小方块的位置信息,包括`x`轴和`y`轴的坐标等信息
   */
  abstract blockPointStyle(blockItem: any, point: Point): void;
  /**
   * 设置next区域的小方块的样式
   * @param blockItem
   * @param point
   */
  abstract nextPointStyle(blockItem: any, point: Point): void;
}

注释已经描述得比较清晰了,分别对外框、内框等进行了设定。

控制台如何实现主题

为了使主题生效,需要在AbstractCanvas子类中调用Theme提供的方法。这里以ConsoleCanvas为例,它的实现如下:

export class ConsoleCanvas extends AbstractCanvas {
  render(): void {
    const { game } = this;
    if (!game) {
      return;
    }
    const { stage, dimension } = game;
    const printArray: string[] = [];
    console.clear();
    const { score, current, next } = stage;
    const { xSize, ySize } = dimension;
    const outLength = 1 + 1 + xSize + 1 + this.rightWidth + 1;
    if (!this.isHideOuter) {
      // 1. 渲染外边框的上边框
      const outLine1 = this.getOutterLine(
        this.outerLeftTopChar +
          this.createChar(xSize + 2 + this.rightWidth, this.horizonalChar) +
          this.outerRightTopChar
      );
      printArray.push(outLine1);
    }

    // 2. 渲染score
    const scoreText = this.theme.scoreTemplate(score);
    const scoreConsoleChar = ConsoleChar.create(scoreText);
    this.theme.scoreStyle(scoreConsoleChar);
    // 计算左侧需要补充的空格
    const leftSpace = this.rightWidth - scoreText.length - 3;
    // 右侧需要补充的空格
    const rightSpace = 3;
    let scoreLine =
      this.getOutterLine(this.outerLeftVerticalChar) +
      this.createChar(xSize + 2 + leftSpace) +
      scoreConsoleChar.ch +
      this.createChar(rightSpace) +
      this.getOutterLine(this.outerRightVerticalChar);
    printArray.push(scoreLine);

    // 3. 渲染内边框的上边框
    let line1 =
      this.getOutterLine(this.outerLeftVerticalChar) +
      this.getInnerLine(this.innerLeftTopChar);
    for (let x = 0; x < xSize; x++) {
      const oneBlockItem = current?.points.find((item) => item.x === x);
      if (oneBlockItem) {
        line1 += this.getInnerLine(bold(this.horizonalChar));
      } else {
        line1 += this.getInnerLine(this.horizonalChar);
      }
    }
    line1 +=
      this.getInnerLine(this.innerRightTopChar) +
      this.createChar(this.rightWidth) +
      this.getOutterLine(this.outerRightVerticalChar);
    printArray.push(line1);
    let line2 =
      this.getOutterLine(this.outerLeftVerticalChar) +
      this.getInnerLine(this.innerLeftBottomChar);
    for (let x = 0; x < xSize; x++) {
      const oneBlockItem = current?.points.find((item) => item.x === x);
      if (oneBlockItem) {
        line2 += this.getInnerLine(bold(this.horizonalChar));
      } else {
        line2 += this.getInnerLine(this.horizonalChar);
      }
    }
    line2 +=
      this.getInnerLine(this.innerRightBottomChar) +
      this.createChar(this.rightWidth) +
      this.getOutterLine(this.outerRightVerticalChar);
    printArray.push(line2);
    if (!this.isHideOuter) {
      const outLine2 = this.getOutterLine(
        this.outerLeftBottomChar +
          this.createChar(xSize + 2 + this.rightWidth, this.horizonalChar) +
          this.outerRightBottomChar
      );
      printArray.push(outLine2);
    }
    if (this.exitMessage) {
      printArray.push(this.exitMessage);
    } else {
      printArray.push("");
    }
    process.stdout.write(this.handleOutput(outLength, printArray).join("\n"));
  }
}

我们看到渲染上边框,调用了getOutterLine方法,这个方法是在AbstractCanvas中定义的,它的实现如下:

export class ConsoleCanvas extends AbstractCanvas {
  // ...
  getOutterLine(char: string): string {
    if (this.isHideOuter) {
      return "";
    }
    const consoleChar = new ConsoleChar(str || "|");
    this.theme.outStyle(consoleChar);
    return consoleChar.ch;
  }
  // ...
}

它内部调用了 theme.outStyle 方法,对应我们上述 theme 的定义。

类似的,对于内边框的渲染,也是调用了getInnerLine方法,它的实现如下:

export class ConsoleCanvas extends AbstractCanvas {
  // ...
  getInnerLine(char: string): string {
    const consoleChar = new ConsoleChar(str || "|");
    this.theme.innerStyle(consoleChar);
    return consoleChar.ch;
  }
  // ...
}

这样,我们就实现了主题的扩展。

主题的扩展

我们可以通过继承AbstractTheme,实现自己的主题,比如实现一个RedTheme

export class RedTheme extends DefaultTheme {
  outStyle(outer: any): void {
    outer.ch = color.red(outer.ch);
  }
}

它实现了outStyle方法,将外边框的颜色设置为红色。

我们使用该主题,效果如下

在这里插入图片描述

详细内容可以我的git项目: https://github.com/shushanfx/tetris
也可以关注我的git账号: https://github.com/shushanfx

自此手撸俄罗斯方块的代码部分就讲到这里,后续将依次为开始展开,从俄罗斯方块开始,我们能走多远。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1914711.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iOS UITableView自带滑动手势和父视图添加滑动手势冲突响应机制探索

场景 我们有时候会遇到这样的一个交互场景&#xff1a;我们有一个UITableView 放在一个弹窗中&#xff0c;这个弹窗可以通过滑动进行展示和消失&#xff08;跟手滑动的方式&#xff09;&#xff0c;然后这个UITableView放在弹窗中&#xff0c;并且可以滚动&#xff0c;展示一些…

成都晨持绪:抖音电商带货需要交钱吗

在抖音这个充满创意与可能的平台上&#xff0c;电商带货成为了一种新兴而又时尚的职业。然而&#xff0c;伴随着无数的点击与转发&#xff0c;有一个问题始终萦绕在人们心头——抖音电商带货需要交钱吗? 如画卷展开&#xff0c;抖音平台以其独特的算法和庞大的用户基础构建了一…

spark shuffle写操作——UnsafeShuffleWriter

PackedRecordPointer 使用long类型packedRecordPointer存储数据。 数据结构为&#xff1a;[24 bit partition number][13 bit memory page number][27 bit offset in page] LongArray LongArray不同于java中long数组。LongArray可以使用堆内内存也可以使用堆外内存。 Memor…

构建高精度室内定位导航系统,从3DGIS到AI路径规划的全面解析

室内定位导航系统是一种利用多种技术实现室内精准定位和导航的智能系统&#xff0c;即便没有卫星信号&#xff0c;也能实现精准导航。维小帮室内定位导航系统是基于自研的地图引擎与先进定位技术&#xff0c;结合智能路径规划算法&#xff0c;解决了人们在大型复杂室内场所最后…

python怎么判断字符串以什么结尾

在python编辑器中新建一个data.py。 写上自己的注释。 然后新建一个变量testname。 利用endswith来判断字符串是不是以“ar”结尾。 将结果打印出来。 选择“run”->“run”。 运行该程序&#xff0c;如果是&#xff0c;就会返回true。

深度探讨:无法恢复主文件表的挑战与解决方案

在数字时代&#xff0c;数据的安全与恢复成为了不容忽视的重要议题。其中&#xff0c;主文件表&#xff08;Master File Table, MFT&#xff09;作为文件系统的核心组件&#xff0c;一旦受损或无法恢复&#xff0c;将直接导致数据访问的障碍&#xff0c;给用户带来巨大困扰。本…

Vue在一个页面调用另一个同级页面的方法

1、建个中转站 2、然后在两个页面都引入它&#xff0c;注意引入路径。 import Utils from src/utils/way 3、调用方的写法 //eg :Utils.$emit(demo, msg) 4、被调用方的写法 //eg :Utils.$on(demo, val>{})

Playwright使用教程【附爬取Leetcode题目URLs以及有道翻译小软件】

前言 playwright是微软设计的一款工具&#xff0c;可以爬取网页&#xff0c;还可以自动化测试自己编写的网站&#xff0c;而且不像bs4、request编写爬虫那么复杂&#xff0c;也不需要考虑反爬技术&#xff0c;只需要知道最基础的前端知识&#xff0c;就可以高效、便捷的编写爬…

【算法训练记录——Day43】

Day43——动态规划Ⅴ 1.kamacoder52_携带研究材料2.leetcode518_零钱兑换Ⅱ3.leetcode377_组合总和Ⅳ 完全背包 1.kamacoder52_携带研究材料 思路&#xff1a;这里每种材料可以选择无数次&#xff0c;因此属于完全背包&#xff0c; 首先回顾一下01背包的核心代码 for(int i 0…

vue3 + tsx 表格 Action 单独封装组件用法

前言 先上图看右侧列 action 的 UI 效果&#xff1a; 正常来说&#xff0c;如果一个表格的附带 action 操作&#xff0c;我们一般会放在最右侧的列里面实现&#xff0c;这个时候有些UI 框架支持在 SFC 模板里面定义额外的 solt&#xff0c;当然如果不支持&#xff0c;更通用的…

医疗器械网络安全 | 漏洞扫描、渗透测试没有发现问题,是否说明我的设备是安全的?

尽管漏洞扫描、模糊测试和渗透测试在评估系统安全性方面是非常重要和有效的工具&#xff0c;但即使这些测试没有发现任何问题&#xff0c;也不能完全保证您的医疗器械是绝对安全的。这是因为安全性的评估是一个多维度、复杂且持续的过程&#xff0c;涉及多个方面和因素。以下是…

7.10号小项目部分说明

总体说明 糖锅小助手 我这次主要对上次糖锅小助手界面添加了一个侧边栏&#xff08;侧边输入框放置了三个按钮&#xff0c;可以跳转到其他ai聊天界面&#xff0c;还可以退出聊天界面回到登录界面&#xff09;和一个日期输入框&#xff08;日期输入框获取时间&#xff0c;根据时…

史上最齐全电动弃流装置(弃流控制柜/流量式雨水弃流装置)这里都有,井座式、304不锈钢材质

电动弃流装置组成部分有&#xff1a;PLC控制柜、雨量传感器/电磁流量计、弃流装置 进出水管可以定制不同接口&#xff0c;可以适用于连接波纹管、PVC管 电动弃流装置的工作原理如下&#xff1a; 首先&#xff0c;雨量传感器或电磁流量计实时监测降雨量或水流流量等相关数据&…

TCP 握手数据流

这张图详细描述了 TCP 握手过程中&#xff0c;从客户端发送 SYN 包到服务器最终建立连接的整个数据流转过程&#xff0c;包括网卡、内核、进程中的各个环节。下面对每个步骤进行详细解释&#xff1a; 客户端到服务器的初始连接请求 客户端发送 SYN 包&#xff1a; 客户端发起…

AI降痕工具:一键去除论文中的AI代写痕迹

在这个充满创意和创新的时代&#xff0c;AI已经渗透到我们生活的方方面面。然而&#xff0c;随着AI的飞速发展&#xff0c;AI的痕迹在论文创作中愈发明显。 这不禁让人思考&#xff0c;如何让论文回归纯粹&#xff0c;展现人类独有的思考和见解。“论文去AI痕迹”不仅是对学术…

vue3-openlayers WebGL加载地图(栅格切片、矢量切片)

本篇介绍一下使用vue3-openlayers WebGL加载地图&#xff08;栅格切片、矢量切片&#xff09; 1 需求 vue3-openlayers WebGL加载地图&#xff08;栅格切片、矢量切片&#xff09; 2 分析 栅格切片使用ol-webgl-tile-layer 矢量切片使用ol-vector-tile-layer&#xff08;默…

海外视频媒体发布/发稿:如何在国外媒体以视频的形式宣发

1. 背景介绍 在如今数字化时代&#xff0c;每个国家都拥有着各自的视频媒体平台&#xff0c;而主流媒体也都纷纷加入了视频发布的行列。视频媒体的宣发形式主要包括油管Youtube等视频分享平台&#xff0c;以及图文配合的发布方式。通过在视频中夹带链接&#xff0c;媒体可以以…

服务器安全运维方案介绍

随着信息技术的飞速发展&#xff0c;服务器已成为企业信息化建设的核心基础设施。然而&#xff0c;服务器安全运维问题也日益凸显&#xff0c;如何保障服务器的稳定运行和数据安全&#xff0c;已成为企业面临的重要挑战。本文旨在介绍一套全面的服务器安全运维方案&#xff0c;…

『C + ⒈』‘\‘

&#x1f942;在反斜杠(\)有⒉种最常用的功能如下所示&#x1f44b; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) {int a 10;int b 20;int c 30;if (a 10 &&\b 20 &&\c 30){printf("Your print\n");}else{prin…

【leetcode刷题笔记】02.复写零

题目&#xff1a; 思路&#xff1a; 代码实现 class Solution { public:void duplicateZeros(vector<int>& arr) {//1.模拟异地操作int prev0,cur0;int lenarr.size();while(cur<len){//arr[prev]不是0就都走一步if(arr[prev]!0){prev;cur;}//arr[prev]是0就pre…