H5游戏开发的一乘轻骑---Phaser

快速入门(普通版)

Phaser 是一款很是优秀的 HTML5 游戏框架,致力于发展 PC 端和移动端的 HTML5 游戏,是一款不可多得的神器。html

本篇文章将带你快速入门游戏的开发(移动端),适合想要了解游戏开发或者想投入游戏开发的人群,毫无疑问,Phaser 是一个值得你学习的游戏框架。jquery

特别注意

Phaser 虽然是一款很是优秀的HTML5游戏框架,可是也有它软肋的地方,如今请注意下面两个 Phaser 处理不了或者处理起来很棘手的地方:git

1. 3D。若是你想学作一个很酷的3D动画,能够学习 Three.js github

2. 视频(移动端)。若是你在移动端页面上须要嵌入视频,请使用原生HTML5标签 video,若是须要对 video 有更好的扩展,能够参考 Video.js web

接下来你能够先打开咱们这篇文章所讲的 案例 (服务器渣渣,可能要等很久很久哈哈哈),看完这个案例,你可能会说,这有什么的,我用Jquery的动画都作得出来,哈哈,之前我也对一个大神说过相似的话,而后人家很清楚地跟我说拿 Phaser 的动画跟 Jquery 的动画比简直是在侮辱 Phaser。npm

好了,话就说到这里,我我的以为太复杂的案例反而会让人理不清思路,便有违咱们的快速入门的宗旨,相信看完这篇文章以后,你能够打开游戏开发的篇章,自由的描绘喜欢的世界。canvas

代码地址跨域

说在前面

首先,为了能正常运行咱们的案例,你须要一个本地服务器。你可能会有疑问,为何咱们不能直接把 index.html  拖进浏览器中并运行呢?浏览器

由于这与用于访问文件的协议有关。当你在网络上请求任何东西时,你须要使用HTTP,而服务器能够保证你只能访问你想要的文件。可是当你直接把 html 文件拖到浏览器上直接运行时,它是经过本地文件系统(file://)加载的,因为本地文件系统没有域的概念,没有服务器级别的安全,只是一个原始文件系统。
安全

您真的但愿JavaScript可以从文件系统中的任何地方加载文件吗?答案固然是不行。你的计算机固然也不会赞成。

而 Phaser 须要加载资源:图像,音频文件,JSON数据,或者其余Javascript文件。为了作到这些,它就须要运行在服务器下。

若是你对如何配置一个本地服务器来运行 html 文件感到毫无头绪,而且这也不是咱们这篇文章的重点,那我能够介绍一个很简单的方式,你能够尝试进行下列操做:

1. 打开命令行,输入如下命令回车: npm install puer -g (若是没有 npm 请自行百度安装)2. puer 安装完成后,在须要搭载在本地服务器的案例根文件夹下调用命令行,输入如下命令回车:puer -p 9999 以下:


3.若是puer运行成功则以下并自动打开浏览器:


以下案例已经搭载在地址为 http://localhost:9999/ 上了,点击 index.html 就能够把 Phaser 运行起来啦


开始第一步

首先建立咱们的目录结构以下:


img文件夹下的图片能够自行从 案例代码地址 上拷贝,而后你会发现,咦,GitHub上怎么多了一张 sprites.png 图片和一个 sprites.xml 文件,这个后面将会讲到,如今咱们只须要准备现阶段须要的,js/libs文件下的 jquery.min.jsphaser.min.js 请自行准备或从 案例代码地址 上拷贝,而后其它文件咱们须要保证空白以便咱们后面迅速填充内容。

第二步

首先固然是向咱们的 index.html 快速填充内容啦,内容以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Phaser快速入门(普通版)</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>
<style>
    html,
    body {
        width: 100%;
        height: 100%;
        font-family: "Helvetica Neue", Helvetica, STHeiTi, sans-serif;
        overflow: hidden;
        background: #fff;
        margin: 0;
        padding: 0;
    }
    .main {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
    }
    .main img {
        width: 100%;
        height: 100%;
    }
</style>
<body>
    <!--H5页面全部动画的canvas载体-->
    <div id="game-container"></div>
    <!--预加载背景,防止预加载的资源加载过久以至屏幕长时间空白-->
    <div class="main">
        <img src="img/bg.png" alt="">
    </div>
</body>
<script src="js/libs/jquery.min.js"></script>
<script src="js/states/utils.js"></script>
<script src="js/libs/phaser.min.js"></script>
<script src="js/states/boot.js"></script>
<script src="js/states/preload.js"></script>
<script src="js/states/state1.js"></script>
<script src="js/states/state2.js"></script>
<script src="js/app.js"></script>
</html>复制代码

好了,内容填充完毕,在这个文件里面首先咱们案例全部动画都是搭载在id为 game-container 的元素上,而后class为 main 的元素是预加载背景,看了注释对它的用意还不是很清楚的话能够不用深究,后面天然就清楚了。

而后你还会发现咱们导入了一个 jquery.min.js ,你可能会有这样的疑问,这是要干吗?难道在使用 Phaser 的时候还须要先导入 Jquery 吗?答案确定不是,导入 Jquery 只是为了在后面证实一些东西。utils.js 是封装了一些咱们在后面常常用到的操做(满屏效果,缩放图片)

接下来咱们把重点放在咱们这个案例运行起来的机制,先来看看咱们的5个核心文件:

<script src="js/states/boot.js"></script>
<script src="js/states/preload.js"></script>
<script src="js/states/state1.js"></script>
<script src="js/states/state2.js"></script>
<script src="js/app.js"></script>复制代码

这5个文件的执行顺序依次是:

app.js -> boot.js -> preload.js -> state1.js -> state2.js

app.js 里面会执行 boot.jspreload.jsstate1.jsstate2.js 这4个场景,所以 app.js 须要在导入 boot.jspreload.jsstate1.jsstate2.js 这4个文件以后导入。接下来逐一解析这5个文件。

app.js

这个文件是咱们 Phaser 执行的第一个文件,主要进行一些游戏开发方面的配置,包括设置咱们资源的路径,挂载元素,添加场景,启动场景等。

具体代码以下:

(function() {
    'use strict'

    // 设置资源目录(项目根目录)
    var baseURI ='../..'

    //将图片目录放在内存中,方便全局调用
    localStorage.baseURI = baseURI

    //设置$('#game-container')的高度等于屏幕的高度(这里用原生js代码书写)
    document.getElementById('game-container').style.height = document.body.clientHeight + 'px'

    //获取屏幕的缩放比
    var Ratio = window.devicePixelRatio

    //获取屏幕的宽和高
    var w = document.documentElement.clientWidth || document.body.clientWidth
    var h = document.documentElement.clientHeight || document.body.clientHeight

    //由于咱们在index.html设置了禁止缩放的meta头
    //<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    //因此当咱们将屏幕的宽和高直接传入Phaser.Game对象时,浏览器会自动将canvas按屏幕的缩放比缩放,也就是当你的屏幕缩放比是2时,canvas宽高你设置成屏幕的宽高时实际看到的却只有一半,所以相应咱们须要将画布放大Ratio倍
    var ww = Ratio * w
    var hh = Ratio * h

    //前两个参数是Phaser要建立的canvas元素的宽高,第三个参数是游戏渲染的引擎,这里让Phaser本身识别设置便可,第四个参数是游戏挂载在哪一个元素上
    var game = new Phaser.Game(ww, hh, Phaser.AUTO, 'game-container')

    //添加场景
    game.state.add('Boot', Boot)
    game.state.add('Preload', Preload)
    game.state.add('State1', State1)
    game.state.add('State2', State2)

    //启动场景
    game.state.start('Boot')

})(window)复制代码

上面的每一行代码都已经加上相应的注释,若是有空的话能够看一下并理解清楚,如今咱们只需知道 app.js 只是用来配置而且启动咱们的第一个场景(boot.js)就行。

在说咱们的场景以前,咱们先来了解什么是场景,场景是咱们 Phaser 游戏开发的主体,咱们全部看到的游戏动画都是在场景里实现,场景能够是一个js自定义对象,也能够是一个函数,只要存在preload、create、update这三个方法中的任意一个,就是一个合法的场景(总共5个方法,另外两个是init和render方法)。

init方法:一些场景的初始化代码能够写在这个方法里,最早执行。

preload方法:用来加载资源的,若是没有init方法则它会最早执行。

create方法:初始化以及构建场景,会等到preload方法里加载的资源所有加载完成后执行。

update方法:更新函数,它会在游戏的每一帧都执行,通常是1/60秒执行一次。

render方法:在游戏的每一渲染周期都会调用,用来作一些自定义的渲染工做。

boot.js

这个场景不会向用户展现,只是为了加载下个场景(即预加载场景preload.js)所需的资源,所以资源不能加载太多,太多的话则屏幕会长时间黑屏(黑屏其实就是这个场景加载资源的过程,由于这是第一个场景,没有上个场景帮你加载好的资源用来构建界面,因此也就没有能够友好显示给用户的界面),而这个问题咱们也在index.html 用 $('.main') 解决,即先展现 $('.main') 这个元素展示出来的界面(界面背景须要跟预加载场景背景同样),以便这个场景加载过久有个良好的用户体验,而当这个场景加载完后 $('.main') 也能够实现跟预加载场景的良好衔接。

具体代码以下:

var Boot = function(game) {
    var baseURI = localStorage.baseURI
    this.init = function() {
        //game.device.desktop判断是WAP端仍是PC端,true为PC端,false为WAP端
        if (!game.device.desktop) {
            //设置游戏背景色
            game.stage.backgroundColor = '#282C34';
            //鼠标指针对象,因为WAP端没有鼠标,所以设置为1(即为null)
            game.input.maxPointers = 1;
            //缩放控制,这里将画布(canvas)拉伸至填满父容器(即#game-container),不保持比例
            game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
            //启用时,显示画布(canvas)将在父容器中水平对齐
            game.scale.pageAlignHorizontally = true;
            //启用时,显示画布(canvas)将在父容器中垂直对齐
            game.scale.pageAlignVertically = true;
            //强制游戏只能以一个方向运行,这里设置游戏仅能在纵向模式运行(true),没法再横向模式运行(false)
            game.scale.forceOrientation(false, true);
            //当forceOrientation设置只能纵向模式运行,把手机横向摆放就会调用enterIncorrectOrientation这个方法
            game.scale.enterIncorrectOrientation.add(enterIncorrectOrientation, this);
            //当forceOrientation设置只能纵向模式运行,把手机横向摆放就会调用enterIncorrectOrientation这个方法,从新将手机纵向摆放就会调用leaveIncorrectOrientation这个方法
            game.scale.leaveIncorrectOrientation.add(leaveIncorrectOrientation, this);
        }else{
            game.scale.pageAlignHorizontally = true
            game.scale.pageAlignVertically = true
            //防止浏览器失去焦点后动画暂停,若是考虑到计算机性能能够设置为false
            game.stage.disableVisibilityChange = true;
        }

    }
    this.preload = function() {
        //设置图片支持跨域请求
        game.load.crossOrigin = true
        //加载预加载界面所需的资源,能够看到图片跟$('.main')界面一致,第一个参数是建立sprite对象时所须要的资源标识,第二个参数是资源所在路径
        game.load.image('start_bg', baseURI + '/img/bg.png')

    }
    this.create = function() {
        //跳转到下一个场景
        game.state.start('Preload');
    }
    function enterIncorrectOrientation() {
        alert('请将手机纵向摆放');
    }
    function leaveIncorrectOrientation() {
        alert('已经将手机纵向摆放');
    }
}
复制代码

上面的每一行代码已经都加上相应的注释,若是有空的话也能够看一下并理解清楚,如今咱们只需知道 boot.js 只是用来设置咱们全部场景而且加载咱们的第二个场景(预加载场景preload.js)所需的资源就行。

preload.js

这个场景是预加载场景,会加载后面全部场景的资源,因为加载的资源比较多,因此在这个场景咱们须要以一种友好的方式展示咱们资源加载的过程,用到的是上个场景已经帮咱们加载完成的 start_bg 资源,和 Phaser 自身的文本对象(显示加载进度)。

preload.js 特别注意咱们须要加载一个帧图片(实现动图效果)

//加载帧图片,第一个参数是建立sprite对象时所须要的资源标识,第二个参数是图片所在路径,第三个参数是标识图片xml文件
game.load.atlasXML('sprites', baseURI + '/img/sprites.png',baseURI + '/img/sprites.xml')复制代码

加载帧图片咱们须要一张包含全部动做的总体图片,还须要一个配置xml文件。那么问题来了,咱们为何须要两个文件呢,为何不直接把帧图片作成动图而后展现,这个问题其实很简单,首先直接一张动图远远比一张静态图片大的多(xml文件过小忽略不计),并且咱们也没法在代码中控制动图的切换速度。

好吧,如今按咱们的想法来,若是咱们有两张须要在 Phaser 作成动图的图片,好比下面:


而后咱们要怎么把它们合成咱们想要的一张总体图和一个配置xml文件呢?这个时候咱们就须要一个软件来帮咱们处理了,这里我来介绍 Shoebox :


官网地址

下载安装完成后,咱们打开 Shoebox ,将须要合成动图的各帧图片一块儿移到 Sprite Sheet ,如图松手放开:


点击 Save ,就已经生成了一张帧图片和一个xml文件:


xml文件主要记载咱们帧图片包含的每张动做图片的位置和名称信息,固然,这不是咱们关注的重点。

具体代码以下:

var Preload = function(game) {
    var baseURI = localStorage.baseURI
    var tool = new utils()
    var w = null
    var h = null
    var start_bg = null
    var loading = null
    this.init = function() {
        //获取画布的宽高,即Ratio倍屏幕宽高
        w = game.width
        h = game.height

        //动画组,方便统一处理多个对象的动画
        group=game.add.group()

        //将$('.main')元素隐藏
        $('.main').hide()

        //因为start_bg跟$('.mian')界面一致,用户是看不出$('.mian')隐藏start_bg显示这个过程的
        //建立sprite对象,第一个参数是画布x坐标(距离画布左边缘多远),第二个参数是画布y坐标(距离画布上边缘多远),第三个参数是构建对象的资源
        start_bg = game.add.sprite(0, 0, 'start_bg')
        //将start_bg宽高设置全屏
        tool.setFull(start_bg)

        //建立文本对象,前面两个参数跟sprite等同,第三个参数是文本内容,第四个参数是文本样式
        loading=game.add.text(w*.5, h*.5, '0%',
            {
                fontSize:60,
                fill:'#ffffff'
            })
        //锚点位置(相对自身),第一个参数是相对自身左移多少(.5是左移自身宽度的50%),第二个参数是相对自身上移多少(.5是上移自身高度的50%)
        loading.anchor.set(.5, .5)
        //loading最后的位置是相对画布居中

        //loading的补间动画,from(从怎样的状态转变到默认状态),to(从默认状态转变到怎样的状态),这里用的是from
        //第一个参数:一个js对象,包含着须要进行动画的属性,{ alpha: 0 }表示透明度为0
        //第二个参数:动画的持续时间
        //第三个参数:动画过程函数,默认为匀速动画Phaser.Easing.Linear.None
        //第四个参数:是否自动开始
        //第五个参数:动画开始前的延迟时间,单位是毫秒
        //第六个参数:动画重复的次数,若是须要动画永远循环,则把该值设为 Number.MAX_VALUE
        //第七个参数:是否自动反转
        game.add.tween(loading).from({ alpha: 0 }, 500, null, true, 0, 0, false)

    }
    this.preload = function() {
        game.load.crossOrigin = true

        //加载帧图片,第一个参数是建立sprite对象时所须要的资源标识,第二个参数是图片所在路径,第三个参数是标识图片xml文件
        game.load.atlasXML('sprites', baseURI + '/img/sprites.png',baseURI + '/img/sprites.xml')
        
        game.load.image('next', baseURI + '/img/next.png')
        game.load.image('img2_1', baseURI + '/img/2_1.jpg')
        game.load.image('img2_2', baseURI + '/img/2_2.jpg')

        //这个方法是文件加载过程,返回的progeress是完成的进度,0~100
        game.load.onFileComplete.add(function(progeress) {
            loading.setText(progeress + '%')
        })

        //全部文件都完成加载时会调用这个方法,咱们能够在调用这个方法的时候跳转到下一个场景,效果等同于在create方法执行game.state.start('State1')
        game.load.onLoadComplete.add(function() {
            game.state.start('State1')
        })

    }
    this.create = function() {
       //game.state.start('State1')    }
    this.update = function() {}
}复制代码

上面的代码咱们要特别注意 $('.main').hide() ,这也是咱们在前面导入 jquery.min.js 的要证实的东西,就是你能够在 Phaser 里面写 Jquery 的任何代码。

若是上面两个场景(boot.jspreload.js)你已经彻底理解和摸透了话,那下面这两个场景(state1.jsstate2.js)估计你可以很快速就看完。

state1.js

这个场景主要应用到 Phaser 的动图,事件还有动画组的运用。在这里细心的小伙伴能够看到咱们把原本写在create方法的初始化以及构建场景的代码写在了preload方法里,就个人经验,只要一个场景不须要预加载资源,preload方法和create方法的效用并无差异。

具体代码以下:

var State1 = function(game) {

    var baseURI = localStorage.baseURI

    var tool = new utils()
    var w = null
    var h = null
    
    var next=null
    var sprites=null
    var group=null

    this.preload = function() {

      w = game.width
      h = game.height

      //动画组,方便统一处理多个对象的动画
      group=game.add.group()

      //第四个参数是没有加入动画时静态展现第几帧图片
      sprites=game.add.sprite(w*.5,h*.6,'sprites',0)
      tool.setSize(sprites,'width',w*.5)
      sprites.anchor.set(.5,1)
      //给sprite对象添加一个新动画,第一个参数是动画名称
      sprites.animations.add('run')
      //播放动画,第一个参数是动画名称,第二个参数是播放的速率,第三个参数是是否循环
      sprites.animations.play('run',4,true)
      
      
      //将start_bg加入动画组
      group.add(sprites)

      next=game.add.sprite(w*.5,h*.8,'next')
      tool.setSize(next,'width',w*.3)
      next.anchor.set(.5,.5)
      game.add.tween(next).to({ width:next.width+20,height:next.height+20 }, 500, Phaser.Easing.Linear.In, true, 0, -1, true)

      group.add(next)

      //默认状况下,游戏对象不会处理任何事件,因此咱们须要让它能够处理事件
      next.inputEnabled = true

      //当对next对象点击而后手指放开的时候触发
      next.events.onInputUp.add(function() {

        //onComplete方法是补间动画完成后的回调,咱们能够在跳转到下一个场景的时候作一些用户体验比较良好的当前场景的退场动画(这里的退场动画相似淡出效果)
        game.add.tween(group).to({ alpha: 0 }, 500, Phaser.Easing.Linear.In, true, 0, 0, false).onComplete.add(function() {
            game.state.start('State2')
        })
        
      })

    }

}复制代码

state2.js

这个场景只是为了承接上个场景 next 按钮的点击跳转。

var State2 = function(game) {
  
    var w = null
    var h = null

    var img2_1=null
    var img2_2=null

    this.preload = function() {
      w = game.width
      h = game.height

      img2_1=game.add.sprite(0,h*.5,'img2_1')
      img2_1.width=w
      img2_1.height=h*.5
      img2_1.anchor.set(0,1)
      game.add.tween(img2_1).to({ y:h }, 2000, Phaser.Easing.Linear.In, true, 500, -1, true)

      img2_2=game.add.sprite(0,h*.5,'img2_2')
      img2_2.width=w
      img2_2.height=h*.5
      game.add.tween(img2_2).to({ y:0 }, 2000, Phaser.Easing.Linear.In, true, 500, -1, true)
    }
    this.create = function() {}
}复制代码

好了,state2.js 就不作过多的赘言了,看到了这里相信你早已知道这个场景是怎样的一种动画效果了。

煮了一天的鸡汤

好了,到这里咱们就好好结束吧,Phaser 还有太多有趣的地方等着你去发掘,若是想要了解更多能够直接去 官网 ,里面有不少有趣的小例子。

在这里特别鸣谢眯眼猫的黎世灿大神带我疯狂入坑,而且对这篇文章的技术支持。

在这里祝贺你们双12快乐,事业进步!

相关文章
相关标签/搜索