基於H5(canvas)的推箱子游戲

有積分的自己自行下載吧,沒有積分的兄弟可以評論留郵箱,看到後會給你們發過去。


遊戲截圖

遊戲截圖     遊戲截圖


 設計過程

1. 設計目標:推箱子游戲

2. 設計思路:
  1.1 觀察遊戲玩法 鏈接:http://www.4399.com/flash/202073.htm
  2.2 分析遊戲:遊戲的玩法是將箱子通過小人推送到目標點,所有箱子到達目標點即爲勝利。
    小人移動的必要條件:1. 玩家按下方向鍵。 2. 移動終點有效(空白或箱子可被推動)。
    小人的移動分析:
        1)小人的移動終點爲空白。此時小人直接到達即可。
        2)小人的移動終點爲牆壁。此時小人的移動無效。
        3)小人的移動終點當前被箱子所佔。此時需要對箱子的移動進行判定。

    箱子移動的必要條件:1.箱子只可在小人的推動下才可進行移。2.移動終點有效(無障礙物)。
    箱子的移動分析:
        1)箱子的移動終點爲空白。可被推動。
        2)箱子的移動終點爲牆壁或被其他箱子所佔。此時箱子不可推動。
        3)箱子的移動終點爲目標點。箱子的移動後需要改變箱子的顯示以達到提示用戶效果。

3. 設計實現:
  遊戲使用 css3 中的 canvas 畫布設計實現(。。。介紹自行百度吧)
  沒有基礎可以去 part1 文件夾裏面看基礎。用到的核心就行畫布畫圖片。
  圖片資源來自於 4399 遊戲,爲方便設計,圖片已經 ps 處理,圖片大小爲 80*80 像素。畫布使用大小50*50,需要進行縮放。

  js 需要設計的東西:
    核心代碼:
        1. 判斷箱子或小人是否可移動。
        2. 繪製遊戲界面。
    用戶事件: 按下移動鍵後的處理函數。

4. 具體實現參見 part2 裏面代碼註釋寫的非常清楚

part1

index.html

<!DOCTYPE html> <!-- 聲明當前文檔爲 html5文檔 -->
<html lang="zh-CN"> <!-- 語言爲中文 -->
<head>
    <meta charset="UTF-8"> <!-- 字符編碼:utf-8 -->
    <title>推箱子</title> <!-- 頁面的標題 -->
    <style>
        /* Flex 佈局語法教程 https://www.runoob.com/w3cnote/flex-grammar.html */

        /* 去除瀏覽器默認的內外邊距 */
        * {
            margin: 0;
            padding: 0;
        }

        html, body {
            width: 100%;
            height: 100%;
        }

        /* 使用 flex 佈局, 主軸爲水平方向, 起點在左端, 在主軸上的居中對齊 */
        .game-frame-wrap {
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: row;
            justify-content: center;
            background-color: #eee; /* 方便區分 */
        }

        /* 使用 flex 佈局, 主軸爲垂直方向, 起點在上沿. 起點在上端, 在主軸上的居中對齊 */
        .game-frame {
            display: flex;
            flex-direction: column;
            justify-content: center;
            background-color: white; /* 方便區分 */
        }

        #game-canvas {
            width: 300px;
            height: 300px;
            background-color: green;
        }

        /* 以上的樣式代碼, 使遊戲窗口保持在瀏覽器的水平垂直中間位置 */
    </style>
</head>
<body>
    <div class="game-frame-wrap">
        <img id="wall" src="images/wall-sheet0.png" width="80" height="80" alt="">
        <div class="game-frame">
            <canvas id="game-canvas" width="300" height="300"></canvas>
        </div>
    </div>

    <script>
        window.onload = function () {
            // HTML5 Canvas 使用教程 https://www.runoob.com/html/html5-canvas.html

            // 獲取 canvas dom 對象
            var canvas = document.getElementById("game-canvas");
            console.log(canvas, typeof canvas, "canvas");

            // 獲取在 canvas 畫布上繪圖的對象
            var ctx = canvas.getContext("2d");
            console.log(ctx, typeof ctx, "ctx");

            // 1. 使用頁面顯示的圖片繪圖
            var wallImg = document.getElementById("wall");
            // 在畫布 x:10, y:10 的位置開始繪圖. 左上角座標原點
            ctx.drawImage(wallImg, 10, 10);

            // 2. 使用圖片資源直接繪圖, 這種方式需要將圖片資源加載好之後才能開始畫圖
            var img = new Image();
            img.src = "./images/crate-sheet0.png";
            img.onload = function () {
                ctx.drawImage(img, 100, 100);
            }
        };
    </script>
</body>
</html>

  crate-sheet0.png crate-sheet0.png        wall-sheet0.png wall-sheet0.png


part2

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>推箱子</title>
    <link rel="stylesheet" href="./css/index.css">
    <script src="js/gameMap.js"></script>

    <script type="text/javascript" src="js/index.js"></script>
</head>
<body>
    <div class="game-frame-wrap">
        <div class="game-frame">
            <canvas id="game-canvas" width="600" height="600"></canvas>
            <div id="game-info"></div>
        </div>
    </div>
</body>
</html>

gameMap.js

var gameMap=[];
// 0: 背景圖片
// 7: 地板圖片
// 1: 牆壁圖片
// 2: 目標點圖片
// 3: 箱子圖片
// 4: 小人圖片
// 5: 箱子就位
gameMap[0]=[
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,1,1,1,0,0,0,0,0],
    [0,0,0,0,1,2,1,0,0,0,0,0],
    [0,0,0,0,1,7,1,1,1,1,0,0],
    [0,0,1,1,1,3,7,3,2,1,0,0],
    [0,0,1,2,7,3,4,1,1,1,0,0],
    [0,0,1,1,1,1,3,1,0,0,0,0],
    [0,0,0,0,0,1,2,1,0,0,0,0],
    [0,0,0,0,0,1,1,1,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0]];

gameMap[1]=[
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,1,1,1,1,1,0,0,0,0,0],
    [0,0,1,4,7,7,1,0,0,0,0,0],
    [0,0,1,7,3,3,1,0,1,1,1,0],
    [0,0,1,7,3,7,1,0,1,2,1,0],
    [0,0,1,1,1,7,1,1,1,2,1,0],
    [0,0,0,1,1,7,7,7,7,2,1,0],
    [0,0,0,1,7,7,7,1,7,7,1,0],
    [0,0,0,1,7,7,7,1,1,1,1,0],
    [0,0,0,1,1,1,1,1,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0]];


gameMap[2]=[
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,1,1,1,1,1,1,1,0,0,0],
    [0,0,1,7,7,7,7,7,1,1,1,0],
    [0,1,1,3,1,1,1,7,7,7,1,0],
    [0,1,7,4,7,3,7,7,3,7,1,0],
    [0,1,7,2,2,1,7,3,7,1,1,0],
    [0,1,1,2,2,1,7,7,7,1,0,0],
    [0,0,1,1,1,1,1,1,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0]];


gameMap[3]=[
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,1,1,1,1,0,0,0,0,0],
    [0,0,1,1,7,7,1,0,0,0,0,0],
    [0,0,1,4,3,7,1,0,0,0,0,0],
    [0,0,1,1,3,7,1,1,0,0,0,0],
    [0,0,1,1,7,3,7,1,0,0,0,0],
    [0,0,1,2,3,7,7,1,0,0,0,0],
    [0,0,1,2,2,5,2,1,0,0,0,0],
    [0,0,1,1,1,1,1,1,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0]];


gameMap[4]=[
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,1,1,1,1,1,0,0,0,0],
    [0,0,0,1,4,7,1,1,1,0,0,0],
    [0,0,0,1,7,3,7,7,1,0,0,0],
    [0,0,1,1,1,7,1,7,1,1,0,0],
    [0,0,1,2,1,7,1,7,7,1,0,0],
    [0,0,1,2,3,7,7,1,7,1,0,0],
    [0,0,1,2,7,7,7,3,7,1,0,0],
    [0,0,1,1,1,1,1,1,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0]];

index.js 部分代碼

window.onload = function () {
    var BLOCK_WIDTH = 50, BLOCK_HEIGHT = 50, WIDTH_SIZE = 12, HEIGHT_SIZE = 12;
    var BLOCK_ENUM = {
        background: 0,
        floor: 7,
        wall: 1,
        point: 2,
        crate: 3,
        player: 4,
        crate_ok: 5
    }; // 定義一些常量

    var currentGameMap; // 當前的遊戲地圖數據 12*12,每格 50px
    var CURRENT_GAME_MAP; // 當前的遊戲地圖數據的原始數據
    var currentLevel; // 當前等遊戲地圖的等級
    var moveCount; // 移動次數
    var imageMapping; // 存儲圖片數據
    var personLocation = {x: 0, y: 0, position: 'down'}; // 存儲小人的位置,方向

    var gameInfo = document.getElementById('game-info');
    var canvas = document.getElementById('game-canvas');
    var ctx = canvas.getContext('2d');

    initGame(0);

    // 玩家按鍵事件監測,單次按下移動一次。不需要按着連續移動。
    window.onkeydown = onkeydown;
    window.onkeyup = function () {
        window.onkeydown = onkeydown;
    };

    function onkeydown(event) {
        switch (event.key) {
            case 'ArrowUp': // 上鍵頭 w
            case 'w':
                tryMove('up');
                break;
            case 'ArrowDown': // 下箭頭 s
            case 's':
                tryMove('down');
                break;
            case 'ArrowLeft': // 左鍵頭 a
            case 'a':
                tryMove('left');
                break;
            case 'ArrowRight':
            case 'd':
                tryMove('right'); // 右箭頭 d
                break;
        }
        window.onkeydown = null; // 與 window.onkeyup 一塊限制按下按鍵觸發一次事件
    }

    // 處理用戶按鍵事件(小人移動),可以移動則移動
    function tryMove(position) {
        personLocation.position = position;
        var p1, p2;
        ...
        // 如果小人能夠移動的話,更新遊戲數據,並重繪地圖
        if(dealMove(p1, p2)){
            moveCount++;
            showGameInfo();
        }
        initGameMap(); // 繪當前更新了數據的地圖, 不移動也重繪一次改變小人的朝向

        // 延時一下等待畫布畫好
        setTimeout(function () {
            // 如果移動完成了進入下一關
            if (checkFinish()) {
                alert('恭喜過關!!');
                initGame(currentLevel + 1)
            }
        },100)

    }

    // 處理小人移動
    function dealMove(p1, p2) {
        ...
    }

    //判斷是否推成功
    function checkFinish() {
        ...
    }

    function initGame(level) {
        if (level >= gameMap.length) {
            alert('恭喜已經全部過關!!');
            return
        }
        currentLevel = level; // 初始化地圖等級
        moveCount = 0; // 初始化移動次數
        imageMapping = {
            background: './images/background.png',
            floor: './images/floor.png',
            wall: './images/wall.png',
            point: './images/point.png',
            crate: './images/crate.png',
            player: './images/player.png',
            crate_ok: './images/crate_ok.png'
        }; // 創建圖片數據源
        currentGameMap = gameMap[currentLevel]; // 初始化遊戲地圖數據
        CURRENT_GAME_MAP = copyArray(currentGameMap);
        // 預加載圖片資源數據,加載好出初始化遊戲地圖
        loadImage(imageMapping, function () {
            initGameMap();
            showGameInfo();
        });
    }

    // 圖片資源必須加載完畢才能使用,所以這裏使用回調函數來初始化地圖
    function loadImage(srcs, callback) {
        var loadedCount = 0, // 已經預加載好的圖片數
            imgCount = 0; // 需要預加載的圖片數

        for (var count in imageMapping) {
            imgCount++;
        }
        for (var key in imageMapping) {
            if(imageMapping.hasOwnProperty(key)){ // 對 對象的 key 進行一下檢查
                var src = imageMapping[key];
                imageMapping[key] = new Image();
                imageMapping[key].src = src;

                imageMapping[key].onload = function () {
                    //判斷是否所有的圖片都預加載完成
                    if (++loadedCount >= imgCount && callback instanceof Function) {
                        callback();
                    }
                }
            }
        }
    }

    // 繪畫地圖的函數
    function initGameMap() {
        for (var i = 0; i < HEIGHT_SIZE; i++) {
            for (var j = 0; j < WIDTH_SIZE; j++) {
                drawBlock(j, i)
            }
        }
    }

    function drawBlock(x, y) {
        // 因爲小人和目標點的背景是透明且小人有方向,所以小人和目標點要單獨處理
        var image = undefined;
        var blockType = currentGameMap[y][x];
        switch (blockType) {
            case BLOCK_ENUM.background:
                image = imageMapping.background;
                break;
            case BLOCK_ENUM.floor: // 地板圖片
                image = imageMapping.floor;
                break;
            case BLOCK_ENUM.wall: // 牆壁圖片
                image = imageMapping.wall;
                break;
            //case BLOCK_ENUM.point: // 目標點圖片, 需要背景圖單獨畫
            //    image = imageMapping.point;
            //    break;
            case BLOCK_ENUM.crate: // 箱子圖片
                image = imageMapping.crate;
                break;
            //case BLOCK_ENUM.player: // 小人圖片, 因爲小人有面朝方向,所以要單獨畫
            //    image = imageMapping.player;
            //    personLocation.x = x;
            //    personLocation.y = y;
            //    break;
            case BLOCK_ENUM.crate_ok: // 箱子已經到位
                image = imageMapping.crate_ok;
                break;
        }
        if (image) {
            // 將80*80的圖片壓縮成50*50畫
            ctx.drawImage(image, 0, 0, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
        } else {
            ctx.drawImage(imageMapping.floor, 0, 0, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
            if (blockType === BLOCK_ENUM.point) {
                ctx.drawImage(imageMapping.point, 0, 0, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
            } else if (blockType === BLOCK_ENUM.player) {
                drawPerson(x, y)
            }
        }
    }

    function drawPerson(x, y) {
        personLocation.x = x;
        personLocation.y = y;
        var sx = 0, sy = 0; // 小人圖像在圖片中的位置
        switch (personLocation.position) {
            case 'up':
                sy = 0;
                break;
            case 'down':
                sy = 80;
                break;
            case 'left':
                sy = 160;
                break;
            case 'right':
                sy = 240;
                break;
        }
        ctx.drawImage(imageMapping.player, sx, sy, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
    }

    function showGameInfo(){
        gameInfo.innerText  = '第' + (currentLevel + 1) + '關,總共' + gameMap.length + '關。移動次數: ' + moveCount;
    }

    //克隆二維數組
    function copyArray(arr) {
        var b = [];
        for (var i = 0; i < arr.length; i++) {
            var c= [];
            for (var j = 0; j < arr.length; j++) {
                c.push(arr[i][j]);
            }
            b.push(c)
        }
        return b;
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章