鸿蒙开发案例:通过三杯猜球学习显示动画

news2024/12/27 11:28:27

【引言】

“三杯猜球”是一个经典的益智游戏,通常由一名表演者和多名参与者共同完成。表演者会将一个小球放在一个杯子下面,然后将三个杯子快速地交换位置,参与者则需要猜出最终哪个杯子下面有小球。本文将介绍如何使用HarmonyOS NEXT技术,如装饰器、状态管理和动画,来实现一个“三杯猜球”游戏。

【实现目标】

创建一个交互式的游戏,让玩家能够:

1. 开始游戏:通过点击“开始游戏”按钮启动游戏,触发杯子间的随机交换。
2. 调整动画速度:允许用户通过界面上的控制器来调整游戏过程中杯子交换的速度。
3. 调整混合次数:让用户可以设置每局游戏中杯子的混合次数。
4. 显示杯子内容:当动画停止后,玩家可以通过点击任意一个杯子来查看其下面是否有小球。
5. 自动重置:如果所有预定的交换次数完成,游戏会自动重置,等待下一轮开始。

【开发逻辑】

1. 定义杯子类:创建 Cup 类,定义杯子的属性和构造函数。
2. 实现游戏逻辑:
• 初始化游戏状态。
• 实现 startGame() 方法,用于开始游戏。
• 实现 moveCups() 方法,用于移动杯子。
• 实现 swapBalls() 方法,用于交换杯子内的球。
• 实现 resetCupPosition() 方法,用于重置杯子的位置。
3. 动画效果:使用动画库(animateToImmediately)实现杯子的动画效果。

【完整代码】

// 使用装饰器来追踪对象的变化
@ObservedV2
class Cup {
  // 使用装饰器来追踪属性的变化
  @Trace positionX: number; // 杯子的X轴位置
  @Trace positionY: number; // 杯子的Y轴位置
  @Trace containsBall: boolean; // 杯子内是否有球
  @Trace isRevealed: boolean; // 杯子是否打开

  // 构造函数初始化杯子的状态
  constructor(hasBall: boolean) {
    this.positionX = 0;
    this.positionY = 0;
    this.containsBall = hasBall;
    this.isRevealed = true;
  }
}

// 游戏入口组件
@Entry
@Component
struct ThreeCupGame {
  // 游戏状态变量
  @State gameCups: Cup[] = [// 初始化三个杯子,其中一个有球
    new Cup(true),
    new Cup(false),
    new Cup(false)
  ];
  @State cupWidth: number = 200; // 杯子宽度
  @State cupSpacing: number = 10; // 杯子之间的间距
  @State animationDurationMs: number = 140; // 动画持续时间(毫秒)
  @State isGameAnimating: boolean = false; // 是否正在动画中
  @State mixingCount: number = 5; // 每局游戏混合次数
  @State currentMixingCount: number = 0; // 当前正在进行的混合次数计数

  // 开始游戏的方法
  startGame() {
    this.currentMixingCount--; // 减少混合次数
    const cupPairs = [[0, 1], [0, 2], [1, 2]]; // 可能的杯子对组合
    const selectedPair = cupPairs[Math.floor(Math.random() * cupPairs.length)]; // 随机选择一对
    this.moveCups(selectedPair[0], selectedPair[1]); // 开始移动选定的两个杯子
  }

  // 移动指定的两个杯子
  moveCups(cupIndex1: number, cupIndex2: number) {
    const direction: number = Math.random() < 0.5 ? -1 : 1; // 随机方向
    const distanceFactor: number = Math.abs(cupIndex1 - cupIndex2); // 距离因子
    const adjustedDistanceFactor: number = distanceFactor === 1 ? 2 : 1; // 根据距离调整因子
    animateToImmediately({
      delay: 0,
      duration: this.animationDurationMs
    }, () => {
      this.gameCups[cupIndex1].positionY = -direction * (this.cupWidth + this.cupSpacing * 2) / adjustedDistanceFactor
    })
    animateToImmediately({
      delay: this.animationDurationMs,
      duration: this.animationDurationMs
    }, () => {
      this.gameCups[cupIndex1].positionX = (this.cupWidth + this.cupSpacing * 2) * distanceFactor
      this.gameCups[cupIndex1].positionY = -direction * (this.cupWidth + this.cupSpacing * 2) / adjustedDistanceFactor
    })
    animateToImmediately({
      delay: this.animationDurationMs * 2,
      duration: this.animationDurationMs
    }, () => {
      this.gameCups[cupIndex1].positionX = (this.cupWidth + this.cupSpacing * 2) * distanceFactor
      this.gameCups[cupIndex1].positionY = 0
    })
    animateToImmediately({
      delay: 0,
      duration: this.animationDurationMs
    }, () => {
      this.gameCups[cupIndex2].positionY = direction * (this.cupWidth + this.cupSpacing * 2) / adjustedDistanceFactor
    })
    animateToImmediately({
      delay: this.animationDurationMs,
      duration: this.animationDurationMs
    }, () => {
      this.gameCups[cupIndex2].positionX = -(this.cupWidth + this.cupSpacing * 2) * distanceFactor
      this.gameCups[cupIndex2].positionY = direction * (this.cupWidth + this.cupSpacing * 2) / adjustedDistanceFactor
    })
    animateToImmediately({
      delay: this.animationDurationMs * 2,
      duration: this.animationDurationMs,
      onFinish: () => {
        this.swapBalls(cupIndex1, cupIndex2)
      }
    }, () => {
      this.gameCups[cupIndex2].positionX = -(this.cupWidth + this.cupSpacing * 2) * distanceFactor
      this.gameCups[cupIndex2].positionY = 0
    })
  }

  // 重置杯子的位置
  resetCupPosition(cupIndex: number) {
    this.gameCups[cupIndex].positionX = 0;
    this.gameCups[cupIndex].positionY = 0;
  }

  // 交换两个杯子内的球
  swapBalls(cupIndex1: number, cupIndex2: number) {
    this.resetCupPosition(cupIndex1);
    this.resetCupPosition(cupIndex2);

    let temporaryBallStatus = this.gameCups[cupIndex1].containsBall;
    this.gameCups[cupIndex1].containsBall = this.gameCups[cupIndex2].containsBall;
    this.gameCups[cupIndex2].containsBall = temporaryBallStatus;

    if (this.currentMixingCount <= 0) {
      this.isGameAnimating = false;
    } else {
      setTimeout(() => {
        this.startGame();
      }, 10);
    }
  }

  // 构建游戏界面
  build() {
    Column({ space: 20 }) {
      // 游戏标题
      Text('猜小球游戏')
        .fontSize(24)
        .margin({ top: 20 });

      // 动画速度控制器
      Counter() {
        Text(`当前速度${this.animationDurationMs}毫秒`)
          .fontColor(Color.Black)
          .fontSize('26lpx');
      }.width('400lpx').onInc(() => {
        this.animationDurationMs += 10;
      }).onDec(() => {
        this.animationDurationMs -= 10;
        this.animationDurationMs = this.animationDurationMs < 10 ? 10 : this.animationDurationMs;
      });

      // 混合次数控制器
      Counter() {
        Text(`每局混合${this.mixingCount}次`)
          .fontColor(Color.Black)
          .fontSize('26lpx');
      }.width('400lpx').onInc(() => {
        this.mixingCount += 1;
      }).onDec(() => {
        this.mixingCount -= 1;
        this.mixingCount = this.mixingCount < 1 ? 1 : this.mixingCount
      });

      // 杯子布局
      Row() {
        ForEach(this.gameCups, (cup: Cup) => {
          Text(cup.isRevealed ? (cup.containsBall ? '小球' : '空') : '')
            .width(`${this.cupWidth}lpx`)
            .height(`${this.cupWidth}lpx`)
            .margin(`${this.cupSpacing}lpx`)
            .backgroundColor(Color.Orange)
            .fontSize(`${this.cupWidth / 4}lpx`)
            .textAlign(TextAlign.Center)
            .fontColor(Color.White)
            .borderRadius(5)
            .translate({ x: `${cup.positionX}lpx`, y: `${cup.positionY}lpx` })
            .onClick(() => {
              if (!this.isGameAnimating) {
                cup.isRevealed = true;
              }
            });
        });
      }.justifyContent(FlexAlign.Center).width('100%').height('720lpx').backgroundColor(Color.Gray);

      // 开始游戏按钮
      Button('开始游戏').onClick(() => {
        if (!this.isGameAnimating) {
          this.currentMixingCount = this.mixingCount;
          this.isGameAnimating = true;
          this.gameCups.forEach(cup => cup.isRevealed = false);
          this.startGame();
        }
      });
    }.width('100%').height('100%');
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2211349.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

加速AI+医疗普惠:AI如何让乳腺癌筛查走进大山?

人工智能与超声技术的结合正在医疗领域引发一场新变革。 近日&#xff0c;贵州省铜仁市石阡县妇幼保健院与广州尚医网信息技术有限公司&#xff08;以下简称“尚医云”&#xff09;联合组织了一场“AI点亮女性健康未来”免费乳腺癌筛查活动&#xff0c;目前已为当地1,500名适龄…

物联网:一种有能力重塑世界的技术

物联网&#xff08;IoT&#xff09;近年来对我们的日常生活产生了如此积极的影响&#xff0c;以至于即使是不懂技术的人也开始相信它所带来的便利以及敏锐的洞察力。 物联网是一场数字技术革命&#xff0c;其意义甚至比工业革命更为重大。物联网是仍处于起步阶段的第四次工业革…

重学SpringBoot3-安装Spring Boot CLI

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-安装Spring Boot CLI 1. 什么是 Spring Boot CLI&#xff1f;2. Spring Boot CLI 的安装2.1. 通过 SDKMAN! 安装2.2. 通过 Homebrew 安装&#xff08;适…

React Query 和 React Context

React Query最佳特性之一是你可以在组件树中的任何位置使用查询&#xff1a;你的 <ProductTable> 组件可以在其需要的地方自带数据获取&#xff1a; function ProductTable() {const productQuery useProductQuery()if (productQuery.data) {return <table>...<…

AI跟踪报道第60期-新加坡内哥谈技术-本周AI新闻: Tesla展示Robotaxi和AI在企业级运用的推进

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

前端开发笔记-- 黑马程序员4

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 css 三角写法用户界面鼠标样式取消表单轮廓vertical-align文本溢出 html5 新标签多媒体标签视频标签![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d85d…

Linux升级openssl版本

Linux升级openssl版本 服务器编译依赖库检查 # yum -y install gcc gcc-c make libtool zlib zlib-devel版本检测 # openssl version OpenSSL 1.0.1e-fips 11 Feb 2013 # ssh -V OpenSSH_6.6.1p1, OpenSSL 1.0.1e-fips 11 Feb 2013下载openssl 地址&#xff1a;https://www.o…

Android中的内容提供者

目录 1.创建内容提供者 1--手动创建一个Android应用程序 2--创建自定义的内容提供者 2.访问其他应用程序 1. 解析URI 2. 查询数据 3. 遍历查询结果 3)案例:读取手机通信录 1.声明权限 2.activity_main.xml文件内容 3.my_phone_list.xml文件内容 4.定义PhoneInfo实体 5.定义MyPh…

西门子S7-200 SMART选型指南之高级功能

介绍了S7-200 SMART控制器的高级功能。用户可以了解到控制器所支持的通信功能、运动控制功能、PID功能和高速计数器能力。这使用户能够评估控制器是否满足其应用的功能要求。 S7-200 SMART 功能 S7-200smart传承了S7-200 系列CPU经典编程理念&#xff0c;可以实现基本编程及高…

CISSP官方模拟测试题

源于CISSP官方习题集最新第4版第9章&#xff08;ISC2 CISSP Certified Information Systems Security Professional Official Practice Tests Fourth Edition, Chapter 9: Practice Test 1&#xff09;。 中英文对照&#xff0c;限时150分钟考试&#xff0c;顺序作答不能回退&…

【大数据技术基础 | 实验二】Linux基础:常用基本命令和文件操作

文章目录 一、实验目的二、实验要求三、实验环境四、常用基本命令1、验证cd和pwd命令2、验证ls命令3、验证mkdir命令4、验证cp、mv和rm命令 五、Linux文件操作1、验证touch命令2、验证cat命令3、验证more命令 六、实验心得 一、实验目的 学会linux常用命令(cd,ls,pwd,mkdir,rm…

STM32 SPI通信 F407外设控制器 W25Q64

SPI介绍 SPI&#xff1a;串行外部设备接口 --- 通信接口 特点&#xff1a;高速同步串行全双工 --- 40MHz 场景&#xff1a;存储器 OLED 无线通信 传感器 硬件连线&#xff1a; …

docker安装elasticsearch(es)+kibana

目录 docker安装elasticsearch 一.准备工作 1.打开docker目录 2.创建elasticsearch目录 3.打开elasticsearch目录 4.拉取elasticsearch镜像 5.检查镜像 二.挂载目录 1.创建数据挂载目录 2.创建配置挂载目录 3.创建插件挂载目录 4.权限授权 三.编辑配置 1.打开con…

优先算法1--双指针

“一念既出&#xff0c;万山无阻。”加油陌生人&#xff01; 目录 1.双指针--移动零 2.双指针-复写零 ok&#xff0c;首先在学习之前&#xff0c;为了方便大家后面的学习&#xff0c;我们这里需要补充一个知识点&#xff0c;我这里所谓的指针&#xff0c;不是之前学习的带有…

RISC-V笔记——语法依赖

1. 前言 Memory consistency model定义了使用Shared memory(共享内存)执行多线程(Multithread)程序所允许的行为规范。RISC-V使用的内存模型是RVWMO(RISC-V Weak Memory Ordering)&#xff0c;该模型旨在为架构师提供更高的灵活性&#xff0c;以构建高性能可拓展的设计&#x…

51单片机的土壤湿度检测控制系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块土壤湿度传感器继电器按键、蜂鸣器、LED等模块构成。适用于智能浇花浇水系统、花卉灌溉系统等相似项目。 可实现功能: 1、LCD1602实时显示土壤湿度 2、土壤湿度传感器&#xff08;滑动变阻器模拟&#xff09;采集…

鸿蒙--商品列表

这里主要利用的是 List 组件 相关概念 Scroll:可滚动的容器组件,当子组件的布局尺寸超过父组件的视口时,内容可以滚动。List:列表包

gitlab保护分支设置

版本&#xff1a;gitlab10.2.2 一旦设置master分支被保护&#xff0c;除了管理员之外的任何用户都无法直接向master提交代码&#xff0c;只要提交代码就会报错 # git push -u origin master Total 0 (delta 0), reused 0 (delta 0) remote: GitLab: You are not allowed to pu…

前端优化,解决页面加载慢

问题&#xff1a;vue项目使用vite打包后&#xff0c;部署在nginx服务器上&#xff0c;页面上访问时很慢&#xff0c;发现有个js文件很大导致加载很慢 先说结论&#xff1a; 方式时间未优化前21s开启压缩&#xff08;6级&#xff09;6s去掉大依赖&#xff08;flowable&#xf…

【CTF-SHOW】 web入门 web11-域名隐藏信息 【详-域名】

这道题的主要思路是通过DNS查询&#xff08;或利用题目中所给的网址直接查询&#xff09;指定域名解析以获得txt记录 1.什么是域名&#xff1f; 域名&#xff08;Domain Name&#xff09; 是互联网上用来标识网站或网络服务的名字&#xff0c;它是一个人类易于记忆和使用的地…