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

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

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

当前游戏也是如此,引入了 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

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

相关推荐

  1. 俄罗斯方块(三)——游戏核心模块设计

    2024-07-11 11:02:02       18 阅读
  2. 俄罗斯方块【python,小游戏

    2024-07-11 11:02:02       26 阅读
  3. python实现俄罗斯方块游戏

    2024-07-11 11:02:02       52 阅读
  4. 俄罗斯方块游戏开发思路随想

    2024-07-11 11:02:02       35 阅读
  5. 使用VHDL实现俄罗斯方块游戏设计

    2024-07-11 11:02:02       41 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 11:02:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 11:02:02       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 11:02:02       58 阅读
  4. Python语言-面向对象

    2024-07-11 11:02:02       69 阅读

热门阅读

  1. 腾讯地图简单功能的封装

    2024-07-11 11:02:02       21 阅读
  2. 第四章 Redis(2023版本IDEA)

    2024-07-11 11:02:02       21 阅读
  3. Spring MVC -01

    2024-07-11 11:02:02       26 阅读
  4. 设计模式03-组合模式

    2024-07-11 11:02:02       21 阅读
  5. .net开发:NPOI生成excel文件到磁盘

    2024-07-11 11:02:02       24 阅读
  6. 应用TensorFlow简单工作流程

    2024-07-11 11:02:02       22 阅读
  7. 每个账号设置独立的cookie

    2024-07-11 11:02:02       24 阅读