Phaserjs V2的state状态解析及技巧

用phaserjs开发了好多游戏了,但是对phaser还是了解不深,只知道怎么去用,今天就特意花点时间研究下phaser的状态管理到底是怎么回事。

首先,new Phaser.Game,以下是Phaser.Game的部分源码:

Phaser.Game = function (width, height, renderer, parent, state, transparent, antialias, physicsConfig) {/*** @property {number} id - Phaser Game ID* @readonly*/this.id = Phaser.GAMES.push(this) - 1;...........................(省略的代码)//  Parse the configuration object (if any)if (arguments.length === 1 && typeof arguments[0] === 'object'){this.parseConfig(arguments[0]);}else{this.config = { enableDebug: true };if (typeof width !== 'undefined'){this._width = width;}if (typeof height !== 'undefined'){this._height = height;}if (typeof renderer !== 'undefined'){this.renderType = renderer;}if (typeof parent !== 'undefined'){this.parent = parent;}if (typeof transparent !== 'undefined'){this.transparent = transparent;}if (typeof antialias !== 'undefined'){this.antialias = antialias;}this.rnd = new Phaser.RandomDataGenerator([(Date.now() * Math.random()).toString()]);this.state = new Phaser.StateManager(this, state);}this.device.whenReady(this.boot, this);return this;};

先看this.device.whenReady(this.boot, this);这段代码,源码的意思是设备准备就绪或者dom文档准备就绪后就执行boot,说白了就是DOMContentLoaded这个事件或者window的load事件的回调,一切准备就绪,执行boot,boot源码如下:

boot: function () {if (this.isBooted){return;}this.onPause = new Phaser.Signal();this.onResume = new Phaser.Signal();this.onBlur = new Phaser.Signal();this.onFocus = new Phaser.Signal();this.isBooted = true;PIXI.game = this;this.math = Phaser.Math;this.scale = new Phaser.ScaleManager(this, this._width, this._height);this.stage = new Phaser.Stage(this);this.setUpRenderer();this.world = new Phaser.World(this);this.add = new Phaser.GameObjectFactory(this);this.make = new Phaser.GameObjectCreator(this);this.cache = new Phaser.Cache(this);this.load = new Phaser.Loader(this);this.time = new Phaser.Time(this);this.tweens = new Phaser.TweenManager(this);this.input = new Phaser.Input(this);this.sound = new Phaser.SoundManager(this);this.physics = new Phaser.Physics(this, this.physicsConfig);this.particles = new Phaser.Particles(this);this.create = new Phaser.Create(this);this.plugins = new Phaser.PluginManager(this);this.net = new Phaser.Net(this);this.time.boot();this.stage.boot();this.world.boot();this.scale.boot();this.input.boot();this.sound.boot();this.state.boot();if (this.config['enableDebug']){this.debug = new Phaser.Utils.Debug(this);this.debug.boot();}else{this.debug = { preUpdate: function () {}, update: function () {}, reset: function () {}, isDisabled: true };}this.showDebugHeader();this.isRunning = true;if (this.config && this.config['forceSetTimeOut']){this.raf = new Phaser.RequestAnimationFrame(this, this.config['forceSetTimeOut']);}else{this.raf = new Phaser.RequestAnimationFrame(this, false);}this._kickstart = true;if (window['focus']){if (!window['PhaserGlobal'] || (window['PhaserGlobal'] && !window['PhaserGlobal'].stopFocus)){window.focus();}}if (this.config['disableStart']){return;}if (this.cache.isReady){this.raf.start();}else{this.cache.onReady.addOnce(function () {this.raf.start();}, this);}}

很有序的初始化一些事件回调和方法,其中world,stage等等都有boot初始化方法,整个Phaser里Phaser.Stage只在这里实例化一次,仅此一次,游戏的舞台就只有这一个stage,stage继承PIXI.DisplayObjectContainer,到这里就都清楚了。

Phaser.World也仅此一次被实例化,它继承自Phaser.Group,Phaser.Group也继承自PIXI.DisplayObjectContainer,但是实例化它的时候,默认是把它添加到Phaser.World里的,

if (parent === undefined){parent = game.world;}

所以Group都是在World里,除非显示指定stage为其parent,实际开发几乎没这个必要。

Phaser.GameObjectFactory类封装了Phaser的组件,如image,sprite等,显示组件基本都是通过game.add直接被添加到World里(bitmapData除外这里不作研究):
image: function (x, y, key, frame, group) {if (group === undefined) { group = this.world; }return group.add(new Phaser.Image(this.game, x, y, key, frame));},

接下来看下this.state = new Phaser.StateManager(this, state);这段代码,Phaser.StateManager也是仅被实例化一次,主要用来管理游戏state的,构造函数有两个参数game和state,game就是我们的游戏对象,stage是默认的

boot: function () {this.game.onPause.add(this.pause, this);this.game.onResume.add(this.resume, this);if (this._pendingState !== null && typeof this._pendingState !== 'string'){this.add('default', this._pendingState, true);}},

默认state的key为'default',通过game.state.add添加state的源码:

add: function (key, state, autoStart) {if (autoStart === undefined) { autoStart = false; }var newState;if (state instanceof Phaser.State){newState = state;}else if (typeof state === 'object'){newState = state;newState.game = this.game;}else if (typeof state === 'function'){newState = new state(this.game);}this.states[key] = newState;if (autoStart){if (this.game.isBooted){this.start(key);}else{this._pendingState = key;}}return newState;},

就是一个包含init,preload,create等的object或function,或者是Phaser.State类,这个类就是封装一个state包含的所有方法,所有方法没有任何实现。

game.state.start源码:

start: function (key, clearWorld, clearCache) {if (clearWorld === undefined) { clearWorld = true; }if (clearCache === undefined) { clearCache = false; }if (this.checkState(key)){//  Place the state in the queue. It will be started the next time the game loop begins.this._pendingState = key;this._clearWorld = clearWorld;this._clearCache = clearCache;if (arguments.length > 3){this._args = Array.prototype.splice.call(arguments, 3);}}},

就是检查下是否是一个State,没什么啊,其实真正的start是在preUpadte里,

preUpdate: function () {if (this._pendingState && this.game.isBooted){var previousStateKey = this.current;//  Already got a state running?this.clearCurrentState();this.setCurrentState(this._pendingState);this.onStateChange.dispatch(this.current, previousStateKey);if (this.current !== this._pendingState){return;}else{this._pendingState = null;}//  If StateManager.start has been called from the init of a State that ALSO has a preload, then//  onPreloadCallback will be set, but must be ignoredif (this.onPreloadCallback){this.game.load.reset(true);this.onPreloadCallback.call(this.callbackContext, this.game);//  Is the loader empty?if (this.game.load.totalQueuedFiles() === 0 && this.game.load.totalQueuedPacks() === 0){this.loadComplete();}else{//  Start the loader going as we have something in the queuethis.game.load.start();}}else{//  No init? Then there was nothing to load eitherthis.loadComplete();}}},

start的时候,this._pendingState设置了当前的state,preUpdate方法先清除上一个state,再设置当前state,

setCurrentState里调用了link方法同时初始化一些事件监听,clearCurrentState方法调用了unlink方法同时移除一些事件监听,link方法如下:
link: function (key) {var state = this.states[key];state.game = this.game;state.add = this.game.add;state.make = this.game.make;state.camera = this.game.camera;state.cache = this.game.cache;state.input = this.game.input;state.load = this.game.load;state.math = this.game.math;state.sound = this.game.sound;state.scale = this.game.scale;state.state = this;state.stage = this.game.stage;state.time = this.game.time;state.tweens = this.game.tweens;state.world = this.game.world;state.particles = this.game.particles;state.rnd = this.game.rnd;state.physics = this.game.physics;state.key = key;},

由此可见每个状态都有game所拥有的几乎所有属性,所以每个state都相当于一个单独的game,包括init,preload,create等等。

setCurrentState:

setCurrentState: function (key) {var state = this.states[key];this.callbackContext = state;this.link(key);//  Used when the state is set as being the current active statethis.onInitCallback = state['init'] || this.dummy;this.onPreloadCallback = state['preload'] || null;this.onLoadRenderCallback = state['loadRender'] || null;this.onLoadUpdateCallback = state['loadUpdate'] || null;this.onCreateCallback = state['create'] || null;this.onUpdateCallback = state['update'] || null;this.onPreRenderCallback = state['preRender'] || null;this.onRenderCallback = state['render'] || null;this.onResizeCallback = state['resize'] || null;this.onPausedCallback = state['paused'] || null;this.onResumedCallback = state['resumed'] || null;this.onPauseUpdateCallback = state['pauseUpdate'] || null;//  Used when the state is no longer the current active statethis.onShutDownCallback = state['shutdown'] || this.dummy;//  Reset the physics system, but not on the first state startif (this.current !== ''){this.game.physics.reset();}this.current = key;this._created = false;//  At this point key and pendingState should equal each otherthis.onInitCallback.apply(this.callbackContext, this._args);//  If they no longer do then the init callback hit StateManager.startif (key === this._pendingState){this._args = [];}this.game._kickstart = true;},

每个state在start的时候都会重新执行init,preload,create,update等方法一遍,this.onInitCallback.apply的时候还把参数传进来了,所以如果想start state的时候传参数,请定义个init方法,接受初始化传过来的data,这种模式犹如class的contructor一样使得

state之间相互独立,又可以相互传值,形成一条state网状链式结构,由于clearCurrentState默认clearWorld = true,所以在切换state的时候会先game.world.shutdown();相当于移除所有舞台元素,同时提供了shutdown方法用于关闭state时做的处理。

整个phaser的state流程是----》

初始化Phaser.Game可设置默认state ==》game.state.add(key,state);添加state,==》start state:game.state.start(key1) ==> 移除key1,game.state.start(key2) ==》如此循环,整个过程所有state共享game同时共享其所有方法和属性。

Phaser的初衷也是以最快的速度完成一个游戏,的确这种相互独立e又可以相互连接的state真是可以为开发节省很多很多时间。

设计思路:一个游戏先preload(一般之前会加个boot)=》menuState = 》gameState1=》gameState2=》gameState3等等=》overState  独立模块或场景都可添加state,UI界面最好是继承Group,不要做state,注意由于state切换的时候会destroy world,所以单例或共享View界面最好在start之前全部先移除,否则会出现destroy摧毁当前的单例view的child等所有 导致单例undefined 错误。

转载于:https://www.cnblogs.com/wangzisheng/p/9051839.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251734.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA_出神入化学习路线大纲

注:参考GitHub上的项目(toBeTopJavaer)总结出来 也是自己的目标。 基础篇:https://www.cnblogs.com/blogzcc/p/10899066.html 进阶篇:https://www.cnblogs.com/blogzcc/p/10899841.html 高级篇:https://www…

Ubuntu安装并使用sogou输入法

1.下载搜狗输入法的安装包 下载地址为:http://pinyin.sogou.com/linux/ ,如下图,要选择与自己系统位数一致的安装包,我的系统是64位,所以我下载64位的安装包 2.按键CtrAltT打开终端,输入以下命令切换到下载文件夹: [ht…

面試題之web

1. django和flask框架的区别? django:大而全的全的框架,重武器;内置很多组件:ORM、admin、Form、ModelForm、中间件、信号、缓存、csrf等 flask: 微型框架、可扩展强,如果开发简单程序使用flask比较快速&am…

python 常用镜像

pip镜像https://pypi.tuna.tsinghua.edu.cn/simplehttps://pypi.douban.io.com/simple pip install python-qt -i https://pypi.tuna.tsinghua.edu.cn/simple清华开源软件镜像:(anaconda)https://mirrors.tuna.tsinghua.edu.cn/https://mirro…

flutter 几秒前, 几分钟前, 几小时前, 几天前...

Show me the code!!! class RelativeDateFormat {static final num ONE_MINUTE 60000;static final num ONE_HOUR 3600000;static final num ONE_DAY 86400000;static final num ONE_WEEK 604800000;static final String ONE_SECOND_AGO "秒前";static final St…

CMake 使用笔记

记录 CMake 相关知识。 Prelude:读文档一定要有耐心! 问题一 CLion: CMakeLists.txt 中 set(CMAKE_CXX_FLAGS -Wall) 不起作用 Solution: 改用 target_compile_options(main PUBLIC -Wall) Reference:target_compile_optionsGCC: Options to …

Docker 完全指南

Docker 最初 dotCloud 公司内部的一个业余项目Docker 基于 Go 语言Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案Docker 的基础是 Linux 容器(LXC)等技术Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多Docker 对…

NOIP 2016【蚯蚓】

好吧,我承认我是个智障…… 这道题一眼看上去就是个堆,然而实际上有单调性。 注意到,如果 \(q 0\) 的话,将蚯蚓的左右两边分开丢进两个队列中,则两个队列都是单调不增的,因为每次取出的蚯蚓长度单调不增。…

Ajax异步(客户端测试)

客户端测试:GET方法实现Ajax异步 var request new XMLHttpRequest(); request.open("GET","sever.php?number" document.getElementById("keyword").value); request.send(); request.onreadystatechange function(){ if(request.…

VS 添加文件添加文件成链接

转载于:https://www.cnblogs.com/wsxkit/p/10907585.html

设计模式——3.观察者模式

观察者模式(Observer) 观察者模式(Observer)简介: 定义一个一对多的依赖关系,让多个观察者对象监听某个主题对象,当主题对象的状态发生改变时,主题对象则通知所有的观察者对象&#…

Android 长按照片保存 工具类

2019独角兽企业重金招聘Python工程师标准>>> public class ImgUtils {public static void saveImageToGallery(Context context, Bitmap bmp) {final String[] items new String[] { "保存图片"};//图片转成Bitmap数组final Bitmap[] bitmap new Bitmap…

反爬机制

一.通过headers反爬虫: Basic Auth这是一种古老的、不安全的用户验证方式,一般会有用户授权的限制,会在headers的Autheration字段里要求加入用户名密码(明文),如果验证失败则请求就会失败,现在这种认证方式正在被淘汰。…

knockout + easyui = koeasyui

在做后台管理系统的同学们,是否有用easyui的经历。虽然现在都是vue、ng、react的时代。但easyui(也就是jquery为基础)还是占有一席之地的。因为他对后端开发者太友好了,太熟悉不过了。要让一个后端开发者来理解vue或者是react的VN…

轻量社交APP系统ThinkSNS 简 权威发布 限时惠购

2019独角兽企业重金招聘Python工程师标准>>> 伴随国内外创业风潮、AI、区块链等互联网软件科技领域的高速发展,2019年,ThinkSNS软件品牌迎来十周年后的新纪元。作为下一个阶段的产品元年,官方于2019年5月正式发售轻量核心社交APP系…

linux下安装oracle sqlplus以及imp、exp工具

一、下载oracle 11g sqlplus软件 linux 64位操作系统,oracle安装包地址 http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html oracle-instantclient11.2-sqlplus-11.2.0.3.0-1.x86_64.rpm  oracle-instantclient11.2-basic-11.2.0.4.0-1.x86_6…

在operator =中要处理“自我赋值”

防止自我赋值很有必要 Widget w; w w; a[i] a[j]; //a[i]和a[j]实际上指向同一个元素 *pi *pj; //pi和pj实际上指向同一个元素 自我赋值的危害: Widget { private:Test *p; }; Widget &Widget::operator(const Widget &w) {delete p;p new int (*w.p);r…

新添加磁盘分区后,找不到新分区

问题:在Vcent中扩容磁盘容量,登录虚拟机fdisk /dev/sda分区后,找不到新分区。 lsblk或者 df -TH fdisk /dev/sda p 尝试解决办法: cd /sys/class/scsi_host/ ls echo "- - -" > /sys/class/scsi_host/host0/scan (中…

Linux一些指令

备忘。。 ~/.bashrc 环境变量文件 xshell5 与本机文件传输 rz接受 sz filename 传输 watch -n 2 nvidia-smi 监视gpu 状态wget 下载单个文件wget http://images.cocodataset.org/zips/train2014.zip给.sh文件添加x执行权限 比如以hello.sh文件为例,chmod ux hello…

C# 通过反射获取方法/类上的自定义特性

1.所有自定义属性都必须继承System.Attribute 2.自定义属性的类名称必须为 XXXXAttribute 即是已Attribute结尾 自定义属性QuickWebApi [AttributeUsage(AttributeTargets.Method, Inherited false, AllowMultiple true)]public class QuickWebApiAttribute: Attribute{publ…