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() {// 初始化棋子布局// 这里应该实现具体的棋子初始化逻辑// 包括红蓝双方的棋子摆放}@Overridepublic 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;@Overridepublic 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"?>
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:width="match_parent"ohos:height="match_parent"ohos:orientation="vertical"ohos:background_element="#F5F5F5"><Textohos: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.GameViewohos:id="$+id:game_view"ohos:width="match_parent"ohos:height="0vp"ohos:weight="1"ohos:margin="5vp"ohos:background_element="#FFFFFF"/><DirectionalLayoutohos: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"><Buttonohos:id="$+id:reset_button"ohos:width="0vp"ohos:height="50vp"ohos:weight="1"ohos:text="重新开始"ohos:text_size="16fp"ohos:margin="5vp"/><Buttonohos: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
- 游戏回放:记录和回放游戏过程
- 音效系统:添加移动、吃子等音效
- 动画效果:棋子移动和战斗动画
- 战绩统计:记录玩家胜负数据
- 多种布局:提供不同的初始棋子布局