iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 14
1

昨天 Day13 我們已經把畫面區塊規劃出來了,今天要來把主畫面地圖畫好。

地圖畫面座標系

我們需要一個 xy 軸的二維直角座標系,這邊我的做法是把地圖切成 30x30 的小方格,讓每個格子有它獨一無二的座標,這樣我們就可以在指定的位置上畫出蛇和食物。

下圖是地圖示意圖,這個直角座標系跟我們國高中在學的座標有一點點不同,就是Y 軸的方向是往下為正向,所以原點在左上角,會這樣做是因為比較符合我們寫程式的習慣,因為假設每一個格子都是一個 div tag,那在程式裡面,這些 div tag 也是由上往下排列。而且這樣也跟瀏覽器座標系統(Browser Coordinate System)是一致的。

Coordinate

[筆記] 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;
}

CSS3 佈局單為 fr

這邊說明一下 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);

格線佈局的基本概念
CSS Grid 屬性介紹

今日成果

做到這邊就可以完成基本的主畫面地圖了,下面是我們今天的成果
add-map-draft

參考程式碼

Snake - Github


上一篇
Day13 - 貪吃蛇篇:頁面佈局規劃
下一篇
Day15 - 貪吃蛇篇:讓蛇的頭動起來
系列文
以經典小遊戲為主題之ReactJS應用練習30

1 則留言

0
Homura
iT邦高手 2 級 ‧ 2018-10-29 20:28:05

原來有實作遊戲的文章啊
今天才發現
馬上訂閱/images/emoticon/emoticon25.gif
期待大大的文章

Taiming iT邦新手 5 級‧ 2018-10-29 21:51:24 檢舉

哇!感謝大大光臨!
第一次有人留言真的很受寵若驚,
小的第一次參加鐵人賽,請多多指教!/images/emoticon/emoticon41.gif

我要留言

立即登入留言