写点什么

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

作者:zhongcx
  • 2024-10-12
    广东
  • 本文字数:3615 字

    阅读完需:约 12 分钟

【引言】


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


【实现目标】


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


  1. 开始游戏:通过点击“开始游戏”按钮启动游戏,触发杯子间的随机交换。

  2. 调整动画速度:允许用户通过界面上的控制器来调整游戏过程中杯子交换的速度。

  3. 调整混合次数:让用户可以设置每局游戏中杯子的混合次数。

  4. 显示杯子内容:当动画停止后,玩家可以通过点击任意一个杯子来查看其下面是否有小球。

  5. 自动重置:如果所有预定的交换次数完成,游戏会自动重置,等待下一轮开始。


【开发逻辑】


  1. 定义杯子类:创建 Cup 类,定义杯子的属性和构造函数。

  2. 实现游戏逻辑:• 初始化游戏状态。• 实现 startGame() 方法,用于开始游戏。• 实现 moveCups() 方法,用于移动杯子。• 实现 swapBalls() 方法,用于交换杯子内的球。• 实现 resetCupPosition() 方法,用于重置杯子的位置。

  3. 动画效果:使用动画库(animateToImmediately)实现杯子的动画效果。


// 使用装饰器来追踪对象的变化@ObservedV2class 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@Componentstruct 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%');  }}
复制代码


用户头像

zhongcx

关注

还未添加个人签名 2024-09-27 加入

还未添加个人简介

评论

发布
暂无评论
鸿蒙开发案例:通过三杯猜球学习显示动画_zhongcx_InfoQ写作社区