基于HTML(canvas)的推箱子游戏

基于HTML(canvas)的推箱子游戏.zip

有积分的自己自行下载吧,没有积分的兄弟可以评论留邮箱,看到后会给你们发过去。


游戏截图

游戏截图     游戏截图


 设计过程

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;
    }
};

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 87
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 87
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漠诽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值