当前位置: 首页 > article >正文

鸿蒙开发案例:绘制中国象棋棋盘与棋子的技术教程

本文将介绍如何使用鸿蒙提供的UI组件来绘制一个中国象棋棋盘并放置棋子。通过本教程,您将学会基本的UI构建技巧,以及如何在鸿蒙环境中创建一个简单的象棋游戏界面。

一、定义棋盘线条与棋子位置

首先,我们需要定义几个基础类来帮助我们构造棋盘。ChessLine类用于表示棋盘上的线段,而MyPosition类则用来记录棋盘上每个位置是否需要特殊的标记(如“兵”、“卒”、“炮”的位置)。

class ChessLine {
  startPoint: [number, number] = [0, 0];
  endPoint: [number, number] = [0, 0];
}

class MyPosition {
  x: number = 0;
  y: number = 0;
  topLeft: boolean = true;
  topRight: boolean = true;
  bottomLeft: boolean = true;
  bottomRight: boolean = true;

  constructor(x: number, y: number, topLeft: boolean, topRight: boolean, bottomLeft: boolean, bottomRight: boolean) {
    this.x = x;
    this.y = y;
    this.topLeft = topLeft;
    this.topRight = topRight;
    this.bottomLeft = bottomLeft;
    this.bottomRight = bottomRight;
  }
}

二、创建棋子类

接下来,我们定义ChessPiece类来代表棋盘上的每一个棋子。这个类包括棋子的颜色、类型等属性,并且有一个方法getColor()来获取棋子的颜色值。

@ObservedV2
class ChessPiece {
  @Trace opacity: number = 1;
  @Trace value: string = "";
  @Trace type: number = 0; // 0: 无棋, 1: 红棋,2: 黑棋

  redColor: string = `rgb(144,11,11)`;
  blackColor: string = `rgb(78,56,23)`;

  constructor(value: string, type: number) {
    this.value = value;
    this.type = type;
  }

  setValue(value: string, type: number) {
    this.value = value;
    this.type = type;
  }

  getColor() {
    if (this.type === 1) {
      return this.redColor;
    } else if (this.type === 2) {
      return this.blackColor;
    }
    return "#00000000";
  }
}

三、构建棋盘

使用ChessBoard类来构建整个棋盘,其中包括棋盘的基本尺寸、棋子数组、棋盘线段数组等。在这个类中,我们还定义了初始化游戏的方法initGame(),它会根据规则在棋盘上放置棋子。

@Entry
@Component
struct ChessBoard {
  cellWidth: number = 70;
  borderPieceWidth: number = 12;
  pieceSize: number = 66;
  pieces: ChessPiece[] = [];
  lines: ChessLine[] = [];
  positions: MyPosition[] = [];
  selectedIndex: number = -1; // -1表示未点击任何棋子,非-1表示当前正在点击的棋子

  aboutToAppear(): void {
    for (let i = 0; i < 9 * 10; i++) {
      this.pieces.push(new ChessPiece("", 0));
    }
    this.initGame();
    // 初始化水平线和垂直线...
  }

  initGame() {
    // 设置棋子初始位置...
  }

  build() {
    Column({ space: 10 }) {
      // 构建棋盘框架和线条...
    }
  }
}

四、绘制棋子

最后,我们需要在棋盘上绘制棋子。这里使用了Flex和ForEach等组件来遍历棋子数组,并根据棋子的类型绘制不同的样式。

Flex({ wrap: FlexWrap.Wrap }) {
  ForEach(this.pieces, (piece: ChessPiece, index: number) => {
    Stack() {
      Text(piece.value)
      // 设置棋子文本样式...
    }
    .opacity(piece.opacity)
    .width(`${this.cellWidth}lpx`)
    .height(`${this.cellWidth}lpx`)
    .onClick(() => {
      // 处理点击事件...
    })
  })
}

【完整代码】

class ChessLine {
  startPoint: [number, number] = [0, 0];
  endPoint: [number, number] = [0, 0];
}

class MyPosition {
  x: number = 0;
  y: number = 0;
  topLeft: boolean = true;
  topRight: boolean = true;
  bottomLeft: boolean = true;
  bottomRight: boolean = true;

  constructor(x: number, y: number, topLeft: boolean, topRight: boolean, bottomLeft: boolean, bottomRight: boolean) {
    this.x = x;
    this.y = y;
    this.topLeft = topLeft;
    this.topRight = topRight;
    this.bottomLeft = bottomLeft;
    this.bottomRight = bottomRight;
  }
}

@ObservedV2
class ChessPiece {
  @Trace opacity: number = 1;
  @Trace value: string = "";
  @Trace type: number = 0; // 0: 无棋, 1: 红棋,2: 黑棋
  redColor: string = `rgb(144,11,11)`;
  blackColor: string = `rgb(78,56,23)`;

  constructor(value: string, type: number) {
    this.value = value;
    this.type = type;
  }

  setValue(value: string, type: number) {
    this.value = value;
    this.type = type;
  }

  getColor() {
    if (this.type === 1) {
      return this.redColor;
    } else if (this.type === 2) {
      return this.blackColor;
    }
    return "#00000000";
  }
}

@Entry
@Component
struct ChessBoard {
  cellWidth: number = 70;
  borderPieceWidth: number = 12;
  pieceSize: number = 66;
  pieces: ChessPiece[] = [];
  lines: ChessLine[] = [];
  positions: MyPosition[] = [];
  selectedIndex: number = -1; // -1表示未点击任何棋子,非-1表示当前正在点击的棋子

  aboutToAppear(): void {
    for (let i = 0; i < 9 * 10; i++) {
      this.pieces.push(new ChessPiece("", 0));
    }
    this.initGame();

    // 初始化水平线和垂直线
    for (let i = 0; i < 10; i++) {
      this.lines.push({
        startPoint: [0, this.cellWidth * i],
        endPoint: [this.cellWidth * 8, this.cellWidth * i]
      });
      this.lines.push({
        startPoint: [this.cellWidth * i, 0],
        endPoint: [this.cellWidth * i, this.cellWidth * (i === 0 || i === 8 ? 9 : 4)]
      });
      this.lines.push({
        startPoint: [this.cellWidth * i, this.cellWidth * 5],
        endPoint: [this.cellWidth * i, this.cellWidth * 9]
      });
    }


    // 初始化九宫格内的斜线
    this.lines.push({
      startPoint: [3 * this.cellWidth, 0],
      endPoint: [5 * this.cellWidth, 2 * this.cellWidth],
    });
    this.lines.push({
      startPoint: [5 * this.cellWidth, 0],
      endPoint: [3 * this.cellWidth, 2 * this.cellWidth],
    });
    this.lines.push({
      startPoint: [3 * this.cellWidth, 7 * this.cellWidth],
      endPoint: [5 * this.cellWidth, 9 * this.cellWidth],
    });
    this.lines.push({
      startPoint: [5 * this.cellWidth, 7 * this.cellWidth],
      endPoint: [3 * this.cellWidth, 9 * this.cellWidth],
    });

    // 兵卒炮位置标
    this.positions.push(new MyPosition(1, 2, true, true, true, true))
    this.positions.push(new MyPosition(7, 2, true, true, true, true))
    this.positions.push(new MyPosition(0, 3, false, true, false, true))
    this.positions.push(new MyPosition(2, 3, true, true, true, true))
    this.positions.push(new MyPosition(4, 3, true, true, true, true))
    this.positions.push(new MyPosition(6, 3, true, true, true, true))
    this.positions.push(new MyPosition(8, 3, true, false, true, false))
    this.positions.push(new MyPosition(1, 7, true, true, true, true))
    this.positions.push(new MyPosition(7, 7, true, true, true, true))
    this.positions.push(new MyPosition(0, 6, false, true, false, true))
    this.positions.push(new MyPosition(2, 6, true, true, true, true))
    this.positions.push(new MyPosition(4, 6, true, true, true, true))
    this.positions.push(new MyPosition(6, 6, true, true, true, true))
    this.positions.push(new MyPosition(8, 6, true, false, true, false))
  }

  initGame() {
    for (let i = 0; i < 9 * 10; i++) {
      this.pieces[i].setValue("", 0);
    }
    this.pieces[0].setValue("车", 2)
    this.pieces[1].setValue("马", 2)
    this.pieces[2].setValue("象", 2)
    this.pieces[3].setValue("士", 2)
    this.pieces[4].setValue("将", 2)
    this.pieces[5].setValue("士", 2)
    this.pieces[6].setValue("象", 2)
    this.pieces[7].setValue("马", 2)
    this.pieces[8].setValue("车", 2)
    this.pieces[19].setValue("炮", 2)
    this.pieces[25].setValue("炮", 2)
    this.pieces[27].setValue("卒", 2)
    this.pieces[29].setValue("卒", 2)
    this.pieces[31].setValue("卒", 2)
    this.pieces[33].setValue("卒", 2)
    this.pieces[35].setValue("卒", 2)

    this.pieces[54].setValue("兵", 1)
    this.pieces[56].setValue("兵", 1)
    this.pieces[58].setValue("兵", 1)
    this.pieces[60].setValue("兵", 1)
    this.pieces[62].setValue("兵", 1)
    this.pieces[64].setValue("炮", 1)
    this.pieces[70].setValue("炮", 1)
    this.pieces[81].setValue("车", 1)
    this.pieces[82].setValue("马", 1)
    this.pieces[83].setValue("相", 1)
    this.pieces[84].setValue("仕", 1)
    this.pieces[85].setValue("帅", 1)
    this.pieces[86].setValue("仕", 1)
    this.pieces[87].setValue("相", 1)
    this.pieces[88].setValue("马", 1)
    this.pieces[89].setValue("车", 1)
  }

  build() {
    Column({ space: 10 }) {
      Column() {
        Stack() {
          // 棋盘矩形边框
          Rect()
            .margin({
              top: `${this.cellWidth / 2 - this.borderPieceWidth / 2}lpx`,
              left: `${this.cellWidth / 2 - this.borderPieceWidth / 2}lpx`
            })
            .width(`${this.cellWidth * 8 + this.borderPieceWidth}lpx`)
            .height(`${this.cellWidth * 9 + this.borderPieceWidth}lpx`)
            .fillOpacity(0)
            .stroke(Color.Black)
            .strokeWidth(`${this.borderPieceWidth / 3}lpx`);

          // 绘制线条
          ForEach(this.lines, (line: ChessLine, _index: number) => {
            Line()
              .margin({ left: `${this.cellWidth / 2}lpx`, top: `${this.cellWidth / 2}lpx` })
              .startPoint([`${line.startPoint[0]}lpx`, `${line.startPoint[1]}lpx`])
              .endPoint([`${line.endPoint[0]}lpx`, `${line.endPoint[1]}lpx`])
              .stroke(Color.Black);
          });
          // 添加"兵卒炮"标记
          ForEach(this.positions, (position: MyPosition, _index: number) => {
            if (position.topLeft) {
              Polyline()
                .margin({
                  left: `${this.cellWidth / 2 - this.borderPieceWidth / 2}lpx`,
                  top: `${this.cellWidth / 2 - this.borderPieceWidth / 2}lpx`
                })
                .points([
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y - this.borderPieceWidth}lpx`],
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y}lpx`],
                  [`${this.cellWidth * position.x - this.borderPieceWidth}lpx`, `${this.cellWidth * position.y}lpx`],
                ])
                .width(1)
                .height(1)
                .fillOpacity(0)
                .stroke(Color.Black);
            }
            if (position.topRight) {
              Polyline()
                .margin({
                  left: `${this.cellWidth / 2 + this.borderPieceWidth / 2}lpx`,
                  top: `${this.cellWidth / 2 - this.borderPieceWidth / 2}lpx`
                })
                .points([
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y - this.borderPieceWidth}lpx`],
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y}lpx`],
                  [`${this.cellWidth * position.x + this.borderPieceWidth}lpx`, `${this.cellWidth * position.y}lpx`],
                ])
                .width(1)
                .height(1)
                .fillOpacity(0)
                .stroke(Color.Black)
            }
            if (position.bottomLeft) {
              Polyline()
                .margin({
                  left: `${this.cellWidth / 2 - this.borderPieceWidth / 2}lpx`,
                  top: `${this.cellWidth / 2 + this.borderPieceWidth / 2}lpx`
                })
                .points([
                  [`${this.cellWidth * position.x - this.borderPieceWidth}lpx`, `${this.cellWidth * position.y}lpx`],
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y}lpx`],
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y + this.borderPieceWidth}lpx`],
                ])
                .width(1)
                .height(1)
                .fillOpacity(0)
                .stroke(Color.Black)
            }
            if (position.bottomRight) {
              Polyline()
                .margin({
                  left: `${this.cellWidth / 2 + this.borderPieceWidth / 2}lpx`,
                  top: `${this.cellWidth / 2 + this.borderPieceWidth / 2}lpx`
                })
                .points([
                  [`${this.cellWidth * position.x + this.borderPieceWidth}lpx`, `${this.cellWidth * position.y}lpx`],
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y}lpx`],
                  [`${this.cellWidth * position.x}lpx`, `${this.cellWidth * position.y + this.borderPieceWidth}lpx`],
                ])
                .width(1)
                .height(1)
                .fillOpacity(0)
                .stroke(Color.Black)
            }
          });

          // 绘制棋子
          Flex({ wrap: FlexWrap.Wrap }) {
            ForEach(this.pieces, (piece: ChessPiece, index: number) => {
              Stack() {
                Text(piece.value)
                  .width(`${this.pieceSize}lpx`)
                  .height(`${this.pieceSize}lpx`)
                  .backgroundColor(piece.type !== 0 ? `rgb(192,149,106)` : Color.Transparent)
                  .textAlign(TextAlign.Center)
                  .fontSize(`${this.pieceSize / 2}lpx`)
                  .fontColor(piece.getColor())
                  .borderColor(piece.getColor())
                  .borderRadius(`50%`)
                  .borderWidth(`2lpx`)
                  .textShadow({
                    radius: 2,
                    color: Color.White,
                    offsetX: 2,
                    offsetY: 2
                  });
                Circle()
                  .width(`${this.pieceSize - 15}lpx`)
                  .height(`${this.pieceSize - 15}lpx`)
                  .fillOpacity(0)
                  .strokeWidth(2)
                  .stroke(piece.getColor())
                  .strokeDashArray([0.2, 1]);
              }
              .opacity(piece.opacity)
              .width(`${this.cellWidth}lpx`)
              .height(`${this.cellWidth}lpx`)
              .onClick(() => {
                if (this.selectedIndex === -1) {
                  this.selectedIndex = index;
                  animateToImmediately({
                    iterations: 3,
                    duration: 300,
                    onFinish: () => {
                      animateToImmediately({
                        iterations: 1,
                        duration: 0
                      }, () => {
                        piece.opacity = 1;
                      });
                    }
                  }, () => {
                    piece.opacity = 0.5;
                  });
                } else {
                  piece.value = this.pieces[this.selectedIndex].value;
                  piece.type = this.pieces[this.selectedIndex].type;
                  this.pieces[this.selectedIndex].value = '';
                  this.pieces[this.selectedIndex].type = 0;
                  this.selectedIndex = -1;
                }
              });
            });
          }.width('100%').height('100%');
        }
        .align(Alignment.TopStart)
        .width(`${this.cellWidth * 9}lpx`)
        .height(`${this.cellWidth * 10}lpx`);
      }
      .padding(10)
      .backgroundColor(Color.Orange)
      .borderRadius(10);

      Button('重新开始').onClick(() => {
        this.initGame();
      });
    }.width('100%');
  }
}


http://www.kler.cn/news/360633.html

相关文章:

  • 为什么springboot项目更多的选择了SpringSecurity,而不是Shiro?
  • 【WebLogic】WebLogic 14c控制台密码文件权限问题分享
  • 2019年计算机网络408真题解析
  • CFG 蒸馏:On Distillation of Guided Diffusion Models
  • 【景观生态学实验】实验四 景观指数计算
  • multi-Head Attention
  • Vue--》掌握自定义依赖引入的最佳实践
  • blender 理解 积木组合 动画制作 学习笔记
  • C# 数据集
  • sql server xml
  • Egg.js使用ejs快速自动生成resetful风格的CRUD接口
  • 数据门户:企业数字化转型的关键作用
  • Oracle里面,with ... as 用法介绍
  • 软考系统分析师知识点十三:软件需求工程
  • 【论文笔记】Adversarial Diffusion Distillation
  • Flux.using 使用说明书
  • LeetCode第101题. 对称二叉树
  • c语言操作符xiangjie
  • 10 django管理系统 - 管理员管理 - 新建管理员(通过模态框和ajax实现)
  • 快乐数--双指针