This article demonstrates how to implement a classic "Whac-A-Mole" game using HarmonyOS NEXT's ArkUI framework. The implementation showcases various UI components, animation handling, and state management capabilities.
Core Code Implementation
1. Hamster Component (Game Character)
import { curves, window } from '@kit.ArkUI'
@Component
struct Hamster {
@Prop cellWidth: number // Cell width for layout calculations
build() {
Stack() { // Stack layout container
// Body
Text()
.width(`${this.cellWidth / 2}lpx`)
.height(`${this.cellWidth / 3 * 2}lpx`)
.backgroundColor("#b49579")
.borderRadius({ topLeft: '50%', topRight: '50%' })
.borderColor("#2a272d")
.borderWidth(1)
// Mouth
Ellipse()
.width(`${this.cellWidth / 4}lpx`)
.height(`${this.cellWidth / 5}lpx`)
.fillOpacity(1)
.fill("#e7bad7")
.stroke("#563e3f")
.strokeWidth(1)
.margin({ top: `${this.cellWidth / 6}lpx`)
// Left eye
Ellipse()
.width(`${this.cellWidth / 9}lpx`)
.height(`${this.cellWidth / 6}lpx`)
.fillOpacity(1)
.fill("#313028")
.stroke("#2e2018")
.strokeWidth(1)
.margin({ bottom: `${this.cellWidth / 3}lpx`, right: `${this.cellWidth / 6}lpx`)
// Right eye
Ellipse()
.width(`${this.cellWidth / 9}lpx`)
.height(`${this.cellWidth / 6}lpx`)
.fillOpacity(1)
.fill("#313028")
.stroke("#2e2018")
.strokeWidth(1)
.margin({ bottom: `${this.cellWidth / 3}lpx`, left: `${this.cellWidth / 6}lpx`)
// Eye highlights
Ellipse()
.width(`${this.cellWidth / 20}lpx`)
.height(`${this.cellWidth / 15}lpx`)
.fillOpacity(1)
.fill("#fefbfa")
.margin({ bottom: `${this.cellWidth / 2.5}lpx`, right: `${this.cellWidth / 6}lpx`)
Ellipse()
.width(`${this.cellWidth / 20}lpx`)
.height(`${this.cellWidth / 15}lpx`)
.fillOpacity(1)
.fill("#fefbfa")
.margin({ bottom: `${this.cellWidth / 2.5}lpx`, left: `${this.cellWidth / 6}lpx`)
}.width(`${this.cellWidth}lpx`).height(`${this.cellWidth}lpx`)
}
}
2. Game Cell Management
@ObservedV2
class Cell {
@Trace scaleOptions: ScaleOptions = { x: 1, y: 1 };
@Trace isSelected: boolean = false
cellWidth: number
selectTime: number = 0
constructor(cellWidth: number) {
this.cellWidth = cellWidth
}
setSelectedTrueTime() {
this.selectTime = Date.now()
this.isSelected = true
}
checkTime(stayDuration: number) {
if (this.isSelected && Date.now() - this.selectTime >= stayDuration) {
this.selectTime = 0
this.isSelected = false
}
}
}
3. Game Timer Implementation
class MyTextTimerModifier implements ContentModifier<TextTimerConfiguration> {
applyContent(): WrappedBuilder<[TextTimerConfiguration]> {
return wrapBuilder(buildTextTimer)
}
}
@Builder
function buildTextTimer(config: TextTimerConfiguration) {
Column() {
Stack({ alignContent: Alignment.Center }) {
Circle({ width: 150, height: 150 })
.fill(config.started ? (config.isCountDown ? 0xFF232323 : 0xFF717171) : 0xFF929292)
Column() {
Text(config.isCountDown ? "Countdown" : "Elapsed Time").fontColor(Color.White)
Text(
(config.isCountDown ? "Remaining: " : "Elapsed: ") +
(config.isCountDown ?
Math.max(config.count/1000 - config.elapsedTime/100, 0).toFixed(0) :
(config.elapsedTime/100).toFixed(0)) + "s"
).fontColor(Color.White)
}
}
}
}
Key Implementation Points
1. Responsive Layout System
- Uses
lpx
units for dynamic sizing - Flexible layout calculations based on cell dimensions
- Adaptive component positioning using margin properties
2. Animation System
- Spring animation curve implementation:
curves.springCurve(10, 1, 228, 30)
- Scale transformations for hit effects
- Timed visibility control for game elements
3. Game State Management
- @ObservedV2 decorator for observable classes
- @trace for state tracking
- Fisher-Yates shuffle algorithm for random position selection:
for (let i = 0; i < availableIndexList.length; i++) {
let index = Math.floor(Math.random() * (availableIndexList.length - i))
// Swap positions
}
4. Game Configuration
- Customizable parameters:
-
gameDuration
: Total game time -
appearanceCount
: Moles per wave -
animationInterval
: Spawn interval -
hamsterStayDuration
: Visible duration
-
Game Features
- Landscape orientation setup:
windowClass.setPreferredOrientation(window.Orientation.LANDSCAPE)
- Score tracking system
- Configurable game parameters
- Visual feedback mechanisms:
- Scale animations
- Color state indicators
- Game completion dialog:
showAlertDialog({ title: 'Game Over', message: `Score: ${currentScore}` })
This implementation demonstrates HarmonyOS NEXT's capabilities in building interactive games with complex state management and smooth animations. The component-based architecture allows for maintainable code structure while leveraging ArkUI's declarative syntax for efficient UI updates.
Developers can extend this foundation by:
- Adding sound effects
- Implementing difficulty levels
- Creating progressive animation systems
- Adding multiplayer support
- Integrating with device sensors for alternative input methods
The complete code demonstrates best practices in HarmonyOS application development, including responsive design, state management, and user interaction handling.
Top comments (0)