1. 项目概述
本跳棋小游戏基于鸿蒙HarmonyOS 5开发,包含完整的游戏逻辑和UI界面,支持六人跳棋对战。
2. 项目结构
/src/main/java/com/example/chinesecheckers/
├── MainAbilitySlice.java // 主界面
├── GameView.java // 游戏核心逻辑
├── Piece.java // 棋子类
├── Player.java // 玩家类
└── resources/
├── base/
│ ├── layout/
│ │ └── ability_main.xml // 布局文件
│ └── graphic/
│ ├── piece_red.xml // 红方棋子样式
│ ├── piece_blue.xml // 蓝方棋子样式
│ └── board_bg.xml // 棋盘背景
3. 核心代码实现
3.1 棋子类(Piece.java)
package com.example.chinesecheckers;
public class Piece {
public static final int RED = 0;
public static final int BLUE = 1;
public static final int GREEN = 2;
public static final int YELLOW = 3;
public static final int PURPLE = 4;
public static final int ORANGE = 5;
private int color; // 棋子颜色
private int row; // 行坐标
private int col; // 列坐标
private boolean selected = false; // 是否选中
public Piece(int color, int row, int col) {
this.color = color;
this.row = row;
this.col = col;
}
// getter和setter方法
public int getColor() { return color; }
public int getRow() { return row; }
public int getCol() { return col; }
public boolean isSelected() { return selected; }
public void setSelected(boolean selected) { this.selected = selected; }
public void setPosition(int row, int col) { this.row = row; this.col = col; }
// 获取棋子颜色值
public int getColorValue() {
switch(color) {
case RED: return 0xFFFF0000;
case BLUE: return 0xFF0000FF;
case GREEN: return 0xFF00FF00;
case YELLOW: return 0xFFFFFF00;
case PURPLE: return 0xFF800080;
case ORANGE: return 0xFFFFA500;
default: return 0xFF000000;
}
}
}
3.2 游戏视图(GameView.java)
package com.example.chinesecheckers;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.app.Context;
import java.util.ArrayList;
import java.util.List;
public class GameView extends Component implements Component.DrawTask {
private static final int BOARD_SIZE = 17; // 棋盘大小(六角形边长)
private Paint boardPaint;
private Paint piecePaint;
private Paint selectedPaint;
private Paint hintPaint;
private List<Piece> pieces = new ArrayList<>();
private Piece selectedPiece = null;
private List<Point> possibleMoves = new ArrayList<>();
private int currentPlayer = Piece.RED;
public GameView(Context context) {
super(context);
init();
}
private void init() {
boardPaint = new Paint();
boardPaint.setColor(new Color(0xFF8B4513)); // 棕色棋盘
piecePaint = new Paint();
piecePaint.setStyle(Paint.Style.FILL_STYLE);
selectedPaint = new Paint();
selectedPaint.setColor(new Color(0x66FFFF00)); // 半透明黄色
hintPaint = new Paint();
hintPaint.setColor(new Color(0x6600FF00)); // 半透明绿色
initPieces();
addDrawTask(this);
setTouchEventListener(this::onTouchEvent);
}
private void initPieces() {
// 初始化六方棋子(每个玩家10颗棋子)
// 红方
initPlayerPieces(Piece.RED, 0, 0);
// 蓝方
initPlayerPieces(Piece.BLUE, 0, BOARD_SIZE-1);
// 其他颜色类似...
}
private void initPlayerPieces(int color, int startRow, int startCol) {
// 根据起始位置初始化玩家棋子
// 实际实现需要考虑六角形棋盘的布局
}
@Override
public void onDraw(Component component, Canvas canvas) {
int width = getWidth();
int height = getHeight();
float hexSize = Math.min(width, height) / (BOARD_SIZE * 1.5f);
float centerX = width / 2f;
float centerY = height / 2f;
// 绘制棋盘
drawBoard(canvas, centerX, centerY, hexSize);
// 绘制可移动位置提示
drawMoveHints(canvas, hexSize);
// 绘制棋子
drawPieces(canvas, hexSize);
}
private void drawBoard(Canvas canvas, float centerX, float centerY, float hexSize) {
// 绘制六角形棋盘
// 实际实现需要绘制多个六边形组成棋盘
}
private void drawMoveHints(Canvas canvas, float hexSize) {
for (Point move : possibleMoves) {
float centerX = getHexCenterX(move.x, move.y, hexSize);
float centerY = getHexCenterY(move.x, move.y, hexSize);
canvas.drawCircle(centerX, centerY, hexSize * 0.3f, hintPaint);
}
}
private void drawPieces(Canvas canvas, float hexSize) {
for (Piece piece : pieces) {
float centerX = getHexCenterX(piece.getRow(), piece.getCol(), hexSize);
float centerY = getHexCenterY(piece.getRow(), piece.getCol(), hexSize);
// 绘制棋子
piecePaint.setColor(new Color(piece.getColorValue()));
canvas.drawCircle(centerX, centerY, hexSize * 0.4f, piecePaint);
// 绘制选中状态
if (piece.isSelected()) {
canvas.drawCircle(centerX, centerY, hexSize * 0.45f, selectedPaint);
}
}
}
private float getHexCenterX(int row, int col, float hexSize) {
// 计算六角形格子的中心X坐标
return hexSize * 1.5f * col + (row % 2) * hexSize * 0.75f;
}
private float getHexCenterY(int row, int col, float hexSize) {
// 计算六角形格子的中心Y坐标
return hexSize * (float)(Math.sqrt(3) / 2) * row;
}
private boolean onTouchEvent(Component component, TouchEvent event) {
if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
int width = getWidth();
int height = getHeight();
float hexSize = Math.min(width, height) / (BOARD_SIZE * 1.5f);
// 获取点击位置
float x = event.getPointerPosition(0).getX();
float y = event.getPointerPosition(0).getY();
// 转换为六角形格子坐标
int[] hexCoord = getHexCoordinate(x, y, hexSize);
if (hexCoord != null) {
handleClick(hexCoord[0], hexCoord[1]);
}
}
return true;
}
private int[] getHexCoordinate(float x, float y, float hexSize) {
// 将屏幕坐标转换为六角形格子坐标
// 实现略...
return new int[]{row, col};
}
private void handleClick(int row, int col) {
// 查找点击的棋子
Piece clickedPiece = getPieceAt(row, col);
if (selectedPiece == null) {
// 选中棋子
if (clickedPiece != null && clickedPiece.getColor() == currentPlayer) {
selectedPiece = clickedPiece;
selectedPiece.setSelected(true);
calculatePossibleMoves();
invalidate();
}
} else {
// 尝试移动棋子
if (clickedPiece != null && clickedPiece.getColor() == currentPlayer) {
// 切换选中
selectedPiece.setSelected(false);
selectedPiece = clickedPiece;
selectedPiece.setSelected(true);
calculatePossibleMoves();
invalidate();
} else {
// 检查是否是合法移动
for (Point move : possibleMoves) {
if (move.x == row && move.y == col) {
movePiece(selectedPiece, row, col);
return;
}
}
}
}
}
private Piece getPieceAt(int row, int col) {
for (Piece piece : pieces) {
if (piece.getRow() == row && piece.getCol() == col) {
return piece;
}
}
return null;
}
private void calculatePossibleMoves() {
possibleMoves.clear();
if (selectedPiece == null) return;
// 实现跳棋移动规则
// 1. 相邻移动
addAdjacentMoves(selectedPiece.getRow(), selectedPiece.getCol());
// 2. 跳跃移动(递归查找所有可能的跳跃路径)
findJumpMoves(selectedPiece.getRow(), selectedPiece.getCol(), new boolean[BOARD_SIZE][BOARD_SIZE]);
}
private void addAdjacentMoves(int row, int col) {
// 六角形六个方向的相邻格子
int[][] directions = {
{0, -1}, {0, 1}, // 左右
{-1, 0}, {1, 0}, // 上下
{row % 2 == 0 ? -1 : 1, -1}, {row % 2 == 0 ? -1 : 1, 1} // 斜向
};
for (int[] dir : directions) {
int newRow = row + dir[0];
int newCol = col + dir[1];
if (isValidPosition(newRow, newCol) && getPieceAt(newRow, newCol) == null) {
possibleMoves.add(new Point(newRow, newCol));
}
}
}
private void findJumpMoves(int row, int col, boolean[][] visited) {
if (visited[row][col]) return;
visited[row][col] = true;
// 六角形六个方向的跳跃
int[][] directions = {
{0, -2}, {0, 2}, // 左右跳跃
{-2, 0}, {2, 0}, // 上下跳跃
{row % 2 == 0 ? -2 : 2, -2}, {row % 2 == 0 ? -2 : 2, 2} // 斜向跳跃
};
for (int[] dir : directions) {
int newRow = row + dir[0];
int newCol = col + dir[1];
if (isValidPosition(newRow, newCol) && getPieceAt(newRow, newCol) == null) {
// 检查中间是否有棋子
int midRow = (row + newRow) / 2;
int midCol = (col + newCol) / 2;
if (getPieceAt(midRow, midCol) != null) {
possibleMoves.add(new Point(newRow, newCol));
findJumpMoves(newRow, newCol, visited); // 递归查找连续跳跃
}
}
}
}
private boolean isValidPosition(int row, int col) {
return row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE;
}
private void movePiece(Piece piece, int toRow, int toCol) {
piece.setPosition(toRow, toCol);
piece.setSelected(false);
selectedPiece = null;
possibleMoves.clear();
// 检查游戏是否结束
if (checkWin(piece.getColor())) {
gameOver(piece.getColor());
return;
}
// 切换到下一个玩家
switchToNextPlayer();
invalidate();
}
private boolean checkWin(int color) {
// 检查玩家是否将所有棋子移动到对面区域
// 实现略...
return false;
}
private void switchToNextPlayer() {
// 六玩家轮流顺序
currentPlayer = (currentPlayer + 1) % 6;
}
private void gameOver(int winner) {
String[] colors = {"红方", "蓝方", "绿方", "黄方", "紫方", "橙方"};
String message = colors[winner] + "获胜!";
if (getContext() instanceof MainAbilitySlice) {
((MainAbilitySlice) getContext()).showGameOverDialog(message);
}
// 重置游戏
resetGame();
}
public void resetGame() {
pieces.clear();
initPieces();
currentPlayer = Piece.RED;
selectedPiece = null;
possibleMoves.clear();
invalidate();
}
}
3.3 主界面(MainAbilitySlice.java)
package com.example.chinesecheckers;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.window.dialog.ToastDialog;
public class MainAbilitySlice extends AbilitySlice {
private GameView gameView;
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);
// 游戏视图
gameView = new GameView(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 -> gameView.resetGame());
// 规则说明按钮
Button ruleButton = new Button(this);
ruleButton.setText("游戏规则");
ruleButton.setClickedListener(component -> showRules());
buttonLayout.addComponent(resetButton);
buttonLayout.addComponent(ruleButton);
layout.addComponent(statusText);
layout.addComponent(gameView);
layout.addComponent(buttonLayout);
super.setUIContent(layout);
}
public void showGameOverDialog(String message) {
new ToastDialog(this)
.setText(message)
.show();
}
private void showRules() {
new ToastDialog(this)
.setText("跳棋规则:\n1. 每次可以移动一步或连续跳跃\n2. 先将所有棋子移动到对面为胜\n3. 点击棋子显示可移动位置")
.show();
}
}
4. 布局文件(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"
ohos:background_element="#F5F5F5">
<Text
ohos:id="$+id:status_text"
ohos:width="match_parent"
ohos:height="50vp"
ohos:text="当前回合: 红方"
ohos:text_size="20fp"
ohos:text_color="#FF0000"
ohos:padding="10vp"
ohos:margin="5vp"
ohos:background_element="#FFFFFF"/>
<com.example.chinesecheckers.GameView
ohos:id="$+id:game_view"
ohos:width="match_parent"
ohos:height="0vp"
ohos:weight="1"
ohos:margin="5vp"
ohos:background_element="#FFFFFF"/>
<DirectionalLayout
ohos:id="$+id:button_layout"
ohos:width="match_parent"
ohos:height="wrap_content"
ohos:orientation="horizontal"
ohos:padding="10vp"
ohos:margin="5vp"
ohos:background_element="#FFFFFF">
<Button
ohos:id="$+id:reset_button"
ohos:width="0vp"
ohos:height="50vp"
ohos:weight="1"
ohos:text="重新开始"
ohos:text_size="16fp"
ohos:margin="5vp"/>
<Button
ohos:id="$+id:rule_button"
ohos:width="0vp"
ohos:height="50vp"
ohos:weight="1"
ohos:text="游戏规则"
ohos:text_size="16fp"
ohos:margin="5vp"/>
</DirectionalLayout>
</DirectionalLayout>
5. 游戏规则实现要点
5.1 六角形棋盘坐标系统
// 六角形格子坐标转换为屏幕坐标
private float getHexCenterX(int row, int col, float hexSize) {
return hexSize * 1.5f * col + (row % 2) * hexSize * 0.75f;
}
private float getHexCenterY(int row, int col, float hexSize) {
return hexSize * (float)(Math.sqrt(3) / 2) * row;
}
// 屏幕坐标转换为六角形格子坐标
private int[] getHexCoordinate(float x, float y, float hexSize) {
float hexHeight = (float)(Math.sqrt(3) / 2) * hexSize;
int row = Math.round(y / hexHeight);
float hexWidth = hexSize * 1.5f;
int col = Math.round((x - (row % 2) * hexSize * 0.75f) / hexWidth);
// 检查是否在有效范围内
if (row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE) {
return new int[]{row, col};
}
return null;
}
5.2 跳跃移动算法
private void findJumpMoves(int row, int col, boolean[][] visited) {
if (visited[row][col]) return;
visited[row][col] = true;
int[][] directions = {
{0, -2}, {0, 2}, // 左右跳跃
{-2, 0}, {2, 0}, // 上下跳跃
{row % 2 == 0 ? -2 : 2, -2}, {row % 2 == 0 ? -2 : 2, 2} // 斜向跳跃
};
for (int[] dir : directions) {
int newRow = row + dir[0];
int newCol = col + dir[1];
if (isValidPosition(newRow, newCol) && getPieceAt(newRow, newCol) == null) {
// 检查中间是否有棋子
int midRow = (row + newRow) / 2;
int midCol = (col + newCol) / 2;
if (getPieceAt(midRow, midCol) != null) {
possibleMoves.add(new Point(newRow, newCol));
findJumpMoves(newRow, newCol, visited); // 递归查找连续跳跃
}
}
}
}