昨天 Day13 我們已經把畫面區塊規劃出來了,今天要來把主畫面地圖畫好。
我們需要一個 xy 軸的二維直角座標系,這邊我的做法是把地圖切成 30x30 的小方格,讓每個格子有它獨一無二的座標,這樣我們就可以在指定的位置上畫出蛇和食物。
下圖是地圖示意圖,這個直角座標系跟我們國高中在學的座標有一點點不同,就是 Y 軸的方向是往下為正向
,所以原點在左上角,會這樣做是因為比較符合我們寫程式的習慣,因為假設每一個格子都是一個 div tag,那在程式裡面,這些 div tag 也是由上往下排列。而且這樣也跟瀏覽器座標系統(Browser Coordinate System)是一致的。
[筆記] Browser Coordinate System │ 瀏覽器座標系統
[Explanation] Concept of Browser Coordinate System
為了讓畫面可以畫出格子,我們先來準備格子的資料結構。
containers/SnakeGame/constants.js
export const GAME_WIDTH = 30;
containers/SnakeGame/reducer.js
const blocks = _.range(0, GAME_WIDTH).map((value, indexY) => (
_.range(0, GAME_WIDTH).map((value, indexX) => (
{
id: indexX + GAME_WIDTH * indexY,
x: indexX,
y: indexY,
}
))
));
因為我們需要的是 30x30 的格子,所以我用兩層迴圈來做,一樣這邊我有用到在 Day05 有提過的 lodash
來幫助我們快速生成這些陣列。
在裡面的每一個格子都各自用一個物件表示,每一個物件裡面存了 id,x座標以及y座標,會需要有一個 id 是因為我們之後用迴圈迭代來產生這些方格的時候,會需要對每個元素賦予一個獨一無二的標示,也就是 key,這個 key可以在 DOM 中的某些元素被改變的時候,幫助 React 識別哪些元素發生了改變。
React - Lists and Keys
得到blocks之後,我們就可以在前端頁面把每個方格子迭代出來,我的作法如下
containers/SnakeGame/index.js
<div className="snake-game__map-wrapper">
{
blocks.map((rows) => (
rows.map((block) => (
<div
key={block.get('id')}
className="snake-game__map-block-item"
>
</div>
))
))
}
</div>
為了讓我們迭代出來的 div tag 可以做棋盤狀的排列,我這邊使用 grid 來佈局,同樣的,我們把 <div className="snake-game__map-wrapper">
透過 display: grid; 宣告為外容器,並且透過 grid-template-columns 與 grid-template-rows 屬性定義了行與列,GAME_WIDTH 參數我一樣宣告在 constants.js 裡面,這邊希望是 30x30 的棋盤,所以 GAME_WIDTH 是 30。
containers/SnakeGame/Styled.js
.snake-game__map-wrapper {
width: ${GAME_WRAPPER_SIZE}px;
height: ${GAME_WRAPPER_SIZE}px;
@media only screen and (max-width: 600px) {
width: calc(100vw - 20px);
height: calc(100vw - 20px);
}
display: grid;
grid-template-columns: repeat(${GAME_WIDTH}, 1fr);
grid-template-rows: repeat(${GAME_WIDTH}, 1fr);
}
.snake-game__map-block-item {
border: 1px solid black;
box-sizing: border-box;
}
這邊說明一下 fr
這個單位(fraction),這個單位能夠將可用的 剩餘空間 做比例分割,舉例來說,如果是
grid-template-columns: 1fr 2fr 1fr;
表示把 column 方向的剩餘空間,分成三個區塊,第一個區塊跟第三個區塊因為是 1fr ,意思是分別佔 1/4 ,分母是 4 是因為(1 + 2 + 1),而第二個區塊佔 2/4 ,依此類推。
而 repeat 是重複隔線的意思,舉例來說,下面兩行是相同的意思。
grid-template-columns: 1fr 1fr 1fr;
grid-template-columns: repeat(3, 1fr);
所以我們剛剛的意思是,我把 row 方向以及 column 方向,在範圍內各平分成 30 等份,每一份是 1fr 的意思。
display: grid;
grid-template-columns: repeat(${GAME_WIDTH}, 1fr);
grid-template-rows: repeat(${GAME_WIDTH}, 1fr);
做到這邊就可以完成基本的主畫面地圖了,下面是我們今天的成果
原來有實作遊戲的文章啊
今天才發現
馬上訂閱
期待大大的文章
哇!感謝大大光臨!
第一次有人留言真的很受寵若驚,
小的第一次參加鐵人賽,請多多指教!