很多鸿蒙新手入门时,都会陷入一个误区:只会抄写官方Demo,不会独立开发完整项目。想要真正吃透ArkTS声明式UI、响应式状态管理、交互逻辑开发,最好的方式就是动手做一个轻量化、功能闭环的实战项目。
今天给大家带来一款超适合新手的 鸿蒙原生石头剪刀布小游戏,基于 HarmonyOS NEXT API23/24 开发,全程纯原生 ArkTS 实现,无第三方依赖。项目涵盖随机算法、胜负逻辑、动态UI渲染、数据统计、状态重置等核心能力,代码简洁、逻辑清晰,非常适合作为鸿蒙入门练手、课程设计、期末作业!
一、项目概述
1.1 项目简介
本项目是一款人机对战的石头剪刀布休闲小游戏,用户可自主选择石头、剪刀、布三种手势,系统随机生成电脑手势,自动判定对局胜负,实时统计对局数据、双方胜场、平局次数,支持一键重置所有数据。
项目虽小,但完整覆盖了移动端开发的核心要素:UI布局、状态驱动、用户交互、算法逻辑、数据统计、动态样式。
1.2 核心功能亮点
•✅ 人机随机对战:电脑自动随机出拳,对局公平随机
•✅ 动态胜负判定:精准实现石头剪刀布经典胜负规则
•✅ 动态UI样式:胜利绿色、失败红色、平局灰色,视觉直观
•✅ 完整数据统计:总局数、玩家胜场、电脑胜场、平局次数
•✅ 流畅动画交互:手势切换自带过渡动画,体验更佳
•✅ 一键重置功能:支持清空所有对局数据,重新开局
•✅ 条件渲染优化:无对局时隐藏统计模块,页面更简洁
1.3 开发环境配置
•开发工具:DevEco Studio 5.0+
•适配SDK:HarmonyOS NEXT API 23(最低兼容)
•目标SDK:HarmonyOS NEXT API 24
•开发语言:ArkTS(TypeScript超集)
•运行设备:鸿蒙NEXT模拟器、真机
•开发框架:原生ArkUI声明式UI
二、项目工程结构解析
本项目采用鸿蒙标准单模块工程结构,无多余冗余文件,结构清晰、层级分明,符合官方开发规范,新手可快速理解各文件作用。
plain text
MyApplication/
├── AppScope/ # 应用全局资源配置
│ └── resources/ # 全局字符串、图标资源
├── entry/ # 应用主功能模块
│ ├── src/main/ets/
│ │ ├── entryability/ # 应用生命周期入口
│ │ │ └── EntryAbility.ets
│ │ └── pages/
│ │ └── Index.ets # 游戏主页面(核心逻辑+UI)
│ ├── resources/ # 页面资源(颜色、尺寸、路由)
│ │ ├── base/
│ │ │ ├── element/ # 颜色、字符串、尺寸资源
│ │ │ └── profile/
│ │ │ └── main_pages.json # 页面路由注册
│ └── module.json5 # 模块核心配置
├── build-profile.json5 # 项目编译版本配置
└── hvigorfile.ts # 工程构建脚本
核心关键文件说明
•EntryAbility.ets:应用全局生命周期入口,管控应用启动、前后台切换
•Index.ets:项目唯一主页面,承载所有UI布局与游戏业务逻辑
•main_pages.json:页面路由注册表,所有页面必须在此注册才可正常预览运行
•module.json5:模块权限、设备适配、启动入口核心配置文件
•build-profile.json5:管控项目SDK适配版本,解决编译兼容问题
三、核心技术知识点前置
3.1 响应式状态管理
ArkTS 核心特性:通过 @State 定义响应式变量,数据驱动UI更新,无需手动刷新页面,变量变更自动触发页面重绘,是鸿蒙声明式开发的核心精髓。
3.2 循环与条件渲染
使用 ForEach 实现列表循环渲染,简化重复UI代码;通过原生 if 实现条件渲染,按需展示模块,优化页面视觉效果。
3.3 线性布局适配
依托 Row/Column 横竖线性布局,搭配layoutWeight 权重属性,实现页面自适应均分布局,适配全系鸿蒙手机屏幕。
3.4 随机算法与逻辑判定
基于 JS 随机数实现电脑随机出拳,通过条件判断完成对局胜负判定,逻辑简洁易懂,适合新手学习算法基础。
四、完整代码从零实现
4.1 数据模型封装
自定义手势数据接口,将图标、文字、业务值解耦,代码更易维护、可扩展。

typescript
// 手势数据模型
interface Choice {
  emoji: string;   // 展示表情图标
  label: string;   // 中文文字描述
  value: string;   // 业务判定值
}

4.2 页面状态定义
统一管理游戏状态、对局数据、UI样式状态,所有变量显式声明类型,规避编译报错。

typescript
@Entry
@Component
struct Index {
  // 对局展示状态
  @State playerChoice: string = '❔';
  @State computerChoice: string = '❔';
  @State result: string = '选择你的出拳';
  @State resultColor: string = '#8E8E93';

  // 对局统计数据
  @State playerScore: number = 0;
  @State computerScore: number = 0;
  @State draws: number = 0;
  @State roundCount: number = 0;

  // 手势常量数组
  readonly choices: Choice[] = [
    { emoji: '✊', label: '石头', value: 'rock' },
    { emoji: '✋', label: '布', value: 'paper' },
    { emoji: '✌️', label: '剪刀', value: 'scissors' }
  ];

4.3 核心游戏逻辑封装
4.3.1 电脑随机出拳
通过随机索引,从手势数组中随机取值,实现电脑随机出拳效果。

typescript
  // 电脑随机获取手势
  getComputerChoice(): string {
    const randomIndex = Math.floor(Math.random() * 3);
    return this.choices[randomIndex].value;
  }

4.3.2 对局胜负核心判定
严格遵循石头剪刀布经典规则,自动判定胜负、更新文案、字体颜色、统计数据。

typescript
  // 对局核心逻辑
  play(playerValue: string): void {
    const computerValue: string = this.getComputerChoice();

    // 匹配展示图标
    const playerEmoji = this.choices.find(c => c.value === playerValue)?.emoji ?? '❔';
    const computerEmoji = this.choices.find(c => c.value === computerValue)?.emoji ?? '❔';

    this.playerChoice = playerEmoji;
    this.computerChoice = computerEmoji;
    this.roundCount++;

    // 胜负判定逻辑
    if (playerValue === computerValue) {
      this.result = '🤝 平局!';
      this.resultColor = '#8E8E93';
      this.draws++;
    } else if (
      (playerValue === 'rock' && computerValue === 'scissors') ||
      (playerValue === 'scissors' && computerValue === 'paper') ||
      (playerValue === 'paper' && computerValue === 'rock')
    ) {
      this.result = '🎉 你赢了!';
      this.resultColor = '#34C759';
      this.playerScore++;
    } else {
      this.result = '😅 电脑赢了!';
      this.resultColor = '#FF3B30';
      this.computerScore++;
    }
  }

4.3.3 游戏数据重置
一键清空所有对局状态和统计数据,恢复初始页面状态。

typescript
  // 重置游戏所有数据
  resetGame(): void {
    this.playerChoice = '❔';
    this.computerChoice = '❔';
    this.result = '选择你的出拳';
    this.resultColor = '#8E8E93';
    this.playerScore = 0;
    this.computerScore = 0;
    this.draws = 0;
    this.roundCount = 0;
  }

4.4 完整UI页面布局
采用分层布局,从上至下依次为标题区、对战卡片、结果展示、操作按钮、数据统计、重置按钮,页面层级清晰、视觉美观。

typescript
  build() {
    Column() {
      // 页面标题
      Text('✂️ 石头剪刀布')
        .fontSize(26)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1C1C1E')
        .margin({ top: 30, bottom: 8 })

      Text('和电脑实时对战!')
        .fontSize(14)
        .fontColor('#8E8E93')
        .margin({ bottom: 24 })

      // 对战卡片区域
      Row() {
        // 玩家区域
        Column() {
          Text('🧑 玩家')
            .fontSize(14)
            .fontColor('#8E8E93')
            .margin({ bottom: 8 })
          Text(this.playerChoice)
            .fontSize(64)
            .animation({ duration: 300 })
          Text(`${this.playerScore}`)
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
            .fontColor('#34C759')
            .margin({ top: 6 })
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Center)

        // 对战标识
        Column() {
          Text('VS')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .fontColor('#FF3B30')
        }
        .layoutWeight(0.5)
        .alignItems(HorizontalAlign.Center)

        // 电脑区域
        Column() {
          Text('🤖 电脑')
            .fontSize(14)
            .fontColor('#8E8E93')
            .margin({ bottom: 8 })
          Text(this.computerChoice)
            .fontSize(64)
            .animation({ duration: 300 })
          Text(`${this.computerScore}`)
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
            .fontColor('#FF3B30')
            .margin({ top: 6 })
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Center)
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#F2F2F7')
      .borderRadius(16)
      .margin({ bottom: 16 })

      // 对局结果展示
      Text(this.result)
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .fontColor(this.resultColor)
        .margin({ top: 4, bottom: 24 })

      // 出拳选择按钮组
      Row() {
        ForEach(this.choices, (choice: Choice) => {
          Column() {
            Text(choice.emoji)
              .fontSize(44)
            Text(choice.label)
              .fontSize(14)
              .fontColor('#1C1C1E')
              .margin({ top: 4 })
          }
          .width(96)
          .height(110)
          .backgroundColor('#F2F2F7')
          .borderRadius(16)
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Center)
          .onClick(() => {
            this.play(choice.value);
          })
        })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
      .margin({ bottom: 24 })

      // 对局统计信息(条件渲染)
      if (this.roundCount > 0) {
        Row() {
          Text(`总局数:${this.roundCount}`)
            .fontSize(14)
            .fontColor('#8E8E93')
          Blank()
          Text(`平局次数:${this.draws}`)
            .fontSize(14)
            .fontColor('#8E8E93')
        }
        .width('100%')
        .padding({ left: 20, right: 20 })
        .margin({ bottom: 16 })
      }

      // 重置按钮
      Button('🔄 重新开局')
        .type(ButtonType.Capsule)
        .width(180)
        .height(44)
        .backgroundColor('#8E8E93')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .onClick(() => this.resetGame())

    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
    .padding(20)
    .alignItems(HorizontalAlign.Center)
  }
}

五、核心配置文件说明
5.1 页面路由配置(main_pages.json)
鸿蒙项目所有页面必须手动注册路由,否则预览器/真机无法加载页面。

json
{
  "src": [
    "pages/Index"
  ]
}

5.2 项目编译版本配置(build-profile.json5)
适配高低版本SDK,兼顾兼容性和新特性,规避编译报错。
json5
{
  "app": {
    "products": [
      {
        "name": "default",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.0(23)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

在这里插入图片描述在这里插入图片描述

六、项目运行流程演示
1.初始状态:页面展示默认问号图标,提示用户选择出拳,无统计数据,页面简洁干净。
2.对局操作:点击石头/布/剪刀任意按钮,系统自动生成电脑手势,300ms过渡动画,实时展示对局结果与胜负颜色。
3.数据统计:完成首局对局后,自动展示总局数、平局次数、双方胜场,数据实时累加。
4.重置开局:点击重新开局按钮,所有UI状态、统计数据一键归零,恢复初始状态。
七、开发踩坑与解决方案
坑1:状态变量不规范导致UI刷新异常
问题:省略变量类型声明,依赖自动推断,部分场景出现状态更新不生效。
解决:所有 @State 变量强制显式声明类型,保证类型统一,杜绝刷新异常。
坑2:误用前端三元/逻辑运算条件渲染
问题:ArkUI不支持 &&、三元表达式渲染UI,直接报错。
解决:严格使用原生 if/else 语句实现条件渲染,符合ArkUI语法规范。
坑3:ForEach事件绑定参数失效
问题:直接写 onClick(this.play()) 会立即执行函数,无法触发点击事件。
解决:使用箭头函数包裹事件,onClick(() => this.play()),延迟触发事件。
坑4:预览器页面空白
问题:代码无报错但页面空白。
解决:检查 main_pages.json 是否正确注册当前页面路径。
八、项目总结与扩展方向
8.1 所学技术总结
通过本项目,可完整掌握鸿蒙入门核心能力:
•熟练掌握 @State 响应式状态管理机制
•精通 Row/Column 自适应布局与权重适配
•掌握 ForEach 循环渲染、if条件渲染用法
•学会随机算法、业务逻辑判定、数据统计开发
•掌握ArkUI动画、动态样式的开发技巧
8.2 进阶优化扩展方向
本项目为基础MVP版本,可自由扩展高级功能,适合进阶学习:
•💾 数据持久化:接入Preferences保存历史战绩、最高连胜记录
•🔊 音效增强:接入音频API,对局胜利/失败添加专属音效
•✨ 动画升级:新增手势翻转、胜负弹窗特效动画
•📊 数据详情:新增历史对局记录页面,查看每局对战详情
•🌙 深色模式:适配系统深色主题,优化夜间体验
九、常见问题FAQ
Q:编译提示SDK版本不匹配?
A:统一项目 compatibleSdkVersion 和运行设备版本,API23/24可向下兼容。
Q:真机调试无法安装应用?
A:开启开发者模式、USB调试,关闭签名校验或配置自动签名。
Q:点击按钮无反应?
A:检查事件绑定是否使用箭头函数,避免函数提前执行。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐