本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目是一款基于Android平台的五子棋小游戏,使用Android Studio开发,结合Java或Kotlin语言实现。应用包含完整的游戏逻辑与交互功能,支持两人对战模式,具备良好的用户界面和响应式布局。项目涵盖Android开发的多个核心技术点,包括界面设计、触控事件处理、胜负判断算法、构建与发布流程,适合用于掌握移动端游戏开发全流程的实战学习。
基于Android开发的五子棋小游戏

1. Android五子棋游戏开发概述

五子棋作为一款经典的策略棋类游戏,因其规则简洁、逻辑清晰,深受各年龄层用户的喜爱。随着移动互联网的普及,将五子棋移植到Android平台,不仅具备良好的用户基础,也为开发者提供了一个实践Android应用开发技巧的理想项目。

本章将介绍为何选择Android平台进行五子棋开发,涵盖其在移动设备上的交互特性与开发便捷性,并明确项目目标:实现落子、悔棋、重置、胜负判断等核心功能。同时,将说明开发工具(如Android Studio)与编程语言(Kotlin为主)的选型依据,为后续章节的开发打下坚实基础。

2. Android开发环境搭建与项目结构配置

Android开发的第一步是搭建开发环境。一个稳定、高效的开发环境对于五子棋这类小游戏的快速迭代和调试至关重要。本章将详细介绍如何安装和配置 Android Studio,创建五子棋项目并配置其基础结构,同时探讨开发过程中常见的问题及其解决方案。

2.1 Android Studio安装与配置

作为 Android 开发的官方推荐 IDE,Android Studio 提供了完整的开发、调试和测试工具链。其基于 IntelliJ IDEA 构建,支持 Kotlin、Java 等主流语言,是开发 Android 应用的首选工具。

2.1.1 下载与安装Android Studio

访问 Android Studio 官网 下载适用于你操作系统的安装包(Windows、macOS 或 Linux)。下载完成后,按照安装向导逐步完成安装流程。

提示 :建议选择“Custom”安装选项,手动选择安装组件以节省磁盘空间。

安装完成后启动 Android Studio,首次启动会提示选择是否导入已有的配置。如果是首次使用,选择“Do not import settings”即可。

2.1.2 配置JDK与Android SDK

Android Studio 安装完成后,默认会安装所需的 JDK 和 Android SDK。但为了确保环境稳定,建议检查以下配置:

JDK配置
  • Android Studio 从 2020 年起内置了 JetBrains Runtime(JBR),兼容性较好。
  • 如果需要使用特定版本的 JDK(如 JDK 11),可在 File > Project Structure > SDKs 中添加。
Android SDK配置
  1. 打开 Settings (Preferences on macOS)。
  2. 进入 Appearance & Behavior > System Settings
  3. 确认 SDK 路径是否正确(默认路径为 C:\Users\用户名\AppData\Local\Android\Sdk )。
  4. Android SDK 页面中选择需要的 SDK 版本(建议选择 Android 10 及以上)。
  5. 安装必要的构建工具(Build-Tools)、平台工具(Platform-Tools)等。

注意 :部分插件或依赖库可能要求特定版本的 SDK 工具,安装时请保持 Build-Tools 与 Gradle 插件版本兼容。

2.1.3 创建并配置AVD虚拟设备

为了在本地测试应用,需要创建 Android Virtual Device(AVD)。

创建 AVD 的步骤如下:
  1. 打开 Tools > Device Manager
  2. 点击 Create device
  3. 选择设备型号(如 Pixel 3a)和系统镜像(建议选择 Google Play 版本)。
  4. 设置内存大小、SD 卡容量等参数。
  5. 完成后点击 Finish ,设备将出现在设备列表中。

提示 :若遇到 AVD 启动缓慢问题,可尝试启用硬件加速(在 BIOS 中开启虚拟化支持)。

示例代码:查看 AVD 命令行信息
# 查看已创建的 AVD 列表
emulator -list-avds

# 启动指定 AVD
emulator -avd Pixel_3a_API_34

2.2 新建五子棋项目并设置基本结构

完成开发环境配置后,接下来将创建五子棋游戏项目并配置其基本结构。

2.2.1 使用Empty Activity模板创建项目

  1. 打开 Android Studio,选择 New Project
  2. 选择 Empty Activity 模板。
  3. 设置项目名称为 FiveInARow
  4. 选择语言为 Kotlin (推荐)或 Java。
  5. 设置最小 SDK 为 API 21(Android 5.0)以上以支持现代特性。
  6. 完成项目创建。

提示 :Kotlin 语言简洁、安全且与 Java 完全兼容,推荐用于新项目开发。

2.2.2 项目目录结构解析(Gradle配置、资源目录等)

创建项目后,可以看到如下主要目录结构:

目录 描述
app/src/main/java Java/Kotlin 源码目录
app/src/main/res 资源文件目录(布局、图片、字符串等)
app/src/main/AndroidManifest.xml 应用清单文件
build.gradle (Project) 项目级 Gradle 构建脚本
build.gradle (Module: app) 模块级 Gradle 构建脚本
gradle.properties Gradle 构建配置文件
示例代码:查看 build.gradle (Module: app) 内容
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.fiveinarow'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.fiveinarow"
        minSdk 21
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.8.0'
}
代码逻辑分析:
  • namespace :应用程序的包名。
  • compileSdk :编译所使用的 Android SDK 版本。
  • defaultConfig :应用的基础配置信息。
  • buildTypes :构建类型配置(如 debug 和 release)。
  • dependencies :模块依赖项,包括 Kotlin、Material Design 等库。

2.2.3 添加必要的依赖库与支持库

为了增强五子棋游戏的功能,我们可以添加以下常用依赖库:

功能
androidx.lifecycle:lifecycle-viewmodel-ktx ViewModel 管理游戏状态
androidx.lifecycle:lifecycle-livedata-ktx LiveData 实现数据驱动 UI
androidx.activity:activity-ktx 更简洁的 Activity 生命周期处理
示例代码:添加 ViewModel 支持

build.gradle (Module: app) 中添加以下依赖:

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'

同步 Gradle 后,即可在项目中使用 ViewModel 和 LiveData。

2.3 开发环境常见问题与解决方案

在开发过程中,开发者常常会遇到环境配置问题,以下是一些常见问题及其解决方法。

2.3.1 启动失败与SDK路径问题

问题描述:

Android Studio 启动时报错,提示找不到 SDK 或路径错误。

解决方案:
  1. 打开 File > Settings > Appearance & Behavior > System Settings
  2. 检查 SDK 路径是否正确。
  3. 如果路径错误,重新指定 SDK 路径。
  4. 若 SDK 损坏,可通过 SDK Manager 重新下载。
示例命令:手动检查 SDK 状态
# 进入 SDK tools 目录
cd $ANDROID_SDK_ROOT/tools/bin

# 列出可用镜像
sdkmanager --list

# 更新 SDK
sdkmanager --update

2.3.2 Gradle同步错误与依赖冲突处理

问题描述:

Gradle 同步失败,提示依赖冲突或版本不匹配。

解决方案:
  1. 查看报错信息中的依赖名称和版本。
  2. build.gradle (Module: app) 中统一版本号。
  3. 使用 implementation 替代 compile (已弃用)。
  4. 使用 --stacktrace --info 查看详细错误。
示例代码:解决依赖冲突
configurations.all {
    resolutionStrategy {
        force 'androidx.appcompat:appcompat:1.6.1'
        force 'androidx.core:core-ktx:1.9.0'
    }
}
流程图:Gradle 构建流程
graph TD
    A[开始构建] --> B[解析 build.gradle 文件]
    B --> C[下载依赖]
    C --> D[编译源码]
    D --> E[打包 APK]
    E --> F[部署到设备]
    F --> G[运行应用]

通过本章内容,我们完成了 Android 开发环境的搭建、五子棋项目的创建与基础结构配置,并探讨了开发过程中常见的问题及其解决方法。接下来的章节中,我们将深入探讨五子棋游戏界面的设计与交互实现。

3. 五子棋游戏界面设计与用户交互实现

在五子棋游戏的开发过程中,界面设计和用户交互是决定用户体验的关键因素。一个清晰、直观的界面不仅能让用户快速理解游戏规则,还能增强游戏的可玩性。本章将从游戏主界面布局设计入手,逐步深入讲解如何通过XML和自定义View实现棋盘绘制、如何绑定触摸事件与落子逻辑,以及如何增强用户交互体验,包括悔棋、重置和状态提示功能的实现。

3.1 游戏主界面布局设计

3.1.1 使用XML实现棋盘网格布局

Android界面布局通常通过XML文件进行描述。为了实现五子棋的棋盘网格,我们可以使用 GridLayout LinearLayout 来模拟15x15的棋盘格。

<!-- res/layout/activity_main.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="16dp">

    <GridLayout
        android:id="@+id/chess_board"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:columnCount="15"
        android:rowCount="15"
        android:background="@drawable/board_background">
    </GridLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:paddingTop="16dp">

        <Button
            android:id="@+id/btn_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="重置" />

        <Button
            android:id="@+id/btn_undo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="悔棋" />
    </LinearLayout>

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="游戏进行中"
        android:textSize="18sp"
        android:layout_marginTop="16dp"/>
</LinearLayout>

代码逻辑分析:

  • 使用 LinearLayout 作为根布局,保证上下结构清晰;
  • 使用 GridLayout 来模拟15x15的棋盘,每个格子将放置一个棋子(ImageButton或ImageView);
  • 添加重置和悔棋按钮,方便用户操作;
  • 添加状态TextView,用于显示当前游戏状态(如“黑方胜利”、“白方胜利”、“平局”等);
  • 设置 layout_weight 属性使棋盘占据主要空间,保持界面比例合理;
  • 棋盘背景通过 board_background 资源设置,可为棋盘增加视觉美感。

参数说明:

  • android:columnCount android:rowCount :设置棋盘的行列数;
  • android:layout_weight :控制布局权重,实现高度自适应;
  • android:gravity :控制子控件的对齐方式。

3.1.2 自定义View绘制棋盘与棋子

为了实现更灵活的棋盘绘制和动画效果,我们可以通过继承 View 类,自定义一个 ChessBoardView ,并在其中绘制棋盘和棋子。

class ChessBoardView(context: Context, attrs: AttributeSet) : View(context, attrs) {
    private val paint = Paint()
    private val blackPiece = BitmapFactory.decodeResource(resources, R.drawable.black_piece)
    private val whitePiece = BitmapFactory.decodeResource(resources, R.drawable.white_piece)
    private val boardSize = 15 // 棋盘大小
    private val cellSize = 0f // 每个格子的大小
    private val pieces = Array(boardSize) { Array<Bitmap?>(boardSize) { null } }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        cellSize = w / boardSize.toFloat()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        // 绘制棋盘线
        paint.color = Color.BLACK
        paint.strokeWidth = 2f
        for (i in 0 until boardSize) {
            canvas.drawLine(i * cellSize, 0f, i * cellSize, height.toFloat(), paint)
            canvas.drawLine(0f, i * cellSize, width.toFloat(), i * cellSize, paint)
        }

        // 绘制棋子
        for (i in 0 until boardSize) {
            for (j in 0 until boardSize) {
                pieces[i][j]?.let { bitmap ->
                    val x = i * cellSize
                    val y = j * cellSize
                    canvas.drawBitmap(bitmap, x - bitmap.width / 2, y - bitmap.height / 2, null)
                }
            }
        }
    }

    fun placePiece(row: Int, col: Int, isBlack: Boolean) {
        pieces[row][col] = if (isBlack) blackPiece else whitePiece
        invalidate()
    }

    fun clearBoard() {
        for (i in 0 until boardSize) {
            for (j in 0 until boardSize) {
                pieces[i][j] = null
            }
        }
        invalidate()
    }
}

代码逻辑分析:

  • onSizeChanged 中计算每个格子的大小 cellSize ,确保棋盘适配不同屏幕;
  • onDraw 方法中绘制棋盘的网格线和已落子的棋子;
  • placePiece 方法用于在指定位置绘制棋子;
  • clearBoard 用于清空棋盘;
  • 使用 BitmapFactory 加载棋子图片资源,避免内存浪费。

参数说明:

  • boardSize :棋盘大小(15x15);
  • cellSize :每个格子的实际像素大小;
  • pieces :二维数组用于记录棋子的位置;
  • paint :绘制棋盘的画笔对象;
  • invalidate() :触发View的重绘,确保界面更新。

3.1.3 使用ConstraintLayout实现响应式界面

为了适配不同屏幕尺寸并实现更灵活的布局,我们可以使用 ConstraintLayout 替代 LinearLayout GridLayout ,提高界面的响应性。

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.gomoku.ChessBoardView
        android:id="@+id/chessBoardView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btnLayout"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintVertical_chainStyle="packed"/>

    <LinearLayout
        android:id="@+id/btnLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">

        <Button
            android:id="@+id/btn_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="重置" />

        <Button
            android:id="@+id/btn_undo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="悔棋" />
    </LinearLayout>

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="游戏进行中"
        android:textSize="18sp"
        app:layout_constraintTop_toBottomOf="@id/chessBoardView"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btnLayout"
        app:layout_constraintVertical_chainStyle="packed"/>
</androidx.constraintlayout.widget.ConstraintLayout>

逻辑分析:

  • ConstraintLayout 通过约束链实现棋盘、按钮和状态文本的垂直分布;
  • 棋盘 ChessBoardView 通过约束上下左右来自动填充可用空间;
  • 按钮和状态信息通过约束链实现垂直居中排列;
  • 这种方式比 LinearLayout 更灵活,适配性更强。

3.2 触摸事件监听与落子逻辑绑定

3.2.1 实现OnTouchListener监听用户点击

用户通过点击屏幕来落子,因此需要为 ChessBoardView 添加 OnTouchListener ,监听用户的触摸事件。

chessBoardView.setOnTouchListener { _, event ->
    if (event.action == MotionEvent.ACTION_DOWN) {
        val row = (event.y / cellSize).toInt()
        val col = (event.x / cellSize).toInt()
        // 调用落子逻辑
        placePiece(row, col)
    }
    true
}

逻辑分析:

  • 通过 MotionEvent.ACTION_DOWN 判断用户点击动作;
  • 计算点击位置对应的行和列;
  • 调用 placePiece 方法完成落子操作;
  • 返回 true 表示事件已被消费。

参数说明:

  • event.x event.y :获取点击的坐标;
  • cellSize :每个格子的像素大小;
  • row col :对应棋盘的行列索引。

3.2.2 将坐标转换为棋盘行列索引

在触摸事件中获取的坐标是屏幕像素坐标,需要将其转换为棋盘的行列索引:

val row = (event.y / cellSize).toInt()
val col = (event.x / cellSize).toInt()

逻辑分析:

  • 用点击坐标除以每个格子的大小,得到行和列索引;
  • 由于 cellSize 是浮点数,除法结果需使用 toInt() 转换为整数;
  • 注意边界处理,避免超出棋盘范围。

3.2.3 落子动画与音效反馈机制

为了增强用户交互体验,可以在落子时添加动画和音效。

private var soundPool: SoundPool? = null
private var blackSoundId = 0
private var whiteSoundId = 0

private fun initSound(context: Context) {
    soundPool = SoundPool.Builder().setMaxStreams(2).build().apply {
        blackSoundId = load(context, R.raw.black_piece_sound, 1)
        whiteSoundId = load(context, R.raw.white_piece_sound, 1)
    }
}

private fun playSound(isBlack: Boolean) {
    soundPool?.play(
        if (isBlack) blackSoundId else whiteSoundId,
        1f, 1f, 0, 0, 1f
    )
}

动画实现示例:

val animator = ObjectAnimator.ofFloat(pieceView, "scaleX", 0f, 1f).setDuration(200)
ObjectAnimator.ofFloat(pieceView, "scaleY", 0f, 1f).setDuration(200).start()
animator.start()

逻辑分析:

  • 使用 SoundPool 加载音频资源,播放落子音效;
  • 动画通过 ObjectAnimator 实现棋子放大效果;
  • 提升用户反馈,增强沉浸感。

3.3 用户交互增强功能实现

3.3.1 悔棋功能的实现与棋盘状态回退

悔棋功能需要记录每次落子的历史记录,并提供回退功能。

data class Move(val row: Int, val col: Int, val isBlack: Boolean)

private val moveHistory = Stack<Move>()

fun placePiece(row: Int, col: Int, isBlack: Boolean) {
    // 记录历史
    moveHistory.push(Move(row, col, isBlack))
    pieces[row][col] = if (isBlack) blackPiece else whitePiece
    invalidate()
}

fun undoLastMove() {
    if (moveHistory.isNotEmpty()) {
        val lastMove = moveHistory.pop()
        pieces[lastMove.row][lastMove.col] = null
        invalidate()
    }
}

逻辑分析:

  • 使用 Stack 保存每次落子的动作;
  • undoLastMove 弹出栈顶动作,清空对应棋子;
  • invalidate() 触发界面重绘,实现悔棋效果。

3.3.2 重置按钮的点击事件与数据清空

重置按钮用于清空棋盘和所有历史记录。

btn_reset.setOnClickListener {
    chessBoardView.clearBoard()
    moveHistory.clear()
    tv_status.text = "游戏进行中"
}

逻辑分析:

  • 调用 clearBoard() 清空棋子;
  • 清空 moveHistory 历史记录;
  • 重置状态文本。

3.3.3 提示信息与游戏状态显示设计

游戏状态提示信息通过 TextView 实时更新。

fun updateGameStatus(status: String) {
    tv_status.text = status
}

状态示例:

  • “黑方胜利”
  • “白方胜利”
  • “平局”
  • “轮到黑方”
  • “轮到白方”

mermaid流程图:

graph TD
    A[用户点击棋盘] --> B[获取坐标]
    B --> C[转换为行列]
    C --> D[判断是否合法]
    D -- 合法 --> E[落子]
    E --> F[更新状态]
    F --> G{胜负判断}
    G -- 是 --> H[显示胜利信息]
    G -- 否 --> I[切换玩家]

表格:游戏状态码对照表

状态码 含义
0 游戏进行中
1 黑方胜利
2 白方胜利
3 平局
4 轮到黑方
5 轮到白方

本章从界面布局设计、自定义View绘制、触摸事件绑定,到交互增强功能(悔棋、重置、状态提示)进行了详尽讲解,并结合代码、表格和流程图展示了实现细节。通过本章内容,读者可以掌握Android五子棋游戏界面设计与交互实现的核心技术,为后续游戏逻辑开发打下坚实基础。

4. 五子棋核心逻辑与胜负判断算法设计

本章深入讲解五子棋游戏的核心逻辑实现,包括棋盘数据结构的设计与实现、胜负判断算法的编写以及游戏逻辑的模块化封装。通过本章内容,开发者将掌握如何高效管理棋盘状态、实现准确的胜负判断逻辑,并通过模块化设计提升代码的可维护性和可扩展性。这些内容对于中高级Android开发者来说,将有助于构建更复杂的游戏逻辑体系。

4.1 棋盘数据结构设计与实现

4.1.1 使用二维数组表示棋盘状态

在五子棋游戏中,棋盘通常为15x15的网格,每一格可以是空位、黑子或白子。为高效表示棋盘状态,最常用的数据结构是二维数组。

public class ChessBoard {
    private static final int EMPTY = 0;
    private static final int BLACK = 1;
    private static final int WHITE = 2;

    private int[][] board;

    public ChessBoard(int size) {
        board = new int[size][size];
        resetBoard();
    }

    public void resetBoard() {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                board[i][j] = EMPTY;
            }
        }
    }

    public int getCell(int row, int col) {
        return board[row][col];
    }

    public boolean setCell(int row, int col, int player) {
        if (board[row][col] == EMPTY) {
            board[row][col] = player;
            return true;
        }
        return false;
    }
}

代码解释:
- EMPTY BLACK WHITE 三个常量分别表示空位、黑子、白子;
- board 为二维数组,表示棋盘上每个位置的状态;
- resetBoard() 方法用于初始化或重置棋盘;
- setCell() 方法用于设置某一位置的棋子,并确保该位置为空。

逻辑分析:
二维数组结构简单、访问高效,适合用于棋盘状态管理。每个位置的值表示当前是否被占据及由哪一方占据,便于后续的胜负判断逻辑实现。

4.1.2 数据结构的初始化与更新策略

在游戏初始化阶段,需要对棋盘进行初始化操作。更新策略则是在每次落子后同步更新棋盘状态。

public class GameViewModel {
    private ChessBoard chessBoard;
    private int currentPlayer;

    public GameViewModel() {
        chessBoard = new ChessBoard(15);
        currentPlayer = ChessBoard.BLACK;
    }

    public boolean placePiece(int row, int col) {
        if (chessBoard.setCell(row, col, currentPlayer)) {
            switchPlayer();
            return true;
        }
        return false;
    }

    private void switchPlayer() {
        currentPlayer = (currentPlayer == ChessBoard.BLACK) ? ChessBoard.WHITE : ChessBoard.BLACK;
    }

    public int getCurrentPlayer() {
        return currentPlayer;
    }

    public void resetGame() {
        chessBoard.resetBoard();
        currentPlayer = ChessBoard.BLACK;
    }
}

代码解释:
- GameViewModel 类封装了游戏逻辑和棋盘数据;
- placePiece() 方法用于在指定位置落子,并切换玩家;
- resetGame() 方法用于重置游戏状态。

更新策略分析:
每次落子成功后,更新棋盘状态并切换玩家,保证游戏流程的正确执行。初始化和重置逻辑保持一致,便于在悔棋或重新开始时快速恢复初始状态。

4.1.3 棋盘数据的持久化与恢复

为了实现悔棋功能或保存游戏进度,可以将棋盘数据进行持久化处理,例如保存为文件或数据库。

public void saveGameState(Context context, int[][] boardState) {
    try {
        FileOutputStream fos = context.openFileOutput("game_state.dat", Context.MODE_PRIVATE);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(boardState);
        oos.close();
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public int[][] loadGameState(Context context) {
    try {
        FileInputStream fis = context.openFileInput("game_state.dat");
        ObjectInputStream ois = new ObjectInputStream(fis);
        int[][] boardState = (int[][]) ois.readObject();
        ois.close();
        fis.close();
        return boardState;
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
        return null;
    }
}

代码解释:
- saveGameState() 方法将棋盘状态保存为本地文件;
- loadGameState() 方法从文件中恢复棋盘状态;
- 使用 ObjectOutputStream ObjectInputStream 实现对象序列化和反序列化。

持久化策略分析:
通过序列化方式保存棋盘数据,可以实现游戏状态的完整恢复。适用于悔棋、退出重进等场景,增强用户体验。

表格:棋盘数据结构比较

数据结构 优点 缺点 适用场景
二维数组 简单高效,易于访问 不支持动态扩展 固定大小棋盘(如15x15)
SparseArray 内存效率高 实现复杂,访问效率略低 大棋盘、稀疏数据
List > 支持动态扩展 性能略差 需要动态调整棋盘尺寸

4.2 游戏规则与胜负判断算法

4.2.1 判断五子连珠的逻辑分析(横向、纵向、斜向)

胜负判断的核心逻辑是判断某一位置是否形成“五子连珠”。需要检查四个方向:横向、纵向、左斜向、右斜向。

public boolean checkWin(int row, int col, int player) {
    return checkDirection(row, col, player, 1, 0) || // 横向
           checkDirection(row, col, player, 0, 1) || // 纵向
           checkDirection(row, col, player, 1, 1) || // 左斜向
           checkDirection(row, col, player, 1, -1);  // 右斜向
}

private boolean checkDirection(int row, int col, int player, int dx, int dy) {
    int count = 1;
    count += countInDirection(row, col, player, dx, dy);
    count += countInDirection(row, col, player, -dx, -dy);
    return count >= 5;
}

private int countInDirection(int row, int col, int player, int dx, int dy) {
    int count = 0;
    int r = row + dx;
    int c = col + dy;
    while (r >= 0 && r < 15 && c >= 0 && c < 15 && board[r][c] == player) {
        count++;
        r += dx;
        c += dy;
    }
    return count;
}

代码解释:
- checkWin() 方法用于判断当前落子是否导致胜利;
- checkDirection() 方法检查某个方向上的连续棋子数量;
- countInDirection() 方法用于在某一方向上统计连续棋子数。

逻辑分析:
每次落子后,从该位置出发,在四个方向上分别统计连续的同色棋子数量,若任一方向上达到5个,则判定胜利。

4.2.2 使用方向向量优化判断效率

使用方向向量可以统一四个方向的判断逻辑,提高代码可读性和效率。

private final int[][] DIRECTIONS = {
    {1, 0},  // 横向
    {0, 1},  // 纵向
    {1, 1},  // 左斜向
    {1, -1}  // 右斜向
};

public boolean checkWin(int row, int col, int player) {
    for (int[] dir : DIRECTIONS) {
        int count = 1;
        count += countInDirection(row, col, player, dir[0], dir[1]);
        count += countInDirection(row, col, player, -dir[0], -dir[1]);
        if (count >= 5) return true;
    }
    return false;
}

代码解释:
- DIRECTIONS 数组保存四个方向的向量;
- checkWin() 方法遍历所有方向,统一调用 countInDirection() 方法进行统计。

效率优化分析:
使用方向向量统一逻辑,避免重复代码,也便于后续扩展(如增加新方向)。

4.2.3 多人对战与AI判断逻辑的统一处理

在多人对战和AI对战中,胜负判断逻辑是相同的。因此,可以将胜负判断逻辑封装为独立方法,供不同模式调用。

public interface GameListener {
    void onGameWin(int winner);
    void onGameDraw();
}

public class GameController {
    private GameViewModel viewModel;
    private GameListener listener;

    public void handleMove(int row, int col) {
        if (viewModel.placePiece(row, col)) {
            if (viewModel.checkWin(row, col)) {
                listener.onGameWin(viewModel.getCurrentPlayer());
            } else if (viewModel.isBoardFull()) {
                listener.onGameDraw();
            }
        }
    }
}

代码解释:
- GameListener 接口用于通知游戏状态变化;
- GameController 类统一处理玩家落子、胜负判断和游戏状态通知。

交互设计分析:
通过接口回调机制,将胜负判断结果通知到UI层或AI逻辑层,实现逻辑与UI的解耦,提高代码复用性。

mermaid流程图:胜负判断流程

graph TD
    A[落子] --> B{是否形成五子连珠}
    B -- 是 --> C[通知胜利]
    B -- 否 --> D{是否棋盘已满}
    D -- 是 --> E[通知平局]
    D -- 否 --> F[继续游戏]

4.3 游戏逻辑的封装与模块化设计

4.3.1 将游戏逻辑封装为独立类或ViewModel

为提高代码可维护性和复用性,将游戏逻辑封装到 ViewModel 中,使用 LiveData 实现数据驱动的UI更新。

public class GameViewModel extends AndroidViewModel {
    private MutableLiveData<int[][]> boardLiveData;
    private MutableLiveData<Integer> currentPlayerLiveData;
    private ChessBoard chessBoard;

    public GameViewModel(@NonNull Application application) {
        super(application);
        chessBoard = new ChessBoard(15);
        boardLiveData = new MutableLiveData<>();
        currentPlayerLiveData = new MutableLiveData<>(ChessBoard.BLACK);
    }

    public void placePiece(int row, int col) {
        if (chessBoard.setCell(row, col, currentPlayerLiveData.getValue())) {
            if (chessBoard.checkWin(row, col, currentPlayerLiveData.getValue())) {
                // 触发胜利状态
            }
            switchPlayer();
            boardLiveData.setValue(chessBoard.getBoard());
        }
    }

    public LiveData<int[][]> getBoardLiveData() {
        return boardLiveData;
    }

    public LiveData<Integer> getCurrentPlayerLiveData() {
        return currentPlayerLiveData;
    }

    private void switchPlayer() {
        currentPlayerLiveData.setValue(
            (currentPlayerLiveData.getValue() == ChessBoard.BLACK) ? ChessBoard.WHITE : ChessBoard.BLACK
        );
    }
}

代码解释:
- GameViewModel 继承 AndroidViewModel ,用于生命周期感知;
- 使用 LiveData 实现棋盘和玩家状态的实时更新;
- placePiece() 方法触发落子、胜负判断和玩家切换。

封装逻辑分析:
通过 ViewModel LiveData 的组合,实现数据与UI的分离,使得游戏逻辑更加清晰、可测试性强。

4.3.2 使用LiveData实现数据驱动UI更新

在UI层观察 LiveData ,自动更新界面状态。

public class GameActivity extends AppCompatActivity {
    private GameViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game);

        viewModel = new ViewModelProvider(this).get(GameViewModel.class);

        viewModel.getBoardLiveData().observe(this, board -> {
            // 更新棋盘UI
        });

        viewModel.getCurrentPlayerLiveData().observe(this, player -> {
            // 更新当前玩家提示
        });
    }

    public void onCellClick(int row, int col) {
        viewModel.placePiece(row, col);
    }
}

代码解释:
- 在 GameActivity 中观察 LiveData ,实现自动更新;
- 点击事件调用 ViewModel 的落子方法。

数据驱动优势:
通过 LiveData ,UI层无需手动管理状态更新,提升开发效率和稳定性。

4.3.3 游戏逻辑与UI层的解耦实践

将游戏逻辑与UI分离,提高代码可维护性和可测试性。

public class GameLogic {
    private ChessBoard chessBoard;
    private int currentPlayer;

    public GameLogic() {
        chessBoard = new ChessBoard(15);
        currentPlayer = ChessBoard.BLACK;
    }

    public boolean placePiece(int row, int col) {
        if (chessBoard.setCell(row, col, currentPlayer)) {
            if (checkWin(row, col)) {
                // 触发胜利
            }
            switchPlayer();
            return true;
        }
        return false;
    }

    public int[][] getBoardState() {
        return chessBoard.getBoard();
    }

    public int getCurrentPlayer() {
        return currentPlayer;
    }

    private void switchPlayer() {
        currentPlayer = (currentPlayer == ChessBoard.BLACK) ? ChessBoard.WHITE : ChessBoard.BLACK;
    }

    public boolean checkWin(int row, int col) {
        return chessBoard.checkWin(row, col, currentPlayer);
    }
}

代码解释:
- GameLogic 类封装了完整的游戏逻辑;
- 提供获取棋盘状态和当前玩家的方法;
- 所有胜负判断逻辑在内部完成。

解耦设计分析:
将游戏逻辑完全封装在 GameLogic 类中,UI层仅负责展示和事件处理,实现高内聚、低耦合的架构设计。

通过本章的深入讲解,开发者可以全面掌握五子棋游戏的核心逻辑实现,包括棋盘数据结构的设计、胜负判断算法的编写以及模块化封装策略。这些内容不仅适用于五子棋开发,也适用于其他棋类或策略类游戏的开发实践。

5. Android应用发布与实战优化总结

本章聚焦于Android应用的最终发布流程与实战优化策略。通过前几章的开发,五子棋游戏的核心功能已基本实现,包括落子、悔棋、胜负判断等。本章将围绕应用的发布准备、兼容性处理、构建流程以及后期优化方向展开,帮助开发者顺利完成从开发到上线的全过程。

5.1 移动端适配与兼容性处理

5.1.1 多种屏幕尺寸的响应式布局适配

在Android开发中,适配不同屏幕尺寸是确保用户体验一致性的关键环节。以下是几种常用的适配策略:

  • 使用ConstraintLayout :通过约束布局实现灵活的控件定位,适应不同分辨率。
  • 资源目录限定符 :如 layout-sw600dp drawable-xhdpi 等,为不同设备提供专属资源。
  • 使用dp与sp单位 :避免使用px,确保在不同密度屏幕上显示一致。
<!-- 示例:使用ConstraintLayout实现适配布局 -->
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/chess_board"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintWidth_percent="0.9"
        app:layout_constraintHeight_percent="0.9"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

5.1.2 不同Android版本的兼容性处理

从Android 6.0(API 23)开始,系统引入了运行时权限机制,开发者需特别注意以下几点:

  • 使用 ContextCompat.checkSelfPermission() 检查权限状态。
  • 使用 ActivityCompat.requestPermissions() 请求权限。
  • 针对Android 10以上版本,需适配Scoped Storage。
// 请求存储权限示例
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}

5.1.3 横竖屏切换与状态保存

为防止横竖屏切换导致界面重绘和数据丢失,可通过以下方式处理:

  • AndroidManifest.xml 中为Activity添加 android:configChanges="orientation|screenSize"
  • 覆写 onConfigurationChanged() 方法。
  • 使用 ViewModel onSaveInstanceState() 保存临时状态。
// 使用ViewModel保存棋盘状态
class GameViewModel : ViewModel() {
    val boardState = MutableLiveData<Array<Array<Int>>>()
}

5.2 APK构建与发布流程

5.2.1 配置签名文件(Keystore)生成

为了发布正式版本,必须生成签名密钥文件(Keystore)。可通过Android Studio的 Build > Generate Signed Bundle / APK 功能完成:

  1. 选择“Create New”创建新的Keystore。
  2. 填写别名(Key Alias)、密码、有效期等信息。
  3. 保存keystore文件并妥善保管。

生成后的配置如下所示:

storeFile=release.keystore
storePassword=your_store_password
keyAlias=your_key_alias
keyPassword=your_key_password

5.2.2 使用Gradle构建Release版本

build.gradle 中配置签名配置,并构建Release版本:

android {
    ...
    signingConfigs {
        release {
            storeFile file("release.keystore")
            storePassword "your_store_password"
            keyAlias "your_key_alias"
            keyPassword "your_key_password"
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

使用以下命令构建APK:

./gradlew assembleRelease

构建完成后,APK文件位于 app/release/ 目录下。

5.2.3 发布到Google Play或国内应用市场

  • Google Play :注册开发者账号(25美元),上传APK文件,填写应用信息、截图、隐私政策等。
  • 国内应用市场 :如应用宝、华为应用市场、小米应用商店等,需分别注册账号并提交审核。

发布前需注意:

  • 应用图标与截图需符合各平台规范。
  • 提供应用简介、更新日志、联系方式。
  • 确保无敏感权限滥用,避免审核驳回。

5.3 项目总结与后续优化方向

5.3.1 五子棋游戏开发过程中的经验总结

通过本项目的开发,我们掌握了以下关键技术点:

  • Android界面布局与自定义View绘制。
  • 触摸事件监听与坐标转换。
  • 游戏逻辑封装与数据绑定(ViewModel + LiveData)。
  • 多版本兼容与适配策略。

项目中遇到的问题如:

  • 横竖屏切换导致的棋盘状态丢失。
  • 多点触控导致的落子冲突。
  • 胜负判断算法的性能瓶颈。

这些问题的解决提升了对Android平台特性的理解。

5.3.2 可扩展功能:AI对战、网络联机、计时系统

为进一步提升游戏体验,可考虑以下功能扩展:

  • AI对战 :引入Minimax算法或Alpha-Beta剪枝实现智能AI。
  • 网络联机 :使用WebSocket或Firebase实现在线对战。
  • 计时系统 :为每位玩家添加倒计时,提升竞技性。

例如,添加计时功能可使用 CountDownTimer

new CountDownTimer(60000, 1000) {
    public void onTick(long millisUntilFinished) {
        textView.setText("剩余时间: " + millisUntilFinished / 1000);
    }
    public void onFinish() {
        textView.setText("时间到!");
    }
}.start();

5.3.3 Android开发中常见的性能优化与代码规范建议

  • 性能优化
  • 减少主线程耗时操作,使用 Handler AsyncTask
  • 避免内存泄漏,使用弱引用或LeakCanary检测。
  • 图片资源使用Glide或Picasso加载,避免OOM。

  • 代码规范建议

  • 使用Kotlin替代Java,提升代码简洁性。
  • 遵循MVC/MVVM架构,保持职责分离。
  • 使用Lint工具检查代码质量,统一命名规范。
graph TD
    A[项目开发] --> B[UI设计]
    A --> C[核心逻辑]
    A --> D[数据绑定]
    B --> E[响应式布局]
    C --> F[胜负判断算法]
    D --> G[ViewModel + LiveData]
    E --> H[适配多分辨率]
    F --> I[方向向量优化]
    G --> J[数据驱动UI]
    H --> K[ConstraintLayout]
    I --> L[横向纵向斜向扫描]
    J --> M[实时更新棋子]

通过以上优化与扩展,五子棋游戏将更具完整性和可维护性,同时也为后续Android项目开发提供了良好的实践基础。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目是一款基于Android平台的五子棋小游戏,使用Android Studio开发,结合Java或Kotlin语言实现。应用包含完整的游戏逻辑与交互功能,支持两人对战模式,具备良好的用户界面和响应式布局。项目涵盖Android开发的多个核心技术点,包括界面设计、触控事件处理、胜负判断算法、构建与发布流程,适合用于掌握移动端游戏开发全流程的实战学习。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐