鸿蒙开发案例:通过三杯猜球学习显示动画
作者:zhongcx
- 2024-10-12 广东
本文字数:3615 字
阅读完需:约 12 分钟
【引言】
“三杯猜球”是一个经典的益智游戏,通常由一名表演者和多名参与者共同完成。表演者会将一个小球放在一个杯子下面,然后将三个杯子快速地交换位置,参与者则需要猜出最终哪个杯子下面有小球。本文将介绍如何使用 HarmonyOS NEXT 技术,如装饰器、状态管理和动画,来实现一个基于浏览器的“三杯猜球”游戏。
【实现目标】
创建一个交互式的游戏,让玩家能够:
开始游戏:通过点击“开始游戏”按钮启动游戏,触发杯子间的随机交换。
调整动画速度:允许用户通过界面上的控制器来调整游戏过程中杯子交换的速度。
调整混合次数:让用户可以设置每局游戏中杯子的混合次数。
显示杯子内容:当动画停止后,玩家可以通过点击任意一个杯子来查看其下面是否有小球。
自动重置:如果所有预定的交换次数完成,游戏会自动重置,等待下一轮开始。
【开发逻辑】
定义杯子类:创建 Cup 类,定义杯子的属性和构造函数。
实现游戏逻辑:• 初始化游戏状态。• 实现 startGame() 方法,用于开始游戏。• 实现 moveCups() 方法,用于移动杯子。• 实现 swapBalls() 方法,用于交换杯子内的球。• 实现 resetCupPosition() 方法,用于重置杯子的位置。
动画效果:使用动画库(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%'); }}复制代码
划线
评论
复制
发布于: 刚刚阅读数: 4
zhongcx
关注
还未添加个人签名 2024-09-27 加入
还未添加个人简介









评论