nodejs 游戏框架_基于nodejs环境用Phaser来制作一个html5游戏

2023-10-30 16:20

本文主要是介绍nodejs 游戏框架_基于nodejs环境用Phaser来制作一个html5游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近对html5小游戏有点感兴趣,就在网上搜罗了一些教程,自己学着实现了简单的flappybird。效果如下图:

78232d58a162eb46b12eb8e7b85b57f1.gif7b8789ce4b69427edea7526f5b5d5437.png6c7eb623f4fad67dcb931b6d871f12d6.gif

今天就跟大家简单介绍下我的实现,相应代码已放置本人github,下面步骤所涉及到的资源素材皆在代码里面。

1、创建node项目

游戏主要是有js来控制实现的,是要靠服务器运行的,这边我就选择了node环境,所以最开始需要先创建node项目,这里就不详细介绍,若是不会的可以参照如何创建nodejs项目?。

2、添加游戏相关空架子

创建完node项目之后,就需要我们在这个框架结构编写游戏了,首先我们需要建与游戏编码相关的一个空架子,目录结构如下:

196f35a2682b2fe61b15dc1e208c1788.png

我们需要编辑的就是views/index.ejs(相当于index.html,主页入口),public/images(游戏图片),public/Javascripts(游戏相关js,main.js就是主要编辑的js,phaser.min.js就是游戏基于的技术实现)

3、初始化

修改node项目主文件index.ejs,引入phaser和main.js

Flappy Bird Clone

4、游戏场景实例化

接下来就需要我们编辑各种游戏场景了,控制整个游戏的流程个运行,这里就需要在mainjs里面实现了,依赖于phaser的技术,这里就不仔细介绍phaser,若有不懂可查看官方文档,或者看这里。

首先这边设定的是四种游戏场景:加载动画(bootState)、预加载(preloadState)、菜单(menuState)、游戏(mainState)

这里用到的就是phaser的state

首先根据设计的四种场景,搭建main.js的空架子,实例化一个game,如下:

//加载动画

var bootState = {};

//预加载

var preloadState = {};

//菜单

var menuState = {};

//游戏

var mainState = {};

//实例化游戏

var game = new Phaser.Game(285, 490);

//把定义好的场景添加到游戏中

game.state.add('boot', bootState);

game.state.add('preload', preloadState);

game.state.add('menu', menuState);

game.state.add('main', mainState);

//调用boot场景来启动游戏

game.state.start('boot');

5、加载动画

我们设定游戏最开始就是加载动画,这边有个小问题,就是这里的gif动画运行不起来,这块暂时没有去优化,所以大家也可以看着办,这块也是可有可无,可根据自己喜好添加,若不想要这一块,将初始运行改为预加载的模块即可,代码如下:

var bootState = {

preload:function () {

game.load.image('loading','/images/flappybird/loading0.gif'); //加载进度条图片资源

},

create: function () {

game.state.start('preload'); //加载完成后,调用preload场景

}

};

6、调用preload场景

游戏所需要用到的所有资源全都在这里进行加载,代码如下:

var preloadState = {

preload : function(){

var preloadSprite = game.add.sprite(0,0,'loading'); //创建显示loading进度的sprite

game.load.setPreloadSprite(preloadSprite); //用setPreloadSprite方法来实现动态进度条的效果

//以下为要加载的资源

game.load.image('background','/images/flappybird/bg_day.png'); //游戏背景图

game.load.image('ground','/images/flappybird/land.png'); //地面

game.load.image('title','/images/flappybird/title.png'); //游戏标题

game.load.image('bird', '/images/Flappy-bird.png');//小鸟

game.load.image('btn','/images/flappybird/button_play.png'); //按钮

game.load.image('pipe', '/images/pipe.png');//管道

game.load.audio('jump', '/images/jump.wav');

game.load.image('ready_text','/images/flappybird/text_ready.png'); //get ready图片

game.load.image('play_tip','/images/flappybird/tutorial.png'); //玩法提示图片

game.load.image('game_over','/images/flappybird/text_game_over.png'); //gameover图片

game.load.image('score_board','/images/flappybird/score_panel.png'); //得分板

},

create : function(){

game.state.start('menu'); //当以上所有资源都加载完成后就可以进入menu游戏菜单场景了

}

};

7、进入菜单场景

加载完资源就开始进入游戏的菜单页面了,从这里开始就是整个游戏的展示主体了。

菜单有四个部分:背景、地板、标题组(包含标题和小鸟)和按钮(游戏开始入口)

实现效果如图:

78232d58a162eb46b12eb8e7b85b57f1.gif

实现原理:

(1)背景和地面:我们看到这两个东西是会动的,地面移动动的速度快一些,背景图慢一些,在Phaser中有专门的东西来处理这种效果,叫做TileSprite,什么是TileSprite呢?TileSprite本质上还是一个sprite对象,不过这个sprite的贴图是可以移动的,并且会自动平铺来弥补移动后的空缺,所以我们的素材图片要是平铺后看不出有缝隙,就可以拿来当做TileSprite的移动贴图了。TileSprite的贴图既可以水平移动也可以垂直移动,或者两者同时移动,我们只需要调用TileSprite对象的autoScroll(x,y)方法就可以使它的贴图动起来了,其中x是水平方向的速度,y是垂直方向的速度。

var menuState = {

create:function () {

game.add.tileSprite(0,0,game.width,game.height,'background').autoScroll(-10,0); //背景图

game.add.tileSprite(0,game.height-80,game.width,112,'ground').autoScroll(-100,0); //地板

}

};

(2)标题组:我们可以看到,标题组有标题文字和小鸟,这两者还是动态的,一起上下移动,这里用到的就是Phaser.Group,也就是组。组相当于一个父容器,我们可以把许多对象放进一个组里,然后就可以使用组提供的方法对这些对象进行一个批量或是整体的操作。我们把标题文字和小鸟装进这个组里面,对这个组进行一个整体的操作。

var menuState = {

create:function () {

...

var titleGroup = game.add.group(); //创建存放标题的组

titleGroup.create(0,0,'title'); //标题

var bird = titleGroup.create(190, 10, 'bird'); //添加bird到组里

bird.animations.add('fly'); //添加动画

bird.animations.play('fly',12,true); //播放动画

titleGroup.x = 35;

titleGroup.y = 100;

game.add.tween(titleGroup).to({ y:120 },1000,null,true,0,Number.MAX_VALUE,true); //标题的补间动画,对这个组添加一个tween动画,让它不停的上下移动

}

};

(3)按钮:按钮就是给一张图片,然后添加点击事件,点击就开始游戏的场景

var menuState = {

create:function () {

...

...

var btn = game.add.button(game.width/2,game.height/2,'btn',function(){//按钮

//点击开始main游戏场景

game.state.start('main');

});

btn.anchor.setTo(0.5,0.5);

}

};

8、游戏场景

这里就是整个游戏的重头戏了,从这里开始就是操作玩游戏的整个过程,都在mainState里面完成,至于每个游戏的小部分,依靠的是里面各个函数体来控制完成

首先,游戏准备部分,游戏还没开始前给出个简易游戏教程指示,这里整体都是静态的:

(1)初始化背景

(2)创建用于存放管道的组

(3)开启地面的物理系统,但是先保持地面不移动

(4)开启小鸟的物理系统,但是先保持小鸟不移动

(5)标题头

(6)教程示意图,添加点击事件,点击开始游戏

var mainState = {

create: function() {

this.background = game.add.tileSprite(0,0,game.width,game.height, 'background');//背景图

this.pipeGroup = game.add.group();//用于存放管道的组,后面会讲到

this.pipeGroup.enableBody = true;

this.ground = game.add.tileSprite(0,game.height-80,game.width,112,'ground'); //地板

game.physics.enable(this.ground,Phaser.Physics.ARCADE);//开启地面的物理系统

this.ground.body.immovable = true; //让地面在物理环境中固定不动

this.bird = game.add.sprite(50,150,'bird'); //鸟

game.physics.enable(this.bird,Phaser.Physics.ARCADE); //开启鸟的物理系统

this.bird.body.gravity.y = 0; //鸟的重力,未开始游戏,先让重力为0,不然鸟会掉下来

this.readyText = game.add.image(game.width/2, 40, 'ready_text'); //get ready 文字

this.playTip = game.add.image(game.width/2,300,'play_tip'); //提示点击屏幕的图片

this.readyText.anchor.setTo(0.5, 0);

this.playTip.anchor.setTo(0.5, 0);

//game.time.events.stop(false); //先不要启动时钟

game.input.onDown.addOnce(this.startGame, this); //点击屏幕后正式开始游戏

},

}

然后,开始玩游戏

(1)从startGame函数开始,玩游戏的主题函数。让背景和地面开始移动,开启小鸟的重力系统,通过点击屏幕让小鸟起飞,去除准备阶段的标题头和玩法提示图片,管道开始移动,然后显示左上角的得分情况。通过update更新游戏情况

var mainState = {

create: function() {

...

},

startGame: function () {

this.gameSpeed = 200; //游戏速度

this.background.autoScroll(-(this.gameSpeed/10),0); //让背景开始移动

this.ground.autoScroll(-this.gameSpeed,0); //让地面开始移动

// 小鸟

game.physics.arcade.enable(this.bird);

this.bird.body.gravity.y = 1000;

this.readyText.destroy(); //去除 'get ready' 图片

this.playTip.destroy(); //去除 '玩法提示 图片

var spaceKey = game.input.keyboard.addKey(

Phaser.Keyboard.SPACEBAR);

spaceKey.onDown.add(this.jump, this);

//管道

this.pipes = game.add.group();

//定时器,每1.5秒调用addRowOfPipes

this.timer = game.time.events.loop(1500, this.addRowOfPipes, this);

//得分:显示左上角的分数

this.score = 0;

this.labelScore = game.add.text(20, 20, "0",

{ font: "30px Arial", fill: "#ffffff" });

// 改变小鸟旋转的中心点

this.bird.anchor.setTo(-0.2, 0.5);

//声音

this.jumpSound = game.add.audio('jump');

},

/**

* 最后update方法是更新函数,它会在游戏的每一帧都执行,以此来创造一个动态的游戏

*/

update: function() {

// 小鸟超过范围区域则重新开始游戏

if (this.bird.y < 0 || this.bird.y > 490)

this.restartGame();

//小鸟慢慢向下旋转,直到某个点

if (this.bird.angle < 20)

this.bird.angle += 1;

//当鸟死亡时,重新开始游戏

//game.physics.arcade.overlap(this.bird, this.pipes, this.restartGame, null, this);

//当鸟死亡时,从屏幕上掉下来

game.physics.arcade.overlap(this.bird, this.pipes, this.hitPipe, null, this);

},

(2)jump函数:点击屏幕控制小鸟飞翔状态

jump: function() {

//在死亡时不能够让鸟跳跃

if (this.bird.alive == false)

return;

// 点击屏幕小鸟的重力系数变为负数,便可起飞

this.bird.body.velocity.y = -350;

//小鸟往上跳的时候,它向上旋转

game.add.tween(this.bird).to({angle: -20}, 100).start();

//小鸟起飞的音效

this.jumpSound.play();

},

(3)管道组:建立管道和管道组,通过startGame添加定时器控制管道定时生成和移动。然后这边还有个分数计算,这里设定就是每生成一个管道就加一分。

addOnePipe: function(x, y) {

var pipe = game.add.sprite(x, y, 'pipe');

this.pipes.add(pipe);

game.physics.arcade.enable(pipe);

pipe.body.velocity.x = -200;

pipe.checkWorldBounds = true;

pipe.outOfBoundsKill = true;

},

addRowOfPipes: function() {

// Randomly pick a number between 1 and 5

// This will be the hole position

var hole = Math.floor(Math.random() * 5) + 1;

// Add the 6 pipes

// With one big hole at position 'hole' and 'hole + 1'

for (var i = 0; i < 8; i++)

if (i != hole && i != hole + 1)

this.addOnePipe(285, i * 60 + 10);

//每创建一个新管子就把分数加1

this.score += 1;

this.labelScore.text = this.score;

},

(3)接下来,就是判断小鸟撞击管道的时候死亡了

hitPipe: function() {

// If the bird has already hit a pipe, do nothing

// It means the bird is already falling off the screen

if (this.bird.alive == false)

return;

// Set the alive property of the bird to false

this.bird.alive = false;

// Prevent new pipes from appearing

game.time.events.remove(this.timer);

// Go through all the pipes, and stop their movement

this.pipes.forEach(function(p){

p.body.velocity.x = 0;

}, this);

}

(4)最后,就是当小鸟各种死亡后,就是游戏结束了,这时候需要启动重新开始游戏的设定。设定跳转菜单页面。

// 重新开始游戏

restartGame: function() {

game.state.start('menu');

},

到这里就结束啦,有兴趣就去github上fork我的代码也行哦!觉得不错的请赏赐一个赞!!

这篇关于nodejs 游戏框架_基于nodejs环境用Phaser来制作一个html5游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/309112

相关文章

VScode连接远程Linux服务器环境配置图文教程

《VScode连接远程Linux服务器环境配置图文教程》:本文主要介绍如何安装和配置VSCode,包括安装步骤、环境配置(如汉化包、远程SSH连接)、语言包安装(如C/C++插件)等,文中给出了详... 目录一、安装vscode二、环境配置1.中文汉化包2.安装remote-ssh,用于远程连接2.1安装2

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

详解如何在React中执行条件渲染

《详解如何在React中执行条件渲染》在现代Web开发中,React作为一种流行的JavaScript库,为开发者提供了一种高效构建用户界面的方式,条件渲染是React中的一个关键概念,本文将深入探讨... 目录引言什么是条件渲染?基础示例使用逻辑与运算符(&&)使用条件语句列表中的条件渲染总结引言在现代

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

mysql-8.0.30压缩包版安装和配置MySQL环境过程

《mysql-8.0.30压缩包版安装和配置MySQL环境过程》该文章介绍了如何在Windows系统中下载、安装和配置MySQL数据库,包括下载地址、解压文件、创建和配置my.ini文件、设置环境变量... 目录压缩包安装配置下载配置环境变量下载和初始化总结压缩包安装配置下载下载地址:https://d

将Python应用部署到生产环境的小技巧分享

《将Python应用部署到生产环境的小技巧分享》文章主要讲述了在将Python应用程序部署到生产环境之前,需要进行的准备工作和最佳实践,包括心态调整、代码审查、测试覆盖率提升、配置文件优化、日志记录完... 目录部署前夜:从开发到生产的心理准备与检查清单环境搭建:打造稳固的应用运行平台自动化流水线:让部署像

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情