写点什么

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

作者:zhongcx
  • 2024-10-17
    北京
  • 本文字数:7380 字

    阅读完需:约 24 分钟

本文将介绍如何使用鸿蒙提供的 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()来获取棋子的颜色值。

@ObservedV2class 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@Componentstruct 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; }}
@ObservedV2class 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@Componentstruct 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%'); }}
复制代码


用户头像

zhongcx

关注

还未添加个人签名 2024-09-27 加入

还未添加个人简介

评论

发布
暂无评论
鸿蒙开发案例:绘制中国象棋棋盘与棋子的技术教程_zhongcx_InfoQ写作社区