下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码,使用Java语言和鸿蒙的Ability框架。
1. 项目结构
/src/main/java/com/example/chess/
├── MainAbilitySlice.java // 主界面逻辑
├── ChessView.java // 游戏视图和逻辑
├── ChessPiece.java // 棋子类
└── resources/
├── base/
│ ├── layout/
│ │ └── ability_main.xml // 布局文件
│ └── graphic/
│ └── board_bg.xml // 棋盘背景
2. 实现代码
ChessPiece.java (棋子类)
package com.example.chess;
public class ChessPiece {
public static final int WHITE = 0;
public static final int BLACK = 1;
// 棋子类型常量
public static final int KING = 0; // 王
public static final int QUEEN = 1; // 后
public static final int ROOK = 2; // 车
public static final int BISHOP = 3; // 象
public static final int KNIGHT = 4; // 马
public static final int PAWN = 5; // 兵
private int type; // 棋子类型
private int color; // 棋子颜色
private int row; // 行
private int col; // 列
private boolean hasMoved = false; // 是否移动过(用于王车易位和兵的初始移动)
public ChessPiece(int type, int color, int row, int col) {
this.type = type;
this.color = color;
this.row = row;
this.col = col;
}
// getter和setter方法
public int getType() {
return type;
}
public int getColor() {
return color;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getCol() {
return col;
}
public void setCol(int col) {
this.col = col;
}
public boolean hasMoved() {
return hasMoved;
}
public void setMoved(boolean hasMoved) {
this.hasMoved = hasMoved;
}
// 获取棋子Unicode字符
public char getSymbol() {
switch (type) {
case KING: return color == WHITE ? '♔' : '♚';
case QUEEN: return color == WHITE ? '♕' : '♛';
case ROOK: return color == WHITE ? '♖' : '♜';
case BISHOP: return color == WHITE ? '♗' : '♝';
case KNIGHT: return color == WHITE ? '♘' : '♞';
case PAWN: return color == WHITE ? '♙' : '♟';
default: return ' ';
}
}
}
ChessView.java (游戏视图和逻辑)
package com.example.chess;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.agp.components.AttrSet;
import ohos.multimodalinput.event.TouchEvent;
import java.util.ArrayList;
import java.util.List;
public class ChessView extends Component implements Component.DrawTask {
private static final int ROWS = 8; // 棋盘行数
private static final int COLS = 8; // 棋盘列数
private Paint lightPaint; // 浅色格子画笔
private Paint darkPaint; // 深色格子画笔
private Paint textPaint; // 文字画笔
private Paint highlightPaint; // 高亮画笔
private Paint moveHintPaint; // 移动提示画笔
private List<ChessPiece> pieces; // 棋子列表
private ChessPiece selectedPiece; // 当前选中的棋子
private int currentPlayer = ChessPiece.WHITE; // 当前玩家
private List<int[]> possibleMoves; // 当前选中棋子的可能移动位置
public ChessView(Context context) {
super(context);
init();
}
public ChessView(Context context, AttrSet attrSet) {
super(context, attrSet);
init();
}
private void init() {
lightPaint = new Paint();
lightPaint.setColor(new Color(0xFFF0D9B5));
lightPaint.setStyle(Paint.Style.FILL_STYLE);
darkPaint = new Paint();
darkPaint.setColor(new Color(0xFFB58863));
darkPaint.setStyle(Paint.Style.FILL_STYLE);
textPaint = new Paint();
textPaint.setColor(new Color(0xFF000000));
textPaint.setTextSize(50);
textPaint.setTextAlign(Paint.Align.CENTER);
highlightPaint = new Paint();
highlightPaint.setColor(new Color(0x6644FF44));
highlightPaint.setStyle(Paint.Style.FILL_STYLE);
moveHintPaint = new Paint();
moveHintPaint.setColor(new Color(0x6644AAFF));
moveHintPaint.setStyle(Paint.Style.FILL_STYLE);
initPieces();
possibleMoves = new ArrayList<>();
addDrawTask(this);
setTouchEventListener(this::onTouchEvent);
}
private void initPieces() {
pieces = new ArrayList<>();
// 初始化白方棋子
pieces.add(new ChessPiece(ChessPiece.ROOK, ChessPiece.WHITE, 7, 0));
pieces.add(new ChessPiece(ChessPiece.KNIGHT, ChessPiece.WHITE, 7, 1));
pieces.add(new ChessPiece(ChessPiece.BISHOP, ChessPiece.WHITE, 7, 2));
pieces.add(new ChessPiece(ChessPiece.QUEEN, ChessPiece.WHITE, 7, 3));
pieces.add(new ChessPiece(ChessPiece.KING, ChessPiece.WHITE, 7, 4));
pieces.add(new ChessPiece(ChessPiece.BISHOP, ChessPiece.WHITE, 7, 5));
pieces.add(new ChessPiece(ChessPiece.KNIGHT, ChessPiece.WHITE, 7, 6));
pieces.add(new ChessPiece(ChessPiece.ROOK, ChessPiece.WHITE, 7, 7));
for (int i = 0; i < 8; i++) {
pieces.add(new ChessPiece(ChessPiece.PAWN, ChessPiece.WHITE, 6, i));
}
// 初始化黑方棋子
pieces.add(new ChessPiece(ChessPiece.ROOK, ChessPiece.BLACK, 0, 0));
pieces.add(new ChessPiece(ChessPiece.KNIGHT, ChessPiece.BLACK, 0, 1));
pieces.add(new ChessPiece(ChessPiece.BISHOP, ChessPiece.BLACK, 0, 2));
pieces.add(new ChessPiece(ChessPiece.QUEEN, ChessPiece.BLACK, 0, 3));
pieces.add(new ChessPiece(ChessPiece.KING, ChessPiece.BLACK, 0, 4));
pieces.add(new ChessPiece(ChessPiece.BISHOP, ChessPiece.BLACK, 0, 5));
pieces.add(new ChessPiece(ChessPiece.KNIGHT, ChessPiece.BLACK, 0, 6));
pieces.add(new ChessPiece(ChessPiece.ROOK, ChessPiece.BLACK, 0, 7));
for (int i = 0; i < 8; i++) {
pieces.add(new ChessPiece(ChessPiece.PAWN, ChessPiece.BLACK, 1, i));
}
}
@Override
public void onDraw(Component component, Canvas canvas) {
int width = getWidth();
int height = getHeight();
float cellWidth = width / (float) COLS;
float cellHeight = height / (float) ROWS;
// 绘制棋盘
drawBoard(canvas, width, height, cellWidth, cellHeight);
// 绘制高亮和移动提示
drawHighlights(canvas, cellWidth, cellHeight);
// 绘制棋子
drawPieces(canvas, cellWidth, cellHeight);
}
private void drawBoard(Canvas canvas, int width, int height, float cellWidth, float cellHeight) {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
float left = j * cellWidth;
float top = i * cellHeight;
float right = left + cellWidth;
float bottom = top + cellHeight;
Paint paint = (i + j) % 2 == 0 ? lightPaint : darkPaint;
canvas.drawRect(new RectFloat(left, top, right, bottom), paint);
}
}
}
private void drawHighlights(Canvas canvas, float cellWidth, float cellHeight) {
// 绘制选中的棋子高亮
if (selectedPiece != null) {
float left = selectedPiece.getCol() * cellWidth;
float top = selectedPiece.getRow() * cellHeight;
float right = left + cellWidth;
float bottom = top + cellHeight;
canvas.drawRect(new RectFloat(left, top, right, bottom), highlightPaint);
}
// 绘制可能的移动位置
for (int[] move : possibleMoves) {
int row = move[0];
int col = move[1];
float left = col * cellWidth;
float top = row * cellHeight;
float right = left + cellWidth;
float bottom = top + cellHeight;
canvas.drawRect(new RectFloat(left, top, right, bottom), moveHintPaint);
}
}
private void drawPieces(Canvas canvas, float cellWidth, float cellHeight) {
for (ChessPiece piece : pieces) {
float centerX = (piece.getCol() + 0.5f) * cellWidth;
float centerY = (piece.getRow() + 0.5f) * cellHeight;
textPaint.setColor(piece.getColor() == ChessPiece.WHITE ? Color.WHITE : Color.BLACK);
canvas.drawText(String.valueOf(piece.getSymbol()), centerX, centerY + 15, textPaint);
}
}
private boolean onTouchEvent(Component component, TouchEvent event) {
if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
int width = getWidth();
int height = getHeight();
float cellWidth = width / (float) COLS;
float cellHeight = height / (float) ROWS;
int col = (int) (event.getPointerPosition(0).getX() / cellWidth);
int row = (int) (event.getPointerPosition(0).getY() / cellHeight);
if (row >= 0 && row < ROWS && col >= 0 && col < COLS) {
handleClick(row, col);
}
}
return true;
}
private void handleClick(int row, int col) {
ChessPiece clickedPiece = getPieceAt(row, col);
if (selectedPiece == null) {
// 没有选中棋子时,尝试选中一个棋子
if (clickedPiece != null && clickedPiece.getColor() == currentPlayer) {
selectedPiece = clickedPiece;
calculatePossibleMoves();
invalidate();
}
} else {
// 已经选中了一个棋子
if (clickedPiece != null && clickedPiece.getColor() == currentPlayer) {
// 点击的是己方棋子,切换选中
selectedPiece = clickedPiece;
calculatePossibleMoves();
invalidate();
} else {
// 检查是否是合法的移动
for (int[] move : possibleMoves) {
if (move[0] == row && move[1] == col) {
// 执行移动
movePiece(selectedPiece, row, col);
return;
}
}
}
}
}
private void calculatePossibleMoves() {
possibleMoves.clear();
if (selectedPiece == null) {
return;
}
int fromRow = selectedPiece.getRow();
int fromCol = selectedPiece.getCol();
switch (selectedPiece.getType()) {
case ChessPiece.KING:
calculateKingMoves(fromRow, fromCol);
break;
case ChessPiece.QUEEN:
calculateQueenMoves(fromRow, fromCol);
break;
case ChessPiece.ROOK:
calculateRookMoves(fromRow, fromCol);
break;
case ChessPiece.BISHOP:
calculateBishopMoves(fromRow, fromCol);
break;
case ChessPiece.KNIGHT:
calculateKnightMoves(fromRow, fromCol);
break;
case ChessPiece.PAWN:
calculatePawnMoves(fromRow, fromCol);
break;
}
}
private void calculateKingMoves(int fromRow, int fromCol) {
// 国王可以移动到周围8个格子
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (i == 0 && j == 0) continue;
int toRow = fromRow + i;
int toCol = fromCol + j;
if (isValidPosition(toRow, toCol)) {
ChessPiece target = getPieceAt(toRow, toCol);
if (target == null || target.getColor() != currentPlayer) {
possibleMoves.add(new int[]{toRow, toCol});
}
}
}
}
// 王车易位(待实现)
}
private void calculateQueenMoves(int fromRow, int fromCol) {
// 后可以沿直线和斜线移动任意距离
calculateRookMoves(fromRow, fromCol);
calculateBishopMoves(fromRow, fromCol);
}
private void calculateRookMoves(int fromRow, int fromCol) {
// 车可以沿直线移动任意距离
int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
for (int[] dir : directions) {
for (int step = 1; step < 8; step++) {
int toRow = fromRow + dir[0] * step;
int toCol = fromCol + dir[1] * step;
if (!isValidPosition(toRow, toCol)) break;
ChessPiece target = getPieceAt(toRow, toCol);
if (target == null) {
possibleMoves.add(new int[]{toRow, toCol});
} else {
if (target.getColor() != currentPlayer) {
possibleMoves.add(new int[]{toRow, toCol});
}
break;
}
}
}
}
private void calculateBishopMoves(int fromRow, int fromCol) {
// 象可以沿斜线移动任意距离
int[][] directions = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}};
for (int[] dir : directions) {
for (int step = 1; step < 8; step++) {
int toRow = fromRow + dir[0] * step;
int toCol = fromCol + dir[1] * step;
if (!isValidPosition(toRow, toCol)) break;
ChessPiece target = getPieceAt(toRow, toCol);
if (target == null) {
possibleMoves.add(new int[]{toRow, toCol});
} else {
if (target.getColor() != currentPlayer) {
possibleMoves.add(new int[]{toRow, toCol});
}
break;
}
}
}
}
private void calculateKnightMoves(int fromRow, int fromCol) {
// 马走日字
int[][] moves = {
{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2},
{1, -2}, {1, 2}, {2, -1}, {2, 1}
};
for (int[] move : moves) {
int toRow = fromRow + move[0];
int toCol = fromCol + move[1];
if (isValidPosition(toRow, toCol)) {
ChessPiece target = getPieceAt(toRow, toCol);
if (target == null || target.getColor() != currentPlayer) {
possibleMoves.add(new int[]{toRow, toCol});
}
}
}
}
private void calculatePawnMoves(int fromRow, int fromCol) {
int direction = (currentPlayer == ChessPiece.WHITE) ? -1 : 1;
// 向前移动一格
int toRow = fromRow + direction;
if (isValidPosition(toRow, fromCol) && getPieceAt(toRow, fromCol) == null) {
possibleMoves.add(new int[]{toRow, fromCol});
// 初始位置可以移动两格
if ((currentPlayer == ChessPiece.WHITE && fromRow == 6) ||
(currentPlayer == ChessPiece.BLACK && fromRow == 1)) {
toRow = fromRow + 2 * direction;
if (getPieceAt(toRow, fromCol) == null) {
possibleMoves.add(new int[]{toRow, fromCol});
}
}
}
// 吃子(斜前方)
int[] cols = {fromCol - 1, fromCol + 1};
for (int toCol : cols) {
toRow = fromRow + direction;
if (isValidPosition(toRow, toCol)) {
ChessPiece target = getPieceAt(toRow, toCol);
if (target != null && target.getColor() != currentPlayer) {
possibleMoves.add(new int[]{toRow, toCol});
}
}
}
// 吃过路兵(待实现)
}
private boolean isValidPosition(int row, int col) {
return row >= 0 && row < ROWS && col >= 0 && col < COLS;
}
private ChessPiece getPieceAt(int row, int col) {
for (ChessPiece piece : pieces) {
if (piece.getRow() == row && piece.getCol() == col) {
return piece;
}
}
return null;
}
private void movePiece(ChessPiece piece, int toRow, int toCol) {
// 检查是否吃子
ChessPiece target = getPieceAt(toRow, toCol);
if (target != null) {
pieces.remove(target);
// 检查是否将死
if (target.getType() == ChessPiece.KING) {
gameOver(currentPlayer);
return;
}
}
// 移动棋子
piece.setRow(toRow);
piece.setCol(toCol);
piece.setMoved(true);
// 兵的升变(待实现)
// 切换玩家
currentPlayer = (currentPlayer == ChessPiece.WHITE) ? ChessPiece.BLACK : ChessPiece.WHITE;
selectedPiece = null;
possibleMoves.clear();
invalidate();
if (getContext() instanceof MainAbilitySlice) {
String player = currentPlayer == ChessPiece.WHITE ? "白方" : "黑方";
((MainAbilitySlice) getContext()).updateStatusText("当前回合: " + player);
}
}
private void gameOver(int winner) {
String message = (winner == ChessPiece.WHITE) ? "白方胜利!" : "黑方胜利!";
if (getContext() instanceof MainAbilitySlice) {
((MainAbilitySlice) getContext()).showGameOverDialog(message);
}
// 重置游戏
initPieces();
currentPlayer = ChessPiece.WHITE;
selectedPiece = null;
possibleMoves.clear();
invalidate();
}
public void resetGame() {
initPieces();
currentPlayer = ChessPiece.WHITE;
selectedPiece = null;
possibleMoves.clear();
invalidate();
if (getContext() instanceof MainAbilitySlice) {
((MainAbilitySlice) getContext()).updateStatusText("当前回合: 白方");
}
}
}
MainAbilitySlice.java (主界面)
package com.example.chess;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.window.dialog.ToastDialog;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Text;
import ohos.agp.components.Button;
public class MainAbilitySlice extends AbilitySlice {
private ChessView chessView;
private Text statusText;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
DirectionalLayout layout = new DirectionalLayout(this);
layout.setOrientation(DirectionalLayout.VERTICAL);
// 状态文本
statusText = new Text(this);
statusText.setText("当前回合: 白方");
statusText.setTextSize(50);
statusText.setPadding(10, 10, 10, 10);
// 象棋视图
chessView = new ChessView(this);
// 按钮布局
DirectionalLayout buttonLayout = new DirectionalLayout(this);
buttonLayout.setOrientation(DirectionalLayout.HORIZONTAL);
buttonLayout.setPadding(10, 10, 10, 10);
// 重新开始按钮
Button resetButton = new Button(this);
resetButton.setText("重新开始");
resetButton.setClickedListener(component -> chessView.resetGame());
// 悔棋按钮
Button undoButton = new Button(this);
undoButton.setText("悔棋");
undoButton.setClickedListener(component -> {
new ToastDialog(this).setText("悔棋功能待实现").show();
});
buttonLayout.addComponent(resetButton);
buttonLayout.addComponent(undoButton);
layout.addComponent(statusText);
layout.addComponent(chessView);
layout.addComponent(buttonLayout);
super.setUIContent(layout);
}
public void showGameOverDialog(String message) {
new ToastDialog(this)
.setText(message)
.show();
statusText.setText("游戏结束 - " + message);
}
public void updateStatusText(String text) {
statusText.setText(text);
}
}
ability_main.xml (布局文件)
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical">
<Text
ohos:id="$+id:status_text"
ohos:width="match_parent"
ohos:height="50vp"
ohos:text="当前回合: 白方"
ohos:text_size="20fp"
ohos:padding="10vp"/>
<com.example.chess.ChessView
ohos:id="$+id:chess_view"
ohos:width="match_parent"
ohos:height="0vp"
ohos:weight="1"/>
<DirectionalLayout
ohos:id="$+id:button_layout"
ohos:width="match_parent"
ohos:height="wrap_content"
ohos:orientation="horizontal"
ohos:padding="10vp">
<Button
ohos:id="$+id:reset_button"
ohos:width="0vp"
ohos:height="50vp"
ohos:weight="1"
ohos:text="重新开始"
ohos:margin="5vp"/>
<Button
ohos:id="$+id:undo_button"
ohos:width="0vp"
ohos:height="50vp"
ohos:weight="1"
ohos:text="悔棋"
ohos:margin="5vp"/>
</DirectionalLayout>
</DirectionalLayout>
3. 功能说明
- 游戏规则:标准国际象棋规则
- 棋子类型:
- 王(King)、后(Queen)、车(Rook)、象(Bishop)、马(Knight)、兵(Pawn)
- 界面元素:
- 棋盘:8×8网格,交替颜色
- 棋子:使用Unicode字符表示
- 状态显示:当前回合信息
- 操作按钮:重新开始、悔棋(待实现)
- 交互:
- 点击选中棋子,显示可移动位置
- 再次点击目标位置移动棋子
- 自动判断移动是否合法
- 自动判断胜负