曾经每个手机上都有的游戏,作为前端如今你也能开发出来了,附教程

用户头像
web前端程序猿
关注
发布于: 2020 年 08 月 21 日
曾经每个手机上都有的游戏,作为前端如今你也能开发出来了,附教程

每个人都喜欢经典游戏。你们当中有多少人还记得旧诺基亚手机上的贪吃蛇游戏?相信很多人都记得。这就是为什么本课我们决定使用HTML5重新创建它的原因。我们将使用一个很棒的开源游戏开发框架,称为Phaser。



您将了解游戏状态以及如何使用预加载,创建和更新方法。这是我们正在构建的游戏的最终版本:



建立



在文末扫码进群下载包含游戏文件结构的zip存档。它包含游戏所需的所有图像资产,但没有代码。接下来我们将要写。



现在打开index.html,为页面添加标题并创建指向所有JS文件的链接。稍后,要玩游戏,只需在浏览器中打开此文件。



<!doctype html> html > < 头 >     meta  charset =  UTF-8” />     < 标题 > Snake </ 标题 >     < 脚本 src =  assets / js / phaser.min.js” ></ script >     script  src =  assets / js / menu.js” ></ script >     script  src =  assets / js / game.js” ></ script >     script  src =  assets / js / game_over.js” ></ script >     script  src =  assets / js / main.js” ></ 脚本 > </ 头 > < 正文 ></ body > </ html >



您的目录应如下所示:





游戏的组织方式



将Phaser中的状态视为游戏的不同部分。以下是我们游戏的状态:



菜单状态。它由menu.js处理,并且仅显示开始图像。单击后,游戏将转换为“ 游戏”状态。

游戏状态。它由game.js处理。这是游戏的实际游戏区域。您控制着蛇,吃了苹果,玩得很开心。死亡时,您将过渡到Game_Over状态。

Game_Over状态。它显示gameover.png并显示您的最后得分。单击它时,您将转换为游戏状态。

main.js是我们的主要JavaScript文件。在这里我们将创建一个新的游戏实例并添加一个菜单状态。



1.加载图像



目前,我们的游戏没有任何作用。让我们对Menu状态进行编码,并使其显示标题屏幕。



在设置过程中,我们在HTML文件中包含了Phaser库。这给我们提供了一个名为的全局对象Phaser。通过它,我们可以访问库的构建游戏的方法和功能。



现在,我们将使用全局Phaser对象,并创建一个新的游戏实例。这是代表我们整个游戏的对象。我们将状态添加到它。



main.js



var game;//创建一个新的游戏实例,宽600像素,高450像素: game = new Phaser.Game(600,450,Phaser.AUTO,'');//第一个参数是如何调用我们的状态。//第二个参数是一个对象,其中包含状态功能 game.state 所需的方法。添加('Menu',Menu);game.state.start('Menu');



现在我们需要初始化菜单状态对象。在menu.js中定义一个新对象并添加以下功能。当状态开始时,将首先调用预加载功能,加载我们游戏所需的所有资产。预加载完成后,将调用create,初始化游戏环境以及我们要在其上进行的所有操作:



menu.js



var Menu = {    预载:功能(} {         //需要加载图片,以便以后我们可以基于它们创建精灵。        //第一个参数是图像的引用方式,        //第二个参数是文件的路径。        game.load.image('menu','./assets/images/menu.png');    },    创建:功能() {         //添加一个精灵到你的游戏,这里的精灵将是游戏的标志        //参数为:X,Y,图像名称(见上文)        这个 .add.sprite(0,0,“菜单”);    }};



由于浏览器的安全性限制,要启动游戏,您需要本地运行的Web服务器。换句话说,如果您只是双击index.html,那么它将不起作用。



如果一切都正确完成,则带有“开始”屏幕的页面应出现在浏览器中。





2.画蛇



正如我们前面提到的,游戏状态是实际游戏的发生位置。这也是我们画蛇的地方。就像我们对Menu状态所做的一样,我们需要在main.js中向全局游戏对象注册Game状态您的代码应如下所示:



main.js



var game;游戏= 新 Phaser.Game(600450,Phaser.AUTO,'');game.state。添加('Menu',Menu);//添加游戏状态。game.state。添加('Game',Game);game.state.start('Menu');



我们还想在menu.js中添加一些代码,以使我们在单击游戏状态时启动游戏状态。为此,我们将用一个按钮替换精灵。添加按钮与添加精灵基本相同,只需要提供一个单击按钮即可调用的功能。这是最终的menu.js代码:



menu.js



var Menu = {    预载:功能(} {         //加载菜单所需的所有资源。        game.load.image('menu','./assets/images/menu.png');    },    创建:功能() {        //添加菜单屏幕。        //它将作为开始游戏的按钮。        此 .add.button(0,0,'菜单',这 .startGame,此);    },    startGame:函数() {        //将状态更改为实际游戏。        这个 .state.start('Game');    }};



现在,我们可以对游戏状态进行编码并绘制蛇了。结构类似于菜单状态之一。



game.js



var蛇,苹果,squareSize,得分,速度,    updateDelay,方向,new_direction,    addNew,游标,scoreTextValue,speedTextValue,     textStyle_Key,textStyle_Value;var Game = {    preload:function ()  {         //这里,我们加载了关卡所需的所有资源。        //在我们的例子中,只有两个正方形-一个用于蛇形,另一个用于苹果。        game.load.image('snake','./assets/images/snake.png');        game.load.image('apple','./assets/images/apple.png');    },    create:function ()  {        //通过在create函数中设置全局变量,我们在游戏开始时对其进行初始化。        //我们需要它们在全球范围内可用,以便更新功能可以更改它们。        蛇= [];                     //这将作为一个堆栈工作,其中包含我们的蛇         苹果的各个部分 = {};                     //苹果的对象;        squareSize = 15 ;                //正方形边的长度。我们的图像是15x15像素。        分数= 0 ;                      //游戏得分。        速度= 0 ;                      // 游戏速度。        updateDelay = 0 ;                //用于控制更新率的变量。        方向= '正确' ;            //我们的蛇的方向。        new_direction = null ;           //用来存储新方向的缓冲区。        addNew = false ;                 //食用苹果后使用的变量。        //设置用于键盘输入的Phaser控制器。        游标= game.input.keyboard.createCursorKeys();        game.stage.backgroundColor = '#061f27' ;        //生成初始的蛇形堆栈。我们的蛇将长10个元素。        //从X = 150 Y = 150开始,并在每次迭代中增加X。        对于(var i = 0 ; i < 10 ; i ++){            snake [i] = game.add.sprite(150 + i * squareSize,150,'snake');  //参数是(X坐标,Y坐标,图像)        }        //生成第一个苹果。        this.generateApple();        //将文字添加到游戏顶部。        textStyle_Key = {字体:“ bold 14px sans-serif”,填充:“#46c0f9”,align:“ center” };        textStyle_Value = {字体:“粗体18px sans-serif”,填充:“#fff”,对齐:“居中” };        // 得分。        game.add.text(30,20,“SCORE”,textStyle_Key);        scoreTextValue = game.add.text(90,18,score.toString(),textStyle_Value);        //速度。        game.add.text(500,20,“SPEED”,textStyle_Key);        speedTextValue = game.add.text(558,18,speed.toString(),textStyle_Value);    },    update:function ()  {         //更新函数不断被高速率调用(大约60fps),        //每次都更新游戏场。        //我们现在暂时将其留空。    },    generateApple:function () {        //在网格上选择一个随机位置。        // X在0到585(39 * 15)        之间// Y在0到435(29 * 15)之间        var randomX = Math.floor(Math.random()* 40)* squareSize,            randomY = Math.floor(Math.random()* 30)* squareSize;        //添加一个新苹果。        苹果= game.add.sprite(randomX,randomY,'apple');    }};



这是蛇和苹果的样子:





3.运动与控制



为了使蛇移动,我们将使用game.js的更新功能。



首先,我们创建事件监听器,以使用箭头键控制蛇的方向。



实际的移动有点复杂,因为更新的触发速度非常快,如果每次移动蛇时都移动它,那么最终将得到一个无法控制的快速爬行动物。为了改变这一点,我们建立了一个if语句,它使用名为updateDelay的计数器变量来检查天气,这是update()的第十次连续调用



如果确实是第十次调用,我们将删除蛇的最后一个正方形(堆栈中的第一个元素),根据当前方向为其赋予新坐标,并将其放置在蛇的头部前面(堆栈的顶部) 。代码如下所示:



更新:function ()  {    //按下箭头键,同时不允许非法改变方向,这会杀死玩家。    如果(cursors.right.isDown && direction!= 'left')    {        new_direction = '正确' ;    }    否则 if(cursors.left.isDown && direction!= 'right')    {        new_direction = '左' ;    }    否则 if(cursors.up.isDown && direction!= 'down')    {        new_direction = '向上' ;    }    否则 if(cursors.down.isDown && direction!= 'up')    {        new_direction = '向下' ;    }    //根据得分计算游戏速度的公式。    //得分越高,游戏速度越高,最高为10;    速度= Math.min(10,Math.floor(score / 5));    //在游戏屏幕上更新速度值。    speedTextValue.text = '' +速度;    //由于Phaser的更新功能的更新速率约为60 FPS,    //我们需要放慢速度以使游戏可玩。    //在每次更新调用时增加一个计数器。    updateDelay ++;    //仅当计数器等于(10-游戏速度)时才进行游戏。    //速度越高,执行的频率就越高,    //使蛇移动得更快。    如果(updateDelay%(10 -speed)== 0){        //蛇的运动        var firstCell = snake [snake.length- 1 ],            lastCell = snake.shift(),            oldLastCellx = lastCell.x,            oldLastCelly = lastCell.y;        //如果已从键盘选择了新的方向,请立即使其成为蛇的方向。        如果(new_direction){            方向= new_direction;            new_direction = null ;        }        //根据方向更改相对于蛇头的最后一个单元格的坐标。        如果(方向== '正确'){            lastCell.x = firstCell.x + 15 ;            lastCell.y = firstCell.y;        }        否则 如果(方向== “左”){            lastCell.x = firstCell.x- 15 ;            lastCell.y = firstCell.y;        }        否则, 如果(direction == 'up'){            lastCell.x = firstCell.x;            lastCell.y = firstCell.y- 15 ;        }        否则, 如果(direction == 'down'){            lastCell.x = firstCell.x;            lastCell.y = firstCell.y + 15 ;        }        //将最后一个单元格放在堆栈的前面。        //将其标记为第一个单元格。        snake.push(lastCell);        firstCell = lastCell;    }}



尝试通过键盘上的箭头键控制蛇。



4.碰撞检测



蛇在运动场中自由漫游的游戏没有什么好玩的。我们需要检测蛇何时与墙壁,苹果或本身接触。这称为碰撞检测。



这通常是通过使用物理引擎完成的,Phaser框架支持其中的一些物理引擎。但是对于像这样的简单游戏,它们太复杂了。相反,我们将通过比较坐标来进行自己的碰撞检测。



在更新功能中,在移动蛇的代码之后,我们调用了许多方法。他们将比较坐标以告诉我们是否发生了碰撞。



更新:功能() {        //蛇移动        // ...         //蛇移动结束        //如果吃了苹果,则增加蛇的长度。        //在蛇的后面创建一个块,并使用上一个最后一个块的旧位置        //(它与蛇的其余部分一起移动了)。        如果(addNew){            snake.unshift(game.add.sprite(oldLastCellx,oldLastCelly,'snake'));            addNew = false ;        }        //检查苹果是否碰撞。        这个 .appleCollision();        //检查是否与自身冲突。参数是蛇的头。        这个 .selfCollision(firstCell);        //检查是否与墙壁碰撞。参数是蛇的头。        这个 .wallCollision(firstCell);    }},appleCollision:函数() {    //检查蛇的任何部分是否与苹果重叠。    //如果苹果在蛇的内部产卵,则需要这样做。    for(var i = 0 ; i <snake.length; i ++){         if(snake [i] .x == apple.x && snake [i] .y == apple.y){            //下次蛇移动时,将在其长度上添加一个新块。            addNew = true ;            //销毁旧苹果。            apple.destroy();            //制作一个新的。            这个 .generateApple();            //增加分数。            分数++;            //刷新记分板。            scoreTextValue.text = score.toString();        }    }},selfCollision:函数(头) {    //检查蛇的头部是否与蛇的任何部分重叠。    for(var i = 0 ; i <snake.length- 1 ; i ++){         if(head.x == snake [i] .x && head.y == snake [i] .y){            //如果是这样,请通过屏幕进入游戏。            game.state.start('Game_Over');        }    }},wallCollision:函数(头) {    //检查蛇的头部是否在游戏区域的边界内。    如果(head.x> = 600 || head.x < 0 || head.y> = 450 || head.y < 0){        //如果不在,我们碰到了墙。通过屏幕转到游戏。        game.state.start('Game_Over');    }}



当蛇与苹果碰撞时,我们会增加蛇的分数和长度。但是当与墙壁或蛇体发生碰撞时,我们应该结束游戏。为此,我们需要进入Game_Over状态。同样,我们需要在main.js中注册它。在该文件底部附近添加以下行:



main.js



game.state。添加('Game_Over',Game_Over);



game_over.js



var Game_Over = {    预载:功能() {         //加载此游戏屏幕所需的图像。        game.load.image('gameover','./assets/images/gameover.png');    },    创建:功能() {        //创建按钮以像在菜单中一样开始游戏。        此 .add.button(0,0,'GAMEOVER' ,这 .startGame,此);        //添加有关上一场比赛得分信息的文本。        game.add.text(235,350,“LAST SCORE”,{ 字体:“粗体16px的无衬线”,填充:“#46c0f9” ,对齐:“中心” });        game.add.text(350,348,score.toString(),{ 字体:“粗体20像素无衬线”,填充:“#FFF” ,对齐:“中心” });    },    startGame:函数() {        //将状态更改回游戏。        这个 .state.start('Game');    }};





OK!我们的游戏准备好了!同时我也试玩了一下,挺不错的,那么今天的分享就到这里,想要这套游戏教程源码的小伙伴可以前端学习交流群获取,里面还会不定时放一些前端学习资料,码字不易,点个赞再走哦!





发布于: 2020 年 08 月 21 日 阅读数: 30
用户头像

web前端程序猿

关注

分享才是最大的价值 2020.08.03 加入

不定时分享前端技术干货,你值得拥有,前端学习交流群:109029339

评论

发布
暂无评论
曾经每个手机上都有的游戏,作为前端如今你也能开发出来了,附教程