用鸿蒙HarmonyOS5实现五子棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的五子棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。

1. 创建项目结构

首先创建一个鸿蒙应用项目,结构如下:

复制代码
/src/main/java/com/example/gobang/
    ├── MainAbilitySlice.java    // 主界面逻辑
    ├── GameView.java           // 游戏视图和逻辑
    └── resources/
        ├── base/
        │   ├── layout/
        │   │   └── ability_main.xml  // 布局文件
        │   └── graphic/
        │       └── board_bg.xml      // 棋盘背景

2. 实现代码

GameView.java (游戏核心逻辑)

复制代码
package com.example.gobang;

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;

public class GameView extends Component implements Component.DrawTask {
    private static final int ROWS = 15; // 棋盘行数
    private static final int COLS = 15; // 棋盘列数
    private static final int WIN_COUNT = 5; // 胜利条件
    
    private int[][] board; // 0-空 1-黑子 2-白子
    private boolean isBlackTurn = true; // 黑棋先行
    private boolean gameOver = false;
    private OnGameOverListener listener;
    
    private Paint linePaint;
    private Paint blackPaint;
    private Paint whitePaint;
    private Paint lastMovePaint;
    
    private int lastMoveX = -1;
    private int lastMoveY = -1;
    
    public GameView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        board = new int[ROWS][COLS];
        
        linePaint = new Paint();
        linePaint.setColor(new Color(0xFF000000));
        linePaint.setStrokeWidth(2);
        
        blackPaint = new Paint();
        blackPaint.setColor(new Color(0xFF000000));
        blackPaint.setStyle(Paint.Style.FILL_STYLE);
        
        whitePaint = new Paint();
        whitePaint.setColor(new Color(0xFFFFFFFF));
        whitePaint.setStyle(Paint.Style.FILL_STYLE);
        
        lastMovePaint = new Paint();
        lastMovePaint.setColor(new Color(0xFFFF0000));
        lastMovePaint.setStrokeWidth(3);
        
        addDrawTask(this);
        setClickedListener((component) -> handleClick());
    }
    
    @Override
    public void onDraw(Component component, Canvas canvas) {
        int width = getWidth();
        int height = getHeight();
        float cellSize = Math.min(width, height) / (float) Math.max(ROWS, COLS);
        
        // 绘制棋盘
        for (int i = 0; i < ROWS; i++) {
            float y = i * cellSize + cellSize / 2;
            canvas.drawLine(0, y, width, y, linePaint);
        }
        
        for (int j = 0; j < COLS; j++) {
            float x = j * cellSize + cellSize / 2;
            canvas.drawLine(x, 0, x, height, linePaint);
        }
        
        // 绘制棋子
        for (int i = 0; i < ROWS; i++) {
            for (int j = 0; j < COLS; j++) {
                if (board[i][j] == 1) {
                    drawPiece(canvas, i, j, cellSize, blackPaint);
                } else if (board[i][j] == 2) {
                    drawPiece(canvas, i, j, cellSize, whitePaint);
                }
            }
        }
        
        // 标记最后一步
        if (lastMoveX != -1 && lastMoveY != -1) {
            float centerX = lastMoveY * cellSize + cellSize / 2;
            float centerY = lastMoveX * cellSize + cellSize / 2;
            float radius = cellSize / 4;
            canvas.drawCircle(centerX, centerY, radius, lastMovePaint);
        }
    }
    
    private void drawPiece(Canvas canvas, int row, int col, float cellSize, Paint paint) {
        float centerX = col * cellSize + cellSize / 2;
        float centerY = row * cellSize + cellSize / 2;
        float radius = cellSize / 2 - 5;
        canvas.drawCircle(centerX, centerY, radius, paint);
    }
    
    private void handleClick() {
        if (gameOver) return;
        
        int width = getWidth();
        int height = getHeight();
        float cellSize = Math.min(width, height) / (float) Math.max(ROWS, COLS);
        
        // 获取点击位置
        float x = getTouchPoint().getX();
        float y = getTouchPoint().getY();
        
        int row = Math.round((y - cellSize / 2) / cellSize);
        int col = Math.round((x - cellSize / 2) / cellSize);
        
        if (row >= 0 && row < ROWS && col >= 0 && col < COLS && board[row][col] == 0) {
            board[row][col] = isBlackTurn ? 1 : 2;
            lastMoveX = row;
            lastMoveY = col;
            
            if (checkWin(row, col)) {
                gameOver = true;
                if (listener != null) {
                    listener.onGameOver(isBlackTurn ? "黑棋胜利!" : "白棋胜利!");
                }
            } else if (isBoardFull()) {
                gameOver = true;
                if (listener != null) {
                    listener.onGameOver("平局!");
                }
            } else {
                isBlackTurn = !isBlackTurn;
            }
            
            invalidate();
        }
    }
    
    private boolean checkWin(int row, int col) {
        int player = board[row][col];
        return checkDirection(row, col, 1, 0, player) || // 水平
               checkDirection(row, col, 0, 1, player) || // 垂直
               checkDirection(row, col, 1, 1, player) || // 对角线
               checkDirection(row, col, 1, -1, player);  // 反对角线
    }
    
    private boolean checkDirection(int row, int col, int dx, int dy, int player) {
        int count = 1;
        
        // 正向检查
        for (int i = 1; i < WIN_COUNT; i++) {
            int r = row + i * dx;
            int c = col + i * dy;
            if (r < 0 || r >= ROWS || c < 0 || c >= COLS || board[r][c] != player) {
                break;
            }
            count++;
        }
        
        // 反向检查
        for (int i = 1; i < WIN_COUNT; i++) {
            int r = row - i * dx;
            int c = col - i * dy;
            if (r < 0 || r >= ROWS || c < 0 || c >= COLS || board[r][c] != player) {
                break;
            }
            count++;
        }
        
        return count >= WIN_COUNT;
    }
    
    private boolean isBoardFull() {
        for (int i = 0; i < ROWS; i++) {
            for (int j = 0; j < COLS; j++) {
                if (board[i][j] == 0) {
                    return false;
                }
            }
        }
        return true;
    }
    
    public void reset() {
        for (int i = 0; i < ROWS; i++) {
            for (int j = 0; j < COLS; j++) {
                board[i][j] = 0;
            }
        }
        isBlackTurn = true;
        gameOver = false;
        lastMoveX = -1;
        lastMoveY = -1;
        invalidate();
    }
    
    public void setOnGameOverListener(OnGameOverListener listener) {
        this.listener = listener;
    }
    
    public interface OnGameOverListener {
        void onGameOver(String message);
    }
}

MainAbilitySlice.java (主界面)

复制代码
package com.example.gobang;

import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Text;
import ohos.agp.window.dialog.ToastDialog;

public class MainAbilitySlice extends AbilitySlice {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        
        DirectionalLayout layout = new DirectionalLayout(this);
        layout.setOrientation(DirectionalLayout.VERTICAL);
        
        // 状态文本
        Text statusText = new Text(this);
        statusText.setText("当前回合: 黑棋");
        statusText.setTextSize(50);
        statusText.setPadding(10, 10, 10, 10);
        
        // 游戏视图
        GameView gameView = new GameView(this);
        gameView.setOnGameOverListener(message -> {
            new ToastDialog(this)
                .setText(message)
                .show();
            statusText.setText("游戏结束 - " + message);
        });
        
        // 重置按钮
        Button resetButton = new Button(this);
        resetButton.setText("重新开始");
        resetButton.setPadding(10, 10, 10, 10);
        resetButton.setClickedListener(component -> {
            gameView.reset();
            statusText.setText("当前回合: 黑棋");
        });
        
        // 添加组件到布局
        layout.addComponent(statusText);
        layout.addComponent(gameView);
        layout.addComponent(resetButton);
        
        super.setUIContent(layout);
    }
}

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.gobang.GameView
        ohos:id="$+id:game_view"
        ohos:width="match_parent"
        ohos:height="0vp"
        ohos:weight="1"/>
        
    <Button
        ohos:id="$+id:reset_button"
        ohos:width="match_parent"
        ohos:height="50vp"
        ohos:text="重新开始"
        ohos:margin="10vp"/>
</DirectionalLayout>

3. 功能说明

  1. ​游戏规则​:标准五子棋规则,先连成五子的一方获胜
  2. ​界面元素​
    • 棋盘:15×15网格
    • 棋子:黑色和白色圆形
    • 状态显示:当前回合信息
    • 重置按钮:重新开始游戏
  3. ​交互​
    • 点击棋盘落子
    • 自动判断胜负
    • 显示最后一步落子位置
相关推荐
二流小码农几秒前
鸿蒙开发:简单实现一个服务卡片
harmonyos
量子位29 分钟前
Day0 迁移、一键部署,华为开源的昇思 MindSpore 成为大模型开发的 “万能钥匙”
人工智能·华为
移动端开发者1 小时前
鸿蒙Next数据面板组件DataPanel介绍
harmonyos
移动端开发者1 小时前
鸿蒙Next使用Canvas绘制一个汽车仪表盘
harmonyos
移动端开发者1 小时前
鸿蒙Next数据量环形图标Gauge介绍
harmonyos
塞尔维亚大汉1 小时前
鸿蒙开发面试真题:鸿蒙操作系统的微内核架构有哪些优势?
面试·harmonyos
我睡醒再说2 小时前
纯血Harmony NETX 5小游戏实践:2048(附源文件)
游戏·华为·harmonyos·arkts
程序员小刘3 小时前
基于uni-app for HarmonyOS5 的跨平台组件库开发指南,以及组件示例
华为·uni-app·harmonyos
别说我什么都不会4 小时前
【OpenHarmony】 鸿蒙事件通信:socket-io
harmonyos
颜颜颜yan_4 小时前
【HarmonyOS5】UIAbility组件生命周期详解:从创建到销毁的全景解析
后端·架构·harmonyos