每个人都喜欢经典游戏。你们当中有多少人还记得旧诺基亚手机上的贪吃蛇游戏?相信很多人都记得。这就是为什么本课我们决定使用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(600,450,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!我们的游戏准备好了!同时我也试玩了一下,挺不错的,那么今天的分享就到这里,想要这套游戏教程源码的小伙伴可以扫描下方二维码去我的前端学习交流群获取,里面还会不定时放一些前端学习资料,码字不易,点个赞再走哦!
QQ扫码加入,免费获取更多前端学习资料
好东西别私藏,赶紧分享给身边的小伙伴哦!特别是刚学前端的小伙伴,赶紧实际测试一下,让你很有成就感的项目!
网友评论