鴻蒙HarmonyOS 5小游戲實踐:打磚塊游戲(附:源代碼)

打磚塊是一款經典的游戲,它簡單易懂卻又充滿挑戰性。本文將介紹如何使用ArkUI框架開發一個完整的打磚塊游戲,涵蓋游戲邏輯設計、UI實現和交互處理等核心內容。

游戲架構設計

我們的打磚塊游戲采用了組件化設計,主要分為兩個部分:

  1. BrickBreakerGame組件:負責游戲的核心邏輯和渲染
  2. BrickBreakerPage組件:作為游戲的主頁面容器

游戲狀態管理使用了枚舉類型GameState來區分三種狀態:

enum GameState {READY,   // 準備中PLAYING, // 游戲中OVER     // 游戲結束
}

核心游戲邏輯實現

1. 游戲初始化

initGame()方法中,我們初始化了游戲的所有元素:

  • 創建磚塊矩陣(5行6列)
  • 設置擋板和球的初始位置
  • 重置分數和生命值
initGame() {this.bricks = [];for (let row = 0; row < BRICK_ROWS; row++) {let colArray: boolean[] = [];for (let col = 0; col < BRICK_COLS; col++) {colArray.push(true);}this.bricks.push(colArray);}// 其他初始化代碼...
}

2. 游戲主循環

游戲主循環通過setInterval實現,每16毫秒執行一次,負責:

  • 更新球的位置
  • 檢測碰撞(墻壁、擋板、磚塊)
  • 處理游戲結束條件
gameLoop() {// 移動球this.ballX += this.ballDX;this.ballY += this.ballDY;// 碰撞檢測邏輯...
}

3. 碰撞檢測

碰撞檢測是游戲的核心邏輯之一,我們實現了:

  • 球與墻壁碰撞:改變球的運動方向
  • 球與擋板碰撞:根據擊中位置改變反彈角度
  • 球與磚塊碰撞:消除磚塊并增加分數

擋板碰撞處理特別加入了根據擊中位置改變反彈角度的邏輯,增加了游戲的可玩性:

const hitPos = (this.ballX - this.paddleX) / PADDLE_WIDTH;
this.ballDX = (hitPos - 0.5) * 10; // -5到5之間的值
this.ballDY = -Math.abs(this.ballDY);

UI設計與實現

1. 游戲元素渲染

使用ArkUI的聲明式語法,我們實現了各種游戲元素的渲染:

磚塊渲染

ForEach(this.bricks, (row: boolean[], rowIndex:number) => {ForEach(row, (brickExists: boolean, colIndex: number) => {if (brickExists) {Column().width(BRICK_WIDTH - 2).height(BRICK_HEIGHT - 2).backgroundColor(this.colors[rowIndex])// 其他樣式...}})
})

擋板和球

// 擋板
Column().width(PADDLE_WIDTH).height(PADDLE_HEIGHT).backgroundColor('#4CAF50').position({ x: this.paddleX, y: GAME_HEIGHT - PADDLE_HEIGHT - 10 })// 球
Column().width(BALL_SIZE).height(BALL_SIZE).backgroundColor('#FFC107').position({ x: this.ballX, y: this.ballY })

2. 游戲狀態UI

根據游戲狀態顯示不同的UI元素:

準備界面

if (this.gameState === GameState.READY) {Column() {Text('打磚塊').fontSize(32)Text('點擊開始游戲').fontSize(20)}// 其他樣式...
}

游戲結束界面

} else if (this.gameState === GameState.OVER) {Column() {Text('游戲結束').fontSize(28)Text(`得分: ${this.score}`).fontSize(22)Text('點擊重新開始').fontSize(18)}// 其他樣式...
}

交互處理

游戲通過觸摸事件來控制擋板移動:

.handleMove(event: TouchEvent) {if (this.gameState === GameState.PLAYING) {const touchX = event.touches[0].x;this.paddleX = Math.max(0,Math.min(GAME_WIDTH - PADDLE_WIDTH, touchX - PADDLE_WIDTH / 2));}
}

點擊事件用于開始游戲或重新開始:

.onClick(() => {if (this.gameState === GameState.READY) {this.startGame();} else if (this.gameState === GameState.OVER) {this.gameState = GameState.READY;}
})

附:源代碼

import { promptAction } from "@kit.ArkUI";
import { Color } from "@ohos.graphics.scene";// 游戲常量定義
const GAME_WIDTH = 360; // 游戲區域寬度
const GAME_HEIGHT = 600; // 游戲區域高度
const PADDLE_WIDTH = 80; // 擋板寬度
const PADDLE_HEIGHT = 15; // 擋板高度
const BALL_SIZE = 15; // 球大小
const BRICK_WIDTH = 60; // 磚塊寬度
const BRICK_HEIGHT = 20; // 磚塊高度
const BRICK_ROWS = 5; // 磚塊行數
const BRICK_COLS = 6; // 磚塊列數
const PADDLE_SPEED = 8; // 擋板移動速度
const BALL_SPEED = 4; // 球初始速度// 游戲狀態
enum GameState {READY,  // 準備中PLAYING, // 游戲中OVER     // 游戲結束
}// 游戲主邏輯
@Component
struct BrickBreakerGame {@State gameState: GameState = GameState.READY@State paddleX: number = GAME_WIDTH / 2 - PADDLE_WIDTH / 2@State ballX: number = GAME_WIDTH / 2@State ballY: number = GAME_HEIGHT - 100@State ballDX: number = BALL_SPEED@State ballDY: number = -BALL_SPEED@State bricks: boolean[][] = []@State score: number = 0@State lives: number = 3private gameLoopId: number = 0@State colors:string[] = ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3']// 初始化游戲initGame() {// 初始化磚塊this.bricks = [];for (let row = 0; row < BRICK_ROWS; row++) {let colArray: boolean[] = [];for (let col = 0; col < BRICK_COLS; col++) {colArray.push(true);}this.bricks.push(colArray);}this.paddleX = GAME_WIDTH / 2 - PADDLE_WIDTH / 2;this.ballX = GAME_WIDTH / 2;this.ballY = GAME_HEIGHT - 100;this.ballDX = BALL_SPEED;this.ballDY = -BALL_SPEED;this.score = 0;this.lives = 3;}// 開始游戲startGame() {this.gameState = GameState.PLAYING;this.initGame();// 啟動游戲循環this.gameLoopId = setInterval(() => {this.gameLoop();}, 16);}// 游戲結束gameOver() {this.gameState = GameState.OVER;clearInterval(this.gameLoopId);promptAction.showToast({message: `游戲結束! 得分: ${this.score}`,duration: 2000});}// 游戲主循環gameLoop() {// 移動球this.ballX += this.ballDX;this.ballY += this.ballDY;// 檢測球與墻壁碰撞if (this.ballX < 0 || this.ballX + BALL_SIZE > GAME_WIDTH) {this.ballDX = -this.ballDX;}if (this.ballY < 0) {this.ballDY = -this.ballDY;}// 檢測球與擋板碰撞if (this.ballY + BALL_SIZE > GAME_HEIGHT - PADDLE_HEIGHT &&this.ballY + BALL_SIZE < GAME_HEIGHT &&this.ballX + BALL_SIZE > this.paddleX &&this.ballX < this.paddleX + PADDLE_WIDTH) {// 根據擊中擋板的位置改變反彈角度const hitPos = (this.ballX - this.paddleX) / PADDLE_WIDTH;this.ballDX = (hitPos - 0.5) * 10; // -5到5之間的值this.ballDY = -Math.abs(this.ballDY); // 確保向上反彈}// 檢測球與磚塊碰撞for (let row = 0; row < BRICK_ROWS; row++) {for (let col = 0; col < BRICK_COLS; col++) {if (this.bricks[row][col]) {const brickX = col * BRICK_WIDTH;const brickY = row * BRICK_HEIGHT + 50; // 頂部留出空間if (this.ballX + BALL_SIZE > brickX &&this.ballX < brickX + BRICK_WIDTH &&this.ballY + BALL_SIZE > brickY &&this.ballY < brickY + BRICK_HEIGHT) {// 創建新數組以確保UI刷新const newBricks = [...this.bricks];newBricks[row] = [...newBricks[row]];newBricks[row][col] = false;this.bricks = newBricks;this.score += 10;// 根據碰撞位置決定反彈方向if (this.ballX + BALL_SIZE / 2 < brickX ||this.ballX + BALL_SIZE / 2 > brickX + BRICK_WIDTH) {this.ballDX = -this.ballDX;} else {this.ballDY = -this.ballDY;}}}}}// 檢測球是否落到底部if (this.ballY + BALL_SIZE > GAME_HEIGHT) {this.lives--;if (this.lives <= 0) {this.gameOver();} else {// 重置球位置this.ballX = GAME_WIDTH / 2;this.ballY = GAME_HEIGHT - 100;this.ballDX = BALL_SPEED;this.ballDY = -BALL_SPEED;}}// 檢查是否所有磚塊都被消除const allBricksGone = this.bricks.every(row =>row.every(brick => !brick));if (allBricksGone) {promptAction.showToast({message: `恭喜通關! 得分: ${this.score}`,duration: 2000});this.initGame(); // 重新開始新一局}}// 處理觸摸移動事件handleMove(event: TouchEvent) {if (this.gameState === GameState.PLAYING) {const touchX = event.touches[0].x;this.paddleX = Math.max(0,Math.min(GAME_WIDTH - PADDLE_WIDTH, touchX - PADDLE_WIDTH / 2));}}build() {Stack() {// 游戲背景Column().width(GAME_WIDTH).height(GAME_HEIGHT).backgroundColor('#1a1a1a').border({ width: 2, color: '#4a4a4a' })// 繪制磚塊ForEach(this.bricks, (row: boolean[], rowIndex:number) => {ForEach(row, (brickExists: boolean, colIndex: number) => {if (brickExists) {Column().width(BRICK_WIDTH - 2).height(BRICK_HEIGHT - 2).backgroundColor(this.colors[rowIndex]).borderRadius(6).shadow({ color: '#00000040', radius: 4 }).position({x: colIndex * BRICK_WIDTH + 1,y: rowIndex * BRICK_HEIGHT + 50})}}, (brickExists: boolean, colIndex: number) => {return colIndex.toString();})})// 繪制擋板Column().width(PADDLE_WIDTH).height(PADDLE_HEIGHT).backgroundColor('#4CAF50').borderRadius(12).shadow({ color: '#00000040', radius: 4 }).position({ x: this.paddleX, y: GAME_HEIGHT - PADDLE_HEIGHT - 10 })// 繪制球Column().width(BALL_SIZE).height(BALL_SIZE).backgroundColor('#FFC107').borderRadius(BALL_SIZE / 2).shadow({ color: '#00000040', radius: 4 }).position({ x: this.ballX, y: this.ballY })// 游戲狀態提示if (this.gameState === GameState.READY) {Column() {Text('打磚塊').fontSize(32).fontColor('#fff').fontWeight(FontWeight.Bold).margin({ bottom: 24 })Text('點擊開始游戲').fontSize(20).fontColor('#ffffffCC').padding({ top: 8, bottom: 8, left: 24, right: 24 }).borderRadius(20).border({ width: 1, color: '#ffffff40' })}.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center).position({ x: GAME_WIDTH / 2, y: GAME_HEIGHT / 2 - 40 })} else if (this.gameState === GameState.OVER) {Column() {Text('游戲結束').fontSize(28).fontColor('#fff').fontWeight(FontWeight.Bold).margin({ bottom: 12 })Text(`得分: ${this.score}`).fontSize(22).fontColor('#ffd700').margin({ bottom: 20 })Text('點擊重新開始').fontSize(18).fontColor('#ffffffCC').padding({ top: 6, bottom: 6, left: 20, right: 20 }).borderRadius(18).border({ width: 1, color: '#ffffff40' })}.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center).position({ x: GAME_WIDTH / 2, y: GAME_HEIGHT / 2 - 20 })}// 分數和生命顯示Row() {Column({ space: 4 }) {Row() {Image($r("app.media.star")).width(16).height(16)Text(`得分: ${this.score}`).fontSize(16).fontColor('#fff').margin({left: 4})}.alignItems(VerticalAlign.Center)Row() {Image($r("app.media.background")).width(16).height(16)Text(`生命: ${this.lives}`).fontSize(16).fontColor('#fff').margin({left: 4})}.alignItems(VerticalAlign.Center)}.padding({ left: 8, right: 8, top: 4, bottom: 4 }).backgroundColor('#00000066').borderRadius(12)}.position({ x: 16, y: 16 })}.width(GAME_WIDTH).height(GAME_HEIGHT).onClick(() => {if (this.gameState === GameState.READY) {this.startGame();} else if (this.gameState === GameState.OVER) {this.gameState = GameState.READY;}}).onTouch((event: TouchEvent) => {if (event.type === TouchType.Move) {this.handleMove(event)}})}
}// 主頁面
@Entry
@Component
struct BrickBreakerPage {build() {Column() {// 游戲標題Row() {Image($r("app.media.background")).width(32).height(32).margin({ right: 10 })Text('打磚塊').fontSize(32).fontWeight(FontWeight.Bold).fontColor('#2c2c2c')}.alignItems(VerticalAlign.Center).margin({ top: 24, bottom: 16 })// 游戲區域BrickBreakerGame().width(GAME_WIDTH).height(GAME_HEIGHT).margin({ top: 12 }).border({ width: 2, color: '#e0e0e0' }).borderRadius(16).shadow({ color: '#00000020', radius: 8 })}.width('100%').height('100%').alignItems(HorizontalAlign.Center).padding({ top: 12, bottom: 12 }).backgroundColor('#f0f0f0')}
}

總結

通過這個打磚塊游戲的開發實踐,我們展示了如何使用ArkUI框架實現一個完整的游戲應用。關鍵點包括:

  1. 游戲狀態管理
  2. 游戲主循環實現
  3. 碰撞檢測算法
  4. 聲明式UI渲染
  5. 用戶交互處理

這個項目不僅演示了ArkUI的基本用法,也展示了如何將游戲邏輯與UI框架結合。開發者可以在此基礎上進一步擴展,如添加音效、更多關卡、特殊道具等功能,使游戲更加豐富有趣。

ArkUI的聲明式開發模式使得游戲UI的實現變得簡潔直觀,而TypeScript的強類型特性則幫助我們在開發復雜游戲邏輯時減少錯誤。這種組合為HarmonyOS應用開發提供了強大的工具集。

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

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

相關文章

Flutter MobX 響應式原理與實戰詳解

&#x1f4da; Flutter 狀態管理系列文章目錄 Flutter 狀態管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux) setState() 使用詳解&#xff1a;原理及注意事項 InheritedWidget 組件使用及原理 Flutter 中 Provider 的使用、注…

淺談國產數據庫多租戶方案:提升云計算與SaaS的資源管理效率

近年來&#xff0c;“數據庫多租戶”這一概念在技術圈內頻頻出現&#xff0c;成為云計算和SaaS&#xff08;軟件即服務&#xff09;架構中的重要組成部分。多租戶架構不僅為企業提供了高效的資源隔離與共享解決方案&#xff0c;還能大幅降低成本&#xff0c;提高系統的可擴展性…

Wpf的Binding

前言 wpf的Binding就像一個橋梁&#xff0c;它的作用就是連接邏輯層與界面層&#xff0c;既能夠把邏輯層的數據搬到界面層展示&#xff0c;又能將界面層的數據更改后傳遞到邏輯層&#xff0c;Binding的數據來源就是Binding的源&#xff0c;數據展示的地方就是Binding的目標。 …

嵌入式單片機中SPI串行外設接口控制與詳解

串行外設接口(Serial Peripheral Interface)的簡稱也叫做SPI,是一種高速的、全雙工同步通信的一種接口,串行外設接口一般是需要4根線來進行通信(NSS、MISO、MOSI、SCK),但是如果打算實現單向通信(最少3根線),就可以利用這種機制實現一對多或者一對一的通信。 第一:…

【世紀龍科技】新能源汽車動力電池總成裝調與檢修教學軟件

在新能源汽車產業“技術迭代快、安全要求高、實操風險大”的背景下&#xff0c;職業院校如何以“項目式教學改革”為突破口&#xff0c;破解傳統實訓“高成本、高風險、低效率”的困局&#xff1f;江蘇世紀龍科技以桌面VR沉浸式技術為支點&#xff0c;推出《新能源動力電池總成…

GO泛型編程面試題及參考答案

目錄 什么是 Go 中的泛型?Go 從哪個版本開始支持泛型? 在 Go 中如何定義一個帶類型參數的函數? 如何為結構體添加類型參數? 使用 any 關鍵字和自定義類型約束有什么區別? 泛型中~T 的語義及其實際應用是什么? 如何在函數中使用多個類型參數?舉例說明。 Go 泛型支…

ReactRouter-404路由配置以及兩種路由模式

404路由 場景&#xff1a;當瀏覽器輸入url的路徑在整個路由配置中都找不到對應的path&#xff0c;為了用戶體驗&#xff0c;可以使用404兜底組件進行渲染 實現步驟 準備一個404組件在路由表數組的末尾&#xff0c;以*號作為路由path配置路由 新建404組件 const NotFound (…

《Kubernetes》Service 詳解+Ingress

主要介紹kubernetes的流量負載組件&#xff1a;Service和Ingress。 1. Service 1.1 Service介紹 在kubernetes中&#xff0c;pod是應用程序的載體&#xff0c;我們可以通過pod的ip來訪問應用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;這也就意味著不方便直接采用…

常見網絡知識,寬帶、路由器

常見網絡知識&#xff0c;寬帶、路由器 1、關于光貓、橋接和路由接法 現在的光貓都帶有路由功能&#xff0c;即光貓、路由一體。不需要路由器也能讓設備連上&#xff0c;但是一般來說路由功能穿墻有限&#xff0c;放在弱電箱/多媒體箱里的光貓發射出來的wifi信號其實是很難在…

Android應用緩存清理利器:WorkManager深度實踐指南

本文將帶你全面掌握使用WorkManager實現緩存清理的技術方案&#xff0c;從基礎原理到性能優化&#xff0c;提供完整代碼實現和工程實踐指南 一、緩存清理的必要性與挑戰 在Android應用開發中&#xff0c;緩存管理是優化應用性能的關鍵環節。隨著應用使用時間增長&#xff0c;緩…

如何理解構件“可獨立部署”的特性

構件的“可獨立部署”特性是其區別于普通代碼模塊的核心特征之一&#xff0c;我們可以通過生活案例和技術原理解釋來理解這一特性&#xff1a; 一、生活類比&#xff1a;從“家電維修”看獨立部署 假設你家的空調壞了&#xff0c;維修時只需拆開空調外機更換壓縮機&#xff0…

uni-app subPackages 分包加載:優化應用性能的利器

&#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 &#x1f35a; 藍橋云課簽約作者、…

CentOS 8 安裝第二個jdk隔離環境

1.適用于原本已經裝了jdk8&#xff0c;現在需要安裝jdk17&#xff1a; 多版本 JDK 共存不希望修改系統默認 JavaDocker 或腳本化部署 2. 下載 Adoptium&#xff08;原 AdoptOpenJDK&#xff09; 的 OpenJDK 17&#xff1a; cd /指定目錄 sudo wget https://github.com/adopti…

Day.43

getitem方法&#xff1a; class MyList: def __init__(self): self.data [10, 20, 30, 40, 50] def __getitem__(self, idx): return self.data[idx] my_list_obj MyList() print(my_list_obj[2]) len方法&#xff1a; class MyList: def __init__(self): self.data [10…

三七互娛GO面經及參考答案

MySQL 有哪些存儲引擎?MyISAM 如何存儲數字類型數據? MySQL 擁有多種存儲引擎,每種都有其獨特的特性和適用場景。常見的存儲引擎包括 InnoDB、MyISAM、Memory、CSV、Archive、Federated 等。 InnoDB 是 MySQL 5.5 版本之后的默認存儲引擎,它支持事務、外鍵、行級鎖和崩潰恢…

git常見問題匯總-重復提交/刪除已提交文件等問題

git常見問題匯總&#xff1a; 1&#xff0c;已經commit的文件需要修改 /刪除&#xff0c;應該怎么處理&#xff1f; 2&#xff0c;自己建的分支“branch1”顯示“rebasing branch1”&#xff0c;這是什么情況&#xff1f; 3&#xff0c;由于內容修改/優化&#xff0c;在同一個…

Python實例題:簡單的 Web 服務器

目錄 Python實例題 題目 要求&#xff1a; 解題思路&#xff1a; 代碼實現&#xff1a; Python實例題 題目 簡單的 Web 服務器 要求&#xff1a; 使用 Python 的 socket 模塊實現一個簡單的 HTTP 服務器。支持以下功能&#xff1a; 處理 GET 和 POST 請求靜態文件服務…

3.Stable Diffusion WebUI本地部署和實踐

本文看了(68 封私信) 逼真的圖片生產 | Stable Diffusion WebUI本地部署看這一篇就夠了 - 知乎和(68 封私信) Stable Diffusion WebUI 實踐: 基本技法及微調 - 知乎&#xff0c;本人根據它們部署了一遍&#xff0c;中間遇到一些報錯&#xff0c;但根據報錯提示解決了問題&#…

阿里最新開源:Mnn3dAvatar 3D數字人框架, 無需聯網,本地部署可離線運行,支持多模態實時交互

Mnn3dAvatar 3D數字人框架是基于阿里巴巴開源的輕量級深度學習推理框架MNN&#xff08;Mobile Neural Network&#xff09;開發的全新3D數字人框架。Aibot親測這是一個可以在本地運行、完全離線、支持多模態實時交互的智能數字人App。可以在本地私有部署。感興趣的同學可以拿來…

03【C++ 入門基礎】函數重載

文章目錄 引言函數重載函數重載的使用函數重載的原理extern “C” 靜態多態 總結 引言 通過00【C 入門基礎】前言得知&#xff0c;C是為了解決C語言在面對大型項目的局限而誕生&#xff1a; C語言面對的現實工程問題&#xff08;復雜性、可維護性、可擴展性、安全性&#xff0…