回复
鸿蒙NEXT开发案例:区字棋
zhongcx
发布于 2024-12-1 09:24
浏览
0收藏
【引言】
"区字棋"是一款简单而有趣的棋类游戏,玩家通过移动棋子,连接棋子之间的线条,最终实现胜利条件。游戏规则简单明了,但需要一定的策略和思考。
【环境准备】
电脑系统:windows 10
开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
工程版本:API 12
真机:Mate 60 Pro
语言:ArkTS、ArkUI
【技术实现】
在本案例中,我们使用了鸿蒙系统提供的UI框架和特性,通过自定义棋子类Cell和连接关系类Connection,实现了棋子之间的移动和连接。同时,利用鸿蒙系统的动画功能,为棋子移动添加了流畅的动画效果,提升了用户体验。
【游戏流程】
游戏加载时,初始化棋盘上的棋子和连接关系。
玩家点击棋子进行移动,判断移动是否合法,若合法则执行移动动画。
移动完成后,切换玩家回合,检查游戏是否结束。
若游戏未结束,AI自动走法,选择最佳移动策略。
游戏结束时,显示胜利者信息,并提供重新开始按钮。
【结语】
【完整代码】
// 导入提示框操作模块
import { promptAction } from '@kit.ArkUI';
// 使用框架提供的特性,如属性追踪等
@ObservedV2
class Cell {
// 定义棋子类型,0为空,1为黑方,2为白方
@Trace user: number = 0;
// 棋子的名字,例如"A"、"B"
name: string = "";
// 棋子的位置坐标
x: number = 0;
y: number = 0;
// 棋子的尺寸
width: number = 100;
height: number = 100;
// 构造函数初始化棋子的状态
constructor(name: string, x: number, y: number, user: number) {
this.user = user; // 设置棋子的用户类型
this.name = name; // 设置棋子的名字
this.x = x; // 设置棋子的X坐标
this.y = y; // 设置棋子的Y坐标
}
// 动画中的X轴偏移量
@Trace animX: number = 0;
// 动画中的Y轴偏移量
@Trace animY: number = 0;
// 获取棋子中心点的X坐标
getCenterX() {
return this.x - this.width / 2; // 计算并返回中心X坐标
}
// 获取棋子中心点的Y坐标
getCenterY() {
return this.y - this.height / 2; // 计算并返回中心Y坐标
}
// 执行棋子移动动画
moveAnimation(animationTime: number, toCell: Cell, callback?: () => void) {
// 设置动画参数
animateToImmediately({
duration: animationTime, // 动画持续时间
iterations: 1, // 动画迭代次数
curve: Curve.Linear, // 动画曲线类型
// 在动画完成后的回调函数
onFinish: () => {
animateToImmediately({
duration: 0, // 立即完成
iterations: 1,
curve: Curve.Linear,
// 在动画完成后的内部回调函数
onFinish: () => {
if (callback) {
callback(); // 调用外部回调函数
}
}
}, () => {
// 重置动画偏移量
this.animX = 0; // X轴偏移量重置
this.animY = 0; // Y轴偏移量重置
// 交换棋子的位置信息
let temp = this.user; // 临时存储当前棋子的用户
this.user = toCell.user; // 将目标棋子的用户赋值给当前棋子
toCell.user = temp; // 将临时存储的用户赋值给目标棋子
});
}
}, () => {
// 设置动画的目标偏移量
this.animX = toCell.x - this.x; // 计算X轴目标偏移量
this.animY = toCell.y - this.y; // 计算Y轴目标偏移量
});
}
}
// 定义棋子之间的连接关系
class Connection {
// 开始和结束节点的名字
startName: string;
endName: string;
// 开始和结束节点的坐标
startX: number;
startY: number;
endX: number;
endY: number;
// 构造函数初始化连接关系
constructor(start: Cell, end: Cell) {
this.startName = start.name; // 设置连接的起始棋子名字
this.endName = end.name; // 设置连接的结束棋子名字
this.startX = start.x; // 设置起始棋子的X坐标
this.startY = start.y; // 设置起始棋子的Y坐标
this.endX = end.x; // 设置结束棋子的X坐标
this.endY = end.y; // 设置结束棋子的Y坐标
}
}
// 定义游戏结构
@Entry
@Component
struct TwoSonChessGame {
// 游戏状态标志,用于控制动画执行
@State isAnimationRunning: boolean = false;
// 当前棋盘上的所有棋子
@State cells: Cell[] = [];
// 当前棋盘上的所有连接关系
@State connections: Connection[] = [];
// 当前玩家,1代表黑方,2代表白方
@State currentPlayer: number = 1;
// 游戏加载时初始化棋盘
aboutToAppear(): void {
// 创建五个棋子
const cellA = new Cell("A", 180, 180, 2); // 创建棋子A
const cellB = new Cell("B", 540, 180, 1); // 创建棋子B
const cellC = new Cell("C", 360, 360, 0); // 创建棋子C
const cellD = new Cell("D", 180, 540, 1); // 创建棋子D
const cellE = new Cell("E", 540, 540, 2); // 创建棋子E
// 将创建的棋子添加到棋盘上
this.cells.push(cellA, cellB, cellC, cellD, cellE);
// 初始化棋子间的连接关系
this.connections.push(new Connection(cellA, cellB)); // A与B连接
this.connections.push(new Connection(cellA, cellC)); // A与C连接
this.connections.push(new Connection(cellA, cellD)); // A与D连接
this.connections.push(new Connection(cellB, cellC)); // B与C连接
this.connections.push(new Connection(cellC, cellD)); // C与D连接
this.connections.push(new Connection(cellC, cellE)); // C与E连接
this.connections.push(new Connection(cellD, cellE)); // D与E连接
}
// 重置游戏状态
resetGame() {
this.currentPlayer = 1; // 重置当前玩家为黑方
this.cells[0].user = 2; // 设置棋子A为白方
this.cells[1].user = 1; // 设置棋子B为黑方
this.cells[2].user = 0; // 设置棋子C为空
this.cells[3].user = 1; // 设置棋子D为黑方
this.cells[4].user = 2; // 设置棋子E为白方
}
// 处理棋子移动
move(cell: Cell) {
// 判断棋子是否可移动
if (this.isCellValid(cell)) {
let targetIndex = this.checkValidMove(cell); // 检查目标位置是否合法
// 如果目标位置合法,则启动动画
if (targetIndex !== -1) {
this.isAnimationRunning = true; // 设置动画正在运行
cell.moveAnimation(300, this.cells[targetIndex], () => {
this.isAnimationRunning = false; // 动画完成后设置为不运行
this.moveCompleted(); // 调用移动完成处理
});
} else {
console.info(`当前位置无法移动`); // 输出无法移动的信息
}
}
}
// 移动完成后处理
moveCompleted() {
// 切换玩家
this.currentPlayer = this.currentPlayer === 1 ? 2 : 1; // 切换当前玩家
// 检查游戏是否结束
if (this.isGameOver()) {
let winner = this.currentPlayer === 1 ? '白棋赢了' : '黑棋赢了'; // 判断赢家
console.info(`${winner}`); // 输出赢家信息
// 显示游戏结束提示
promptAction.showDialog({
title: '游戏结束', // 提示框标题
message: `${winner}`, // 提示框内容
buttons: [{ text: '重新开始', color: '#ffa500' }] // 提示框按钮
}).then(() => {
this.resetGame(); // 重新开始游戏
});
} else {
// 如果是白方回合,则进行AI自动走法
if (this.currentPlayer === 2) {
this.aiMove(); // 调用AI走法
}
}
}
// AI走法
aiMove() {
let whiteCells = this.cells.filter(cell => cell.user === 2 && this.checkValidMove(cell) !== -1); // 获取可移动的白棋
// 根据当前情况选择最优走法
if (whiteCells.length === 1) {
this.move(whiteCells[0]); // 只有一个可移动棋子,直接移动
} else if (whiteCells.length === 2) {
let moveIndex = this.chooseBestMove(whiteCells); // 选择最佳走法
this.move(whiteCells[moveIndex]); // 移动最佳棋子
}
}
// 选择最佳走法
chooseBestMove(whiteCells: Cell[]): number {
let emptyIndex = this.cells.findIndex(cell => cell.user === 0); // 找到空棋子的位置
let bestMoveIndex = -1; // 初始化最佳移动索引
for (let i = 0; i < whiteCells.length; i++) {
let tempUser = whiteCells[i].user; // 临时存储当前白棋的用户
whiteCells[i].user = this.cells[emptyIndex].user; // 将空位置的用户赋值给当前白棋
this.cells[emptyIndex].user = tempUser; // 将当前白棋的用户赋值给空位置
this.currentPlayer = 1; // 设置当前玩家为黑方
let isGameOver = this.isGameOver(); // 检查游戏是否结束
tempUser = whiteCells[i].user; // 恢复当前白棋的用户
whiteCells[i].user = this.cells[emptyIndex].user; // 恢复空位置的用户
this.cells[emptyIndex].user = tempUser; // 恢复空位置的用户
this.currentPlayer = 2; // 恢复当前玩家为白方
if (isGameOver) {
bestMoveIndex = i; // 如果游戏结束,记录最佳移动索引
break; // 退出循环
}
}
if (bestMoveIndex === -1) {
bestMoveIndex = Math.floor(Math.random() * 2); // 如果没有找到最佳移动,随机选择一个
}
return bestMoveIndex; // 返回最佳移动索引
}
// 判断棋子是否有效
isCellValid(cell: Cell): boolean {
return (cell.user === 1 && this.currentPlayer === 1) || (cell.user === 2 && this.currentPlayer === 2); // 判断棋子是否属于当前玩家
}
// 判断游戏是否结束
isGameOver(): boolean {
for (let i = 0; i < this.cells.length; i++) {
if (this.currentPlayer == this.cells[i].user && this.checkValidMove(this.cells[i]) != -1) {
return false; // 如果当前玩家还有可移动的棋子,游戏未结束
}
}
return true; // 否则游戏结束
}
// 检查是否为有效走法
checkValidMove(cell: Cell): number {
for (let i = 0; i < this.connections.length; i++) {
if (cell.name === this.connections[i].startName) { // 如果棋子是连接的起始点
for (let j = 0; j < this.cells.length; j++) {
if (this.cells[j].name === this.connections[i].endName && this.cells[j].user === 0) {
return j; // 返回目标位置的索引
}
}
} else if (cell.name === this.connections[i].endName) { // 如果棋子是连接的结束点
for (let j = 0; j < this.cells.length; j++) {
if (this.cells[j].name === this.connections[i].startName && this.cells[j].user === 0) {
return j; // 返回目标位置的索引
}
}
}
}
return -1; // 如果没有找到有效走法,返回-1
}
// 构建棋盘界面
build() {
Column({ space: 10 }) { // 创建一个垂直排列的列
Stack() { // 创建一个堆叠布局
ForEach(this.connections, (connection: Connection, _index) => { // 遍历所有连接关系
Line() // 绘制连接线
.width(5) // 设置线宽
.height(5) // 设置线高
.startPoint([`${connection.startX}lpx`, `${connection.startY}lpx`]) // 设置起始点
.endPoint([`${connection.endX}lpx`, `${connection.endY}lpx`]) // 设置结束点
.stroke(Color.Black) // 设置线条颜色
.fill(Color.Green); // 设置填充颜色
});
ForEach(this.cells, (cell: Cell, _index) => { // 遍历所有棋子
Text() // 绘制棋子
.width(`${cell.width}lpx`) // 设置棋子宽度
.height(`${cell.height}lpx`) // 设置棋子高度
.margin({ left: `${cell.getCenterX()}lpx`, top: `${cell.getCenterY()}lpx` }) // 设置棋子位置
.translate({ x: `${cell.animX}lpx`, y: `${cell.animY}lpx` }) // 设置动画偏移
.backgroundColor(cell.user === 0 ? Color.Transparent : // 设置背景颜色
(cell.user === 1 ? Color.Black : Color.White)) // 根据用户类型设置颜色
.borderRadius('50%') // 设置圆角
.onClick(() => { // 设置点击事件
if (this.isAnimationRunning) { // 如果动画正在运行
console.info(`动画执行中`); // 输出信息
return; // 退出
}
this.move(cell); // 调用移动函数
});
});
}
.align(Alignment.TopStart) // 设置对齐方式
.width('720lpx') // 设置宽度
.height('720lpx') // 设置高度
.backgroundColor(Color.Orange); // 设置背景颜色
Button('重新开始').onClick(() => { // 创建重新开始按钮
if (this.isAnimationRunning) { // 如果动画正在运行
console.info(`动画执行中`); // 输出信息
return; // 退出
}
this.resetGame(); // 调用重置游戏函数
});
}
}
}
分类
标签
赞
收藏
回复
相关推荐