【JavaScript游戏开发】使用HTML5+Canvas+JavaScript 封装的一个超级马里奥游戏(包含源码)

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/81941806 ...
版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/81941806

这个游戏基本上是建立在JavaScript模块化的开发基础上进行封装的,对游戏里面需要使用到的游戏场景进行了封装,分别实现了Game,Sprite,enemy,player, base,Animation 等游戏类,后续代码还可以继续优化,最终实现的效果如下:

其他的所有核心代码已经开源:https://github.com/xiugangzhang/SuperMarioGame

在线预览游戏效果:

http://htmlpreview.github.io/?https://github.com/xiugangzhang/SuperMarioGame/blob/master/index.html

感兴趣的朋友可以对其进行继续优化,加上后续的其他功能!

其中游戏Game类代码如下:

// 完成Game类的封装
function Game(cfg) {
    for (var attr in cfg) {
        // 这里的this指的就是Game的对象
        this[attr] = cfg[attr];
    }
}

// 定义原型方法和属性
Game.prototype = {
    constructor: Game,

    // 游戏画布的初始化(这个是游戏画布的默认宽度和高度)
    width: 640,
    height: 480,

    // 画布canvas和绘图句柄gc(在构造函数中已经完成了初始化)
    canvas: null,
    gc: null,

    // 帧速率和时间间隔
    FPS: 40,
    sleep: 0,

    // 游戏中的精灵
    sprites: null,


    // 游戏中的运动的背景

    skyOffset: 0,
    grassOffset: 0,
    treeOffset: 0,
    nearTreeoffset: 0,

    TREE_VELOCITY: 20,
    FAST_TREE_VELOCITY: 40,
    SKY_VELOCITY: 8,
    GRASS_VELOCITY: 75,

    lastTime: 0,

    lastUpdateFPS: 0,
    lastUpdateTime: 0,


    // 游戏场景的初始化(主要场景参数的初始化处理)
    init: function () {
        // 直接手动创建canvas元素
        this.canvas = document.createElement("canvas");
        this.canvas.width = this.width;
        this.canvas.height = this.height;


        document.body.appendChild(this.canvas);


        // 设置我的绘图句柄
        this.gc = this.canvas.getContext("2d");

        // 初始化键盘的事件
        this.initEvent();

        // 帧速率不为空,设置我的间隔时间
        if (this.FPS) {
            this.sleep = Math.floor(1000 / this.FPS);
        }

        // 当前的精灵(要么是自己, 要么是一个空数组)
        this.sprites = this.sprites || [];
        // 对每一个精灵完成初始化
        for (var i = 0, len = this.sprites.length; i < len; i++) {
            this.sprites[i].init();
        }

    },

    // 初始化键盘的事件
    initEvent: function () {
        // 按下按键
        document.addEventListener("keydown", function (ev) {
            keyState[ev.keyCode] = true;
            console.log(keyState);
        }, true);

        // 松开按键
        document.addEventListener("keyup", function (ev) {
            keyState[ev.keyCode] = false;
            console.log(keyState);
        }, true);
    },

    // 游戏开始, 就进入到主循环
    start: function () {
        // this指向的是Game这个对象
        var Me = this;

        // 记录一下,游戏开始的时间
        Me.startTime = Date.now();


        // 主循环
        this.mainLoop = setInterval(function () {
            // 距离上一次间隔的时间
            var deltaTime = Me.sleep;

            // 在主循环的执行过程中来实现碰撞检测的功能(一直在不断地检测是否发生了碰撞)
            Me.run(deltaTime);


        }, Me.sleep);

    },

    // 主循环中需要执行的操作
    run: function (deltaTime) {
        // 显示当前游戏持续进行的时间(玩家在这个游戏中持续的时间就是他的分数)
        var playedTime = Date.now() - this.startTime;
        // 在主界面上面显示时间(span标签)
        document.getElementById("timeCount").innerHTML = playedTime.toString();
        document.getElementById("lifeCount").innerHTML = this.sprites[0].HP.toString();

        // 开始碰撞检测
        var coll = this.checkCollide();
        // 只要coll不为空, 就说明有其他玩家和我发生了碰撞
        if (coll) {
            // 如果发生敌人和玩家的碰撞, 就结束游戏(有三个生命值)
            if (this.sprites[0].HP > 0) {
                this.sprites[0].HP--;
            }
        }
        // 我是精灵角色中的第0个角色,直接得到我的生命值并显示
        document.getElementById("lifeCount").innerHTML = this.sprites[0].HP.toString();

        if (this.sprites[0].HP == 0) {
            // 1. 清空主循环中的定时器
            clearInterval(this.mainLoop);
            alert("Game Over.\n Your score : " + playedTime);
            // 2.直接退出程序
            return;
        }


        // 更新画布
        this.update(deltaTime);
        // 清空画布
        this.clear(deltaTime);
        // 重绘画布
        this.draw(deltaTime);


        // 进入主循环之后, 还要不断地处理接收键盘事件
        this.handleInput();
    },

    // 开始实现碰撞的检测, 返回true就表示发生了玩家和敌人的碰撞
    checkCollide: function () {
        // 1.拿到我的玩家这个对象
        var player = this.sprites[0];
        //  注意这里是从第一个场景中的人物和我来逐一检测(我是第0个人物, 其他的都是敌人)
        for (var i = 1, len = this.sprites.length; i < len; i++) {
            var sprite = this.sprites[i];
            // 对于游戏场景中的除了自己的其他所有的精灵和我一一进行碰撞检测
            var coll = sprite.collideWidthOther(player);
            if (coll) {
                return coll;
            }
        }
        return false;
    },

    // 更新精灵的状态
    update: function (deltaTime) {
        for (var i = 0, len = this.sprites.length; i < len; i++) {
            var sprite = this.sprites[i];
            // 开始更新每一个精灵的坐标状态(运动状态信息)
            sprite.update(deltaTime);
        }
    },

    // 清空画布信息
    clear: function () {
        // 清空画布
        this.gc.clearRect(0, 0, this.canvas.width, this.canvas.height);

        var fps = this.caculateFPS();
        this.fps = fps;

        // 显示帧速率到画布上面
        var now = Date.now();
        if (now - this.lastUpdateTime > 1000) {
            this.lastUpdateTime = now;
            this.lastUpdateFPS = fps;

            document.getElementById("fps").innerText = this.lastUpdateFPS.toFixed();
        }


        this.initGameMap();

    },

    // 绘制背景地图
    initGameMap: function () {
        var fps = this.fps;

        // 实现移动的位移量
        this.skyOffset = this.skyOffset < this.canvas.width ?
            this.skyOffset + this.SKY_VELOCITY / fps : 0;

        this.grassOffset = this.grassOffset < this.canvas.width ?
            this.grassOffset + this.GRASS_VELOCITY / fps : 0;

        this.treeOffset = this.treeOffset < this.canvas.width ?
            this.treeOffset + this.TREE_VELOCITY / fps : 0;

        this.nearTreeOffset = this.nearTreeOffset < this.canvas.width ?
            this.nearTreeOffset + this.FAST_TREE_VELOCITY / fps : 0;

        var sky = ImgCache["sky"],
            tree = ImgCache["tree-twotrunks"],
            nearTree = ImgCache["smalltree"],
            grass = ImgCache["grass"],
            grass2 = ImgCache["grass2"];

        this.gc.save();
        this.gc.translate(-this.skyOffset, 0);
        this.gc.drawImage(sky, 0, 0);
        this.gc.drawImage(sky, sky.width - 2, 0);
        this.gc.restore();

        this.gc.save();
        this.gc.translate(-this.treeOffset, 0);
        this.gc.drawImage(tree, 100, 240);
        this.gc.drawImage(tree, 1100, 240);
        this.gc.drawImage(tree, 400, 240);
        this.gc.drawImage(tree, 1400, 240);
        this.gc.drawImage(tree, 700, 240);
        this.gc.drawImage(tree, 1700, 240);
        this.gc.restore();

        this.gc.save();
        this.gc.translate(-this.nearTreeOffset, 0);
        this.gc.drawImage(nearTree, 250, 240);
        this.gc.drawImage(nearTree, 1250, 240);
        this.gc.drawImage(nearTree, 800, 240);
        this.gc.drawImage(nearTree, 1800, 240);
        this.gc.restore();

        this.gc.save();
        this.gc.translate(-this.grassOffset, 0);

        this.gc.drawImage(grass, 0, this.canvas.height - grass.height);

        this.gc.drawImage(grass, grass.width - 5,
            this.canvas.height - grass.height);

        this.gc.drawImage(grass2, 0, this.canvas.height - grass2.height);

        this.gc.drawImage(grass2, grass2.width,
            this.canvas.height - grass2.height);
        this.gc.restore();
    },

    // 绘制背景滚动的效果
    caculateFPS: function (now) {
        if (now == undefined) {
            now = Date.now();
        }

        var fps = 1000 / (now - this.lastTime);
        this.lastTime = now;
        return fps;
    },


    // 开始重新绘制精灵
    draw: function (deltaTime) {
        for (var i = 0, len = this.sprites.length; i < len; i++) {
            var sprite = this.sprites[i];
            // 开始绘制
            sprite.draw(this.gc);
        }

    },


    // 游戏中的处理用户的输入
    handleInput: function () {
        for (var i = 0, len = this.sprites.length; i < len; i++) {
            var sprite = this.sprites[i];
            // 先判断一下,这个精灵有没有handleInput属性
            if (sprite.handleInput) {
                // 如果这个精灵有这个属性或者方法的话, 就去调用精灵自己的处理函数
                sprite.handleInput();
            }
        }
    }
}

鉴于代码太多,已经将SuperMario的所有核心代码开源:

开源地址:https://github.com/xiugangzhang/SuperMarioGame

 

 

相关文章
|
5月前
|
移动开发 前端开发 JavaScript
征信报告修改器,征信报告生成器,制作软件无痕修改软件【js+html+css】
本项目为信用评分模拟器教学工具,采用HTML5实现,仅供学习参考。核心功能通过JavaScript构建,包含虚拟数据生成、权重分配及信用因素分析(如还款记录、信用使用率等)。
|
5月前
|
存储 自然语言处理 前端开发
抖音快手小红书虚拟评论截图生成器,模拟对话制作工具,html+js+css
这是一款纯前端实现的多平台虚拟评论生成器,支持抖音、快手、小红书风格,适用于产品演示与UI设计。采用Vanilla JS与Flexbox布局,利用IndexedDB存储数据,CSS Variables切换主题。
|
5月前
|
前端开发 JavaScript
个人征信电子版无痕修改, 个人信用报告pdf修改,js+html+css即可实现【仅供学习用途】
本代码展示了一个信用知识学习系统的前端实现,包含评分计算、因素分析和建议生成功能。所有数据均为模拟生成
|
5月前
|
存储 前端开发 安全
病历单生成器在线制作,病历单生成器app,HTML+CSS+JS恶搞工具
本项目为医疗病历模拟生成器,旨在为医学教学和软件开发测试提供数据支持,严格遵守《医疗机构病历管理规定》。
|
5月前
|
前端开发 容器
处方单图片生成器, 处方单在线制作免费,js+css+html恶搞神器
这是一个电子处方模拟生成系统,使用html2canvas库实现图片导出功能。系统生成的处方单包含多重防伪标识,并明确标注为模拟数据,仅供学习
|
5月前
|
前端开发
个人征信PDF无痕修改软件,个人征信模板可编辑,个人征信报告p图神器【js+html+css仅供学习用途】
这是一款信用知识学习系统,旨在帮助用户了解征信基本概念、信用评分计算原理及信用行为影响。系统通过模拟数据生成信用报告,涵盖还款记录
|
5月前
|
前端开发 JavaScript 容器
制作b超单生成器, 假怀孕b超单图片制作, p图医院证明【css+html+js装逼恶搞神器】
本资源提供一个适合用于熟人之间恶搞的工具,效果逼真,仅供学习参考与娱乐。包含前端技术学习要点:语义化布局、响应式设计、Flexbox、图片自适应
|
5月前
|
前端开发
医院检查单子p图软件,在线制作仿真病历,js+css+html装逼神器
本示例展示如何用HTML/CSS创建医疗信息页面,内容仅供学习参考。页面模拟“阳光医院体检中心”场景,提供预约功能验证(如姓名、手机号、日期)。所有数据仅用于演示

热门文章

最新文章

下一篇
oss云网关配置