【1】引言
“指尖輪盤”是一個簡單而有趣的互動遊戲(類似抓鬮),這個應用透過觸控螢幕的方式,讓玩家參與一個激動人心的遊戲,最終選出幸運的贏家。未來可以進一步擴充套件功能,如增加遊戲模式、最佳化動畫效果、增加音效等,提升使用者體驗。
【2】環境準備
電腦系統:windows 10
開發工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
工程版本:API 12
真機:mate60 pro
語言:ArkTS、ArkUI
【功能概述】
1. 顯示標題和遊戲說明,引導玩家參與遊戲。
2. 支援多位玩家參與,每位玩家以不同顏色的圓形表示。
3. 根據觸控螢幕的手指數量,動態更新介面狀態。
4. 實現倒數計時功能,倒數計時結束後隨機選擇一位玩家作為贏家。
【技術實現要點】
1. 使用鴻蒙系統提供的元件和狀態管理功能,構建介面和處理使用者互動。
2. 利用動畫效果,為玩家圓形新增縮放動畫,增強視覺效果。
3. 透過定時器實現倒數計時和隨機選擇玩家的功能。
4. 處理觸控事件,根據手指數量更新玩家位置和介面狀態。
【開發流程】
1. 建立玩家位置類(PlayerPosition),用於管理玩家屬性和動畫效果。
2. 設計入口元件(WheelGamePage),包含玩家列表、倒數計時、觸控事件處理等功能。
3. 構建UI介面,顯示標題、說明文字和玩家圓形,實現動態更新和互動效果。
4. 實現倒數計時和隨機選擇玩家的邏輯,提升遊戲體驗。
【完整程式碼】
@ObservedV2 // 觀察類,用於觀察屬性變化 class PlayerPosition { @Trace isVisible: boolean = false // 玩家是否可見 @Trace startX: number = 0 // 玩家起始X座標 @Trace startY: number = 0 // 玩家起始Y座標 @Trace scaleOptions: ScaleOptions = { x: 0.5, y: 0.5 } // 玩家縮放選項 cellWidth: number = 100 // 玩家圓形的寬度 color: string // 玩家顏色 constructor(color: string) { // 建構函式,初始化顏色 this.color = color } isRunningAnimation: boolean = false // 動畫是否正在執行 setShowAnimation() { // 設定顯示動畫 if (!this.isRunningAnimation) { // 如果動畫未執行 this.isRunningAnimation = true // 標記為正在執行 animateToImmediately({ // 開始動畫 delay: 0, duration: 500, curve: Curve.Linear, iterations: 1, onFinish: () => { // 動畫完成後的回撥 console.info(`onFinish 1`) animateToImmediately({ // 開始第二個動畫 delay: 0, duration: 300, iterations: -1, curve: Curve.Linear, onFinish: () => { // 第二個動畫完成後的回撥 console.info(`onFinish 2`) } }, () => { this.scaleOptions = { x: 1.1, y: 1.1 } // 設定縮放 }) } }, () => { this.scaleOptions = { x: 1.0, y: 1.0 } // 動畫結束時重置縮放 }) } } } @Entry // 入口元件 @Component struct WheelGamePage { @State playerList: PlayerPosition[] = [// 玩家列表 new PlayerPosition("#26c2ff"), new PlayerPosition("#978efe"), new PlayerPosition("#c389fe"), new PlayerPosition("#ff85bd"), new PlayerPosition("#ff7051"), new PlayerPosition("#fea800"), new PlayerPosition("#ffcf18"), new PlayerPosition("#a9c92a"), ] @State showTitle: boolean = true // 是否顯示標題 @State showInstructions: boolean = true // 是否顯示說明 @State showCountdown: boolean = false // 是否顯示倒數計時 @State @Watch('countdownNumberChanged') countdownNumber: number = 3 // 倒數計時數字 @State selectIndex: number = -1 // 最終選中的玩家下標 countdownTimerId: number = 0 // 倒數計時定時器ID randomSelectionTimerId: number = 0 // 隨機選擇定時器ID countdownNumberChanged() { // 倒數計時變化的處理函式 if (this.countdownNumber <= 0) { // 如果倒數計時結束 this.startRandomSelection(); // 開始隨機選擇 } else { this.selectIndex = -1; // 結束隨機顯示 } } startRandomSelection() { // 開始隨機選擇玩家 const len = this.playerList.length + Math.floor(Math.random() * this.playerList.length); // 隨機長度 const visiblePlayers = this.playerList.filter(player => player.isVisible); // 可見玩家列表 console.info(`visiblePlayers:${JSON.stringify(visiblePlayers)}`); let count = 0; // 當前計數 let iteration = 0; // 當前迭代次數 clearInterval(this.randomSelectionTimerId); // 清除之前的定時器 this.randomSelectionTimerId = setInterval(() => { // 設定新的定時器 console.info(`count:${count}`); console.info(`iteration:${iteration}`); console.info(`len:${len}`); if (iteration >= len) { // 如果達到迭代次數 clearInterval(this.randomSelectionTimerId); // 清除定時器 return; } this.selectIndex = count++ % visiblePlayers.length; // 隨機選擇玩家 iteration++; // 增加迭代次數 }, 150); // 每150毫秒執行一次 } updatePlayerPositions(touchPoints: TouchObject[]) { // 更新玩家位置 this.playerList.forEach((player, index) => { // 遍歷玩家列表 if (index < touchPoints.length) { // 如果觸控點數量大於玩家索引 player.isVisible = true // 設定玩家可見 player.setShowAnimation() // 設定動畫 player.startX = touchPoints[index].x // 更新X座標 player.startY = touchPoints[index].y // 更新Y座標 } else { player.isVisible = false // 設定玩家不可見 } }) } updateTextState(touchCount: number) { // 更新文字狀態 this.countdownNumber = 3 // 重置倒數計時 if (touchCount === 0) { // 如果沒有觸控 this.showTitle = true // 顯示標題 this.showInstructions = true // 顯示說明 this.showCountdown = false // 隱藏倒數計時 } else if (touchCount === 1) { // 如果有一個觸控 this.showTitle = false // 隱藏標題 this.showInstructions = true // 顯示說明 this.showCountdown = false // 隱藏倒數計時 } else if (touchCount >= 2) { // 如果有兩個或更多觸控 this.showTitle = false // 隱藏標題 this.showInstructions = false // 隱藏說明 this.showCountdown = true // 顯示倒數計時 clearInterval(this.countdownTimerId) // 清除之前的倒數計時 this.countdownTimerId = setInterval(() => { // 設定新的倒數計時 if (this.countdownNumber >= 0) { // 如果倒數計時未結束 this.countdownNumber-- // 倒數計時減一 } else { clearInterval(this.countdownTimerId) // 倒數計時結束,清除定時器 } }, 1000) // 每秒執行一次 } } build() { // 構建UI Stack() { // 建立堆疊佈局 Text("指尖輪盤")// 顯示標題文字 .width('100%')// 寬度100% .height(80)// 高度80 .textAlign(TextAlign.Center)// 文字居中 .fontSize(20)// 字型大小20 .fontColor("#0c0c0c")// 字型顏色 .visibility(this.showTitle ? Visibility.Visible : Visibility.Hidden)// 根據狀態設定可見性 .draggable(false) // 不可拖動 Stack() { // 建立另一個堆疊佈局 Text(`1. 邀請您的朋友一起加入這場激動人心的遊戲吧!只需2到${this.playerList.length}位玩家即可開始。\n 2. 準備好後,請每位參與者伸出一根手指輕輕按住手機螢幕。倒數計時3秒後,遊戲自動啟動,或者您也可以手動點選“開始”按鈕。\n 3. 緊緊握住你的手指,直到動畫結束。幸運之神將會眷顧其中一位玩家,成為本局的贏家!`)// 顯示說明文字 .textAlign(TextAlign.JUSTIFY)// 文字對齊 .fontSize(20)// 字型大小20 .fontColor("#0c0c0c")// 字型顏色 .padding(20)// 內邊距20 .visibility(this.showInstructions ? Visibility.Visible : Visibility.None)// 根據狀態設定可見性 .draggable(false) // 不可拖動 Text(this.countdownNumber > 0 ? `倒數計時${this.countdownNumber}秒後開始` : ``)// 顯示倒數計時文字 .textAlign(TextAlign.Center)// 文字居中 .fontSize(20)// 字型大小20 .fontColor("#0c0c0c")// 字型顏色 .padding(20)// 內邊距20 .visibility(this.showCountdown ? Visibility.Visible : Visibility.None)// 根據狀態設定可見性 .draggable(false) }.width('100%').height('100%') // 設定堆疊寬高 .draggable(false) ForEach(this.playerList, (player: PlayerPosition, index: number) => { // 遍歷玩家列表 Stack() { // 建立堆疊佈局 Circle()// 建立外圈圓形 .width(player.cellWidth + 10)// 外圈寬度比玩家寬度大10 .height(player.cellWidth + 10)// 外圈高度比玩家高度大10 .fill(player.color)// 填充外圈顏色 .fillOpacity(0.5)// 設定外圈透明度為0.5 .visibility(player.isVisible ? Visibility.Visible : Visibility.None)// 根據玩家可見性設定外圈可見性 .draggable(false)// 外圈不可拖動 .scale(player.scaleOptions) // 設定外圈縮放選項 Circle()// 建立內圈圓形 .width(player.cellWidth)// 內圈寬度 .height(player.cellWidth)// 內圈高度 .fill(player.color)// 填充內圈顏色 .fillOpacity(1)// 設定內圈透明度為1 .visibility(player.isVisible ? Visibility.Visible : Visibility.None)// 根據玩家可見性設定內圈可見性 .draggable(false)// 內圈不可拖動 .scale(player.scaleOptions) // 設定內圈縮放選項 }.draggable(false) // 堆疊佈局不可拖動 .scale(this.selectIndex == index ? { x: 1.5, y: 1.5 } : { x: 1.0, y: 1.0 }) // 根據選中狀態設定縮放 .margin({ left: player.startX - player.cellWidth / 2, top: player.startY - player.cellWidth / 2 }) // 設定玩家位置 }) } .align(Alignment.TopStart) // 設定堆疊對齊方式 .height('100%') // 設定堆疊高度為100% .width('100%') // 設定堆疊寬度為100% .draggable(false) // 堆疊不可拖動 .onTouch((event: TouchEvent) => { // 處理觸控事件 if (event.type == TouchType.Down) { // 按下事件 this.updatePlayerPositions(event.touches) // 更新玩家位置 this.updateTextState(event.touches.length) // 更新文字狀態 } else if (event.type == TouchType.Move) { // 移動事件 this.updatePlayerPositions(event.touches) // 更新玩家位置 } else if (event.type == TouchType.Up) { // 抬起事件 this.updateTextState(event.touches.length - 1) // 更新文字狀態 if (event.touches.length - 1 === 0) { // 如果沒有觸控 this.updatePlayerPositions([]) // 清空玩家位置 } } else if (event.type == TouchType.Cancel) { // 取消事件 this.updatePlayerPositions(event.touches) // 更新玩家位置 } }) } }