鴻蒙元服務實戰-笑笑五子棋(4)
我們在這一章節主要實現五子棋的基本邏輯
核心目錄結構
├─ets
│ ├─entryability
│ │ EntryAbility.ets
│ │
│ ├─entryformability
│ │ EntryFormAbility.ets
│ │
│ ├─pages
│ │ Index.ets
│ │
│ ├─views
│ │ About.ets
│ │ Home.ets
│ │
│ └─widget
│ └─pages
│ WidgetCard.ets
│
└─resources
├─base
│ ├─element
│ │ color.json
│ │ float.json
│ │ string.json
│ │
│ ├─media
│ │ right.svg
│ │ startIcon.png
│ │
│ └─profile
│ form_config.json
│ main_pages.json
│
├─en_US
│ └─element
│ string.json
│
├─rawfile
└─zh_CN
└─element
string.json
## 沉浸式設計

1. `entry/src/main/ets/entryability/EntryAbility.ets` 中統一設置
```javascript
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.getMainWindow()
.then(windowClass = > {
// 設置沉浸式
windowClass.setWindowLayoutFullScreen(true)
// 頂部狀態欄
const topAvoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
const topRectHeight = topAvoidArea.topRect.height;
// px轉vp
const vpTopHeight = px2vp(topRectHeight)
// 底部導航條
const bottomAvoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
const bottomRectHeight = bottomAvoidArea.bottomRect.height;
const vpBottomHeight = px2vp(bottomRectHeight)
AppStorage.setOrCreate('topRect', vpTopHeight)
AppStorage.setOrCreate('bottomRect', vpBottomHeight)
})
windowStage.loadContent('pages/Index', (err) = > {
// ...
});
}
頁面通過 padding 避開頂部和底部
@StorageProp("topRect") topRect: number = 0 @StorageProp("bottomRect") bottomRect: number = 0 build() { Column() { // ... } .width('100%') .height('100%') .linearGradient({ colors: [["#DEF9ED", 0], ["#F4F5F7", 0.4]] }) .padding({ top: this.topRect, bottom: this.bottomRect }) }
AtomicServiceTabs
AtomicServiceTabs是元服務獨有的 tab 組件。Tabs組件后續不再支持在元服務中進行使用。,對 Tabs 組件一些不需提供給用戶自定義設計的屬性進行簡化,限制最多顯示 5 個頁簽,固定頁簽樣式,位置和大小。
基本用法
AtomicServiceTabs({
// 內容
tabContents: [
() = > {
// 自定義構建函數
this.tabContent1();
},
() = > {
// 自定義構建函數
this.tabContent2();
},
],
// 標題
tabBarOptionsArray: [
new TabBarOptions(
$r("sys.media.save_button_picture"),
"玩吧",
"#666",
"#07C160"
),
new TabBarOptions($r("sys.media.AI_keyboard"), "關于", "#666", "#07C160"),
],
// 標題顯示的位置
tabBarPosition: TabBarPosition.BOTTOM,
// 背景顏色
barBackgroundColor: 0xffffff,
});
Home 和 About
Home 表示首頁,用來顯示主要內容
About 表示關于,用來存放項目的基本信息
他們目前都是普通的組件,分別放在 tabContent1 和 tabContent2 內
引入 canvas
在 Home 中開始引入canvas
@Component
export struct Home {
settings: RenderingContextSettings = new RenderingContextSettings(true);
ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
build() {
Column() {
Canvas(this.ctx)
.width(width)
.height(width)
.backgroundColor(Color.Orange)
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
繪制棋盤
繪制棋盤的思路如下:
- 確定要繪制多少個格子。
- 每一個格子多大
這里的想法比較簡單:
- 確定要繪制的格子是 15 個。
gridSize: number = 15;
- 每一個格多大,由屏幕寬度決定。比如屏幕寬度的 90%,然后分成 15 份。每一份就是格子的寬度
// 獲取屏幕的寬度的 90% const width = px2vp(display.getDefaultDisplaySync().availableWidth) * 0.9; // 棋盤是正方形的,所以高度和寬度相等 const height = width; cellSize: number = width / this.gridSize;
- 然后封裝描繪畫面的方法
drawBoard
// 繪制棋盤
drawBoard = () = > {
this.ctx.clearRect(0, 0, width, height);
// 繪制網格
this.ctx.strokeStyle = "#000";
this.ctx.lineWidth = 1;
for (let i = 0; i < this.gridSize; i++) {
this.ctx.beginPath();
this.ctx.moveTo(this.cellSize * i, 0);
this.ctx.lineTo(this.cellSize * i, height);
this.ctx.stroke();
this.ctx.beginPath();
this.ctx.moveTo(0, this.cellSize * i);
this.ctx.lineTo(width, this.cellSize * i);
this.ctx.stroke();
}
};
- canvas 準備好的時候開始繪制
Canvas(this.ctx) .width(width) .height(width) .backgroundColor(Color.Orange) .onReady(() = > { this.drawBoard(); });
點擊下棋
點擊下棋要是做挺多的處理的,比如:
- 當前是下黑棋還是白棋
- 下完這一子之后,勝利了還是繼續下。
我們開始吧:
- 初始化棋盤數據,它是一個二維數組,下棋的時候,其實也是往里面填充內容
// 棋盤數據 board: number[][] = []
- 初始化當前下棋的角色 設定 1:黑旗 ,2:白旗
currentPlayer: number = 1; // 當前玩家 (1: 黑子, 2: 白子)
- 聲明初始化棋盤的函數,負責初始化棋盤數據和描繪棋盤
initGame = () = > { this.board = [] for (let index = 0; index < this.gridSize; index++) { const arr: number[] = [] for (let index2 = 0; index2 < this.gridSize; index2++) { // 0 表示當前沒有人在下棋 arr.push(0) } this.board.push(arr) } // this.currentPlayer = 1; // this.gameOver = false; // this.textContent = '輪到黑子落子'; this.drawBoard(); } ------------------- Canvas(this.ctx) .width(width) .height(width) .backgroundColor(Color.Orange) .onReady(() = > { this.initGame() })
- 聲明點擊棋盤事件,事件中執行下棋邏輯
handleClick = async (event: ClickEvent) = > { const x = event.x; const y = event.y; const col = Math.floor(x / this.cellSize); const row = Math.floor(y / this.cellSize); if (this.board[row] && this.board[row][col] === 0) { this.board[row][col] = this.currentPlayer; this.drawBoard(); this.currentPlayer = this.currentPlayer === 1 ? 2 : 1; } else { promptAction.showToast({ message: `請點擊中棋盤對位位置` }); } };
- 調整
drawBoard
函數,根據this.board[row][col]
描繪出旗子// 繪制棋盤 drawBoard = () = > { this.ctx.clearRect(0, 0, width, height); // 繪制網格 this.ctx.strokeStyle = "#000"; this.ctx.lineWidth = 1; for (let i = 0; i < this.gridSize; i++) { this.ctx.beginPath(); this.ctx.moveTo(this.cellSize * i, 0); this.ctx.lineTo(this.cellSize * i, height); this.ctx.stroke(); this.ctx.beginPath(); this.ctx.moveTo(0, this.cellSize * i); this.ctx.lineTo(width, this.cellSize * i); this.ctx.stroke(); } // 繪制已落的棋子 for (let row = 0; row < this.gridSize; row++) { for (let col = 0; col < this.gridSize; col++) { if (this.board[row][col] !== 0) { this.ctx.beginPath(); this.ctx.arc( col * this.cellSize + this.cellSize / 2, row * this.cellSize + this.cellSize / 2, this.radius, 0, 2 * Math.PI ); this.ctx.fillStyle = this.board[row][col] === 1 ? "black" : "white"; this.ctx.fill(); this.ctx.stroke(); } } } };
- 效果
判斷輸贏
五子棋判斷輸贏的方法比較簡單,只需要知道是否有五子連珠就行
定義判斷輸贏的方法
checkWin
// 判斷是否有五子連珠 checkWin = (row: number, col: number) = > { // 定義一個接口abc,用于表示方向相關的偏移量,dr表示行方向的偏移量,dc表示列方向的偏移量 interface abc { dr: number dc: number } // 定義一個包含四個方向偏移量信息的數組,分別對應不同的檢查方向 const directions: abc[] = [ { dr: 0, dc: 1 }, // 水平方向,行偏移量為0,列偏移量為1,即向右檢查 { dr: 1, dc: 0 }, // 垂直方向,行偏移量為1,列偏移量為0,即向下檢查 { dr: 1, dc: 1 }, // 主對角線方向,行和列偏移量都為1,向右下方向檢查 { dr: 1, dc: -1 }// 副對角線方向,行偏移量為1,列偏移量為 -1,即向右上方向檢查 ]; // 遍歷四個不同的方向,依次檢查每個方向上是否有五子連珠情況 for (let i = 0; i < directions.length; i++) { const dr = directions[i].dr; const dc = directions[i].dc; let count = 1; // 向一個方向檢查(從當前落子位置開始,沿著指定方向向前檢查) // 循環嘗試查找連續相同顏色的棋子,最多查找連續4個(因為已經有當前落子算1個了,湊夠5個判斷贏) for (let i = 1; i < 5; i++) { let r = row + dr * i; let c = col + dc * i; // 判斷當前位置是否在棋盤范圍內,并且此位置的棋子顏色是否和當前玩家的棋子顏色相同 if (r >= 0 && r < this.gridSize && c >= 0 && c < this.gridSize && this.board[r][c] === this.currentPlayer) { count++; } else { break; } } // 向另一個方向檢查(從當前落子位置開始,沿著指定方向的反方向檢查) // 同樣循環嘗試查找連續相同顏色的棋子,最多查找連續4個 for (let i = 1; i < 5; i++) { let r = row - dr * i; let c = col - dc * i; if (r >= 0 && r < this.gridSize && c >= 0 && c < this.gridSize && this.board[r][c] === this.currentPlayer) { count++; } else { break; } } // 如果在當前方向(正方向和反方向結合起來)上連續相同顏色的棋子數量達到或超過5個,則表示當前玩家勝利 if (count >= 5) { return true; } } // 如果遍歷完所有方向都沒有出現五子連珠的情況,則返回false,表示當前落子未形成勝利局面 return false; }
在點擊下棋時 判斷是否輸贏
handleClick
if (this.board[row] && this.board[row][col] === 0) { this.board[row][col] = this.currentPlayer; this.drawBoard(); if (this.checkWin(row, col)) { // 執行后續邏輯
在
handleClick
中判斷輸贏后,再做后續的一些小邏輯- 如 還沒決定輸贏,繼續下棋
- 決定輸贏了,彈出對話框恭喜勝利者, 詢問是否還要再下一盤。。
- 完整代碼
// 處理玩家落子 handleClick = async (event: ClickEvent) = > { if (this.gameOver) { return; } const x = event.x; const y = event.y; const col = Math.floor(x / this.cellSize); const row = Math.floor(y / this.cellSize); if (this.board[row] && this.board[row][col] === 0) { this.board[row][col] = this.currentPlayer; this.drawBoard(); if (this.checkWin(row, col)) { this.textContent = this.currentPlayer === 1 ? "黑子勝利!" : "白子勝利!"; this.gameOver = true; // AlertDialog.show({ message: this.textContent }) const res = await promptAction.showDialog({ title: this.textContent, message: "重新再來一盤嗎", buttons: [ { text: "不了", color: "#000" }, { text: "來吧", color: "#0094ff" }, ], }); if (res.index === 1) { this.initGame(); } } else { this.currentPlayer = this.currentPlayer === 1 ? 2 : 1; this.textContent = this.currentPlayer === 1 ? "輪到黑子落子" : "輪到白子落子"; } } else { promptAction.showToast({ message: `請點擊中棋盤對位位置` }); } };
效果
總結
本章節多了一些業務的具體實現,尤其是下棋的一些邏輯處理上。
如果你興趣想要了解更多的鴻蒙應用開發細節和最新資訊,歡迎在評論區留言或者私信或者看我個人信息,可以加入技術交流群。
審核編輯 黃宇
-
Canvas
+關注
關注
0文章
20瀏覽量
11105 -
鴻蒙
+關注
關注
57文章
2457瀏覽量
43447
發布評論請先 登錄
相關推薦
評論