鴻蒙HarmonyOS 5小游戲實踐:記憶翻牌(附:源代碼)

記憶翻牌游戲是一款經典的益智游戲,它能有效鍛煉玩家的記憶力和觀察能力。本文將詳細介紹如何使用鴻蒙(HarmonyOS)的ArkUI框架開發一款完整的記憶翻牌游戲,涵蓋游戲設計、核心邏輯實現和界面構建的全過程。

游戲設計概述

記憶翻牌游戲的基本規則很簡單:玩家需要翻開卡片并找出所有匹配的卡片對。在我們的實現中,游戲包含以下特點:

  • 4×4的棋盤布局(16張卡片,8對圖案)
  • 使用可愛的動物表情符號作為卡片內容
  • 計時和計步功能
  • 新游戲和重新開始功能
  • 游戲勝利提示

游戲狀態管理

在鴻蒙開發中,狀態管理是關鍵。我們使用@State裝飾器來管理游戲的各種狀態:

@State cards: Card[] = [];          // 所有卡片數組
@State firstCard: number | null = null;  // 第一張翻開的卡片索引
@State secondCard: number | null = null; // 第二張翻開的卡片索引
@State moves: number = 0;          // 移動步數
@State gameOver: boolean = false;   // 游戲是否結束
@State timer: number = 0;          // 游戲用時

這種狀態管理方式確保了當這些值發生變化時,UI能夠自動更新。

核心游戲邏輯實現

1. 游戲初始化

游戲初始化包括創建卡片對、洗牌和設置初始狀態:

startNewGame() {// 重置游戲狀態this.moves = 0;this.timer = 0;this.gameOver = false;this.firstCard = null;this.secondCard = null;// 創建卡片對let cardValues: string[] = [];for (let i = 0; i < this.PAIRS_COUNT; i++) {cardValues.push(this.CARD_TYPES[i]);cardValues.push(this.CARD_TYPES[i]);}// 洗牌this.shuffleArray(cardValues);// 初始化卡片狀態this.cards = cardValues.map(value => ({value,flipped: false,matched: false})).slice(0); // 使用slice(0)確保UI更新// 開始計時this.timerInterval = setInterval(() => {this.timer++;}, 1000);
}

2. 洗牌算法

我們使用經典的Fisher-Yates洗牌算法來隨機排列卡片:

private shuffleArray(array: string[]) {for (let i = array.length - 1; i > 0; i--) {const j = Math.floor(Math.random() * (i + 1));const temp = array[i];array[i] = array[j];array[j] = temp;}
}

3. 卡片點擊處理

卡片點擊是游戲的核心交互,需要處理多種情況:

handleCardClick(index: number) {// 檢查是否可點擊if (this.gameOver || this.cards[index].matched || this.cards[index].flipped) {return;}if (this.firstCard !== null && this.secondCard !== null) {return;}// 創建新數組觸發UI更新let newCards = this.cards.slice(0);newCards[index].flipped = true;this.cards = newCards;// 設置第一張或第二張卡片if (this.firstCard === null) {this.firstCard = index;} else {this.secondCard = index;this.moves++;this.checkMatch(); // 檢查匹配}
}

4. 匹配檢查

匹配檢查邏輯決定了游戲的勝負:

private checkMatch() {if (this.firstCard === null || this.secondCard === null) return;if (this.cards[this.firstCard].value === this.cards[this.secondCard].value) {// 匹配成功let newCards = this.cards.slice(0);newCards[this.firstCard].matched = true;newCards[this.secondCard].matched = true;this.cards = newCards;this.firstCard = null;this.secondCard = null;this.checkGameOver(); // 檢查游戲是否結束} else {// 不匹配,1秒后翻回setTimeout(() => {let newCards = this.cards.slice(0);if (this.firstCard !== null) newCards[this.firstCard].flipped = false;if (this.secondCard !== null) newCards[this.secondCard].flipped = false;this.cards = newCards;this.firstCard = null;this.secondCard = null;}, 1000);}
}

界面構建

鴻蒙的ArkUI框架提供了聲明式的UI構建方式,我們使用Grid布局來構建4×4的游戲棋盤:

build() {Column() {// 游戲標題和信息顯示Text('記憶翻牌游戲').fontSize(24).fontWeight(FontWeight.Bold)Row() {Text(`步數: ${this.moves}`)Text(`時間: ${this.formatTime(this.timer)}`)}// 游戲棋盤Grid() {ForEach(this.cards, (card: Card, index) => {GridItem() {this.CardView(card, index)}})}.columnsTemplate('1fr 1fr 1fr 1fr').rowsTemplate('1fr 1fr 1fr 1fr')// 新游戲按鈕Button('新游戲').onClick(() => this.startNewGame())// 游戲結束提示if (this.gameOver) {Text('恭喜通關!')}}
}

卡片視圖使用Stack和Column組合實現:

@Builder
CardView(card: Card, index: number) {Stack() {Column() {if (!card.flipped) {Text('?') // 卡片背面} else {Text(card.value) // 卡片正面}}.backgroundColor(card.flipped ? (card.matched ? '#4CAF50' : '#FFFFFF') : '#2196F3').borderRadius(10)}.onClick(() => {this.handleCardClick(index);})
}

關鍵技術與注意事項

  1. 狀態管理:在鴻蒙開發中,直接修改數組元素不會觸發UI更新。我們需要使用slice(0)創建新數組,然后修改并重新賦值給狀態變量。
  2. 定時器管理:游戲計時器需要在組件銷毀或游戲重新開始時正確清理,避免內存泄漏。
  3. UI更新優化:通過將卡片視圖提取為獨立的@Builder方法,可以提高代碼的可讀性和維護性。
  4. 用戶體驗
    • 添加了1秒的延遲讓玩家有機會記住不匹配的卡片
    • 匹配成功的卡片變為綠色,提供視覺反饋
    • 顯示游戲時間和步數,增加挑戰性

附:代碼

// MemoryGame.ets
@Entry
@Component
struct MemoryGame {// 游戲配置private readonly CARD_TYPES = ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼'];private readonly PAIRS_COUNT = 8;private readonly BOARD_SIZE = 4;// 游戲狀態@State cards: Card[] = [];@State firstCard: number | null = null;@State secondCard: number | null = null;@State moves: number = 0;@State gameOver: boolean = false;@State timer: number = 0;private timerInterval: number | null = null;aboutToAppear() {this.startNewGame();}startNewGame() {if (this.timerInterval) {clearInterval(this.timerInterval);}this.moves = 0;this.timer = 0;this.gameOver = false;this.firstCard = null;this.secondCard = null;let cardValues: string[] = [];for (let i = 0; i < this.PAIRS_COUNT; i++) {cardValues.push(this.CARD_TYPES[i]);cardValues.push(this.CARD_TYPES[i]);}this.shuffleArray(cardValues);// 使用slice(0)創建新數組觸發UI更新// 初始化卡片this.cards = cardValues.map(value => {let card: Card = {value: value,flipped: false,matched: false};return card}).slice(0);this.timerInterval = setInterval(() => {this.timer++;}, 1000);}private shuffleArray(array: string[]) {for (let i = array.length - 1; i > 0; i--) {const j = Math.floor(Math.random() * (i + 1));const temp = array[i];array[i] = array[j];array[j] = temp;}}handleCardClick(index: number) {if (this.gameOver || this.cards[index].matched || this.cards[index].flipped) {return;}if (this.firstCard !== null && this.secondCard !== null) {return;}// 創建新數組觸發UI更新let newCards = this.cards.slice(0);newCards[index].flipped = true;this.cards = newCards;if (this.firstCard === null) {this.firstCard = index;} else {this.secondCard = index;this.moves++;this.checkMatch();}}private checkMatch() {if (this.firstCard === null || this.secondCard === null) return;if (this.cards[this.firstCard].value === this.cards[this.secondCard].value) {// 匹配成功let newCards = this.cards.slice(0);newCards[this.firstCard].matched = true;newCards[this.secondCard].matched = true;this.cards = newCards;this.firstCard = null;this.secondCard = null;this.checkGameOver();} else {// 不匹配,1秒后翻回setTimeout(() => {let newCards = this.cards.slice(0);if (this.firstCard !== null) newCards[this.firstCard].flipped = false;if (this.secondCard !== null) newCards[this.secondCard].flipped = false;this.cards = newCards;this.firstCard = null;this.secondCard = null;}, 1000);}}private checkGameOver() {this.gameOver = this.cards.every(card => card.matched);if (this.gameOver && this.timerInterval) {clearInterval(this.timerInterval);}}private formatTime(seconds: number): string {const mins = Math.floor(seconds / 60);const secs = seconds % 60;return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;}build() {Column() {Text('記憶翻牌游戲').fontSize(24).fontWeight(FontWeight.Bold).margin({ bottom: 20 })Row() {Text(`步數: ${this.moves}`).fontSize(16).layoutWeight(1)Text(`時間: ${this.formatTime(this.timer)}`).fontSize(16).layoutWeight(1)}.width('100%').margin({ bottom: 20 })Grid() {ForEach(this.cards, (card: Card, index) => {GridItem() {this.CardView(card, index)}})}.columnsTemplate('1fr 1fr 1fr 1fr').rowsTemplate('1fr 1fr 1fr 1fr').width('100%').height(400).margin({ bottom: 20 })Button('新游戲').width(200).height(40).backgroundColor('#4CAF50').fontColor(Color.White).onClick(() => this.startNewGame())if (this.gameOver) {Text('恭喜通關!').fontSize(20).fontColor(Color.Red).margin({ top: 20 })}}.width('100%').height('100%').padding(20).justifyContent(FlexAlign.Center)}@BuilderCardView(card: Card, index: number) {Stack() {Column() {if (!card.flipped) {Text('?').fontSize(30)} else {Text(card.value).fontSize(30)}}.width('90%').height('90%').backgroundColor(card.flipped ? (card.matched ? '#4CAF50' : '#FFFFFF') : '#2196F3').borderRadius(10).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}.width('100%').height('100%').onClick(() => {this.handleCardClick(index);})}
}interface Card {value: string;flipped: boolean;matched: boolean;
}

總結

通過這個記憶翻牌游戲的開發,我們學習了鴻蒙應用開發中的幾個重要概念:

  1. 使用@State管理應用狀態
  2. 聲明式UI構建方式
  3. 數組狀態更新的正確方法
  4. 定時器的使用和管理
  5. 用戶交互處理的最佳實踐

這款游戲雖然簡單,但涵蓋了鴻蒙應用開發的許多核心概念。開發者可以在此基礎上進一步擴展,比如添加難度選擇、音效、動畫效果、高分記錄等功能,打造更加豐富的游戲體驗。

鴻蒙的ArkUI框架為開發者提供了強大的工具來構建響應式、高性能的應用。通過這個實戰項目,希望能幫助開發者更好地理解鴻蒙應用開發的思路和方法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/912408.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/912408.shtml
英文地址,請注明出處:http://en.pswp.cn/news/912408.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Linux庖丁解牛】— 文件系統!

1 引?"塊"概念 其實硬盤是典型的“塊”設備&#xff0c;操作系統讀取硬盤數據的時候&#xff0c;其實是不會?個個扇區地讀取&#xff0c;這樣 效率太低&#xff0c;?是?次性連續讀取多個扇區&#xff0c;即?次性讀取?個”塊”&#xff08;block&#xff09;。…

如何通過自動化減少重復性工作

通過自動化減少重復性工作的關鍵策略包括&#xff1a;1、識別可被規則化操作的任務、2、引入RPA&#xff08;機器人流程自動化&#xff09;工具、3、整合AI與業務流程系統、4、部署腳本與低代碼平臺、5、持續優化自動化場景與效率。 其中&#xff0c;“引入RPA工具”被廣泛認為…

知識變現全鏈路設計:從IP打造到商業閉環的系統方法論|創客匠人

一、變現低效根源&#xff1a;碎片化努力為何換不來持續增長&#xff1f; 創客匠人服務上千位知識創業者后發現&#xff0c;變現乏力多因缺乏系統設計&#xff1a;某營銷專家的課程因定位模糊、表達生硬、渠道單一&#xff0c;低價仍少有人問。文檔中提出的“六大超級設計公式…

如何利用人工智能大模型提升流量質量

摘要 流量質量是衡量數字化營銷效果的重要指標之一&#xff0c;它反映了用戶對網站或應用的興趣和滿意度。流量質量的常用評估方法有點擊率、跳出率和用戶停留時間等。本文將介紹如何利用人工智能大模型來分析和優化這些指標&#xff0c;提高流量質量&#xff0c;從而提升數字…

從單體架構到微服務:微服務架構演進與實踐

一、單體架構的困境與演進 &#xff08;一&#xff09;單體應用的初始優勢與演進路徑 在系統發展的初期&#xff0c;單體架構憑借其簡單性和開發效率成為首選。單體應用將整個系統的所有功能模塊整合在一個項目中&#xff0c;以單一進程的方式運行&#xff0c;特別適合小型系…

Elasticsearch 自定義排序:使用 Painless 腳本實現復雜排序邏輯

需求背景&#xff1a; 從es查詢數據出來的時候&#xff0c;要求type為CATALOG的數據排在最前面&#xff0c;也就是目錄類型的要放在最前面&#xff0c;而且要求按照層級排序&#xff0c;從L1到L5順序排序 直接上解法&#xff1a; {//查詢條件"query": {"bool…

華為云Flexus+DeepSeek征文|華為云數字人 + DeepSeek:智能交互的革命性突破

目錄 前言 關于華為云數字人和云服務 1、華為云數字人 &#xff08;1&#xff09;MetaStudio介紹 &#xff08;2&#xff09;應用場景 &#xff08;3&#xff09;功能特性 &#xff08;4&#xff09;使用體驗 2、華為云云服務 華為云數字人結合DeepSeek的核心流程 1、…

【GESP】C++四級練習 luogu-P5729 【深基5.例7】工藝品制作

GESP C四級練習&#xff0c;二維/多維數組練習&#xff0c;難度★★☆☆☆。 題目題解詳見&#xff1a;【GESP】C四級練習 luogu-P5729 【深基5.例7】工藝品制作 | OneCoder 【GESP】C四級練習 luogu-P5729 【深基5.例7】工藝品制作 | OneCoderGESP C四級練習&#xff0c;二維…

通過npm install -g yarn安裝Yarn顯示Proxy代理相關問題如何解決?

手動下載yarn.msi安裝包或者yarn.js文件 參考&#xff1a;windows 怎么下載yarn安裝包并將下載的yarn文件移動到全局目錄并添加執行權限&#xff1f;-CSDN博客

arm交叉編譯qt應用中含opengl問題解決

問題是采用正點原子方案中&#xff0c;用虛擬機交叉編譯含opengl的qt程序會出現編譯失敗問題&#xff0c;因為正點原子中的交叉編譯qt源碼時沒有編opengl。 野火似乎有解決&#xff1a; https://doc.embedfire.com/linux/rk356x/Qt/zh/latest/lubancat_qt/install/install_arm…

服務器排查與加固服務詳細介紹

一、服務概述 服務器排查與加固服務是針對企業核心信息資產&#xff08;服務器&#xff09;的全方位安全保障方案&#xff0c;旨在通過系統性排查潛在風險、修復漏洞、優化配置&#xff0c;提升服務器抗攻擊能力&#xff0c;確保業務連續性和數據安全性。該服務覆蓋硬件、操作…

提升開發思維的設計模式(下)

上期回顧 提升開發思維的設計模式&#xff08;上&#xff09; 2. 設計模式分類&#xff08;23種設計模式&#xff09; 2.13 組合模式&#xff08;Composite Pattern&#xff09; 將對象組合成樹形結構&#xff0c;以表示“整體-部分”的層次結構。 通過對象的多態表現&#…

h5學習筆記:前端打包

這2天做了一個實驗。在非module傳統的網頁&#xff0c;要實現改名和避免緩存。原本這個事情早在幾年前就做過借助gulp的方式或者fis3 的工具來完成。然而隨著nodejs 來到了24版本后&#xff0c;似乎nodejs的版本這事情就變動復雜多變了。 為什么那么麻煩&#xff1f;實際上開發…

14.OCR字符識別

目錄 1. 識別方法 1. OCR識別 2. OCR識別方法1-助手識別 3. OCR識別方法2-算子分割識別 4.文本分割識別 2. 文本分割 1. 借用助手設置參數文本分割+混合識別 2. 借用助手設置參數文本分割場景2 3.不同字符場景 1.傾斜字符 1. 識別方法 1. OCR識別 *OCR *1. 概念 * …

如果將Word里每頁的行數設置成50行

https://www.zhihu.com/question/357856175 本文來自知乎林聽晴 第一步&#xff1a;新建一個Word文檔 打開“頁面布局”&#xff0c;之后點擊圖片圈起來的小圖標&#xff0c;即可出現“頁面設置”頁面。 ? ? 路徑&#xff1a;頁面設置—文檔網絡&#xff0c;可以看到默認行…

WebRTC(十一):RTCP和SRTCP

RTCP 基本概念 RTCP 是 RTP 的控制協議&#xff0c;用于監控媒體傳輸質量和參與者狀態&#xff0c;并與 RTP 一起工作。RTP 用于傳輸媒體數據&#xff08;如音視頻&#xff09;&#xff0c;RTCP 則用于傳輸控制信息。 RTCP 通常和 RTP 同時使用&#xff0c;并通過 不同端口&…

將element-plus table背景改成透明色

方法一:全局修改(推薦) /* 全局透明表格樣式 */ .el-table, .el-table__header-wrapper, .el-table__body-wrapper, .el-table__row {background-color: transparent !important; }/* 可選:自定義表頭和斑馬紋行的透明度 */ .el-table__header th {background-color: rgba(…

安全運營中的漏洞管理和相關KPI

漏洞管理一直是企業網絡安全運維中的關鍵環節,但又是安全運維的痛點。不僅要投入大量的人力物力,還無法被其他運維團隊所理解。那么,向領導層和相關團隊反映出當前漏洞管理的現狀和挑戰便是一個急需解決的問題。 通過有效的數據講好故事,發現問題,或許是做好漏洞管理的突破…

機器學習框架(1)

以吳恩達的《機器學習》課程為藍本&#xff0c;整理課程框架&#xff0c;自己學習的簡單記錄。 課程講解很清楚&#xff0c;建議有空可以看看原課程。 01 單變量線性回歸 回歸偏向于連續屬性&#xff0c;分類偏向于離散屬性。 監督學習是給定標簽的學習&#xff1b;而無監督學…

AI Ready數據庫,OceanBase打了一個樣

大數據產業創新服務媒體 ——聚焦數據 改變商業 過去一年&#xff0c;企業對AI的興趣不減。從接入大模型&#xff0c;到部署RAG&#xff08;檢索增強生成&#xff09;系統、探索AI Agent&#xff0c;AI從“新技術”變成了“業務工具”的候選項。但一個技術能否真正落地&#x…