【金沙澳门官网网址】微信小游戏和白鹭引擎开

作者: 前端知识  发布:2019-11-14

前言

文章按照作者调研和开发顺序初步介绍和理解了微信小游戏和白鹭引擎,并产出了基于白鹭引擎的应用初始化程序egret-wechat-start。  以下是正文——

 

显示深度控制

Texture Merger

Texture Merger 可将零散纹理拼合为整图,同时也可以解析SWF、GIF动画,制作Egret位图文本,导出可供Egret使用的配置文件。  我主要使用其中的精灵图功能,把图片集合到一张图上,并且会同时导出一个json的精灵图的在图片中的位置等配置信息

获取显示深度

this.getChildIndex();

具体代码:

console.log(
    "display indexes:",
    this.getChildIndex(bg),
    this.getChildIndex(Pohwaran),
    this.getChildIndex(Garen),
    this.getChildIndex(Graves),
    this.getChildIndex(Minami),
    this.getChildIndex(Ayanami),
    this.getChildIndex(Konan)
);

// display indexes: 0 1 2 3 4 5 6

当这一切都准备好以后,剩下的就是体力活啦,当然还有游戏最重要的核心玩法实现、动画和交互效果,这些可能是一个游戏实现难度最大的部分。仓库地址: 。

1 赞 收藏 评论

金沙澳门官网网址 1

资源加载

Egret中所有的资源都是动态加载的。

微信小游戏

修改显示深度

this.setChildIndex( <x>, <深度数值(最大值是显示列表长度-1)> );

具体代码:

this.setChildIndex(Graves,this.getChildIndex(Garen));

// display indexes: 0 1 3 2 4 5 6

显示深度的规则:

  1. 某一个显示深度只能对应一个显示对象,一个显示对象也只能有一个显示深度。

  2. 显示深度总是从零开始连续的,当某个深度位置的显示对象被设置为其他深度时,原来的深度会自动被紧邻的比其深度值大1位置的显示对象占据,后续深度位置的显示对象会依次往前排。

  3. 某一容器内的显示列表的深度最大值是显示列表长度-1。

开始egret开发

你可以快速浏览一遍官方教程,以便更好对下文有所理解, 。  文章不是教程所以会省略掉那些白鹭官网里的教程。  现在我们使用egret launcher创建一个初始化项目,初始化后的文件结构如下图,我展开了resource和src文件夹,因为我们需要操作的主要是这两个文件夹,resource文件夹主要是存放静态资源,我们的代码都在src里,白鹭使用的是typescript。

金沙澳门官网网址 2

在wing工具里,我们可以马上开启调试,就可以在浏览器或者它自带的容器里预览效果。  main.ts是启动文件,main中首先使用await对resource中定义好的图片资源进行了预加载,所以预览开始后会出现loading效果,loading的绘制是写在src中LoadingUI.ts,图片加载完成以后,main里直接创建了下图2的页面,并且添加了一个按钮,点击后会出现一个弹窗。  效果如下图。

金沙澳门官网网址 3   金沙澳门官网网址 4 金沙澳门官网网址 5

至此,初始化demo已经告诉了我们如何绘制图像和绑定事件了,如下图,我只截取了click按钮的代码,图像绘制首先需要创建一个相应的egret或者eui对象,比如eui.Button、egret.TextField、egret.Bitmap等等,然后给对象设置相应属性,比如label、x y坐标,width, height等。  再使用main的addChild载入到画布中(下面的this就是main对象,main继承于eui.UILayer)。  demo中的代码在载入loading的时候,使用了this.stage.addChild,直接addChild或者使用stage.addChild都可以载入到画布中。  白鹭封装的addEventListener方法和原生js的监听方法是一样的使用方法。

金沙澳门官网网址 6

demo的代码说到这里总结一下,我们在main入口对象中可以使用addChild载入一个视图对象到画布中,比如文本,按钮等。  我们也可以在main里addChild一个视图容器A,视图容器A也可以添加文本按钮等,那我们在视图容器A中再次addChild视图容器B,那么这样就形成了层级嵌套main->A->B,如果想象成dom元素就是div.main->div.A->div.B的关系,我们用代码来对比一下:

class Main extends eui.UILayer { protected createChildren(): void { let A = new egret.DisplayObjectContainer(); this.addChild(A); let textA = new egret.TextField(); textA.text = 'text A Description'; A.addChild(textA); let B = new egret.DisplayObjectContainer(); A.addChild(B); let buttonB = new eui.Button(); buttonB.label = 'button B'; B.addChild(buttonB); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Main extends eui.UILayer {
 
 
    protected createChildren(): void {
 
        let A = new egret.DisplayObjectContainer();
        this.addChild(A);
 
        let textA = new egret.TextField();
        textA.text = 'text A Description';
        A.addChild(textA);
 
        let B = new egret.DisplayObjectContainer();
        A.addChild(B);
        
        let buttonB = new eui.Button();
        buttonB.label = 'button B';
        B.addChild(buttonB);
    }
 
}

对应

<div class="main"> <div class="A"> <span>text A Description</span> <div class="B"> <button value="button B"></button> </div> </div> </div>

1
2
3
4
5
6
7
8
<div class="main">
    <div class="A">
        <span>text A Description</span>
        <div class="B">
            <button value="button B"></button>
        </div>
    </div>
</div>

根据以上代码的理解和我们要做的需求(实现一个回合制游戏,这个游戏也有很多页面,首页就包含很多按钮和可能出现的弹窗,也有各种列表页,还有最关键的战斗页面)。  我在main里写一个initElement方法,创建基层容器,代码如下图,addChild默认根据先后顺序确定上下层关系,先载入的在下层。  首先最下层创建了一个背景层,接着是ScrollView和baseContent,页面容器会载入到他们之中,如果页面需要滚动会把页面视图对象载入到SV中,不需要滚动会载入到baseContent中,Layer和loading在更上层的位置。

金沙澳门官网网址 7

基层容器准备好以后,我们可以创建一个首页页面。  我会创建3个文件:base.ts,Index_ui.ts,Index.ts。  Index继承Index_ui,Index_ui继承base。  所有的_ui都会继承base,base会定义通用方法和属性。  因为一个页面到最后可能代码量会比较大,甚至比较乱,所以才把一个页面拆分成page和page_ui,_ui里写视图相关代码,page里调用_ui的方法、处理请求和编写逻辑,达到视图和逻辑分离的效果。  当首页写好以后,需要创建一个简易路由,用路由提供的方法把Index添加到SV容器中。  我把路由直接写到了main中,changePage就是页面切换的方法,代码大致如下:

金沙澳门官网网址 8

通过remove和add视图容器达到了切换页面的效果。  下面说说编写_ui页面的规则,下面是Index_ui的部分代码,el_layout提前把页面元素的布局信息提前定义并统一管理。  把Index逻辑页面需要操作的元素引用到$el对象里方便调用和操作。  把数据信息统一放在$data中。  创建页面视图元素之前,需要把第一个元素的y坐标传给 $firstEleY 这是为了后面pageContentCenter方法能获取到准确的页面内容高度,pageContentCenter要执行在所有页面元素创建完成之后,pageContentCenter会根据当前页面的高度再匹配当前设备的高度进行垂直居中。

class Index_ui extends Base { public el_layout = { indexbg: {x:0, y:0, w:750, h:1665}, gold: {x:300, y:100, w:300, h:39} }; public constructor() { super(); this.RES_index = RES.getRes('index'); this.RES_common = RES.getRes('common'); } public RES_index; public RES_common; public $el = { gold: Object(egret.TextField) } public $data = { gold: '0' } public async createView() {       //背景       let RES_bg = new egret.Bitmap( RES.getRes('indexbg') );       $util.setLayout(RES_bg, this.el_layout['indexbg']);       RES_bg.fillMode = egret.BitmapFillMode.REPEAT;       this.$main.PageBg.addChild(RES_bg); //顶部元素必传值 this.$firstEleY = this.el_layout.gold.y; this.pageContentCenter(true);//根据内容计算处理居中 } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Index_ui extends Base {
    public el_layout = {
        indexbg: {x:0, y:0, w:750, h:1665},
        gold: {x:300, y:100, w:300, h:39}
    };
    public constructor() {
        super();
        this.RES_index = RES.getRes('index');
        this.RES_common = RES.getRes('common');
    }
    public RES_index;
    public RES_common;
    public $el = {
        gold: Object(egret.TextField)
    }
    public $data = {
        gold: '0'
    }
 
    public async createView() {
 
      //背景
              let RES_bg = new egret.Bitmap( RES.getRes('indexbg') );
      $util.setLayout(RES_bg, this.el_layout['indexbg']);
      RES_bg.fillMode = egret.BitmapFillMode.REPEAT;
              this.$main.PageBg.addChild(RES_bg);
 
        //顶部元素必传值
        this.$firstEleY = this.el_layout.gold.y;
 
        this.pageContentCenter(true);//根据内容计算处理居中
    }
}

一个简易的开发封装的核心代码已经搭建好了,而后我们还需要封装一些其它工具类,如下图:配置文件($config)、封装拦截器($api)、滤镜($filter)、工具函数($util)、微信api封装(Wx)。  Platform.ts是白鹭自动生成的文件,根据它的规则自己写了一个Wx.ts文件,由于不同平台的接口形式各有不同,白鹭推荐开发者通过这种方式封装平台逻辑,以保证整体结构的稳定,白鹭推荐开发者将所有接口封装为基于 Promise 的异步形式。

金沙澳门官网网址 9

和src同级的还有一个texture文件夹,里面是TextureMeger使用精灵图的相关文件,放在仓库里是方便后期管理。

金沙澳门官网网址 10

简易的初始化demo,我已经更新到github上。  egret-resource是源码,egret-resource_wxgame是白鹭打包后的文件夹,它在开发者工具里运行。  egret-resource_wxgame应该在ignore里忽略,这里没有忽略是方便下载源码的朋友直接在开发者工具里运行demo。  当前程序使用白鹭引擎版本5.2.5。

金沙澳门官网网址 11

demo里随便写了几个页面,看下效果:

金沙澳门官网网址 12

不可逾越的显示深度最大值

this.setChildIndex( captain, <比显示列表长度大就可以> );

具体看代码:

this.setChildIndex(Graves,10);

// display indexes: 0 1 2 6 3 5 4

会发现深度并没有变成10,而是自动取允许的最大值6。

这是引擎自动处理的,也算是一种容错功能吧。

Egret Engine2D

开发中主要的核心api

加载资源的代码分析

在进一步显示图片前,我们了解一下资源加载的代码。 再回过头看看加载资源的代码。加载资源的过程整体分为两部分,第一步首先加载资源配置清单,第二步就是加载资源。

在onAddToStage方法中,有代码:

RES.addEventListener(RES.ResourceEvent.CONFIG_COMPLETE, this.onConfigComplete, this);
RES.loadConfig("resource/default.res.json", "resource/");

这是专门用来加载资源配置的代码。 首先添加一个针对 CONFIG_COMPLETE 事件的侦听,然后执行加载。 配置加载完成时,即会执行 onConfigComplete 方法。

在onConfigComplete方法中,有如下 :

RES.removeEventListener(RES.ResourceEvent.CONFIG_COMPLETE, this.onConfigComplete, this);
RES.addEventListener(RES.ResourceEvent.GROUP_COMPLETE, this.onResourceLoadComplete, this);
RES.addEventListener(RES.ResourceEvent.GROUP_LOAD_ERROR, this.onResourceLoadError, this);
RES.addEventListener(RES.ResourceEvent.GROUP_PROGRESS, this.onResourceProgress, this);
RES.loadGroup("figure");

第一行移除了对 CONFIG_COMPLETE 事件的侦听,这是一个推荐做法,因为我们不再需要加载配置文件,该侦听也就没有作用了。及时移除事件侦听可以删除不必要的引用,使得不再需要使用的对象能被垃圾回收及时清理,避免内存泄露。

接着,加入了对资源组事件的侦听。

首先是对资源组加载完成的侦听,这是必须的,因为程序的流程需要从这里进行,即程序需要在某种资源加载完成后进行预期的后续流程。 另外,任何加载都需要稳定的网络,而网络出现各种中断是很常见的情况,所以需要添加对加载错误事件的侦听,以在这种情况作出相应的处理,通常是重新加载或者是提示用户检查网络。 可选的,可以加入对加载进度的侦听,通常是通过某种样式的进度条显示给用户当前进度,这在所加载的内容需要耗时较长时对于用户体验非常重要。

对于加载错误和进度的侦听处理,我们这里不做过多说明。

在加载完成的处理,即 onResourceLoadComplete 中,通过检查当前加载完成的资源组名称,来做对应的处理。确认当前加载的资源组是 figure 后,便进入程序的正式流程 createGameScene 中。

egret launcher

当然还需要安装一个egret launcher来管理引擎、工具和项目打包,小游戏就需要打包之后才能在微信开发者工具里使用

金沙澳门官网网址 13

金沙澳门官网网址 14

 

加入声音

var b_sound: egret.Sound = RES.getRes("bgMusic");

var b_channel: egret.SoundChannel = b_sound.play(0,1);

成了一个 sound 对象并调用 soundplay 方法,其中的第一个参数 0 表示播放的开始时间,第二个参数表示播放次数,如果将第二个参数设置为负数将循环播放。

play 方法返回了一个 SoundChannel 对象。通过操作 channnel 对象可以控制声音的音量大小停止播放等。

还有踩过很多坑,下面记录一下:

  • 在公众号后台把设置里的服务类设置成游戏类,输入appId后会自动打开开发者工具游戏开发的界面
  • 金沙澳门官网网址 ,小游戏自定义字体微信支持程度差

  • 部分功能和api需要注册的小程序才能使用,比如转发功能,目前注册了一个个人小游戏用于前期开发

  • 使用wing工具编辑代码,编译调试,编译后的代码会存放在bin-debug文件夹里,我用的mac,项目菜单里有三个选项编译、调试和清理。我新增了一个xx文件,却在调试的时候一直报错,检查浏览器source里也没有新增的文件,bin-debug也没有,弄了很久,一直以为是自己代码写错了,最后意识到可能是编译器有问题,这个时候我点击了清理按钮,新增的文件就在bin-debug里出现了。应该是个bug,要多注意检查bin-debug里的文件是否有更新

  • RES.getResByUrl是网络异步加载,需要提前addChild保证层级正常,请求完成再修改对象的texture属性,也可以通过addChildAt方法指定层级。

  • TextField  字体size小于10会影响布局,文本是否换行取决于设置的元素高度

  • webgl模式无法加载网络url图片
  • scrollView有addChild方法,但是方法里的代码是直接抛错,表示不能用这个接口。它的子元素绑定touchStart move等事件会失效,所以目前又增加里一个baseContent,根据需求切换父容器
  • measuredHeight这个测量接口只会测量最上面元素和最下面元素的实际高度,所以第一个元素如果y值大于0要注意配置$firstEleY
  • 所有图片用工具压缩,会减少上传代码的大小和提升资源加载速度

 

设计并实现一组Tween动画

类内部建立记录次数变量

private times:number;

点击次数控制的代码:

this.times = -1;
    var self = this;
    this.stage.addEventListener(egret.TouchEvent.TOUCH_TAP,function() {
        switch(  self.times % 5) {
            case 0:
                egret.Tween.get(Pohwaran).to({ x: Graves.x },300,egret.Ease.circIn);
                egret.Tween.get(Graves).to({ x: Pohwaran.x },300,egret.Ease.circIn);
                break;
            case 1:
                egret.Tween.get(Garen).to({ alpha: .3 },300,egret.Ease.circIn).to({ alpha: 1 },300,egret.Ease.circIn);
                break;
            case 2:
                egret.Tween.get(Minami).to({ scaleX: .4,scaleY: .4 },500,egret.Ease.circIn).to({ scaleX: 1,scaleY: 1 },500,egret.Ease.circIn);
                break;
            case 3:
                egret.Tween.get(Ayanami).to({ y: Ayanami.y   40 },500,egret.Ease.circIn).to({ y: Ayanami.y},500,egret.Ease.circIn);
                break;
            case 4:
                egret.Tween.get(Konan).to({ x: Konan.x   40 },500,egret.Ease.circIn).to({ x: Konan.x },500,egret.Ease.circIn);
                break;
        }
    },this);
  • Tween.get 传入需要对其进行动画控制的对象,并返回一个 Tween 对象。

  • to 设置 Tween 对象的动画。to方法包含三个参数:

    • 第一个参数是动画目标属性组,这个参数可以对目标对象本身的各项属性进行设定,就是动画结束时的状态,可以设定一个或多个属性。

      • x/y 位置
      • alpha 透明度
      • scaleX/scaleY 缩放因数
    • 第二个参数是动画时间,以毫秒计。

    • 第三个参数是补间方程,即对动画区间内每个时间点的属性值设定分布。在 egret.Ease 已经提供了丰富的补间方程,可以根据自己的喜好选择。

Egret 扩展库

扩展库在核心引擎功能之上提供了更高级的api,扩展库在引擎配置文件里配置好以后,会直接把方法和对象载入到egret全局对象中,目前我主要使用的扩展库有:

  1.  RES:  资源管理库
  2.  EUI: EUI是一套基于Egret核心显示列表的UI扩展库,它封装了大量的常用UI组件,能够满足大部分的交互界面需求,即使更加复杂的组件需求,您也可以基于EUI已有组件进行组合或扩展,从而快速实现需求。
  3.  Game:这个库好像没有什么专门的定义,我主要使用了:ScrollView 滚动视图。 来处理需要滚动的页面
  4.  Tween: 缓动动画库,类似于GreenSock库金沙澳门官网网址 15

egret.TextField

TextField是egret的文本渲染类,采用浏览器/设备的API进行渲染,在不同的浏览器/设备中由于字体渲染方式不一,可能会有渲染差异如果开发者希望所有平台完全无差异,请使用BitmapText

var tx:egret.TextField = new egret.TextField();
tx.text = "Hi,你好,我是duminghong";
tx.size = 32;
tx.x = 20;
tx.y = 20;
tx.textColor = 0xfefefe;
tx.width = this.stage.stageWidth - 40;
this.addChild( tx );
  • text 设置文本
  • size 设置文字大小
  • x、y 设置文本对象的x和y坐标
  • textColor 设置文本颜色
  • width 设置文本的宽度
  • this.addChild 将某个显示对象添加到某个显示容器上

Egret Wing

白鹭开发的代码编辑器,像其他编辑器一样,推荐使用它。

使用WebSocket通讯

众所周知,WebSocket为Web应用提供了更高效的通讯方式。 本节介绍WebSocket的基本用法。

确保项目支持WebSocket
从Egret1.5.0开始,以官方扩展模块的形式支持WebSocket。在现有的Egret项目中,修改egretProperties.json中的”modules”,添加”socket”模块:

{
    "name": "socket"
}

注意 添加模块的时候要注意保证 json 的语法正确。

在项目所在目录内执行一次引擎编译:

egret build -e

本步骤已经完成,现在项目中既可以使用WebSocket相关的API了。

官方文档

如何开发和理解微信小游戏,先从官方文档和官方demo入手。  提供一个链接,可以快速浏览一下官方文档再继续看下面的内容。  这里对微信文档做个简单的理解总结,小游戏和小程序很多地方类似,都是提供了同一套微信Api,比如获取用户信息、toast等等,只是有部分提供的api不同。  小游戏对canvas做了封装,通过 wx.createCanvas() 创建画布,``getContext获取对象后,剩下的就是对原生canvas接口的操作了。  理解到这一点之后,我们就会发现小游戏仅仅是封装了下创建画布的接口,剩下的就是用户需要在画布里用原生canvas绘制了,并没有提供其他方便开发的功能。到此我们再看看微信开发者工具创建小游戏项目时,初始化的一个飞机游戏的demo。

金沙澳门官网网址 16

是如上图的一个很简单的游戏,说下这个游戏的大致实现逻辑:

1.  绘制游戏区域,背景图片

  1. 创建敌机对象,用户飞机对象,子弹对象

  2. 控制3种对象载入画布和位置改变,控制背景图片移动,添加音效

  3. 判断子弹碰撞,机身碰撞,并且生成对应结果(敌机消失,游戏结束)

游戏中和用户有交互操作有拖动飞机和弹框中的按钮,总体是一个很简单的小游戏,实现过程也并不复杂。  官方demo中最核心的动画内容就在loop方法里,使用的是帧动画( requestAnimationFrame )来实现界面动画。  针对游戏实现动画效果主要有两种方式,一种就是requestAnimationFrame帧动画,一种是用定时器实现。  帧动画和设备的处理速度有关系,默认1秒60帧,但是在手机设备里即便很简单的动画,性能差点的设备可能帧率都只有20-30左右。  因为帧动画每秒就要调用n次,也许并不需要那么高频率的函数调用,而定时器总的来说对时间的把控和函数调用次数更准确。 比如这个飞机游戏里如果有血条的概念,血条的加减其实可以用单独的定时器来控制。 一个游戏里可以两种方式都使用,根据应用场景选择更合理的方式。

现在根据一个新的需求来做一个游戏,再来理解小游戏的开发。  现在需求实现一个回合制游戏,这个游戏也有很多页面,首页就包含很多按钮和可能出现的弹窗,也有各种列表页,还有最关键的战斗页面。  在做实现需求之前,需要提供一些公共的基础模块:资源预加载,接口拦截器,简易路由等等。  跳过这些阶段,如果我们拿到ui设计,开始做首页了,首页有很多按钮,我们需要给A按钮添加绑定事件,那我们需要给canvas画布绑定一个点击事件,点击触发以后我们获取到当前用户点击位置,并取出A按钮的位置宽高并计算出范围,进行判断是否点击位置在范围内,最后再触发绑定的方法。 好像有点麻烦,但是还能实现,继续做下去。  后来需要在首页做一个弹框,这个时候,给弹框的B按钮绑定点击事件,又需要通过同样的方法判断是否点击到B按钮。  这个时候弹框的B按钮刚好和A按钮重叠都在一个点击范围内,那按钮A和B的回调都会被执行。  代码如下:

JavaScript

canvas.addEventListener('click', (event)=>{ 获取event对象x,y 获取 buttonA:x,y,width,height 判断是否点击 获取 buttonB:x,y,width,height 判断是否点击 })

1
2
3
4
5
6
7
8
9
canvas.addEventListener('click', (event)=>{
    获取event对象x,y
 
    获取 buttonA:x,y,width,height
    判断是否点击
 
    获取 buttonB:x,y,width,height
    判断是否点击
})

一个弹窗上面的按钮点击,反而把弹框下面的按钮也点击到了,这不符合预期,那要解决这个问题,我们还需要一个层级管理器,根据层级判断谁应该触发,谁不应该触发。  目前就事件处理我们需要实现两个基础功能,事件监听池和元素对象层级管理器,因为事件只能绑定在canvas上,canvas事件触发以后,需要一个事件监听池来遍历监听池里的元素对象并判断谁被触发了(监听池也会随时增减监听对象),监听池获取的依然是一个对象集,层级管理器判断出对象集里最上层的元素进行触发。  想想功能好像越来越复杂了。  目前还没考虑完善,不仅仅是事件处理问题,还可能会有其它大大小小的问题。  用canvas原生开发,工作量可能会非常大。  所以这样看来,自己把这些实现了是不科学的,需要使用三方引擎开发才行。  因为两年前用过白鹭引擎,所以就事件监听和层级管理这个事情,我知道白鹭引擎已经实现了,除开事件,图形绘制,动画等等印象中白鹭都提供了,如果用引擎开发小游戏实现成本被大大降低。

待续...

微信小游戏和白鹭引擎开发实践

2018/09/05 · JavaScript · 小游戏

原文出处: 子慕大诗人   

Tween动画效果

所谓Tween动画,就是设计某种属性(比如位置、透明度和缩放)的两个不同状态,然后在给定的时间内从一个状态平滑过渡到另外一个状态。

本文由金沙澳门官网发布于前端知识,转载请注明出处:【金沙澳门官网网址】微信小游戏和白鹭引擎开

关键词: 金沙澳门官网

上一篇:常见面试题之CSS部分
下一篇:没有了