This article demonstrates a simple "Guess the Ball" game implementation using HarmonyOS NEXT, featuring state management with decorators and animation control.
Core Implementation
1. Cup Class with State Tracking
// Use decorator for object change tracking
@ObservedV2
class Cup {
// Track property changes with decorators
@Trace positionX: number; // X-axis position
@Trace positionY: number; // Y-axis position
@Trace containsBall: boolean; // Whether contains the ball
@Trace isRevealed: boolean; // Whether cup is opened
// Constructor to initialize cup states
constructor(hasBall: boolean) {
this.positionX = 0;
this.positionY = 0;
this.containsBall = hasBall;
this.isRevealed = true;
}
}
2. Game Component Structure
@Entry
@Component
struct ThreeCupGame {
// Game state variables
@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;
// Game initialization
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]);
}
// Cup movement logic
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;
// Animation sequence for first cup
animateToImmediately({
delay: 0,
duration: this.animationDurationMs
}, () => {
this.gameCups[cupIndex1].positionY = -direction * (this.cupWidth + this.cupSpacing * 2) / adjustedDistanceFactor
});
// Animation sequence for second cup
animateToImmediately({
delay: 0,
duration: this.animationDurationMs
}, () => {
this.gameCups[cupIndex2].positionY = direction * (this.cupWidth + this.cupSpacing * 2) / adjustedDistanceFactor
});
// Animation completion handler
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
});
}
// Ball swapping logic
swapBalls(cupIndex1: number, cupIndex2: number) {
[this.gameCups[cupIndex1].containsBall, this.gameCups[cupIndex2].containsBall] =
[this.gameCups[cupIndex2].containsBall, this.gameCups[cupIndex1].containsBall];
if (this.currentMixingCount <= 0) {
this.isGameAnimating = false;
} else {
setTimeout(() => this.startGame(), 10);
}
}
// UI Construction
build() {
Column({ space: 20 }) {
Text('Guess the Ball Game')
.fontSize(24)
.margin({ top: 20 });
// Animation speed control
Counter() {
Text(`Current Speed: ${this.animationDurationMs}ms`)
.fontColor(Color.Black)
.fontSize('26lpx');
}.onInc(() => this.animationDurationMs += 10)
.onDec(() => this.animationDurationMs = Math.max(10, this.animationDurationMs - 10));
// Mixing count control
Counter() {
Text(`Mixes per Round: ${this.mixingCount}`)
.fontColor(Color.Black)
.fontSize('26lpx');
}.onInc(() => this.mixingCount += 1)
.onDec(() => this.mixingCount = Math.max(1, this.mixingCount - 1));
// Cups layout
Row() {
ForEach(this.gameCups, (cup: Cup) => {
Text(cup.isRevealed ? (cup.containsBall ? 'Ball' : 'Empty') : '')
.size(`${this.cupWidth}lpx`)
.margin(`${this.cupSpacing}lpx`)
.backgroundColor(Color.Orange)
.translate({ x: `${cup.positionX}lpx`, y: `${cup.positionY}lpx` })
.onClick(() => {
if (!this.isGameAnimating) cup.isRevealed = true;
});
});
}.height('720lpx').backgroundColor(Color.Gray);
// Start button
Button('Start Game').onClick(() => {
if (!this.isGameAnimating) {
this.currentMixingCount = this.mixingCount;
this.isGameAnimating = true;
this.gameCups.forEach(cup => cup.isRevealed = false);
this.startGame();
}
});
}.size('100%');
}
}
Key Features
- State Management:
-
@ObservedV2
and@Trace
decorators enable efficient state tracking Automatic UI updates when cup properties change
Animation System:
Sequential animations using
animateToImmediately
Configurable duration and delay parameters
Smooth transition effects for cup movements
Game Logic:
Random cup pair selection
Configurable mixing count
Ball position swapping mechanism
UI Controls:
Interactive speed adjustment
Visual feedback for cup states
Responsive layout adaptation
Usage
- Adjust animation speed using the speed control
- Set desired mixing count
- Click "Start Game" to begin
- Click cups after mixing to reveal contents
- Adjust parameters between rounds
This implementation demonstrates HarmonyOS NEXT's capabilities in handling complex state management, smooth animations, and responsive UI interactions. The decorator-based approach provides efficient state tracking while maintaining clean code structure.
Top comments (0)