1. 项目概述
本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。
2. 项目结构
/src/main/java/com/example/militarychess/
├── MainAbilitySlice.java // 主界面
├── GameView.java // 游戏核心逻辑
├── ChessPiece.java // 棋子类
├── GameLogic.java // 游戏规则逻辑
└── resources/
├── base/
│ ├── layout/
│ │ └── ability_main.xml // 布局文件
│ └── graphic/
│ ├── piece_red.xml // 红方棋子样式
│ └── piece_blue.xml // 蓝方棋子样式
3. 实现步骤
3.1 棋子类实现(ChessPiece.java)
package com.example.militarychess;
public class ChessPiece {
public static final int RED = 0;
public static final int BLUE = 1;
// 棋子类型
public static final int FLAG = 0; // 军旗
public static final int MINE = 1; // 地雷
public static final int BOMB = 2; // 炸弹
public static final int MARSHAL = 3; // 司令
public static final int GENERAL = 4; // 军长
public static final int DIVISION = 5; // 师长
public static final int BRIGADE = 6; // 旅长
public static final int REGIMENT = 7; // 团长
public static final int BATTALION = 8; // 营长
public static final int COMPANY = 9; // 连长
public static final int PLATOON = 10; // 排长
public static final int ENGINEER = 11; // 工兵
private int type; // 棋子类型
private int color; // 棋子颜色
private int row; // 行
private int col; // 列
private boolean visible = 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 isVisible() { return visible; }
public void setVisible(boolean visible) { this.visible = visible; }
// 获取棋子名称
public String getName() {
String[] names = {"军旗", "地雷", "炸弹", "司令", "军长", "师长",
"旅长", "团长", "营长", "连长", "排长", "工兵"};
return names[type];
}
// 获取棋子简称
public String getShortName() {
String[] names = {"旗", "雷", "弹", "司", "军", "师",
"旅", "团", "营", "连", "排", "工"};
return names[type];
}
}
3.2 游戏视图实现(GameView.java)
package com.example.militarychess;
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.List;
public class GameView extends Component implements Component.DrawTask {
private static final int ROWS = 12; // 棋盘行数
private static final int COLS = 5; // 棋盘列数
private Paint linePaint; // 棋盘线画笔
private Paint redPiecePaint; // 红方棋子画笔
private Paint bluePiecePaint; // 蓝方棋子画笔
private Paint textPaint; // 文字画笔
private Paint selectedPaint; // 选中状态画笔
private List<ChessPiece> pieces; // 棋子列表
private ChessPiece selectedPiece; // 当前选中的棋子
private int currentPlayer = ChessPiece.RED; // 当前玩家
public GameView(Context context) {
super(context);
init();
}
public GameView(Context context, AttrSet attrSet) {
super(context, attrSet);
init();
}
private void init() {
linePaint = new Paint();
linePaint.setColor(new Color(0xFF000000));
linePaint.setStrokeWidth(2);
redPiecePaint = new Paint();
redPiecePaint.setColor(new Color(0xFFFF0000));
redPiecePaint.setStyle(Paint.Style.FILL_STYLE);
bluePiecePaint = new Paint();
bluePiecePaint.setColor(new Color(0xFF0000FF));
bluePiecePaint.setStyle(Paint.Style.FILL_STYLE);
textPaint = new Paint();
textPaint.setColor(new Color(0xFFFFFFFF));
textPaint.setTextSize(40);
textPaint.setTextAlign(Paint.Align.CENTER);
selectedPaint = new Paint();
selectedPaint.setColor(new Color(0x6600FF00));
selectedPaint.setStyle(Paint.Style.FILL_STYLE);
initPieces();
addDrawTask(this);
setTouchEventListener(this::onTouchEvent);
}
private void initPieces() {
// 初始化棋子布局
// 这里应该实现具体的棋子初始化逻辑
// 包括红蓝双方的棋子摆放
}
@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);
// 绘制棋子
drawPieces(canvas, cellWidth, cellHeight);
}
private void drawBoard(Canvas canvas, int width, int height, float cellWidth, float cellHeight) {
// 绘制横线
for (int i = 0; i <= ROWS; i++) {
float y = i * cellHeight;
canvas.drawLine(0, y, width, y, linePaint);
}
// 绘制竖线
for (int j = 0; j <= COLS; j++) {
float x = j * cellWidth;
canvas.drawLine(x, 0, x, height, linePaint);
}
// 绘制行营和大本营
drawSpecialAreas(canvas, cellWidth, cellHeight);
}
private void drawSpecialAreas(Canvas canvas, float cellWidth, float cellHeight) {
// 实现行营和大本营的特殊标记
}
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;
float radius = Math.min(cellWidth, cellHeight) * 0.4f;
// 绘制棋子背景
Paint bgPaint = piece.getColor() == ChessPiece.RED ? redPiecePaint : bluePiecePaint;
canvas.drawCircle(centerX, centerY, radius, bgPaint);
// 绘制棋子文字
if (piece.isVisible() || piece.getColor() == currentPlayer) {
canvas.drawText(piece.getShortName(), centerX, centerY + 15, textPaint);
}
// 绘制选中状态
if (piece == selectedPiece) {
canvas.drawCircle(centerX, centerY, radius + 5, selectedPaint);
}
}
}
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;
invalidate();
}
} else {
// 已经选中了一个棋子
if (clickedPiece != null && clickedPiece.getColor() == currentPlayer) {
// 点击的是己方棋子,切换选中
selectedPiece = clickedPiece;
invalidate();
} else {
// 尝试移动棋子
if (isValidMove(selectedPiece, row, col)) {
movePiece(selectedPiece, row, col);
}
}
}
}
private ChessPiece getPieceAt(int row, int col) {
for (ChessPiece piece : pieces) {
if (piece.getRow() == row && piece.getCol() == col) {
return piece;
}
}
return null;
}
private boolean isValidMove(ChessPiece piece, int toRow, int toCol) {
// 实现军棋移动规则判断
// 包括铁路线移动、普通移动、吃子规则等
return true;
}
private void movePiece(ChessPiece piece, int toRow, int toCol) {
// 实现棋子移动逻辑
// 包括吃子判断、胜负判断等
// 移动完成后切换玩家
currentPlayer = (currentPlayer == ChessPiece.RED) ? ChessPiece.BLUE : ChessPiece.RED;
selectedPiece = null;
invalidate();
}
public void resetGame() {
initPieces();
currentPlayer = ChessPiece.RED;
selectedPiece = null;
invalidate();
}
}
3.3 主界面实现(MainAbilitySlice.java)
package com.example.militarychess;
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);
}
private void showRules() {
new ToastDialog(this)
.setText("军棋规则:\n1. 工兵可以挖地雷\n2. 炸弹可以与任何棋子同归于尽\n3. 大本营内的棋子不能移动")
.show();
}
}
3.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.militarychess.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>
4. 游戏规则实现要点
4.1 棋子移动规则
private boolean isValidMove(ChessPiece piece, int toRow, int toCol) {
// 1. 检查目标位置是否合法
if (toRow < 0 || toRow >= ROWS || toCol < 0 || toCol >= COLS) {
return false;
}
// 2. 检查是否是大本营(不能移动)
if (isHeadquarters(piece.getRow(), piece.getCol())) {
return false;
}
// 3. 铁路线移动规则
if (isRailway(piece.getRow(), piece.getCol())) {
// 铁路线上可以任意距离直线移动
return canMoveOnRailway(piece, toRow, toCol);
}
// 4. 普通移动规则(一步一格)
int rowDiff = Math.abs(toRow - piece.getRow());
int colDiff = Math.abs(toCol - piece.getCol());
return (rowDiff == 1 && colDiff == 0) || (rowDiff == 0 && colDiff == 1);
}
4.2 吃子规则
private boolean canEat(ChessPiece attacker, ChessPiece defender) {
// 1. 炸弹可以和任何棋子同归于尽
if (attacker.getType() == ChessPiece.BOMB || defender.getType() == ChessPiece.BOMB) {
return true;
}
// 2. 工兵可以挖地雷
if (attacker.getType() == ChessPiece.ENGINEER && defender.getType() == ChessPiece.MINE) {
return true;
}
// 3. 地雷只能被工兵挖
if (defender.getType() == ChessPiece.MINE) {
return false;
}
// 4. 军旗不能被移动
if (defender.getType() == ChessPiece.FLAG) {
return false;
}
// 5. 普通棋子比较大小
return attacker.getType() <= defender.getType();
}
5. 特殊功能实现
5.1 行营保护
private boolean isCamp(int row, int col) {
// 定义行营位置
int[][] camps = {{1, 1}, {1, 3}, {3, 1}, {3, 3},
{8, 1}, {8, 3}, {10, 1}, {10, 3}};
for (int[] camp : camps) {
if (camp[0] == row && camp[1] == col) {
return true;
}
}
return false;
}
5.2 铁路线移动
private boolean canMoveOnRailway(ChessPiece piece, int toRow, int toCol) {
// 检查是否在同一条铁路线上
if (piece.getRow() != toRow && piece.getCol() != toCol) {
return false;
}
// 检查路径上是否有其他棋子阻挡
if (piece.getRow() == toRow) {
int minCol = Math.min(piece.getCol(), toCol);
int maxCol = Math.max(piece.getCol(), toCol);
for (int col = minCol + 1; col < maxCol; col++) {
if (getPieceAt(piece.getRow(), col) != null) {
return false;
}
}
} else {
int minRow = Math.min(piece.getRow(), toRow);
int maxRow = Math.max(piece.getRow(), toRow);
for (int row = minRow + 1; row < maxRow; row++) {
if (getPieceAt(row, piece.getCol()) != null) {
return false;
}
}
}
return true;
}
6. 扩展功能建议
- 网络对战:实现双人在线对战功能
- AI对手:添加不同难度的电脑AI
- 游戏回放:记录和回放游戏过程
- 音效系统:添加移动、吃子等音效
- 动画效果:棋子移动和战斗动画
- 战绩统计:记录玩家胜负数据
- 多种布局:提供不同的初始棋子布局