Merge branch 'master' into tile-collision-update

This commit is contained in:
Richard Davey 2019-10-02 15:15:18 +01:00 committed by GitHub
commit 3a3ce8b914
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1778 changed files with 853243 additions and 378601 deletions

View file

@ -8,4 +8,10 @@ src/geom/polygon/Earcut.js
src/utils/array/StableSort.js
src/utils/object/Extend.js
src/structs/RTree.js
src/boot/ScaleManager.js
plugins/spine/dist/
plugins/spine/src/runtimes/
webpack.*
webpack.config.js
webpack.dist.config.js
webpack.fb.config.js
webpack.fb.dist.config.js

View file

@ -16,7 +16,8 @@
"CANVAS_RENDERER": true,
"Phaser": true,
"process": true,
"ActiveXObject": true
"ActiveXObject": true,
"FBInstant": true
},
"rules": {
@ -71,7 +72,8 @@
"eol-last": [ "error" ],
"func-call-spacing": [ "error", "never" ],
"indent": [ "error", 4, { "SwitchCase": 1 } ],
"key-spacing": [ "error", { "beforeColon": false, "afterColon": true }],
"key-spacing": [ "error", { "beforeColon": false, "afterColon": true } ],
"keyword-spacing": [ "error", { "after": true } ],
"linebreak-style": [ "off" ],
"lines-around-comment": [ "error", { "beforeBlockComment": true, "afterBlockComment": false, "beforeLineComment": true, "afterLineComment": false, "allowBlockStart": true, "allowBlockEnd": false, "allowObjectStart": true, "allowArrayStart": true }],
"new-parens": "error",
@ -83,7 +85,7 @@
"no-trailing-spaces": [ "error", { "skipBlankLines": true, "ignoreComments": true } ],
"no-underscore-dangle": "off",
"no-whitespace-before-property": "error",
"object-curly-newline": [ "error", { "multiline": true, "minProperties": 0 } ],
"object-curly-newline": [ "error", { "multiline": true, "minProperties": 0, "consistent": true } ],
"one-var-declaration-per-line": [ "error", "initializations" ],
"quote-props": [ "error", "as-needed" ],
"quotes": [ "error", "single" ],

View file

@ -56,7 +56,7 @@ If your PR is doing little more than changing the Phaser source code into a form
## I don't really like git / node.js, but I can fix this bug
That is fine too. While Pull Requests are the best thing in the world for us, they are not the only way to help. You're welcome to post fixes to our forum or even just email them to us. All we ask is that you still adhere to the guidelines presented here re: JSHint, etc.
That is fine too. While Pull Requests are the best thing in the world for us, they are not the only way to help. You're welcome to post fixes to our forum or even just email them to us. All we ask is that you still adhere to the guidelines presented here re: ESLint, etc.
## Code Style Guide
@ -76,5 +76,5 @@ Thanks to Chad for creating the original Pixi.js Contributing file which we adap
[1]: http://jsfiddle.net
[2]: http://jsbin.com/
[3]: http://nodejs.org
[4]: http://www.html5gamedevs.com/forum/33-phaser-3/
[4]: https://phaser.discourse.group/
[5]: https://codepen.io/pen?template=YeEWom "Phaser 3 game template"

5
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,5 @@
# These are supported funding model platforms
github: photonstorm
patreon: photonstorm
custom: https://phaser.io/community/donate

View file

@ -1,9 +0,0 @@
This repo is for Phaser 3 related issues only. If you've found an issue with Phaser 2 please see the [Phaser CE (Community Edition)](https://github.com/photonstorm/phaser-ce) repo instead.
This should not be used for technical support. If you're struggling trying to use Phaser then post your question to the [forum](http://www.html5gamedevs.com/forum/33-phaser-3/), [Slack](https://phaser.io/community/slack) or [Discord](https://phaser.io/community/discord) channels. GitHub Issues are for bugs and feature requests only.
API errors must include example code showing what happens, and why you don't believe this is the expected behavior. Issues posted without code take _far_ longer to get resolved, _if ever_. Feel free to use a site such as jsBin or [CodePen](https://codepen.io/pen?template=YeEWom) to demo the problem. If we can run it, and see the error, we can usually fix it.
If your Issue contains _any_ form of hostility it will be instantly closed and you will be blocked from access to all our repos.
**Be nice. We do this for free.**

59
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,59 @@
---
name: "\U0001F41B Bug Report"
about: Report a bug found while using Phaser 3
---
<!--
Thank you for taking the time to contribute towards Phaser. Before submitting your issue, check the following:
1. This repo is for Phaser 3 only. Phaser 2.x issues should be raised in the [Phaser CE](https://github.com/photonstorm/phaser-ce) repo.
2. This repo should not be used for technical support. If you're struggling to use Phaser then post your question to the [forum](https://phaser.discourse.group/), [Slack](https://phaser.io/community/slack) or [Discord](https://phaser.io/community/discord) channels. GitHub Issues are for bugs and feature requests only.
3. Make sure your issue isn't a duplicate, or has already been fixed.
4. If your issue contains _any_ form of hostility it will be closed and you will be blocked from access to all our repos. Be nice. We do this for free.
5. If all the above is ok, fill out the template below.
-->
## Version
<!--
Enter the version of Phaser you're using. You can find this output to the Dev Tools console in your browser.
-->
* Phaser Version:
<!--
Place the operating system **below** this comment.
-->
* Operating system:
<!--
If the bug is browser specific, please enter the version **below** this comment:
-->
* Browser:
## Description
<!--
Write a detailed description of the bug **below** this comment. Include the expected behavior and what actually happens. If the issue is device specific, please say so.
-->
## Example Test Code
<!--
All issues must have source code demonstrating the problem. We automatically close issues after 30 days if no code is provided.
The code can be pasted directly below this comment, or you can link to codepen, jsbin, or similar. The code will ideally be runnable instantly. The more work involved in turning your code into a reproducible test case, the longer it will take the fix the issue.
-->
## Additional Information
<!--
Is there anything else you think would be useful for us to reproduce the error? Do you have a solution in mind?
If you have any screen shots or gifs demonstrating the issue (if it can't be seen when running your code), please include those too.
-->

View file

@ -0,0 +1,15 @@
---
name: "\U0001F389 Feature Request"
about: Share an idea about a feature you'd like to see in Phaser
---
**We're constantly trying to make Phaser 3 better**
So we'd love to hear about feature requests for the v3 API. Screenshots, example code or links to blog posts / other APIs are encouraged.
Please try and keep the requests sensible. We will only keep those we feel are within our abilities to add to Phaser, or that are a good fit for our API.
**[Optional] Do you want to help provide this feature?**
If you would like to get involved in helping build the feature, please let us know. We can then discuss implementation with you and the best way to approach it. Again, screenshots or mockups are really useful.

14
.github/no-response.yml vendored Normal file
View file

@ -0,0 +1,14 @@
# Configuration for no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 30
# Label requiring a response
# TODO: also close `needs-reproduction` issues (blocked by https://github.com/probot/no-response/issues/11)
responseRequiredLabel: 🤷‍♂️ More info needed
# Comment to post when closing an issue due to lack of response.
closeComment: >
Thank you for taking time to open this issue.
We havent gotten a response to our questions above. With the details currently given in the issue we dont have enough information to take action.
So were going to close this issue. We can re-open it if you find the time to provide the information we need.

2
.gitignore vendored
View file

@ -14,3 +14,5 @@ node_modules/
/npm-debug.log
build/
out/
scripts/tsgen/test/bin/
scripts/tsgen/test/output.txt

View file

@ -12,6 +12,6 @@ env:
- TERM=dumb
script:
- yarn install
- yarn run lint
- yarn run build
- npm install
- npm run lint
- npm run build

File diff suppressed because it is too large Load diff

454
README.md
View file

@ -4,15 +4,13 @@
Phaser is a fast, free, and fun open source HTML5 game framework that offers WebGL and Canvas rendering across desktop and mobile web browsers. Games can be compiled to iOS, Android and native apps by using 3rd party tools. You can use JavaScript or TypeScript for development.
Phaser is available in two versions: Phaser 3 and [Phaser CE - The Community Edition](https://github.com/photonstorm/phaser-ce). Phaser CE is a community-lead continuation of the Phaser 2 codebase and is hosted on a separate repo. Phaser 3 is the next generation of Phaser.
Along with the fantastic open source community, Phaser is actively developed and maintained by [Photon Storm](http://www.photonstorm.com). As a result of rapid support, and a developer friendly API, Phaser is currently one of the [most starred](https://github.com/collections/javascript-game-engines) game frameworks on GitHub.
Thousands of developers from indie and multi-national digital agencies, and universities worldwide use Phaser. You can take a look at their incredible [games](https://phaser.io/games/).
**Visit:** The [Phaser website](https://phaser.io) and follow on [Twitter](https://twitter.com/phaser_) (#phaserjs)<br />
**Learn:** [API Docs](https://github.com/photonstorm/phaser3-docs), [Support Forum][forum] and [StackOverflow](https://stackoverflow.com/questions/tagged/phaser-framework)<br />
**Code:** 700+ [Examples](https://labs.phaser.io) (source available in this [repo][examples])<br />
**Learn:** [API Docs](https://photonstorm.github.io/phaser3-docs/index.html), [Support Forum][forum] and [StackOverflow](https://stackoverflow.com/questions/tagged/phaser-framework)<br />
**Code:** 1700+ [Examples](https://phaser.io/examples) (source available in this [repo][examples])<br />
**Read:** The [Phaser World](#newsletter) Newsletter<br />
**Chat:** [Slack](https://phaser.io/community/slack) and [Discord](https://phaser.io/community/discord)<br />
**Extend:** With [Phaser Plugins](https://phaser.io/shop/plugins)<br />
@ -24,27 +22,21 @@ Grab the source and join the fun!
<div align="center"><img src="https://phaser.io/images/github/news.jpg"></div>
> 13th July 2018
> 8th August 2019
It has been exactly one month since 3.10 was released and now we're back with 3.11 :) As usual the Change Log is significant, so please take your time to digest it. There are a huge number of significant improvements including lots of updates to the Camera system including camera alpha, deadzone support, fixes to bounds calculations and the smoothness of follower scrolling. I've also recoded the Texture Tint Pipeline, which was the main pipeline in use by WebGL. As well as removing over a thousand lines of duplicate code I've consolidated lots of common functions and more importantly, moved the rendering to the Game Objects themselves. This means if you now do a custom build of Phaser you can shave off loads more KB than ever before.
I'm pleased to announce that Phaser 3.19 is now available. This release includes our brand new Spine plugin. Spine allows you to bring animation to life in your games, offering a dedicated 2D skeletal animation system and workflow. Our plugin makes integration with Phaser seamless and is fully updated for the Spine 3.7 Runtimes with support for WebGL and Canvas renderers. This version will properly batch Spine skeletons where possible, potentially saving hundreds of draw calls. The plugin is fully documented and exports both debug and minified files, suitable for ES6 'importing' or ES5 plugin inclusion. The whole plugin is just 68KB in size (min+gz), or a paltry 57KB if you only need the Canvas renderer! That's a really tiny payload for such a massive feature-set. You can find out more about Spine from the [Esoteric Software website](http://esotericsoftware.com/).
Also new in 3.11 is support for texture cropping! You can now crop Game Objects with your own rectangle, which is extremely handy for things like progress or health bars without needing to use a mask. There's a new Tint Mode as well, which allows you to fully tint an object with any color - great for making your sprites flash white when hit! Bitmap Text has been given a shot in the arm as well. With new bounds calculations, multi-line support, cached callback data and speed improvements everywhere. Tilemaps have also been improved. I recoded the way in which tile culling was calculated. It's faster than ever and provides lots of new culling options for you. From cull padding to disabling it entirely, or even providing your own cull callback function. Dynamic Tilemap Layers also now work with the Lights2D Pipeline :) It doesn't end there - thanks to community contributions we also added rounded rectangle support to the Graphics class, the ability to scale TileSprite textures, Rectangle intersection tests and lots more.
3.19 also introduces a huge overhaul to the Tween system. Tweens now have 100% documentation and we've extended their capabilities significantly. There are lots of new Tween Events to listen for, such as 'COMPLETE' or 'REPEAT' to allow you to trigger actions without needing to create callbacks. Tweens can now tween both 'from' and 'to' a value, with the ability to set a starting value for any tweened property. There are lots of new handy methods and properties, such as `Tween.hasStarted` and a rewrite of the Tween seeking function, so it now allows you to seek to any point in time across a tween. Finally, we've added in the great new 'StaggerBuilder'. This allows you to easily add staggered offsets to a bunch of tween targets, including all kinds of options such as staggering across a grid layout, stagger directions, starting values and a lot more. Please see the docs and examples for more details.
There are, of course, plenty of bug fixes and updates too. I'd urge you to carefully read the Change Log, especially if upgrading from an earlier version in an existing project. Hundreds more areas have been covered with documentation too. We're very nearly there with regard to 100% documentation coverage.
As usual, it doesn't end there, though :) Another very useful feature is `Shader.setRenderToTexture`. This allows you to redirect a shader to its own framebuffer / WebGL Texture instead of to the display list. This allows you to use the output of the shader as an input for another shader, by mapping a sampler2D uniform to it. It also allows you to save the Shader to the Texture Manager, allowing you to use it as a texture for any other texture based Game Object such as a Sprite. Combined with the new `setSampler2DBuffer` method you can now easily chain shaders together, using them as buffers for other shaders.
3.11 is yet another huge release and represents tireless effort on my part to get it into this shape. My aim has always been to continue the mission of enhancing Phaser 3 as quickly as I can. It means releasing significant updates in relatively short periods of time. But it also means I'm jumping on bug reports as quickly as I can, keeping the issues list total nice and low (the vast majority of the items in there are feature requests now!) - a massive thank-you to all of you who support Phaser on Patreon and PayPal. It's your support that allows me to work on this full-time, to the benefit of everyone.
One thing I've been seeing asked for a lot on the Phaser Discord is the ability to 'save' a Render Texture to an image. So, I added the new methods `RenderTexture.snapshot` and `snapshotArea`. This allows you to grab whatever is on the Render Texture at that point in time and turn it into an Image. You could then save this image to the Texture Manager, if needed, or just save it out to the filesystem, or transmit it to as web service. Great for things like avatar creators or art packages.
As always, please check out the [Change Log](#changelog) for comprehensive details about what recent versions contain.
You'll find loads more great new features, updates and fixes. So, as usual, please do spend some time digging through the [Change Log](#changelog). I assure you, it's worth while :)
**About Phaser 3**
A massive thank-you to everyone who supports Phaser on Patreon and PayPal. Your continued backing has allowed me to work on Phaser all year, and this great new releases is the very real result of that. If you've ever considered becoming a backer, now is the perfect time!
After 1.5 years in the making, tens of thousands of lines of code, hundreds of examples and countless hours of relentless work: Phaser 3 is finally out. It has been a real labor of love and then some!
Please understand this is a bleeding-edge and brand new release. There are features we've had to leave out, areas of the documentation that need completing and so many cool new things we wanted to add. But we had to draw a line in the sand somewhere and 3.0.0 represents that.
For us this is just the start of a new chapter in Phaser's life. We will be jumping on bug reports as quickly as we can and releasing new versions rapidly. We've structured v3 in such a way that we can push out point releases as fast as needed.
We publish our [Developer Logs](https://phaser.io/phaser3/devlog) in the [Phaser World](https://phaser.io/community/newsletter) newsletter. Subscribe to stay in touch and get all the latest news from us and the wider Phaser community.
If you'd like to stay abreast of developments then I publish my [Developer Logs](https://phaser.io/phaser3/devlog) in the [Phaser World](https://phaser.io/community/newsletter) newsletter. Subscribe to stay in touch and get all the latest news from the core team and the wider community.
You can also follow Phaser on [Twitter](https://twitter.com/phaser_) and chat with fellow Phaser devs in our [Slack](https://phaser.io/community/slack) and [Discord](https://phaser.io/community/discord) channels.
@ -60,19 +52,32 @@ Rich - [@photonstorm](https://twitter.com/photonstorm)
![Support Phaser](https://phaser.io/images/github/div-support-phaser.png "Support Phaser")
Developing Phaser takes a lot of time, effort and money. There are monthly running costs as well as countless hours of development time, community support, and assistance resolving issues.
Because Phaser is an open source project, we cannot charge for it in the same way as traditional retail software. What's more, we don't ever want to. After all, it's built on, and was born from, open web standards. It's part of our manifesto that the core framework will always be free, even if you use it commercially, as many of you do.
If you have found Phaser useful in your development life or have made income as a result of it please support our work via:
**You may not realize it, but because of this, we rely 100% on community backing to fund development.**
* A monthly contribution on [Patreon](https://www.patreon.com/photonstorm).
* A [one-off donation](https://phaser.io/community/donate) with PayPal.
* Purchase any of our [plugins or books](https://phaser.io/shop).
Those funds allow Phaser to improve, and when it improves, everyone involved benefits. Your support helps secure a constant cycle of updates, fixes, new features and planning for the future.
It all helps and genuinely contributes towards future development.
There are other benefits to [backing Phaser](https://www.patreon.com/join/photonstorm), too:
Extra special thanks to our top-tier sponsors: [Orange Games](http://orangegames.com) and [CrossInstall](https://crossinstall.com).
![Backers Perks](https://phaser.io/images/github/patreon-perk-chart.png)
![Sponsors](https://phaser.io/images/github/patreon-sponsors-2018-1.png "Top Patreon Sponsors")
We use [Patreon](https://www.patreon.com/photonstorm) to manage the backing and you can [support Phaser](https://www.patreon.com/join/photonstorm?) from $1 per month. The amount you pledge is entirely up to you and can be changed as often as you like. Patreon renews monthly, just like Netflix. You can, of course, cancel at any point. Tears will be shed on this end, but that's not your concern.
Extra special thanks to the following companies who's support makes Phaser possible:
* [Cerebral Fix](https://cerebralfix.com)
* [CrossInstall](https://crossinstall.com)
* [Facebook](https://www.facebook.com)
* [Game Distribution](https://gamedistribution.com)
* [GameCommerce](https://www.gamecommerce.com)
* [Mozilla](https://www.mozilla.org)
* [Texture Packer](https://www.codeandweb.com/texturepacker/tutorials/how-to-create-sprite-sheets-for-phaser3?utm_source=ad&utm_medium=banner&utm_campaign=phaser-2018-10-16)
* [Twilio](https://www.twilio.com)
* [Y8 Games](https://www.y8.com)
* [Poki](https://developers.poki.com/)
![Sponsors](https://phaser.io/images/github/sponsors-2019-08.png "Awesome Sponsors")
![Phaser Newsletter](https://phaser.io/images/github/div-newsletter.png "Phaser Newsletter")
@ -80,7 +85,7 @@ Extra special thanks to our top-tier sponsors: [Orange Games](http://orangegames
We publish the [Phaser World](https://phaser.io/community/newsletter) newsletter. It's packed full of the latest Phaser games, tutorials, videos, meet-ups, talks, and more. The newsletter also contains our weekly Development Progress updates which let you know about the new features we're working on.
Over 120 previous editions can be found on our [Back Issues](https://phaser.io/community/backissues) page.
Over 140 previous editions can be found on our [Back Issues](https://phaser.io/community/backissues) page.
![Download Phaser](https://phaser.io/images/github/div-download.png "Download Phaser")
<a name="download"></a>
@ -106,33 +111,43 @@ npm install phaser
[Phaser is on jsDelivr](https://www.jsdelivr.com/projects/phaser) which is a "super-fast CDN for developers". Include the following in your html:
```html
<script src="//cdn.jsdelivr.net/npm/phaser@3.11/dist/phaser.js"></script>
<script src="//cdn.jsdelivr.net/npm/phaser@3.19.0/dist/phaser.js"></script>
```
or the minified version:
```html
<script src="//cdn.jsdelivr.net/npm/phaser@3.11/dist/phaser.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/phaser@3.19.0/dist/phaser.min.js"></script>
```
### API Documentation
1. Go to https://photonstorm.github.io/phaser3-docs/index.html to read the docs online. Use the drop-down menus at the top to navigate the name spaces, classes and Game Objects lists. If you wish to run the docs locally you can ...
2. Checkout the [phaser3-docs](https://github.com/photonstorm/phaser3-docs) repository and then read the documentation by pointing your browser to the local `docs/` folder, and again selecting from the Classes or Namespaces links at the top of the page.
Go to https://photonstorm.github.io/phaser3-docs/index.html to read the docs online. Use the drop-down menus at the top to navigate the namespaces, classes and Game Objects lists.
Or, if you wish to run the docs locally you can checkout the [phaser3-docs](https://github.com/photonstorm/phaser3-docs) repository and then read the documentation by pointing your browser to the `docs/` folder.
The documentation for Phaser 3 is an on-going project. Please help us by searching the Phaser code for any instance of the string `[description]` and then replacing it with some documentation.
### TypeScript Definitions
[TypeScript Definitions](https://github.com/photonstorm/phaser3-docs/tree/master/typescript) are now available.
The [TypeScript definitions](https://github.com/photonstorm/phaser/tree/master/types) can be found inside the `types` folder. They are also referenced in the types entry in `package.json`.
They are automatically generated from the jsdoc comments in the Phaser source code. If you wish to help refine them then you must edit the Phaser jsdoc blocks directly. You can find more details, including the source to the conversion tool we wrote in the Docs repo.
Depending on your project, you may need to add the following to your `tsconfig.json` file:
As soon as we're happy with the accuracy of the TS defs we'll merge them into the main repo, for now, please download them from the docs repo, linked above, and add them to your project. When we release new versions of Phaser we publish new TS defs too.
```json
"typeRoots": [
"./node_modules/phaser/types"
],
"types": [
"Phaser"
]
```
The defs are automatically generated from the JSDoc comments found in the Phaser source code. If you wish to help refine them then you must edit the Phaser JSDoc blocks directly, not the defs file. You can find more details about the parser we built in the `scripts/tsgen` folder.
### Webpack
We use Webpack to build Phaser and we take advantage of its conditional build flag feature to handle renderer swapping. If you wish to use Webpack with Phaser then please use our [Phaser 3 Project Template](https://github.com/photonstorm/phaser3-project-template) as it's already set-up to handle the build conditions Phaser needs. Recent changes to our build steps mean you should now be able to us any other packager, like Parcel, without any config changes.
We use Webpack to build Phaser and we take advantage of its conditional build flag feature to handle renderer swapping. If you wish to use Webpack with Phaser then please use our [Phaser 3 Project Template](https://github.com/photonstorm/phaser3-project-template) as it's already set-up to handle the build conditions Phaser needs. Recent changes to our build steps mean you should now be able to use any other packager, like Parcel, without any config changes.
### License
@ -143,17 +158,60 @@ Phaser is released under the [MIT License](https://opensource.org/licenses/MIT).
<img src="https://phaser.io/images/github/learn.jpg" align="right">
Phaser 3 is so new the "paint is still wet", but tutorials and guides are starting to come out!
Tutorials and guides on Phaser 3 development are being published every week.
* [Getting Started with Phaser 3](https://phaser.io/tutorials/getting-started-phaser3) (useful if you are completely new to Phaser)
* [Making your first Phaser 3 Game](https://phaser.io/tutorials/making-your-first-phaser-3-game)
* [Phaser 3 Bootstrap and Platformer Example](https://phaser.io/news/2018/02/phaser-3-bootstrap-platformer)
* The [Complete Phaser 3 Game Development course](https://academy.zenva.com/product/html5-game-phaser-mini-degree/?a=13) contains over 15 hours of videos covering all kinds of important topics.
* Plus, there are [over 700 Phaser tutorials](http://phaser.io/learn) listed on the official website.
Also, please subscribe to the [Phaser World](https://phaser.io/community/newsletter) newsletter for details about new tutorials as they are published.
### Facebook Instant Games
Phaser 3.13 introduced the new [Facebook Instant Games](http://phaser.io/news/2018/10/facebook-instant-games-phaser-tutorial) Plugin. The plugin provides a seamless bridge between Phaser and version 6.2 of the Facebook Instant Games SDK. Every single SDK function is available via the plugin and we will keep track of the official SDK to make sure they stay in sync.
The plugin offers the following features:
* Easy integration with the Phaser Loader so load events update the Facebook progress circle.
* Events for every plugin method, allowing the async calls of the SDK to be correctly inserted into the Phaser game flow. When SDK calls resolve they will surface naturally as a Phaser event and you'll know you can safely act upon them without potentially doing something mid-way through the game step.
* All Plugin methods check if the call is part of the supported APIs available in the SDK, without needing to launch an async request first.
* Instant access to platform, player and locale data.
* Easily load player photos directly into the Texture Manager, ready for use with a Game Object.
* Subscribe to game bots.
* The plugin has a built-in Data Manager which makes dealing with data stored on Facebook seamless. Just create whatever data properties you need and they are automatically synced.
* Support for FB stats, to retrieve, store and increment stats into cloud storage.
* Save Session data with built-in session length validation.
* Easy context switching, to swap between game instances and session data retrieval.
* Easily open a Facebook share, invite, request or game challenge window and populate the text and image content using any image stored in the Texture cache.
* Full Leaderboard support. Retrieve, scan and update leaderboard entries, as well as player matching.
* Support for in-app purchases, with product catalogs, the ability to handle purchases, get past purchases and consume previously unlocked purchases.
* Easily preload a set of interstitial ads, in both banner and video form, then display the ad at any point in your game, with in-built tracking of ads displayed and inventory available.
* Plus other features, such as logging to FB Analytics, creating short cuts, switching games, etc.
We've 3 tutorials related to Facebook Instant Games and Phaser:
* [Getting Started with Facebook Instant Games](http://phaser.io/news/2018/10/facebook-instant-games-phaser-tutorial)
* [Facebook Instant Games Leaderboards Tutorial](http://phaser.io/news/2018/11/facebook-instant-games-leaderboards-tutorial)
* [Displaying Ads in your Instant Games](http://phaser.io/news/2018/12/facebook-instant-games-ads-tutorial)
A special build of Phaser with the Facebook Instant Games Plugin ready-enabled is [available on jsDelivr](https://www.jsdelivr.com/projects/phaser). Include the following in your html:
```html
<script src="//cdn.jsdelivr.net/npm/phaser@3.19.0/dist/phaser-facebook-instant-games.js"></script>
```
or the minified version:
```html
<script src="//cdn.jsdelivr.net/npm/phaser@3.19.0/dist/phaser-facebook-instant-games.min.js"></script>
```
The build files are in the git repository in the `dist` folder, and you can also include the plugin in custom builds.
### Source Code Examples
During our development of Phaser 3, we created hundreds of examples with the full source code and assets. Until these examples are fully integrated into the Phaser website, you can browse them on [Phaser 3 Labs](https://labs.phaser.io), or clone the [examples repo][examples]. We are constantly adding to and refining these examples.
During our development of Phaser 3, we created hundreds of examples with the full source code and assets ready available. These examples are now fully integrated into the [Phaser website](https://phaser.io/examples). You can also browse them on [Phaser 3 Labs](https://labs.phaser.io) via a more advanced interface, or clone the [examples repo][examples]. We are constantly adding to and refining these examples.
### Create Your First Phaser 3 Example
@ -163,7 +221,7 @@ Create an `index.html` page locally and paste the following code into it:
<!DOCTYPE html>
<html>
<head>
<script src="https://labs.phaser.io/build/phaser-arcade-physics.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/phaser@3.19.0/dist/phaser-arcade-physics.min.js"></script>
</head>
<body>
@ -259,11 +317,13 @@ There are both plain and minified compiled versions of Phaser in the `dist` fold
### Custom Builds
Phaser 3 is built using Webpack and we take advantage of the Webpack definePlugin feature to allow for conditional building of the Canvas and WebGL renderers. As of Phaser 3.7 we have updated our webpack config to make our source far easier to consume in other package managers like Parcel and Electron. Please look our webpack config files to get an idea of the settings we use.
Phaser 3 is built using Webpack and we take advantage of the Webpack definePlugin feature to allow for conditional building of the Canvas and WebGL renderers and extra plugins. You can custom the build process to only include the features you require. Doing so can cut the main build file size down to just 70KB.
Read our [comprehensive guide](https://phaser.io/phaser3/devlog/127) on creating Custom Builds of Phaser 3 for full details.
### Building from Source
If you wish to build Phaser 3 from source, ensure you have the required packages by cloning the repository and then running `npm install`.
If you wish to build Phaser 3 from source, ensure you have the required packages by cloning the repository and then running `npm install` on your source directory.
You can then run `webpack` to create a development build in the `build` folder which includes source maps for local testing. You can also `npm run dist` to create a minified packaged build in the `dist` folder. For a list of all commands available use `npm run help`.
@ -272,200 +332,160 @@ You can then run `webpack` to create a development build in the `build` folder w
# Change Log
## Version 3.11.0 - Leafa - 13th July 2018
## Version 3.19.0 - Naofumi - 8th August 2019
### Camera - New Features, Updates and Fixes
### Tween Updates
* All of the 2D Camera classes are now 100% covered by JSDocs!
* All of the 3D Camera classes are now deprecated and will be removed in the next version. They will be moved to a stand-alone plugin.
* `Camera.alpha` (and its related method `Camera.setAlpha`) allows you to set an alpha level for the entire camera. This impacts everything it is rendering, even if those objects also have their own alpha values too. You can tween the property to make the camera contents fade in / out, or otherwise set it as needed in your game.
* `Camera.deadzone` (and its related method `Camera.setDeadzone`) allows you to specify the deadzone for a camera. The deadzone is a rectangular region used when a camera is following a target. If the target is within the deadzone then the camera will not scroll. As soon as the target leaves the deadzone, the camera will begin tracking it (applying lerp if needed.) It allows you to set a region of the camera in which a player can move freely before tracking begins. The deadzone is re-centered on the camera mid point every frame, meaning you can also use the rectangle for other in-game checks as needed.
* `Camera.pan` is a new Camera Effect that allows you to control automatic camera pans between points in your game world. You can specify a duration and ease type for the pan, and it'll emit events just like all other camera effects, so you can hook into the start, update and completion of the pan. See the examples and docs for more details.
* `Camera.zoom` is a new Camera Effect that allows you to control automatic camera zooming. You can specify a duration and ease type for the zoom, as well as the zoom factor of course, and it'll emit events just like all other camera effects, so you can hook into the start, update and completion of the zoom. Used in combination with the new Pan effect you can zoom and pan around with ease. See the examples and docs for more details.
* `Camera.midPoint` is a new Vec2 property that is updated every frame. Use it to obtain exactly where in the world the center of the camera is currently looking.
* `Camera.displayWidth` is a new property that returns the display width of the camera, factoring in the current zoom level.
* `Camera.displayHeight` is a new property that returns the display height of the camera, factoring in the current zoom level.
* `Camera.worldView` is a new property, an instance of a Rectangle, that contains the dimensions of the area of the world currently visible by the camera. You can use it for intersection or culling tests that don't need to factor in camera rotation.
* `Camera.dirty` is a new boolean property. A dirty Camera has had either its viewport size, bounds, scroll, rotation or zoom levels changed since the last frame. The flag is reset in the `postCameraRender` method, but until that point can be checked and used.
* `Camera.centerOn` is a new method that will move the camera so its viewport is centered on the given coordinates. A handy way of jumping to different points around a map without needing to calculate the scroll offsets.
* The Camera bounds didn't factor in the camera zoom properly, meaning you would often not be able to reach the corners of a camera bound world at a zoom level other than 1. The bounds are now calculated each frame to ensure they match the zoom level and it will no longer allow you to scroll off the edge of the bounds. Fix #3547 (thanks @nkholski)
* `Camera.centerToBounds` didn't take the bounds offset into account, so bounds at non-zero positions wouldn't center properly. All bounds now center correctly. Fix #3706 (thanks @cyantree)
* `Camera.setBounds` has a new optional argument `centerOn`. If specified it will automatically center the camera on the new bounds given.
* The Camera will no longer stutter when following Game Objects at high zoom levels.
* `Camera._id` has been renamed to `Camera.id`, a read-only bitmask used for camera exclusion from Game Objects.
* The Camera Manager `cameraPool` has been removed entirely. It was mostly pointless in practice as Cameras are not regenerated frequently enough to need pooling. It also didn't maintain the bitmask list correctly before.
* `CameraManager.resetAll` now destroys all current Cameras, resets the camera ID marker to 1 and adds a single new Camera.
* `CameraManager.currentCameraId` has been removed. IDs are assigned more intelligently now, via the `getNextID` internal method.
* `CameraManager.addExisting` no longer needs to be passed a Camera that already exists in the pool (as the pool has been removed), meaning you can now create your own Cameras and pass them to `addExisting` and have them treated as normal cameras and not be ignored by the manager. They are also assigned a proper ID when added.
* `CameraManager.addExisting` has a new boolean argument `makeMain` which will make the new camera the main one.
* `CameraManager.getTotal` is a new method that will return the total number of Cameras being managed, with an optional `isVisible` argument, that only counts visible cameras if set.
* `CameraManager.remove` can now take an array of cameras to be removed from the manager, as well as a single camera.
* `CameraManager.remove` would previously not allow you to remove a camera if it meant there would be no cameras left in the Camera Manager. This restriction has been removed. A Camera Manager can now run even with zero cameras. Your game obviously won't display anything, but it's still now possible.
* `CameraManager.remove` will now return the total number of Cameras removed.
* All Tween classes and functions have 100% complete JSDocs :)
* `StaggerBuilder` is a new function that allows you to define a staggered tween property. For example, as part of a tween config: `delay: this.tweens.stagger(500)` would stagger the delay by 500ms for every target of the tween. You can also provide a range: `delay: this.tweens.stagger([ 500, 1000 ])` which is spread across all targets. Finally, you can provide a Stagger Config object as the second argument. This allows you to define a stagger grid, direction, starting value and more. Please see the API Docs and new Examples for further details.
* `Tween` now extends the Event Emitter class, allowing it to emit its own events and be listened to.
* `Tween.ACTIVE_EVENT` is a new event that is dispatched when a tween becomes active. Listen to it with `tween.on('active')`.
* `Tween.COMPLETE_EVENT` is a new event that is dispatched when a tween completes or is stopped. Listen to it with `tween.on('complete')`.
* `Tween.LOOP_EVENT` is a new event that is dispatched when a tween loops, after any loop delay expires. Listen to it with `tween.on('loop')`.
* `Tween.REPEAT_EVENT` is a new event that is dispatched when a tween property repeats, after any repeat delay expires. Listen to it with `tween.on('repeat')`.
* `Tween.START_EVENT` is a new event that is dispatched when a tween starts. Listen to it with `tween.on('start')`.
* `Tween.UPDATE_EVENT` is a new event that is dispatched when a tween property updates. Listen to it with `tween.on('update')`.
* `Tween.YOYO_EVENT` is a new event that is dispatched when a tween property yoyos, after any hold delay expires. Listen to it with `tween.on('yoyo')`.
* `Tween.onActive` is a new callback that is invoked the moment the Tween Manager brings the tween to life, even though it may not have yet started actively tweening anything due to delay settings.
* `Tween.onStart` is now only invoked when the Tween actually starts tweening a value. Previously, it was invoked as soon as the Tween Manager activated the Tween. This has been recoded and this action is now handled by the `onActive` callback. Fix #3330 (thanks @wtravO)
* `Tween.seek` has been rewritten so you can now seek to any point in the Tween, regardless of repeats, loops, delays and hold settings. Seeking will not invoke any callbacks or events during the seek. Fix #4409 (thanks @cristib84)
* You can now set `from` and `to` values for a property, i.e. `alpha: { from: 0, to: 1 }` which would set the alpha of the target to 0 and then tween it to 1 _after_ any delays have expired. Fix #4493 (thanks @BigZaphod)
* You can now set `start` and `to` values for a property, i.e. `alpha: { start: 0, to: 1 }` which would set the alpha of the target to 0 immediately, as soon as the Tween becomes active, and then tween it to 1 over the duration of the tween.
* You can now set `start`, `from` and `to` values for a property, i.e. `alpha: { start: 0, from: 0.5, to: 1 }` which would set the alpha of the target to 0 immediately, as soon as the Tween becomes active, then after any delays it would set the alpha to 0.5 and then tween it to 1 over the duration of the Tween.
* `Tween.hasStarted` is a new property that holds a flag signifying if the Tween has started or not. A Tween that has started is one that is actively tweening a property and not just in a delayed state.
* `Tween.startDelay` is a new property that is set during the Tween init to hold the shortest possible time before the Tween will start tweening a value. It is decreased each update until it hits zero, after which the `onStart` callback is invoked.
* `Tween.init` and `Tween.play` have been rewritten so they are not run multiple times when a Tween is paused before playback, or is part of a Timeline. This didn't cause any problems previously, but it was a redundant duplication of calls.
* `Tween.onLoop` will now be invoked _after_ the `loopDelay` has expired, if any was set.
* `Tween.onRepeat` will now be invoked _after_ the `repeatDelay` has expired, if any was set.
* `easeParams` would be ignored for tweens that _didn't_ use a string for the ease function name. Fix #3826 (thanks @SBCGames)
* You can now specify `easeParams` for any custom easing function you wish to use. Fix #3826 (thanks @SBCGames)
* All changes to `Tween.state` are now set _before_ any events or callbacks, allowing you to modify the state of the Tween in those handlers (thanks @Cudabear)
* `Tween.dispatchTweenEvent` is a new internal method that handles dispatching the new Tween Events and callbacks. This consolidates a lot of duplicate code into a single method.
* `Tween.dispatchTweenDataEvent` is a new internal method that handles dispatching the new TweenData Events and callbacks. This consolidates a lot of duplicate code into a single method.
* `Tween.isSeeking` is a new internal boolean flag that is used to keep track of the seek progress of a Tween.
* `Timeline.onLoop` will now be invoked _after_ the `loopDelay` has expired, if any was set.
* `Timeline.onComplete` will now be invoked _after_ the `completeDelay` has expired, if any was set.
* All changes to `Timeline.state` are now set _before_ any events or callbacks, allowing you to modify the state of the Timeline in those handlers.
* The `TIMELINE_LOOP_EVENT` has had the `loopCounter` argument removed from it. It didn't actually send the number of times the Timeline had looped (it actually sent the total remaining).
* When a TweenData completes it will now set the `current` property to be exactly either `start` or `end` depending on playback direction.
* When a TweenData completes it will set the exact `start` or `end` value into the target property.
* `TweenData` has a new function signature, with the new `index` and `getActive`arguments added to it. `TweenBuilder` has been updated to set these, but if you create any TweenData objects directly, use the new signature.
* `TweenData.getActiveValue` is a new property that, if not null, returns a value to immediately sets the property value to on activation.
* `GetEaseFunction`, and by extension anything that uses it, such as setting the ease for a Tween, will now accept a variety of input strings as valid. You can now use lower-case, such as `back`, and omit the 'ease' part of the direction, such as `back.in` or `back.inout`.
* The signature of `getStart` and `getEnd` custom property functions has changed to `(target, key, value, targetIndex, totalTargets, tween)`, previously it was just `(target, key, value)`. Custom functions don't need to change as the new arguments are in addition to those sent previously.
* The signature of the LoadValue generator functions (such as `delay` and `repeat`) has changed to `(target, key, value, targetIndex, totalTargets, tween)` to match those of the custom property functions. If you used a custom generator function for your Tween configs you'll need to modify the signature to the new one.
* Tweens created via `TweenManager.create` wouldn't start when `Tween.play` was called without first making them active manually. They now start automatically. Fix #4632 (thanks @mikewesthad)
### Round Pixels Changes
### Spine Updates
Before explaining the changes it's worth covering what the three different game config properties do:
The Spine Plugin is now 100% complete. It has been updated to use the Spine 3.7 Runtimes. Improvements have been made across the entire plugin, including proper batched rendering support in WebGL, cleaner skin and slot functions and lots and lots of updates. It's fully documented and there are lots of examples to be found. The following legacy bugs have also been fixed:
`roundPixels` - this will cause the renderer to draw most Game Objects at whole integer positions. Their actual positions can be anything, but the renderer will floor the values to ensure they are integers immediately before drawing. It only works on texture based Game Objects. Graphics objects, for instance, ignore this property.
`antialias` - when set to `true` WebGL textures are created using `gl.LINEAR`, which allows WebGL to try its best to interpolate the texture when rendered at non-texture frame sizes. This can happen if you scale a Game Object, or zoom a Camera. In both cases it will need to interpolate the pixel values to accommodate the new size. If this property is set to `false` then it will use `gl.NEAREST` instead. This uses a nearest neighbor method of interpolation, and is nearly always the better option if you need to keep the textures crisp, such as when using scaled pixel art. Disabling `antialias` invokes nearest-neighbor interpolation on the game canvas itself as well. If you need a mixture of aliased and anti-aliased textures in your game, then you can change them on a per-texture basis by using `Texture.setFilter`.
There is a third game config property called `pixelArt`. If set to `true` it's the same thing as enabling `roundPixels` and disabling `antialias`. This is the optimum setting for pixel art games.
* Both renderers will now check for `pixelArt` OR `antialias` before setting the canvas scale mode. Both values are checked during texture creation as well.
* If in your game config you have enabled either pixel art mode or roundPixels, then all Cameras will have their `roundPixels` values set to `true` by default. You can toggle this by changing the `CameraManager.roundPixels` property, or change it on a camera-by-camera basis, as needed.
* `Camera.roundPixels` is now used across all rendering code for both Canvas and WebGL. Previously, it would check the renderer config value, but now all renderer code uses the camera value to decide if it should floor the drawing position or not.
### Texture Tint Pipeline - New Features, Updates and Fixes
The Texture Tint Pipeline has been rewritten to tidy up hundreds of lines of duplicate code and to move the responsibility of drawing to the Game Objects themselves. Previously, had you excluded say Tilemaps from your build of Phaser, the renderer would still include masses of code dealing with the drawing of them. This task has been moved to the Game Objects and the pipeline just provides a set of clean utility functions for batching, flushing and drawing.
The decision to make this change was not taken lightly. However, I felt that none of the pipelines actually lived up to their name. You could never actually pass objects through one pipeline to another as they didn't have entry and exit points and were instead just glorified singular batches. Although you could change the pipeline being used on a Game Object this action meant that every pipeline had to be responsible for every single type of Game Object, both now and in the future, and they were full of redundant stub functions as a result. The payload size was also considerable. It has now gone from 1,961 lines of code at 76 KB down to 729 lines of code and 27 KB. It's not the only file to benefit either. The `ForwardDiffuseLightPipeline` also reduced from 402 lines (15.7 KB) down to 159 lines and 6 KB. Sizes include comments and are un-minified. In a production bundle the difference will be even greater. This is work we will continue in the next release as we do the same updates to the FlatTintPipeline, responsible for rendering Graphics objects, and look at consolidating the shaders allowing you to use Graphics and Sprites mixed in the display list with no shader swapping cost.
* You can now set the WebGL batch size in the Game Config via the property `batchSize`. The default is 2000 before the batch will flush, which is a happy average between desktop and mobile. If targeting desktop specifically, you may wish to increase this value to reduce draw calls.
* There is a new method `batchVertices` which will add a vertices block to the current batch. This is now used internally by nearly all render functions.
* The shader has a new attribute: `tintEffect`. This is a single FLOAT.
* The vertex size has increased by 1 FLOAT to account for the extra shader attribute.
* All of the rendering functions now use the `TransformMatrix` class far more than before. This allows the matrix operations to be run-time compiled and cut down on masses of code.
* The `drawTexture` method has been removed. It has been replaced by `drawTextureFrame` which has a new and more concise signature. See the API docs for details.
* The `batchTileSprite` method has been removed. It is now handled in the TileSprite WebGL Render function.
* The `drawStaticTilemapLayer` method has been removed. It is now handled in the Static Tilemap Layer WebGL Render function.
* The `drawEmitterManager` method has been removed. It is now handled in the Particle Manager WebGL Render function.
* The `batchText` method has been removed. It is now handled in the Static Text WebGL Render function.
* The `batchDynamicTilemapLayer` method has been removed. It is now handled in the Dynamic Tilemap Layer WebGL Render function.
* The `batchMesh` method has been removed. It is now handled in the Mesh WebGL Render function.
* The `batchBitmapText` method has been removed. It is now handled in the BitmapText WebGL Render function.
* The `batchDynamicBitmapText` method has been removed. It is now handled in the DynamicBitmapText WebGL Render function.
* The `batchBlitter` method has been removed. It is now handled in the Blitter WebGL Render function.
Due to the changes in the Texture Tint Pipeline the `Textures.Frame` class has also been updated. The following changes concern the Frame UV data:
* Previously, the UV data spanned 8 properties: `x0`, `y0`, `x1`, `y1`, `x2`, `y2`, `x3` and `y3` and was stored in the `data.uvs` object. These have been replaced with directly accessible properties: `u0`, `v0`, `u1` and `v1`. These 4 properties are used directly in all renderer code now. Although it was clearer having 8 properties, 4 of them were just duplicates, so we've traded a little clarity for a smaller overall object and less dictionary look-ups.
* `Frame.uvs` (and the corresponding `Frame.data.uvs`) object has been removed.
### New Tint Effects
As well as tidying the Texture Tint Pipeline, I also updated the shader. It now has a new attribute 'tintEffect' which allows you to control how a tint is applied to a Game Object. The default way tinting worked was for the tint color values to be multiplied with the texture pixel values. This meant you were unable to do things like tint a Game Object white, because multiplying a color by white doesn't change it. The new tint mode allows you to literally replace the pixel color values.
* `setTintFill` is a new method available to all Game Objects that have the Tint component. It differs from `setTint` in that the colors literally replace the pixel values from the texture (while still respecting the alpha). This means you can now create effects such as flashing a sprite white if it gets hit, or red for damage, etc. You can still use different colors per corner of the Game Object, allowing you to create nice seamless gradient effects.
* `tintFill` is a new boolean property that allows you to toggle between the two different tint types: multiply or replace.
* `isTinted` is a new read-only boolean indicating if a Game Object is tinted or not. Handy for knowing if you need to clear a tint after an effect.
* `Mesh.tintFill` allows you to control the tint effect applied to the Mesh vertices when color blending.
The Tint component documentation has been overhauled to explain these differences in more detail, and you can find lots of new examples as well.
### New Texture Crop Component
There is a new Game Object Component called `TextureCrop`. It replaces the Texture Component (which still exists) and adds in the ability to crop the texture being used. This component is now being used by the `Sprite` and `Image` Game Objects.
* You can crop the frame being used via the new `setCrop` method. The crop is a rectangle that limits the area of the texture frame that is visible during rendering. Cropping a Game Object does not change its size, dimensions, physics body or hit area, it just changes what is shown when rendered. This is ideal for hiding part of a Sprite without using a mask, or for effects like displaying a progress or loading bar. Cropping works even when the Game Object is flipped, or is a trimmed frame from an atlas.
* You can toggle the crop on a Game Object by changing the `isCropped` boolean at any point.
* The crop is automatically re-applied when the texture or frame of a Game Object is changed. If you wish to disable this, turn off the crop before changing the frame.
### BitmapText New Features, Updates and Bug Fixes
* Multi-line BitmapText objects can now be aligned. The constructor has a new argument `align` which can accept either left-aligned (the default), center aligned, or right-aligned. Alignment works by calculating the longest line of text in the object and then offsetting the other lines to match it.
* `BitmapText.setCenterAlign` is a new chainable method to center-align the text.
* `BitmapText.setLeftAlign` is a new chainable method to left-align the text.
* `BitmapText.setRightAlign` is a new chainable method to right-align the text.
* `BitmapText.align` is a new property that holds the alignment of the text.
* `BitmapText.setFont` is a new method that allows you to change the font it is rendering with.
* Internally all of the BitmapText properties have been renamed with an underscore (i.e. `letterSpacing` is now `_letterSpacing`), so as to not change the API, getters and setters for them all have been added.
* Internally there is a new `dirty` flag that tracks if any part of the BitmapText has changed. This is used when getting the BitmapText's bounds object, as used in the renderer for line alignment, and in properties like `width` and `height`. The dirty flag ensures the bounds are only recalculated if something has changed, cutting down on un-necessary calculations.
* `GetBitmapTextSize`, which is used internally in the BitmapText Game Objects, will now produce different bounds from the previous version. Previously, the bounds were tight against the letters in the text. However, this meant the bounds were not properly aligned with the origin of the BitmapText, and consequently you'd get different bounds if the text consisted of different characters. The bounds are now calculated purely based on the glyph data and letter spacing values. This will give a far more consistent overall experience, but it does mean if you were using the bounds to position text previously, you'll need to revisit that code again. See issue #3799 for more details (and to discuss this further if you wish) (thanks @SBCGames)
* `GetBitmapTextSize` and its exposed method `BitmapText.getTextBounds` now factor in the display origin of the BitmapText into the `global` position returned.
* The `BitmapText` WebGL Renderer incorrectly calculated the font scale at very small sizes, causing characters to overlap when they shouldn't. Scale is now applied to the correct component parts in the render code.
* Under WebGL `BitmapText` would be cut off if you specified a resolution value > 1. Fix #3642 (thanks @kanthi0802)
* Under WebGL, `DynamicBitmapText` that had a crop set on it would fail to render if anything was above it on the display list. It now crops properly, no matter what is above or below it on the display list.
* The `DynamicBitmapText` class now extends the `BitmapText` class. This saves on lots of space in the bundle and consolidates functionality between the two. Please be aware of it if you have classes that extend either of them.
* If you were using the `displayCallback` in the `DynamicBitmapText` class it would generate a brand new object containing all the glyph data, every frame, for every glyph, and send it to the callback. This has been changed so it now uses a new cached local object: `callbackData`. This object is recycled for every glyph, stopping un-needed gc from building up.
### Dynamic Tilemap Layer New Features, Updates and Bug Fixes
* `DynamicTilemapLayer.tilesDrawn` is a read-only property that contains the number of tiles sent to the renderer in the previous frame.
* `DynamicTilemapLayer.tilesTotal` is a read-only property that contains the total number of tiles in the layer, updated every frame.
* `DynamicTilemapLayer.skipCull` and its associated chainable method `setSkipCull` allows you to control if the cameras should cull the layer tiles before rendering them or not. By default they will cull, to avoid over-rendering, but in some circumstances you may wish to disable this and can now do so by toggling this property.
* The `CullTiles` component, as used by the Dynamic Tilemap, has been recoded from scratch to take advantage of updates in the Camera system. It will now properly cull tiles, irrespective of the layer scale, or camera zoom. It also now supports the layers `skipCull` property, allowing you to override the culling. The Dungeon Generator labs demo now works again as a result of this fix, and has been updated with a debug mode and camera control UI. You can edit the example source to swap between 4 different dungeon layouts, from 2500 tiles up to 1 million tiles. There are limitations to the way the culling works though. If you rotate the camera you may find you see the cull edge. You can disable this using the new `skipCull` property. Fixing this also fixed #3818 (thanks @Mursaat)
* `DynamicTilemapLayer.cullPaddingX`, `cullPaddingY` and the associated chainable method `setCullPadding` allows you to control how many additional tiles are added into the cull rectangle when it is calculated. If you find that your camera size and zoom settings are causing tiles to get prematurely culled, resulting in clipping during scrolling, then set the `cullPadding` values to add extra layers of tiles to the calculations in both directions without needing to disable culling entirely.
* `DynamicTilemapLayer.cullCallback` allows you to change the function that is used to perform the tile culling. By default it will call `TilemapComponents.CullTiles` but you can override this to call any function you like. It is sent 3 arguments: the layer data, the camera and the array to store the tiles in. Using this feature you can now create whatever culling system you require, should the default one prove to not be suitable for your game. Fix #3811 (thanks @georgzoeller)
* Dynamic Tilemap Layers now properly support the Lights2D Pipeline. This means you can provide a normal map for the layer tileset and it'll illuminate with the Lights shader properly. See the new `light map` example in the labs for a demonstration. Note that there are limits on the number of tiles that can be rendered with lighting enabled. Fix #3544 (thanks @FrancescoNegri)
* Adding Spine to physics causes position to become NaN. Fix #4501 (thanks @hizzd)
* Destroying a Phaser Game instance and then re-creating it would cause an error trying to re-create Spine Game Objects ("Cannot read property get of null"). Fix #4532 (thanks @Alex-Badea)
* Rendering a Spine object when a Camera has `renderToTexture` enabled on it would cause the object to be vertically flipped. It now renders correctly in both cases. Fix #4647 (thanks @probt)
### New Features
* `Graphics.fillRoundedRect` will draw a stroked rounded rectangle to a Graphics object. The radius of the corners can be either a number, or an object, allowing you to specify different radius per corner (thanks @TadejZupancic)
* `Graphics.strokeRoundedRect` will draw a filled rounded rectangle to a Graphics object. The radius of the corners can be either a number, or an object, allowing you to specify different radius per corner (thanks @TadejZupancic)
* `ParticleEmitter.stop` is a new chainable method to stop a particle emitter. It's the same as setting `on` to `false` but means you don't have to break the method flow to do so (thanks @samme)
* `ScenePlugin.pause` (and the corresponding methods in Scene Systems and the Scene Manager) now has a new optional `data` argument, which is passed to the target Scene and emitted in its 'pause' event.
* `ScenePlugin.resume` (and the corresponding methods in Scene Systems and the Scene Manager) now has a new optional `data` argument, which is passed to the target Scene and emitted in its 'resume' event.
* `ScenePlugin.sleep` (and the corresponding methods in Scene Systems and the Scene Manager) now has a new optional `data` argument, which is passed to the target Scene and emitted in its 'sleep' event.
* `ScenePlugin.wake` (and the corresponding methods in Scene Systems and the Scene Manager) now has a new optional `data` argument, which is passed to the target Scene and emitted in its 'wake' event.
* `ScenePlugin.setActive` now has a new optional `data` argument, which is passed to the target Scene and emitted in its 'pause' or 'resume' events.
* `TileSprite.tileScaleX` and `tileScaleY` are two new properties that allow you to control the scale of the texture within the Tile Sprite. This impacts the way the repeating texture is scaled, and is independent to scaling the Tile Sprite itself. It works in both Canvas and WebGL mode.
* `TransformMatrix.copyFrom` is a new method that will copy the given matrix into the values of the current one.
* `TransformMatrix.multiplyWithOffset` is a new method that will multiply the given matrix with the current one, factoring in an additional offset to the results. This is used internally by the renderer code in various places.
* `Rectangle.Intersection` will take two Rectangle objects and return the area of intersection between them. If there is no intersection, an empty Rectangle is returned.
* `Pointer.prevPosition` is a new Vector2 that stores the previous position of the Pointer, prior to the most recent DOM event. You can use this when performing calculations between the old and current positions, such as for tracking the pointer speed.
* `Pointer.getInterpolatedPosition` is a new method that will return an array of smoothly interpolated values between the old and previous position of the Pointer. You can configure how many interpolation steps should take place (the default is 10) and provide an output array to store them in. This method is handy if you've got an object tracking a pointer and you want to ensure it has smooth movement (as the DOM will often process pointer events at a faster rate than the game loop can update).
* `TransformMatrix.copyFromArray` will populate a matrix from the given array of values. Where 0, 1, 2, 3, 4 and 5 map to a, b, c, d, e and f.
* `WebGLPipeline` has a new over-rideable method called `boot` which is called when the renderer and all core game systems have finished being set-up.
* `KeyboardPlugin.checkDown` is a new method that allows you to check if a Key is being pressed down or not in an update loop. The difference between this method and checking the `Key.isDown` property directly is that you can provide a duration to this method. For example, if you wanted a key press to fire a bullet, but you only wanted it to be able to fire every 100ms, then you can call this method with a `duration` of 100 and it will only return `true` every 100ms.
* `Shader.setRenderToTexture` is a new method that will redirect the Shader to render to its own framebuffer / WebGLTexture instead of to the display list. This allows you to use the output of the shader as an input for another shader, by mapping a sampler2D uniform to it. It also allows you to save the Shader to the Texture Manager, allowing you to use it as a texture for any other texture based Game Object such as a Sprite.
* `Shader.setSampler2DBuffer` is a new method that allows you to pass a WebGLTexture directly into a Shader as a sampler2D uniform, such as when linking shaders together as buffers for each other.
* `Shader.renderToTexture` is a new property flag that is set if you set the Shader to render to a texture.
* `Shader.framebuffer` is a new property that contains a WebGLFramebuffer reference which is set if you set the Shader to render to a texture.
* `Shader.glTexture` is a new property that contains a WebGLTexture reference which is set if you set the Shader to render to a texture.
* `Shader.texture` is a new property that contains a Phaser Texture reference which is set if you set the Shader to save to the Texture Manager.
* `TextureManager.addGLTexture` is a new method that allows you to add a WebGLTexture directly into the Texture Manager, saved under the given key.
* `TextureSource.isGLTexture` is a new boolean property that reflects if the data backing the underlying Texture Source is a WebGLTexture or not.
* `TextureTintPipeline.batchSprite` will now flip the UV if the TextureSource comes from a GLTexture.
* `Math.ToXY` is a new mini function that will take a given index and return a Vector2 containing the x and y coordinates of that index within a grid.
* `RenderTexture.glTexture` is a new property that holds a reference to the WebGL Texture being used by the Render Texture. Useful for passing to a shader as a sampler2D.
* `GroupCreateConfig.quantity` - when creating a Group using a config object you can now use the optional property `quantity` to set the number of objects to be created. Use this for quickly creating groups of single frame objects that don't need the advanced capabilities of `frameQuantity` and `repeat`.
* `Pointer.locked` is a new read-only property that indicates if the pointer has been Pointer Locked, or not, via the Pointer Lock API.
* `WebGLRenderer.snapshotFramebuffer`, and the corresponding utility function `WebGLSnapshot`, allows you to take a snapshot of a given WebGL framebuffer, such as the one used by a Render Texture or Shader, and either get a single pixel from it as a Color value, or get an area of it as an Image object, which can then optionally be saved to the Texture Manager for use by Game Object textures.
* `CanvasRenderer.snapshotCanvas` allows you to take a snapshot of a given Canvas object, such as the one used by a Render Texture, and either get a single pixel from it as a Color value, or get an area of it as an Image object, which can then optionally be saved to the Texture Manager for use by Game Object textures.
* `RenderTexture.snapshot` is a new method that will take a snapshot of the whole current state of the Render Texture and return it as an Image object, which could then be saved to the Texture Manager if needed.
* `RenderTexture.snapshotArea` is a new method that will take a snapshot of an area of a Render Texture and return it as an Image object, which could then be saved to the Texture Manager if needed.
* `RenderTexture.snapshotPixel` is a new method that will take extract a single pixel color value from a Render Texture and return it as a Color object.
* The `SnapshotState` object has three new properties: `isFramebuffer` boolean and `bufferWidth` and `bufferHeight` integers.
* `Game.CONTEXT_LOST_EVENT` is a new event that is dispatched by the Game instance when the WebGL Renderer webgl context is lost. Use this instead of the old 'lostContextCallbacks' for cleaner context handling.
* `Game.CONTEXT_RESTORED_EVENT` is a new event that is dispatched by the Game instance when the WebGL Renderer webgl context is restored. Use this instead of the old 'restoredContextCallbacks' for cleaner context handling.
* `WebGLRenderer.currentType` contains the type of the Game Object currently being rendered.
* `WebGLRenderer.newType` is a boolean that indicates if the current Game Object has a new type, i.e. different to the previous one in the display list.
* `WebGLRenderer.nextTypeMatch` is a boolean that indicates if the _next_ Game Object in the display list has the same type as the one being currently rendered. This allows you to build batching into separated Game Objects.
* `PluginManager.removeGameObject` is a new method that allows you to de-register custom Game Object types from the global Game Object Factory and/or Creator. Useful for when custom plugins are destroyed and need to clean-up after themselves.
* `GEOM_CONST` is a new constants object that contains the different types of Geometry Objects, such as `RECTANGLE` and `CIRCLE`.
* `Circle.type` is a new property containing the shapes geometry type, which can be used for quick type comparisons.
* `Ellipse.type` is a new property containing the shapes geometry type, which can be used for quick type comparisons.
* `Line.type` is a new property containing the shapes geometry type, which can be used for quick type comparisons.
* `Point.type` is a new property containing the shapes geometry type, which can be used for quick type comparisons.
* `Polygon.type` is a new property containing the shapes geometry type, which can be used for quick type comparisons.
* `Rectangle.type` is a new property containing the shapes geometry type, which can be used for quick type comparisons.
* `Triangle.type` is a new property containing the shapes geometry type, which can be used for quick type comparisons.
* `InputPlugin.enableDebug` is a new method that will create a debug shape for the given Game Objects hit area. This allows you to quickly check the size and placement of an input hit area. You can customzie the shape outline color. The debug shape will automatically track the Game Object to which it is bound.
* `InputPlugion.removeDebug` will remove a Debug Input Shape from the given Game Object and destroy it.
* `Pointer.updateWorldPoint` is a new method that takes a Camera and then updates the Pointers `worldX` and `worldY` values based on the cameras transform (thanks @Nick-lab)
* `ScaleManager._resetZoom` is a new internal flag that is set when the game zoom factor changes.
* `Texture.remove` is a new method that allows you to remove a Frame from a Texture based on its name. Fix #4460 (thanks @BigZaphod)
### Updates
* DataManager.removeValue (and by extension the `remove` method too) will not emit the parent of the DataManager as the 2nd argument in the `removedata` event, to keep it consistent with the set events (thanks @rexrainbow)
* The docs for the Loader `filecomplete` event said that you could listen for a specific file using its type and key, i.e.: `filecomplete-image-monster`, however, the code used an underscore instead of a hyphen. We feel the hyphen looks cleaner, so the Loader code has been updated, meaning you can now use the hyphen version of the event properly (thanks @NokFrt)
* If a Game Object is already being dragged, it cannot be dragged by another pointer (in multi-touch mode) until the original pointer has released it (thanks @rexrainbow)
* Calling `Tween.play` on a tween created via `TweenManager.create` wouldn't actually start playback until the tween was first added to the Tween Manager. Now, calling `play` will have it automatically add itself to the Tween Manager if it's not already in there. Fix #3763 (thanks @pantoninho)
* If the Blitter object has no Bobs to render it will now abort immediately, avoiding several context calls in Canvas mode.
* `Scene.run` will now pass the optional `data` object in all cases, no matter if it's waking, resuming or starting a Scene (thanks @rook2pawn)
* `ScenePlugin.start` and `ScenePlugin.restart` will now always queue the op with the Scene Manager, regardless of the state of the Scene, in order to avoid issues where plugins carry on running for a frame before closing down. Fix #3776 (thanks @jjalonso)
* `Tileset.glTexture` is a new property that maps to the WebGL Texture for the Tileset image. It's used internally by the renderer to avoid expensive object look-ups and is set automatically in the `Tileset.setImage` method.
* `Frame.glTexture` is a new property that maps to the WebGL Texture for the Frames Texture Source image. It's used internally by the renderer to avoid expensive object look-ups and is set automatically in the `Frame` constructor.
* `TransformMatrix.e` and `TransformMatrix.f` are two new properties that are an alias for the `tx` and `ty` values.
* `Graphics.arc` has a new optional argument `overshoot`. This is a small value that is added onto the end of the `endAngle` and allows you to extend the arc further than the default 360 degrees. You may wish to do this if you're trying to draw an arc with an especially thick line stroke, to ensure there are no gaps. Fix #3798 (thanks @jjalonso)
* The TextureManager Sprite Sheet Parser will now throw a concise console warning if you specify invalid frame sizes that would result in no frames being generated (thanks @andygroff)
* The `Quad` Game Object now has a new `setFrame` method that allows you to change the frame being rendered by the Quad, including using frames that are part of a texture atlas. Fix #3161 (thanks @halgorithm)
* The `ScenePlugin` will now queue all of the following ops with the Scene Manager: `start`, `run`, `pause`, `resume`, `sleep`, `wake`, `switch` and `stop`. This means for all of these calls the Scene Manager will add the call into its queue and process it at the start of the next frame. This fixes #3812 and keeps things more predictable (thanks @Waclaw-I)
* `TransformMatrix.multiply` has a new optional argument `out` which is a matrix to store the multiplication results in. If not given it will act as before, multiplying the current matrix.
* `Zones` now have a NOOP `setAlpha` method, which allows them to be added into Containers (thanks @TadejZupancic)
* The `setPipeline` method now returns the instance of the Game Object on which it was called. It used to return the pipeline that was set, but this made it non-chainable which broke with the conventions set in all the other `set` methods. If you use `setPipeline` in your code anywhere to retrieve the pipeline reference, please use the `pipeline` property of the Game Object instead.
* When calling `setHitArea` and not providing a shape (i.e. a texture based hit area), it will now set `customHitArea` to `false` by default (thanks @rexrainbow)
* The Shader will no longer set uniforms if the values are `null`, saving on GL ops.
* The Animation Manager will now emit a console warning if you try and play an animation on a Sprite that doesn't exist.
* The Animation component will no longer start an animation on a Sprite if the animation doesn't exist. Previously it would throw an error saying "Unable to read the property getFirstTick of null".
* `InputManager.onPointerLockChange` is a new method that handles pointer lock change events and dispatches the lock event.
* `CanvasTexture` has been added to the `Textures` namespace so it can be created without needing to import it. The correct way to create a `CanvasTexture` is via the Texture Manager, but you can now do it directly if required. Fix #4651 (thanks @Jugacu)
* The `SmoothedKeyControl` minimum zoom a Camera can go to is now 0.001. Previously it was 0.1. This is to make it match the minimum zoom a Base Camera can go to. Fix #4649 (thanks @giviz)
* `WebGLRenderer.lostContextCallbacks` and the `onContextLost` method have been removed. Please use the new `CONTEXT_LOST` event instead.
* `WebGLRenderer.restoredContextCallbacks` and the `onContextRestored` method have been removed. Please use the new `CONTEXT_RESTORED` event instead.
* `TextureManager.getBase64` will now emit a console warning if you try to get a base64 from a non-image based texture, such as a WebGL Texture.
* The `WebAudioSoundManager` will now remove the document touch handlers even if the Promise fails, preventing it from throwing a rejection handler error.
* `GameObjectFactory.remove` is a new static function that will remove a custom Game Object factory type.
* `GameObjectCreator.remove` is a new static function that will remove a custom Game Object creator type.
* `CanvasTexture.getPixels` now defaults to 0x0 by width x height as the default area, allowing you to call the method with no arguments to get all the pixels for the canvas.
* `CreateDOMContainer` will now use `div.style.cssText` to set the inline styles of the container, so it now works on IE11. Fix #4674 (thanks @DanLiamco)
* `TransformMatrix.rotation` now returns the properly normalized rotation value.
* `PhysicsEditorParser` has now been exposed under the `Phaser.Physics.Matter` namespace, so you can call methods on it directly.
* Calling `CanvasTexture.update` will now automatically call `refresh` if running under WebGL. This happens for both `draw` and `drawFrame`, meaning you no longer need to remember to call `refresh` after drawing to a Canvas Texture in WebGL, keeping it consistent with the Canvas renderer.
* `Frame.destroy` will now null the Frames reference to its parent texture, glTexture and clear the data and customData objects.
* The Container renderer functions will now read the childs `alpha` property, instead of `_alpha`, allowing it to work with more variety of custom children.
### Bug Fixes
* The DataManager `changedata` event was emitting the original value of the data instead of new value (thanks @iamchristopher)
* The LoaderPlugin didn't emit the `filecomplete` event if any of files failed to load, causing it to fail to run the Scene `create` function as well. Fix #3750 (thanks @NokFrt)
* Fix setter calls in BuildGameObjectAnimation so it will now properly set the delay, repeat, repeat delay and yoyo of a config based animation (thanks @DannyT)
* The Arcade Body `blocked.none` property is now set to `false` after separation with static bodies or tiles. Previously, the blocked direction was set correctly, but the `none` remained `true` (thanks @samme)
* `Bob.setFrame` didn't actually set the frame on the Bob, now it does. Fix #3774 (thanks @NokFrt)
* `Bob.alpha` was ignored by the canvas renderer, only working in WebGL. This has now been fixed.
* Although the Blitter object had the Alpha component, setting it made no difference. Setting Blitter alpha now impacts the rendering of all children, in both Canvas and WebGL, and you can also specify an alpha per Bob as well.
* `SceneManager.run` would ignore scenes that are currently in the queue of scenes pending to be added. This has now been fixed so that the scene is queued to be started once it's ready (thanks @rook2pawn)
* `GameObject.disableInteractive` was toggling input. Every second call would turn the input back on (thanks @TadejZupancic)
* The position of the TilemapLayer wasn't taken into account when culling tiles for the Camera. It's now calculated as part of the cull flow (thanks @Upperfoot)
* Fix extra argument passing in Array.Each (thanks @samme)
* TileSprite was using the Size component instead of ComputedSize, meaning its `getBounds` and `displayWidth` and `displayHeight` results were incorrect. Fix #3789 (thanks @jjalonso)
* `ArrayUtils.AddAt` didn't calculate the array offset correctly if you passed an array in to be merged with an existing array. This also caused Container.addAt to fail if an array was passed to it. Fix #3788 (thanks @jjalonso)
* The `Pointer.camera` property would only be set if there was a viable Game Object in the camera view. Now it is set regardless, to always be the Camera the Pointer interacted with.
* Added the Mask component to Container. It worked without it, but this brings it in-line with the documentation and other Game Objects. Fix #3797 (thanks @zilbuz)
* The DataManager couldn't redefine previously removed properties. Fix #3803 (thanks @AleBles @oo7ph)
* The Canvas DrawImage function has been recoded entirely so it now correctly supports parent matrix and camera matrix calculations. This fixes an issue where children inside Containers would lose their rotation, and other issues, when in the Canvas Renderer. Fix #3728 (thanks @samid737)
* `clearMask(true)` would throw an exception if the Game Object didn't have a mask. Now it checks first before destroying the mask. Fix #3809 (thanks @NokFrt)
* In the WebGL `GeometryMask` the stencil has been changed from `INVERT` to `KEEP` in order to fix issues when masking Graphics objects and other complex objects. Fix #3807. This also fixes the issue where children in Containers would display incorrectly outside of a Geometry mask. Fix #3746 (thanks @zilbuz @oklar)
* `BitmapMask.destroy` will now remove the textures and framebuffers that it created from the WebGL Renderer as part of the destroy process. Fix #3771 (thanks @nunof07)
* The Scale Manager would throw the error 'TypeError: this.removeFullscreenTarget is not a function' when entering full-screen mode. It would still enter fullscreen, but the error would appear in the console. Fix #4605 (thanks @darklightcode)
* `Tilemap.renderDebug` was calling out-dated Graphics API methods, which would cause the debug to fail (thanks @Fabadiculous)
* The `Matter.Factory.constraint`, `joint` and `worldConstraint` methods wouldn't allow a zero length constraint to be created due to a falsey check of the length argument. You can now set length to be any value, including zero, or leave it undefined to have it automatically calculated (thanks @olilanz)
* `Pointer.getDuration` would return a negative / static value on desktop, or NaN on mobile, because the base time wasn't being pulled in from the Input Manager properly. Fix #4612 (thanks @BobtheUltimateProgrammer)
* `Pointer.downTime`, `Pointer.upTime` and `Pointer.moveTime` would be set to NaN on mobile browsers where Touch.timeStamp didn't exist. Fix #4612 (thanks @BobtheUltimateProgrammer)
* `WebGLRenderer.setScissor` will default the `drawingBufferHeight` if no argument is provided, stopping NaN scissor heights.
* If you called `Scene.destroy` within a Game Object `pointerdown` or `pointerup` handler, it would cause the error "Cannot read property 'game' of null" if the event wasn't cancelled in your handler. It now checks if the manager is still there before accessing its property. Fix #4436 (thanks @jcyuan)
* The `Arc / Circle` Game Object wasn't rendering centered correctly in WebGL due to an issue in a previous size related commit, it would be half a radius off. Fix #4620 (thanks @CipSoft-Components @rexrainbow)
* Destroying a Scene in HEADLESS mode would throw an error as it tried to access the gl renderer in the Camera class. Fix #4467 (thanks @AndreaBoeAbrahamsen @samme)
* `Tilemap.createFromObjects` would ignore the `scene` argument passed in to the method. It's now used (thanks @samme)
* Fixed a bug in the WebGL and Canvas Renderers where a Sprite with a `flipX` or `flipY` value set would render the offset frames slightly out of place, causing the animation to appear jittery. Also, the sprite would be out of place by its origin. Fix #4636 #3813 (thanks @jronn @B3L7)
* Animations with custom pivots, like those created in Texture Packer with the pivot option enabled, would be mis-aligned if flipped. They now render in the correct position, regardless of scale or flip on either axis. Fix #4155 (thanks @Zax37)
* Removing a frame from a 2 frame animation would cause an error when a Sprite using that animation next tried to render. Fix #4621 (thanks @orlicgms)
* Calling `Animation.setRepeat()` wouldn't reset the `repeatCounter` properly, causing Sprite bound animation instances to fail to change their repeat rate. Fix #4553 (thanks @SavedByZero)
* The `UpdateList.remove` method wouldn't flag the Game Object for removal properly if it was active. It now checks that the Game Object is in the current update list and hasn't already been inserted into the 'pending removal' list before flagging it. Fix #4544 (thanks @jcyuan)
* `DynamicTilemapLayer.destroy` will now no longer run its destroy sequence again if it has already been run once. Fix #4634 (thanks @CipSoft-Components)
* `StaticTilemapLayer.destroy` will now no longer run its destroy sequence again if it has already been run once.
* `Shader.uniforms` now uses Extend instead of Clone to perform a deep object copy, instead of a shallow one, avoiding multiple instances of the same shader sharing uniforms. Fix #4641 (thanks @davidmball)
* Calling `input.mouse.requestPointerLock()` will no longer throw an error about being unable to push to the Input Manager events queue.
* The `POINTERLOCK_CHANGE` event is now dispatched by the Input Manager again.
* The `Pointer.movementX` and `Pointer.movementY` properties are now taken directly from the DOM pointer event values, if the pointer is locked, and no longer incremental. Fix #4611 (thanks @davidmball)
* The `Pointer.velocity` and `Pointer.midPoint` values are now updated every frame. Based on the `motionFactor` setting they are smoothed towards zero, for velocity, and the pointer position for the mid point. This now happens regardless if the Pointer moves or not, which is how it was originally intended to behave.
* The `DESTROY` event hook wasn't removed from Group children when destroying the Group and `destroyChildren` was set to false. Now, the hook is removed regardless (thanks @rexrainbow)
* The WebGL Lost and Restored Context callbacks were never removed, which could cause them to hold onto stale references. Fix #3610 (thanks @Twilrom)
* `Origin.updateDisplayOrigin` no longer applies a Math.floor to the display origins, allowing you to have a 0.x origin for a Game Object that only has a width or height of 1. This fixes issues with things like 1x1 rectangles displaying incorrectly during rendering. Fix #4126 (thanks @rexrainbow)
* `InputManager.resetCursor` will now check if the canvas element still exists before resetting the cursor on it. Fix #4662 (thanks @fromnowhereuser)
* It was not possible to set the zoom value of the Scale Manager back to 1 again, having changed it to a different value. Fix #4633 (thanks @lgibson02 @BinaryMoon)
### Examples, Documentation and TypeScript
My thanks to the following for helping with the Phaser 3 Examples, Docs and TypeScript definitions, either by reporting errors, fixing them or helping author the docs:
@DannyT @squilibob @dvdbrink @t1gu1 @cyantree @DrevanTonder @mikewesthad @tarsupin @shadowofsoul
Also, a special mention to @andygroff for his excellent work enhancing the search box on the examples site, and @hexus for his assistance completing the documentation for the Game Objects.
@vacarsu @KennethGomez @samme @ldd @Jazcash @jcyuan @LearningCode2023 @PhaserEditor2D
Please see the complete [Change Log](https://github.com/photonstorm/phaser/blob/master/CHANGELOG.md) for previous releases.
Looking for a v2 change? Check out the [Phaser CE Change Log](https://github.com/photonstorm/phaser-ce/blob/master/CHANGELOG.md)
![Contributing](https://phaser.io/images/github/div-contributing.png "Contributing")
<a name="contributing"></a>
@ -487,14 +507,14 @@ Phaser is a [Photon Storm](http://www.photonstorm.com) production.
Created by [Richard Davey](mailto:rich@photonstorm.com). Powered by coffee, anime, pixels and love.
The Phaser logo and characters are &copy; 2018 Photon Storm Limited.
The Phaser logo and characters are &copy; 2019 Photon Storm Limited.
All rights reserved.
"Above all, video games are meant to be just one thing: fun. Fun for everyone." - Satoru Iwata
[get-js]: https://github.com/photonstorm/phaser/releases/download/v3.11/phaser.js
[get-minjs]: https://github.com/photonstorm/phaser/releases/download/v3.11/phaser.min.js
[get-js]: https://github.com/photonstorm/phaser/releases/download/v3.19.0/phaser.js
[get-minjs]: https://github.com/photonstorm/phaser/releases/download/v3.19.0/phaser.min.js
[clone-http]: https://github.com/photonstorm/phaser.git
[clone-ssh]: git@github.com:photonstorm/phaser.git
[clone-ghwin]: github-windows://openRepo/https://github.com/photonstorm/phaser
@ -503,4 +523,4 @@ All rights reserved.
[issues]: https://github.com/photonstorm/phaser/issues
[examples]: https://github.com/photonstorm/phaser3-examples
[contribute]: https://github.com/photonstorm/phaser/blob/master/.github/CONTRIBUTING.md
[forum]: http://www.html5gamedevs.com/forum/33-phaser-3/
[forum]: https://phaser.discourse.group/

View file

@ -6,15 +6,14 @@ const exec = require('child_process').exec;
module.exports = {
mode: 'development',
context: `${__dirname}/src/`,
context: `${__dirname}/../src/`,
entry: {
phaser: './phaser.js',
'phaser-core': './phaser-core.js'
phaser: './phaser.js'
},
output: {
path: `${__dirname}/build/`,
path: `${__dirname}/../build/`,
filename: '[name].js',
library: 'Phaser',
libraryTarget: 'umd',
@ -29,7 +28,11 @@ module.exports = {
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(true)
"typeof WEBGL_RENDERER": JSON.stringify(true),
"typeof EXPERIMENTAL": JSON.stringify(true),
"typeof PLUGIN_CAMERA3D": JSON.stringify(false),
"typeof PLUGIN_FBINSTANT": JSON.stringify(false),
"typeof FEATURE_SOUND": JSON.stringify(true)
}),
{
apply: (compiler) => {

View file

@ -4,22 +4,23 @@ const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const basePath = __dirname;
const targetFolder = 'dist';
module.exports = {
mode: 'production',
context: `${__dirname}/src/`,
context: `${__dirname}/../src/`,
entry: {
phaser: './phaser.js',
'phaser.min': './phaser.js',
'phaser-arcade-physics': './phaser-arcade-physics.js',
'phaser-arcade-physics.min': './phaser-arcade-physics.js',
'phaser-core': './phaser-core.js',
'phaser-core.min': './phaser-core.js'
'phaser-arcade-physics.min': './phaser-arcade-physics.js'
},
output: {
path: `${__dirname}/dist/`,
path: `${__dirname}/../dist/`,
filename: '[name].js',
library: 'Phaser',
libraryTarget: 'umd',
@ -49,9 +50,15 @@ module.exports = {
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(true)
"typeof WEBGL_RENDERER": JSON.stringify(true),
"typeof EXPERIMENTAL": JSON.stringify(false),
"typeof PLUGIN_CAMERA3D": JSON.stringify(false),
"typeof PLUGIN_FBINSTANT": JSON.stringify(false),
"typeof FEATURE_SOUND": JSON.stringify(true)
}),
new CleanWebpackPlugin([ 'dist' ])
new CleanWebpackPlugin([targetFolder], {
root: basePath + '/../'
})
]
};

View file

@ -0,0 +1,50 @@
'use strict';
const webpack = require('webpack');
const exec = require('child_process').exec;
module.exports = {
mode: 'development',
context: `${__dirname}/../src/`,
entry: {
phaser: './phaser.js'
},
output: {
path: `${__dirname}/../build/`,
filename: 'phaser-facebook-instant-games.js',
library: 'Phaser',
libraryTarget: 'umd',
sourceMapFilename: '[file].map',
devtoolModuleFilenameTemplate: 'webpack:///[resource-path]', // string
devtoolFallbackModuleFilenameTemplate: 'webpack:///[resource-path]?[hash]', // string
umdNamedDefine: true
},
performance: { hints: false },
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(true),
"typeof EXPERIMENTAL": JSON.stringify(false),
"typeof PLUGIN_CAMERA3D": JSON.stringify(false),
"typeof PLUGIN_FBINSTANT": JSON.stringify(true),
"typeof FEATURE_SOUND": JSON.stringify(true)
}),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
exec('node scripts/copy-to-examples-fb.js', (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
});
});
}
}
],
devtool: 'source-map'
};

View file

@ -0,0 +1,54 @@
'use strict';
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
mode: 'production',
context: `${__dirname}/../src/`,
entry: {
'phaser-facebook-instant-games': './phaser.js',
'phaser-facebook-instant-games.min': './phaser.js'
},
output: {
path: `${__dirname}/../dist/`,
filename: '[name].js',
library: 'Phaser',
libraryTarget: 'umd',
umdNamedDefine: true
},
performance: { hints: false },
optimization: {
minimizer: [
new UglifyJSPlugin({
include: /\.min\.js$/,
parallel: true,
sourceMap: false,
uglifyOptions: {
compress: true,
ie8: false,
ecma: 5,
output: {comments: false},
warnings: false
},
warningsFilter: () => false
})
]
},
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(true),
"typeof EXPERIMENTAL": JSON.stringify(false),
"typeof PLUGIN_CAMERA3D": JSON.stringify(false),
"typeof PLUGIN_FBINSTANT": JSON.stringify(true),
"typeof FEATURE_SOUND": JSON.stringify(true)
})
]
};

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

81861
dist/phaser-core.js vendored

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

193878
dist/phaser-facebook-instant-games.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

314281
dist/phaser.js vendored

File diff suppressed because it is too large Load diff

2
dist/phaser.min.js vendored

File diff suppressed because one or more lines are too long

4424
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,29 +1,52 @@
{
"name": "phaser",
"version": "3.12.0-beta1",
"release": "Silica",
"version": "3.20.0-beta1",
"release": "Fitoria",
"description": "A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.",
"author": "Richard Davey <rich@photonstorm.com> (http://www.photonstorm.com)",
"logo": "https://raw.github.com/photonstorm/phaser/master/phaser-logo-small.png",
"homepage": "http://phaser.io",
"bugs": "https://github.com/photonstorm/phaser/issues",
"license": "MIT",
"licenseUrl": "http://www.opensource.org/licenses/mit-license.php",
"main": "./src/phaser.js",
"types": "./types/phaser.d.ts",
"repository": {
"type": "git",
"url": "https://photonstorm@github.com/photonstorm/phaser.git"
},
"scripts": {
"beta": "npm publish --tag beta",
"help": "node scripts/help.js",
"build": "webpack",
"watch": "webpack --watch",
"dist": "webpack --config webpack.dist.config.js",
"build": "webpack --config config/webpack.config.js",
"watch": "webpack --watch --config config/webpack.config.js",
"buildfb": "webpack --config config/webpack.fb.config.js",
"watchfb": "webpack --config config/webpack.fb.config.js --watch",
"dist": "webpack --config config/webpack.dist.config.js",
"distfb": "webpack --config config/webpack.fb.dist.config.js",
"distfull": "npm run dist && npm run distfb",
"plugin.cam3d": "webpack --config plugins/camera3d/webpack.config.js",
"plugin.spine": "webpack --config plugins/spine/webpack.config.js",
"plugin.spine.dist": "webpack --config plugins/spine/webpack.auto.dist.config.js",
"plugin.spine.watch": "webpack --config plugins/spine/webpack.auto.config.js --watch --display-modules",
"plugin.spine.dev": "webpack --config plugins/spine/webpack.auto.config.js",
"plugin.spine.canvas.dist": "webpack --config plugins/spine/webpack.canvas.dist.config.js",
"plugin.spine.canvas.watch": "webpack --config plugins/spine/webpack.canvas.config.js --watch --display-modules",
"plugin.spine.canvas.dev": "webpack --config plugins/spine/webpack.canvas.config.js",
"plugin.spine.webgl.dist": "webpack --config plugins/spine/webpack.webgl.dist.config.js",
"plugin.spine.webgl.watch": "webpack --config plugins/spine/webpack.webgl.config.js --watch --display-modules",
"plugin.spine.webgl.dev": "webpack --config plugins/spine/webpack.webgl.config.js",
"plugin.spine.full": "npm run plugin.spine.dev && npm run plugin.spine.canvas.dev && npm run plugin.spine.webgl.dev",
"plugin.spine.full.dist": "npm run plugin.spine.dist && npm run plugin.spine.canvas.dist && npm run plugin.spine.webgl.dist",
"lint": "eslint --config .eslintrc.json \"src/**/*.js\"",
"lintfix": "eslint --config .eslintrc.json \"src/**/*.js\" --fix",
"sloc": "node-sloc \"./src\" --include-extensions \"js\"",
"bundleshaders": "node scripts/bundle-shaders.js",
"postinstall": "node scripts/support.js"
"postinstall": "node scripts/support.js",
"build-tsgen": "cd scripts/tsgen && tsc",
"tsgen": "cd scripts/tsgen && jsdoc -c jsdoc-tsd.conf.json",
"test-ts": "cd scripts/tsgen/test && tsc > output.txt",
"ts": "npm run tsgen && npm run test-ts",
"tsdev": "npm run build-tsgen && npm run tsgen && npm run test-ts"
},
"keywords": [
"2d",
@ -41,15 +64,22 @@
"clean-webpack-plugin": "^0.1.19",
"eslint": "^4.19.1",
"eslint-plugin-es5": "^1.3.1",
"fs-extra": "^6.0.0",
"fs-extra": "^6.0.1",
"node-sloc": "^0.1.11",
"uglifyjs-webpack-plugin": "^1.2.7",
"uglifyjs-webpack-plugin": "^1.3.0",
"vivid-cli": "^1.1.2",
"webpack": "^4.16.0",
"webpack-cli": "^2.1.5",
"webpack": "^4.23.0",
"webpack-cli": "^3.1.1",
"webpack-shell-plugin": "^0.5.0"
},
"dependencies": {
"eventemitter3": "^3.1.0"
"dts-dom": "^3.2.0",
"eventemitter3": "^3.1.0",
"exports-loader": "^0.7.0",
"imports-loader": "^0.8.0",
"jsdoc": "^3.6.1",
"path": "^0.12.7",
"remove-files-webpack-plugin": "^1.1.3",
"typescript": "^3.4.5"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

18079
plugins/camera3d/dist/camera3d.js vendored Normal file

File diff suppressed because it is too large Load diff

1
plugins/camera3d/dist/camera3d.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,91 @@
Phaser 3 Camera 3D Plugin
=========================
In Phaser 3.12 Camera 3D support was moved to its own external plugin.
There are two ways to use this in your games:
## 1. External Plugin
You can copy the `dist/camera3d.min.js` file to your project folder and preload it into Phaser:
```
function preload ()
{
this.load.scenePlugin('Camera3DPlugin', 'plugins/camera3d.min.js', 'Camera3DPlugin', 'cameras3d');
}
```
Then you can use it like usual.
## 2. Bundled Plugin
If you prefer you can configure Phaser to include it when it builds its dist files.
To do this you need to edit the webpack config files and change the following:
```
"typeof PLUGIN_CAMERA3D": JSON.stringify(false)
```
to
```
"typeof PLUGIN_CAMERA3D": JSON.stringify(true)
```
Then rebuild Phaser via webpack. The plugin will now be included by default and can be called from your game code.
## Using the Plugin
Here is a basic example of using the plugin. You can find many more in the Phaser 3 Examples repo in the [cameras/3D Camera](https://github.com/photonstorm/phaser3-examples/tree/master/public/src/camera/3D%20camera) folder.
```
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scene: {
preload: preload,
create: create,
update: update
}
};
var camera;
var transform;
var game = new Phaser.Game(config);
function preload ()
{
this.load.scenePlugin('Camera3DPlugin', 'plugins/camera3d.min.js', 'Camera3DPlugin', 'cameras3d');
this.load.image('particle', 'assets/sprites/mushroom2.png');
}
function create ()
{
camera = this.cameras3d.add(85).setZ(300).setPixelScale(128);
var sprites = camera.createRect({ x: 4, y: 4, z: 16 }, { x: 48, y: 48, z: 32 }, 'particle');
// Our rotation matrix
transform = new Phaser.Math.Matrix4().rotateX(-0.01).rotateY(-0.02).rotateZ(0.01);
}
function update ()
{
camera.transformChildren(transform);
}
```
## Building the External Plugin
If you wish to edit the plugin use the following files:
`src/Camera3DPlugin.js` is the entry point for the external plugin. Edit this file if you're loading the plugin at run-time. Once you have finished making your changes, run the command `npm run plugin.cam3d` from the command-line to build a new version of the external plugin with Webpack.
## Changing the Bundled Plugin
`src/index.js` is the entry point for the bundled plugin. In here you'll find the module exports that Phaser uses when including the plugin internally. The file `CameraManager.js` is the Scene System. All other files are shared between both the external and bundled versions of the plugin.

View file

@ -4,16 +4,16 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Class = require('../../utils/Class');
var Matrix4 = require('../../math/Matrix4');
var RandomXYZ = require('../../math/RandomXYZ');
var RandomXYZW = require('../../math/RandomXYZW');
var RotateVec3 = require('../../math/RotateVec3');
var Set = require('../../structs/Set');
var Sprite3D = require('../../gameobjects/sprite3d/Sprite3D');
var Vector2 = require('../../math/Vector2');
var Vector3 = require('../../math/Vector3');
var Vector4 = require('../../math/Vector4');
var Class = require('../../../src/utils/Class');
var Matrix4 = require('../../../src/math/Matrix4');
var RandomXYZ = require('../../../src/math/RandomXYZ');
var RandomXYZW = require('../../../src/math/RandomXYZW');
var RotateVec3 = require('../../../src/math/RotateVec3');
var Set = require('../../../src/structs/Set');
var Sprite3D = require('./sprite3d/Sprite3D');
var Vector2 = require('../../../src/math/Vector2');
var Vector3 = require('../../../src/math/Vector3');
var Vector4 = require('../../../src/math/Vector4');
// Local cache vars
var tmpVec3 = new Vector3();
@ -112,13 +112,12 @@ var Camera = new Class({
*/
this.position = new Vector3();
// The mapping from 3D size units to pixels.
// In the default case 1 3D unit = 128 pixels. So a sprite that is
// 256 x 128 px in size will be 2 x 1 units.
// Change to whatever best fits your game assets.
/**
* [description]
* The mapping from 3D size units to pixels.
* In the default case 1 3D unit = 128 pixels. So a sprite that is
* 256 x 128 px in size will be 2 x 1 units.
* Change to whatever best fits your game assets.
*
* @name Phaser.Cameras.Sprite3D#pixelScale
* @type {number}
@ -300,6 +299,9 @@ var Camera = new Class({
{
this.children.set(sprite3D);
this.displayList.add(sprite3D.gameObject);
this.updateList.add(sprite3D.gameObject);
this.updateChildren();
return sprite3D;

View file

@ -0,0 +1,340 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var BuildGameObject = require('../../../src/gameobjects/BuildGameObject');
var BuildGameObjectAnimation = require('../../../src/gameobjects/BuildGameObjectAnimation');
var Class = require('../../../src/utils/Class');
var GetAdvancedValue = require('../../../src/utils/object/GetAdvancedValue');
var OrthographicCamera = require('./OrthographicCamera');
var PerspectiveCamera = require('./PerspectiveCamera');
var ScenePlugin = require('../../../src/plugins/ScenePlugin');
var Sprite3D = require('./sprite3d/Sprite3D');
/**
* @classdesc
* The Camera 3D Plugin adds a new Camera type to Phaser that allows for movement and rendering
* in 3D space. It displays a special type of Sprite called a Sprite3D that is a billboard sprite,
* with a z-axis allowing for perspective depth.
*
* This is an external plugin which you can include in your game by preloading it:
*
* ```javascript
* this.load.scenePlugin({
* key: 'Camera3DPlugin',
* url: 'plugins/camera3d.min.js',
* sceneKey: 'cameras3d'
* });
* ```
*
* Once loaded you can create a 3D Camera using the `camera3d` property of a Scene:
*
* `var camera = this.cameras3d.add(85).setZ(500).setPixelScale(128);`
*
* See the examples for more information.
*
* @class Camera3DPlugin
* @constructor
*
* @param {Phaser.Scene} scene - The Scene to which this plugin is being installed.
* @param {Phaser.Plugins.PluginManager} pluginManager - A reference to the Phaser Plugin Manager.
*/
var Camera3DPlugin = new Class({
Extends: ScenePlugin,
initialize:
function Camera3DPlugin (scene, pluginManager)
{
ScenePlugin.call(this, scene, pluginManager);
/**
* An Array of the Camera objects being managed by this Camera Manager.
*
* @name Camera3DPlugin#cameras
* @type {Phaser.Cameras.Sprite3D.Camera[]}
* @since 3.0.0
*/
this.cameras = [];
// Register the Sprite3D Game Object
pluginManager.registerGameObject('sprite3D', this.sprite3DFactory, this.sprite3DCreator);
},
/**
* Creates a new Sprite3D Game Object and adds it to the Scene.
*
* @method Phaser.GameObjects.GameObjectFactory#sprite3D
* @since 3.0.0
*
* @param {number} x - The horizontal position of this Game Object.
* @param {number} y - The vertical position of this Game Object.
* @param {number} z - The z position of this Game Object.
* @param {string} texture - The key of the Texture this Game Object will use to render with, as stored in the Texture Manager.
* @param {(string|integer)} [frame] - An optional frame from the Texture this Game Object is rendering with.
*
* @return {Phaser.GameObjects.Sprite3D} The Game Object that was created.
*/
sprite3DFactory: function (x, y, z, key, frame)
{
var sprite = new Sprite3D(this.scene, x, y, z, key, frame);
this.displayList.add(sprite.gameObject);
this.updateList.add(sprite.gameObject);
return sprite;
},
/**
* Creates a new Sprite3D Game Object and returns it.
*
* @method Phaser.GameObjects.GameObjectCreator#sprite3D
* @since 3.0.0
*
* @param {object} config - The configuration object this Game Object will use to create itself.
* @param {boolean} [addToScene] - Add this Game Object to the Scene after creating it? If set this argument overrides the `add` property in the config object.
*
* @return {Phaser.GameObjects.Sprite3D} The Game Object that was created.
*/
sprite3DCreator: function (config, addToScene)
{
if (config === undefined) { config = {}; }
var key = GetAdvancedValue(config, 'key', null);
var frame = GetAdvancedValue(config, 'frame', null);
var sprite = new Sprite3D(this.scene, 0, 0, key, frame);
if (addToScene !== undefined)
{
config.add = addToScene;
}
BuildGameObject(this.scene, sprite, config);
// Sprite specific config options:
BuildGameObjectAnimation(sprite, config);
return sprite;
},
/**
* This method is called automatically, only once, when the Scene is first created.
* Do not invoke it directly.
*
* @method Phaser.Cameras.Scene3D.CameraManager#boot
* @private
* @since 3.5.1
*/
boot: function ()
{
this.systems.events.once('destroy', this.destroy, this);
},
/**
* This method is called automatically by the Scene when it is starting up.
* It is responsible for creating local systems, properties and listening for Scene events.
* Do not invoke it directly.
*
* @method Camera3DPlugin#start
* @private
* @since 3.5.0
*/
start: function ()
{
var eventEmitter = this.systems.events;
eventEmitter.on('update', this.update, this);
eventEmitter.once('shutdown', this.shutdown, this);
},
/**
* [description]
*
* @method Camera3DPlugin#add
* @since 3.0.0
*
* @param {number} [fieldOfView=80] - [description]
* @param {number} [width] - [description]
* @param {number} [height] - [description]
*
* @return {Phaser.Cameras.Sprite3D.PerspectiveCamera} [description]
*/
add: function (fieldOfView, width, height)
{
return this.addPerspectiveCamera(fieldOfView, width, height);
},
/**
* [description]
*
* @method Camera3DPlugin#addOrthographicCamera
* @since 3.0.0
*
* @param {number} width - [description]
* @param {number} height - [description]
*
* @return {Phaser.Cameras.Sprite3D.OrthographicCamera} [description]
*/
addOrthographicCamera: function (width, height)
{
var config = this.scene.sys.game.config;
if (width === undefined) { width = config.width; }
if (height === undefined) { height = config.height; }
var camera = new OrthographicCamera(this.scene, width, height);
this.cameras.push(camera);
return camera;
},
/**
* [description]
*
* @method Camera3DPlugin#addPerspectiveCamera
* @since 3.0.0
*
* @param {number} [fieldOfView=80] - [description]
* @param {number} [width] - [description]
* @param {number} [height] - [description]
*
* @return {Phaser.Cameras.Sprite3D.PerspectiveCamera} [description]
*/
addPerspectiveCamera: function (fieldOfView, width, height)
{
var config = this.scene.sys.game.config;
if (fieldOfView === undefined) { fieldOfView = 80; }
if (width === undefined) { width = config.width; }
if (height === undefined) { height = config.height; }
var camera = new PerspectiveCamera(this.scene, fieldOfView, width, height);
this.cameras.push(camera);
return camera;
},
/**
* [description]
*
* @method Camera3DPlugin#getCamera
* @since 3.0.0
*
* @param {string} name - [description]
*
* @return {(Phaser.Cameras.Sprite3D.OrthographicCamera|Phaser.Cameras.Sprite3D.PerspectiveCamera)} [description]
*/
getCamera: function (name)
{
for (var i = 0; i < this.cameras.length; i++)
{
if (this.cameras[i].name === name)
{
return this.cameras[i];
}
}
return null;
},
/**
* [description]
*
* @method Camera3DPlugin#removeCamera
* @since 3.0.0
*
* @param {(Phaser.Cameras.Sprite3D.OrthographicCamera|Phaser.Cameras.Sprite3D.PerspectiveCamera)} camera - [description]
*/
removeCamera: function (camera)
{
var cameraIndex = this.cameras.indexOf(camera);
if (cameraIndex !== -1)
{
this.cameras.splice(cameraIndex, 1);
}
},
/**
* [description]
*
* @method Camera3DPlugin#removeAll
* @since 3.0.0
*
* @return {(Phaser.Cameras.Sprite3D.OrthographicCamera|Phaser.Cameras.Sprite3D.PerspectiveCamera)} [description]
*/
removeAll: function ()
{
while (this.cameras.length > 0)
{
var camera = this.cameras.pop();
camera.destroy();
}
return this.main;
},
/**
* [description]
*
* @method Camera3DPlugin#update
* @since 3.0.0
*
* @param {number} timestep - [description]
* @param {number} delta - [description]
*/
update: function (timestep, delta)
{
for (var i = 0, l = this.cameras.length; i < l; ++i)
{
this.cameras[i].update(timestep, delta);
}
},
/**
* The Scene that owns this plugin is shutting down.
* We need to kill and reset all internal properties as well as stop listening to Scene events.
*
* @method Camera3DPlugin#shutdown
* @private
* @since 3.0.0
*/
shutdown: function ()
{
var eventEmitter = this.systems.events;
eventEmitter.off('update', this.update, this);
eventEmitter.off('shutdown', this.shutdown, this);
this.removeAll();
},
/**
* The Scene that owns this plugin is being destroyed.
* We need to shutdown and then kill off all external references.
*
* @method Camera3DPlugin#destroy
* @private
* @since 3.0.0
*/
destroy: function ()
{
this.shutdown();
this.pluginManager = null;
this.game = null;
this.scene = null;
this.systems = null;
}
});
module.exports = Camera3DPlugin;

View file

@ -4,10 +4,10 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Class = require('../../utils/Class');
var Class = require('../../../src/utils/Class');
var OrthographicCamera = require('./OrthographicCamera');
var PerspectiveCamera = require('./PerspectiveCamera');
var PluginCache = require('../../plugins/PluginCache');
var PluginCache = require('../../../src/plugins/PluginCache');
/**
* @classdesc
@ -57,7 +57,6 @@ var CameraManager = new Class({
scene.sys.events.on('start', this.start, this);
},
/**
* This method is called automatically, only once, when the Scene is first created.
* Do not invoke it directly.

View file

@ -5,8 +5,8 @@
*/
var Camera = require('./Camera');
var Class = require('../../utils/Class');
var Vector3 = require('../../math/Vector3');
var Class = require('../../../src/utils/Class');
var Vector3 = require('../../../src/math/Vector3');
// Local cache vars
var tmpVec3 = new Vector3();

View file

@ -5,8 +5,8 @@
*/
var Camera = require('./Camera');
var Class = require('../../utils/Class');
var Vector3 = require('../../math/Vector3');
var Class = require('../../../src/utils/Class');
var Vector3 = require('../../../src/math/Vector3');
// Local cache vars
var tmpVec3 = new Vector3();

View file

@ -4,11 +4,11 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Class = require('../../utils/Class');
var GameObject = require('../GameObject');
var Sprite = require('../sprite/Sprite');
var Vector2 = require('../../math/Vector2');
var Vector4 = require('../../math/Vector4');
var Class = require('../../../../src/utils/Class');
var GameObject = require('../../../../src/gameobjects/GameObject');
var Sprite = require('../../../../src/gameobjects/sprite/Sprite');
var Vector2 = require('../../../../src/math/Vector2');
var Vector4 = require('../../../../src/math/Vector4');
/**
* @classdesc

View file

@ -4,10 +4,10 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var BuildGameObject = require('../BuildGameObject');
var BuildGameObjectAnimation = require('../BuildGameObjectAnimation');
var GameObjectCreator = require('../GameObjectCreator');
var GetAdvancedValue = require('../../utils/object/GetAdvancedValue');
var BuildGameObject = require('../../../../src/gameobjects/BuildGameObject');
var BuildGameObjectAnimation = require('../../../../src/gameobjects/BuildGameObjectAnimation');
var GameObjectCreator = require('../../../../src/gameobjects/GameObjectCreator');
var GetAdvancedValue = require('../../../../src/utils/object/GetAdvancedValue');
var Sprite3D = require('./Sprite3D');
/**

View file

@ -5,7 +5,7 @@
*/
var Sprite3D = require('./Sprite3D');
var GameObjectFactory = require('../GameObjectFactory');
var GameObjectFactory = require('../../../../src/gameobjects/GameObjectFactory');
/**
* Creates a new Sprite3D Game Object and adds it to the Scene.

View file

@ -0,0 +1,47 @@
'use strict';
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
context: `${__dirname}/src/`,
entry: {
camera3d: './Camera3DPlugin.js',
'camera3d.min': './Camera3DPlugin.js'
},
output: {
path: `${__dirname}/dist/`,
filename: '[name].js',
library: 'Camera3DPlugin',
libraryTarget: 'var'
},
performance: { hints: false },
optimization: {
minimizer: [
new UglifyJSPlugin({
include: /\.min\.js$/,
parallel: true,
sourceMap: false,
uglifyOptions: {
compress: true,
ie8: false,
ecma: 5,
output: {comments: false},
warnings: false
},
warningsFilter: () => false
})
]
},
plugins: [
new CleanWebpackPlugin([ 'dist' ])
]
};

View file

@ -0,0 +1,84 @@
Phaser 3 Facebook Instant Games Plugin
======================================
Starting with version 3.13 Phaser now has a dedicated plugin that provides complete support for Facebook Instant Games.
If you are using the pre-built versions of Phaser then use the file `phaser-facebook-instant-games.js` which you'll find in the `dist` folder.
If you are creating your own builds using Webpack then make sure you enable the plugin using the Webpack DefinePlugin control:
```
"typeof PLUGIN_FBINSTANT": JSON.stringify(true)
```
Building Phaser via Webpack with this set to `true` will include the plugin in the build.
## Using the Plugin
You need to include the Facebook SDK in your html and wait for the `initializeAsync` Promise to resolve before you should create your game instance:
```
<!DOCTYPE html>
<html>
<head>
<title>Phaser 3 Facebook Instant Games</title>
<meta charset="utf-8">
<script src="https://connect.facebook.net/en_US/fbinstant.6.2.js"></script>
<script src="lib/phaser-facebook-instant-games.js"></script>
</head>
<body>
FBInstant.initializeAsync().then(function() {
var config = {
type: Phaser.AUTO,
width: window.innerWidth,
height: window.innerHeight,
scene: ...
};
new Phaser.Game(config);
});
</body>
</html>
```
Now, when your game starts, you'll know that the FBInstant API is enabled and ready for use. To access the plugin use `this.facebook` from within any Scene, i.e.:
```
this.add.text(0, 0).setText(this.facebook.playerName);
```
### Preloader
To hook your games preloader into the Facebook Instant Games loader you should use a Preloader Scene like below:
```
class Preloader extends Phaser.Scene {
constructor ()
{
super('Preloader');
}
preload ()
{
this.facebook.showLoadProgress(this);
this.facebook.once('startgame', this.startGame, this);
// Now load all of your assets
}
startGame ()
{
this.scene.start('MainMenu');
}
}
```
In the above Scene it has hooked the Facebook preloader with the Phaser Loader, so every file that loads (once you add some) will make the Facebook loader update. When the load is over it will call the `startGame` function that just changes the Scene to another one.
Please look at the plugin documentation and the Facebook SDK documentation for more details about what features are available.

View file

@ -0,0 +1,26 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* @typedef {object} AdInstance
*
* @property {any} instance - Represents an instance of an ad.
* @property {string} placementID - The Audience Network placement ID of this ad instance.
* @property {boolean} shown - Has this ad already been shown in-game?
* @property {boolean} video - Is this a video ad?
*/
var AdInstance = function (placementID, instance, video)
{
return {
instance: instance,
placementID: placementID,
shown: false,
video: video
};
};
module.exports = AdInstance;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,308 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Class = require('../../../src/utils/Class');
var EventEmitter = require('eventemitter3');
var LeaderboardScore = require('./LeaderboardScore');
/**
* @classdesc
* This class represents one single Leaderboard that belongs to a Facebook Instant Game.
*
* You do not need to instantiate this class directly, it will be created when you use the
* `getLeaderboard()` method of the main plugin.
*
* @class FacebookInstantGamesLeaderboard
* @memberOf Phaser
* @constructor
* @extends Phaser.Events.EventEmitter
* @since 3.13.0
*
* @param {Phaser.FacebookInstantGamesPlugin} plugin - A reference to the Facebook Instant Games Plugin.
* @param {any} data - An Instant Game leaderboard instance.
*/
var Leaderboard = new Class({
Extends: EventEmitter,
initialize:
function Leaderboard (plugin, data)
{
EventEmitter.call(this);
/**
* A reference to the Facebook Instant Games Plugin.
*
* @name Phaser.FacebookInstantGamesLeaderboard#plugin
* @type {Phaser.FacebookInstantGamesPlugin}
* @since 3.13.0
*/
this.plugin = plugin;
/**
* An Instant Game leaderboard instance.
*
* @name Phaser.FacebookInstantGamesLeaderboard#ref
* @type {any}
* @since 3.13.0
*/
this.ref = data;
/**
* The name of the leaderboard.
*
* @name Phaser.FacebookInstantGamesLeaderboard#name
* @type {string}
* @since 3.13.0
*/
this.name = data.getName();
/**
* The ID of the context that the leaderboard is associated with, or null if the leaderboard is not tied to a particular context.
*
* @name Phaser.FacebookInstantGamesLeaderboard#contextID
* @type {string}
* @since 3.13.0
*/
this.contextID = data.getContextID();
/**
* The total number of player entries in the leaderboard.
* This value defaults to zero. Populate it via the `getEntryCount()` method.
*
* @name Phaser.FacebookInstantGamesLeaderboard#entryCount
* @type {integer}
* @since 3.13.0
*/
this.entryCount = 0;
/**
* The players score object.
* This value defaults to `null`. Populate it via the `getPlayerScore()` method.
*
* @name Phaser.FacebookInstantGamesLeaderboard#playerScore
* @type {LeaderboardScore}
* @since 3.13.0
*/
this.playerScore = null;
/**
* The scores in the Leaderboard from the currently requested range.
* This value defaults to an empty array. Populate it via the `getScores()` method.
* The contents of this array are reset each time `getScores()` is called.
*
* @name Phaser.FacebookInstantGamesLeaderboard#scores
* @type {LeaderboardScore[]}
* @since 3.13.0
*/
this.scores = [];
this.getEntryCount();
},
/**
* Fetches the total number of player entries in the leaderboard.
*
* The data is requested in an async call, so the result isn't available immediately.
*
* When the call completes this Leaderboard will emit the `getentrycount` event along with the count and name of the Leaderboard.
*
* @method Phaser.FacebookInstantGamesLeaderboard#getEntryCount
* @since 3.13.0
*
* @return {this} This Leaderboard instance.
*/
getEntryCount: function ()
{
var _this = this;
this.ref.getEntryCountAsync().then(function (count)
{
_this.entryCount = count;
_this.emit('getentrycount', count, _this.name);
}).catch(function (e)
{
console.warn(e);
});
return this;
},
/**
* Updates the player's score. If the player has an existing score, the old score will only be replaced if the new score is better than it.
* NOTE: If the leaderboard is associated with a specific context, the game must be in that context to set a score for the player.
*
* The data is requested in an async call, so the result isn't available immediately.
*
* When the call completes this Leaderboard will emit the `setscore` event along with the LeaderboardScore object and the name of the Leaderboard.
*
* If the save fails the event will send `null` as the score value.
*
* @method Phaser.FacebookInstantGamesLeaderboard#setScore
* @since 3.13.0
*
* @param {integer} score - The new score for the player. Must be a 64-bit integer number.
* @param {(string|any)} [data] - Metadata to associate with the stored score. Must be less than 2KB in size. If an object is given it will be passed to `JSON.stringify`.
*
* @return {this} This Leaderboard instance.
*/
setScore: function (score, data)
{
if (data === undefined) { data = ''; }
if (typeof data === 'object')
{
data = JSON.stringify(data);
}
var _this = this;
this.ref.setScoreAsync(score, data).then(function (entry)
{
if (entry)
{
var score = LeaderboardScore(entry);
_this.playerScore = score;
_this.emit('setscore', score, _this.name);
}
else
{
_this.emit('setscore', null, _this.name);
}
}).catch(function (e)
{
console.warn(e);
});
return this;
},
/**
* Gets the players leaderboard entry and stores it in the `playerScore` property.
*
* The data is requested in an async call, so the result isn't available immediately.
*
* When the call completes this Leaderboard will emit the `getplayerscore` event along with the score and the name of the Leaderboard.
*
* If the player has not yet saved a score, the event will send `null` as the score value, and `playerScore` will be set to `null` as well.
*
* @method Phaser.FacebookInstantGamesLeaderboard#getPlayerScore
* @since 3.13.0
*
* @return {this} This Leaderboard instance.
*/
getPlayerScore: function ()
{
var _this = this;
this.ref.getPlayerEntryAsync().then(function (entry)
{
if (entry)
{
var score = LeaderboardScore(entry);
_this.playerScore = score;
_this.emit('getplayerscore', score, _this.name);
}
else
{
_this.emit('getplayerscore', null, _this.name);
}
}).catch(function (e)
{
console.warn(e);
});
return this;
},
/**
* Retrieves a set of leaderboard entries, ordered by score ranking in the leaderboard.
*
* The data is requested in an async call, so the result isn't available immediately.
*
* When the call completes this Leaderboard will emit the `getscores` event along with an array of LeaderboardScore entries and the name of the Leaderboard.
*
* @method Phaser.FacebookInstantGamesLeaderboard#getScores
* @since 3.13.0
*
* @param {integer} [count=10] - The number of entries to attempt to fetch from the leaderboard. Currently, up to a maximum of 100 entries may be fetched per query.
* @param {integer} [offset=0] - The offset from the top of the leaderboard that entries will be fetched from.
*
* @return {this} This Leaderboard instance.
*/
getScores: function (count, offset)
{
if (count === undefined) { count = 10; }
if (offset === undefined) { offset = 0; }
var _this = this;
this.ref.getEntriesAsync(count, offset).then(function (entries)
{
_this.scores = [];
entries.forEach(function (entry)
{
_this.scores.push(LeaderboardScore(entry));
});
_this.emit('getscores', _this.scores, _this.name);
}).catch(function (e)
{
console.warn(e);
});
return this;
},
/**
* Retrieves a set of leaderboard entries, based on the current player's connected players (including the current player), ordered by local rank within the set of connected players.
*
* The data is requested in an async call, so the result isn't available immediately.
*
* When the call completes this Leaderboard will emit the `getconnectedscores` event along with an array of LeaderboardScore entries and the name of the Leaderboard.
*
* @method Phaser.FacebookInstantGamesLeaderboard#getConnectedScores
* @since 3.16.0
*
* @return {this} This Leaderboard instance.
*/
getConnectedScores: function ()
{
var _this = this;
this.ref.getConnectedPlayerEntriesAsync().then(function (entries)
{
_this.scores = [];
entries.forEach(function (entry)
{
_this.scores.push(LeaderboardScore(entry));
});
_this.emit('getconnectedscores', _this.scores, _this.name);
}).catch(function (e)
{
console.warn(e);
});
return this;
}
});
module.exports = Leaderboard;

View file

@ -0,0 +1,34 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* @typedef {object} LeaderboardScore
*
* @property {integer} score - An integer score value.
* @property {string} scoreFormatted - The score value, formatted with the score format associated with the leaderboard.
* @property {integer} timestamp - The Unix timestamp of when the leaderboard entry was last updated.
* @property {integer} rank - The entry's leaderboard ranking.
* @property {string} data - The developer-specified payload associated with the score, or null if one was not set.
* @property {string} playerName - The player's localized display name.
* @property {string} playerPhotoURL - A url to the player's public profile photo.
* @property {string} playerID - The game's unique identifier for the player.
*/
var LeaderboardScore = function (entry)
{
return {
score: entry.getScore(),
scoreFormatted: entry.getFormattedScore(),
timestamp: entry.getTimestamp(),
rank: entry.getRank(),
data: entry.getExtraData(),
playerName: entry.getPlayer().getName(),
playerPhotoURL: entry.getPlayer().getPhoto(),
playerID: entry.getPlayer().getID()
};
};
module.exports = LeaderboardScore;

View file

@ -0,0 +1,32 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var GetFastValue = require('../../../src/utils/object/GetFastValue');
/**
* @typedef {object} Product
*
* @property {string} [title] - The title of the product.
* @property {string} [productID] - The product's game-specified identifier.
* @property {string} [description] - The product description.
* @property {string} [imageURI] - A link to the product's associated image.
* @property {string} [price] - The price of the product.
* @property {string} [priceCurrencyCode] - The currency code for the product.
*/
var Product = function (data)
{
return {
title: GetFastValue(data, 'title', ''),
productID: GetFastValue(data, 'productID', ''),
description: GetFastValue(data, 'description', ''),
imageURI: GetFastValue(data, 'imageURI', ''),
price: GetFastValue(data, 'price', ''),
priceCurrencyCode: GetFastValue(data, 'priceCurrencyCode', '')
};
};
module.exports = Product;

View file

@ -0,0 +1,32 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var GetFastValue = require('../../../src/utils/object/GetFastValue');
/**
* @typedef {object} Purchase
*
* @property {string} [developerPayload] - A developer-specified string, provided during the purchase of the product.
* @property {string} [paymentID] - The identifier for the purchase transaction.
* @property {string} [productID] - The product's game-specified identifier.
* @property {string} [purchaseTime] - Unix timestamp of when the purchase occurred.
* @property {string} [purchaseToken] - A token representing the purchase that may be used to consume the purchase.
* @property {string} [signedRequest] - Server-signed encoding of the purchase request.
*/
var Purchase = function (data)
{
return {
developerPayload: GetFastValue(data, 'developerPayload', ''),
paymentID: GetFastValue(data, 'paymentID', ''),
productID: GetFastValue(data, 'productID', ''),
purchaseTime: GetFastValue(data, 'purchaseTime', ''),
purchaseToken: GetFastValue(data, 'purchaseToken', ''),
signedRequest: GetFastValue(data, 'signedRequest', '')
};
};
module.exports = Purchase;

View file

@ -0,0 +1,20 @@
var fs = require('fs-extra');
var source = './plugins/spine/dist/';
var dest = '../phaser3-examples/public/plugins/';
if (fs.existsSync(dest))
{
fs.copySync(source, dest, { overwrite: true });
}
else
{
console.log('Copy-to-Examples failed: Phaser 3 Examples not present at ../phaser3-examples');
}
dest = '../100-phaser3-snippets/public/libs/';
if (fs.existsSync(dest))
{
fs.copySync(source, dest, { overwrite: true });
}

29737
plugins/spine/dist/SpineCanvasPlugin.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

32230
plugins/spine/dist/SpinePlugin.js vendored Normal file

File diff suppressed because it is too large Load diff

1
plugins/spine/dist/SpinePlugin.min.js vendored Normal file

File diff suppressed because one or more lines are too long

33307
plugins/spine/dist/SpinePluginDebug.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

31829
plugins/spine/dist/SpineWebGLPlugin.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,249 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Class = require('../../../src/utils/Class');
var GetFastValue = require('../../../src/utils/object/GetFastValue');
var ImageFile = require('../../../src/loader/filetypes/ImageFile.js');
var IsPlainObject = require('../../../src/utils/object/IsPlainObject');
var JSONFile = require('../../../src/loader/filetypes/JSONFile.js');
var MultiFile = require('../../../src/loader/MultiFile.js');
var TextFile = require('../../../src/loader/filetypes/TextFile.js');
/**
* @typedef {object} Phaser.Loader.FileTypes.SpineFileConfig
*
* @property {string} key - The key of the file. Must be unique within both the Loader and the Texture Manager.
* @property {string} [textureURL] - The absolute or relative URL to load the texture image file from.
* @property {string} [textureExtension='png'] - The default file extension to use for the image texture if no url is provided.
* @property {XHRSettingsObject} [textureXhrSettings] - Extra XHR Settings specifically for the texture image file.
* @property {string} [normalMap] - The filename of an associated normal map. It uses the same path and url to load as the texture image.
* @property {string} [atlasURL] - The absolute or relative URL to load the atlas data file from.
* @property {string} [atlasExtension='txt'] - The default file extension to use for the atlas data if no url is provided.
* @property {XHRSettingsObject} [atlasXhrSettings] - Extra XHR Settings specifically for the atlas data file.
*/
/**
* @classdesc
* A Spine File suitable for loading by the Loader.
*
* These are created when you use the Phaser.Loader.LoaderPlugin#spine method and are not typically created directly.
*
* For documentation about what all the arguments and configuration options mean please see Phaser.Loader.LoaderPlugin#spine.
*
* @class SpineFile
* @extends Phaser.Loader.MultiFile
* @memberof Phaser.Loader.FileTypes
* @constructor
*
* @param {Phaser.Loader.LoaderPlugin} loader - A reference to the Loader that is responsible for this file.
* @param {(string|Phaser.Loader.FileTypes.SpineFileConfig)} key - The key to use for this file, or a file configuration object.
* @param {string|string[]} [jsonURL] - The absolute or relative URL to load the JSON file from. If undefined or `null` it will be set to `<key>.json`, i.e. if `key` was "alien" then the URL will be "alien.json".
* @param {string} [atlasURL] - The absolute or relative URL to load the texture atlas data file from. If undefined or `null` it will be set to `<key>.txt`, i.e. if `key` was "alien" then the URL will be "alien.txt".
* @param {boolean} [preMultipliedAlpha=false] - Do the textures contain pre-multiplied alpha or not?
* @param {XHRSettingsObject} [jsonXhrSettings] - An XHR Settings configuration object for the json file. Used in replacement of the Loaders default XHR Settings.
* @param {XHRSettingsObject} [atlasXhrSettings] - An XHR Settings configuration object for the atlas data file. Used in replacement of the Loaders default XHR Settings.
*/
var SpineFile = new Class({
Extends: MultiFile,
initialize:
function SpineFile (loader, key, jsonURL, atlasURL, preMultipliedAlpha, jsonXhrSettings, atlasXhrSettings)
{
var i;
var json;
var atlas;
var files = [];
var cache = loader.cacheManager.custom.spine;
// atlas can be an array of atlas files, not just a single one
if (IsPlainObject(key))
{
var config = key;
key = GetFastValue(config, 'key');
json = new JSONFile(loader, {
key: key,
url: GetFastValue(config, 'jsonURL'),
extension: GetFastValue(config, 'jsonExtension', 'json'),
xhrSettings: GetFastValue(config, 'jsonXhrSettings')
});
atlasURL = GetFastValue(config, 'atlasURL');
preMultipliedAlpha = GetFastValue(config, 'preMultipliedAlpha');
if (!Array.isArray(atlasURL))
{
atlasURL = [ atlasURL ];
}
for (i = 0; i < atlasURL.length; i++)
{
atlas = new TextFile(loader, {
key: key,
url: atlasURL[i],
extension: GetFastValue(config, 'atlasExtension', 'atlas'),
xhrSettings: GetFastValue(config, 'atlasXhrSettings')
});
atlas.cache = cache;
files.push(atlas);
}
}
else
{
json = new JSONFile(loader, key, jsonURL, jsonXhrSettings);
if (!Array.isArray(atlasURL))
{
atlasURL = [ atlasURL ];
}
for (i = 0; i < atlasURL.length; i++)
{
atlas = new TextFile(loader, key + '_' + i, atlasURL[i], atlasXhrSettings);
atlas.cache = cache;
files.push(atlas);
}
}
files.unshift(json);
MultiFile.call(this, loader, 'spine', key, files);
this.config.preMultipliedAlpha = preMultipliedAlpha;
},
/**
* Called by each File when it finishes loading.
*
* @method Phaser.Loader.FileTypes.SpineFile#onFileComplete
* @since 3.19.0
*
* @param {Phaser.Loader.File} file - The File that has completed processing.
*/
onFileComplete: function (file)
{
var index = this.files.indexOf(file);
if (index !== -1)
{
this.pending--;
if (file.type === 'text')
{
// Inspect the data for the files to now load
var content = file.data.split('\n');
// Extract the textures
var textures = [];
for (var t = 0; t < content.length; t++)
{
var line = content[t];
if (line.trim() === '' && t < content.length - 1)
{
line = content[t + 1];
textures.push(line);
}
}
var config = this.config;
var loader = this.loader;
var currentBaseURL = loader.baseURL;
var currentPath = loader.path;
var currentPrefix = loader.prefix;
var baseURL = GetFastValue(config, 'baseURL', this.baseURL);
var path = GetFastValue(config, 'path', this.path);
var prefix = GetFastValue(config, 'prefix', this.prefix);
var textureXhrSettings = GetFastValue(config, 'textureXhrSettings');
loader.setBaseURL(baseURL);
loader.setPath(path);
loader.setPrefix(prefix);
for (var i = 0; i < textures.length; i++)
{
var textureURL = textures[i];
var key = 'SP' + this.multiKeyIndex + '_' + textureURL;
var image = new ImageFile(loader, key, textureURL, textureXhrSettings);
this.addToMultiFile(image);
loader.addFile(image);
}
// Reset the loader settings
loader.setBaseURL(currentBaseURL);
loader.setPath(currentPath);
loader.setPrefix(currentPrefix);
}
}
},
/**
* Adds this file to its target cache upon successful loading and processing.
*
* @method Phaser.Loader.FileTypes.SpineFile#addToCache
* @since 3.19.0
*/
addToCache: function ()
{
if (this.isReadyToProcess())
{
var fileJSON = this.files[0];
fileJSON.addToCache();
var atlasCache;
var atlasKey = '';
var combinedAtlasData = '';
var preMultipliedAlpha = (this.config.preMultipliedAlpha) ? true : false;
for (var i = 1; i < this.files.length; i++)
{
var file = this.files[i];
if (file.type === 'text')
{
atlasKey = file.key.substr(0, file.key.length - 2);
atlasCache = file.cache;
combinedAtlasData = combinedAtlasData.concat(file.data);
}
else
{
var src = file.key.trim();
var pos = src.indexOf('_');
var key = src.substr(pos + 1);
this.loader.textureManager.addImage(key, file.data);
}
file.pendingDestroy();
}
atlasCache.add(atlasKey, { preMultipliedAlpha: preMultipliedAlpha, data: combinedAtlasData });
this.complete = true;
}
}
});
module.exports = SpineFile;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* The Complete Event.
*
* @event SpinePluginEvents#COMPLETE
* @since 3.19.0
*/
module.exports = 'complete';

View file

@ -0,0 +1,13 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* The Dispose Event.
*
* @event SpinePluginEvents#DISPOSE
* @since 3.19.0
*/
module.exports = 'dispose';

View file

@ -0,0 +1,13 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* The End Event.
*
* @event SpinePluginEvents#END
* @since 3.19.0
*/
module.exports = 'end';

View file

@ -0,0 +1,13 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* The Custom Event Event.
*
* @event SpinePluginEvents#EVENT
* @since 3.19.0
*/
module.exports = 'event';

View file

@ -0,0 +1,13 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* The Interrupted Event.
*
* @event SpinePluginEvents#INTERRUPTED
* @since 3.19.0
*/
module.exports = 'interrupted';

View file

@ -0,0 +1,13 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* The Start Event.
*
* @event SpinePluginEvents#START
* @since 3.19.0
*/
module.exports = 'start';

View file

@ -0,0 +1,20 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* @namespace SpinePluginEvents
*/
module.exports = {
COMPLETE: require('./COMPLETE_EVENT'),
DISPOSE: require('./DISPOSE_EVENT'),
END: require('./END_EVENT'),
EVENT: require('./EVENT_EVENT'),
INTERRUPTED: require('./INTERRUPTED_EVENT'),
START: require('./START_EVENT')
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,125 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var CounterClockwise = require('../../../../src/math/angle/CounterClockwise');
var RadToDeg = require('../../../../src/math/RadToDeg');
var Wrap = require('../../../../src/math/Wrap');
/**
* Renders this Game Object with the Canvas Renderer to the given Camera.
* The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera.
* This method should not be called directly. It is a utility function of the Render module.
*
* @method SpineGameObject#renderCanvas
* @since 3.19.0
* @private
*
* @param {Phaser.Renderer.Canvas.CanvasRenderer} renderer - A reference to the current active Canvas renderer.
* @param {SpineGameObject} src - The Game Object being rendered in this call.
* @param {number} interpolationPercentage - Reserved for future use and custom pipelines.
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested
*/
var SpineGameObjectCanvasRenderer = function (renderer, src, interpolationPercentage, camera, parentMatrix)
{
var context = renderer.currentContext;
var plugin = src.plugin;
var skeleton = src.skeleton;
var skeletonRenderer = plugin.skeletonRenderer;
var GameObjectRenderMask = 15;
var willRender = !(GameObjectRenderMask !== src.renderFlags || (src.cameraFilter !== 0 && (src.cameraFilter & camera.id)));
if (!skeleton || !willRender)
{
return;
}
var camMatrix = renderer._tempMatrix1;
var spriteMatrix = renderer._tempMatrix2;
var calcMatrix = renderer._tempMatrix3;
spriteMatrix.applyITRS(src.x, src.y, src.rotation, Math.abs(src.scaleX), Math.abs(src.scaleY));
camMatrix.copyFrom(camera.matrix);
if (parentMatrix)
{
// Multiply the camera by the parent matrix
camMatrix.multiplyWithOffset(parentMatrix, -camera.scrollX * src.scrollFactorX, -camera.scrollY * src.scrollFactorY);
// Undo the camera scroll
spriteMatrix.e = src.x;
spriteMatrix.f = src.y;
// Multiply by the Sprite matrix, store result in calcMatrix
camMatrix.multiply(spriteMatrix, calcMatrix);
}
else
{
spriteMatrix.e -= camera.scrollX * src.scrollFactorX;
spriteMatrix.f -= camera.scrollY * src.scrollFactorY;
// Multiply by the Sprite matrix, store result in calcMatrix
camMatrix.multiply(spriteMatrix, calcMatrix);
}
skeleton.x = calcMatrix.tx;
skeleton.y = calcMatrix.ty;
skeleton.scaleX = calcMatrix.scaleX;
// Inverse or we get upside-down skeletons
skeleton.scaleY = calcMatrix.scaleY * -1;
if (src.scaleX < 0)
{
skeleton.scaleX *= -1;
src.root.rotation = RadToDeg(calcMatrix.rotationNormalized);
}
else
{
// +90 degrees to account for the difference in Spine vs. Phaser rotation
src.root.rotation = Wrap(RadToDeg(CounterClockwise(calcMatrix.rotationNormalized)) + 90, 0, 360);
}
if (src.scaleY < 0)
{
skeleton.scaleY *= -1;
if (src.scaleX < 0)
{
src.root.rotation -= (RadToDeg(calcMatrix.rotationNormalized) * 2);
}
else
{
src.root.rotation += (RadToDeg(calcMatrix.rotationNormalized) * 2);
}
}
if (camera.renderToTexture)
{
skeleton.y = calcMatrix.ty;
skeleton.scaleY *= -1;
}
// Add autoUpdate option
skeleton.updateWorldTransform();
skeletonRenderer.ctx = context;
skeletonRenderer.debugRendering = (plugin.drawDebug || src.drawDebug);
context.save();
skeletonRenderer.draw(skeleton);
context.restore();
};
module.exports = SpineGameObjectCanvasRenderer;

View file

@ -0,0 +1,25 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var renderWebGL = require('../../../../src/utils/NOOP');
var renderCanvas = require('../../../../src/utils/NOOP');
if (typeof WEBGL_RENDERER)
{
renderWebGL = require('./SpineGameObjectWebGLRenderer');
}
if (typeof CANVAS_RENDERER)
{
renderCanvas = require('./SpineGameObjectCanvasRenderer');
}
module.exports = {
renderWebGL: renderWebGL,
renderCanvas: renderCanvas
};

View file

@ -0,0 +1,162 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2019 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var CounterClockwise = require('../../../../src/math/angle/CounterClockwise');
var RadToDeg = require('../../../../src/math/RadToDeg');
var Wrap = require('../../../../src/math/Wrap');
/**
* Renders this Game Object with the WebGL Renderer to the given Camera.
* The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera.
* This method should not be called directly. It is a utility function of the Render module.
*
* @method SpineGameObject#renderWebGL
* @since 3.19.0
* @private
*
* @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active WebGL renderer.
* @param {SpineGameObject} src - The Game Object being rendered in this call.
* @param {number} interpolationPercentage - Reserved for future use and custom pipelines.
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested
*/
var SpineGameObjectWebGLRenderer = function (renderer, src, interpolationPercentage, camera, parentMatrix)
{
var plugin = src.plugin;
var skeleton = src.skeleton;
var sceneRenderer = plugin.sceneRenderer;
var GameObjectRenderMask = 15;
var willRender = !(GameObjectRenderMask !== src.renderFlags || (src.cameraFilter !== 0 && (src.cameraFilter & camera.id)));
if (!skeleton || !willRender)
{
// Reset the current type
renderer.currentType = '';
// If there is already a batch running, we need to close it
if (!renderer.nextTypeMatch)
{
// The next object in the display list is not a Spine object, so we end the batch
sceneRenderer.end();
renderer.rebindPipeline(renderer.pipelines.TextureTintPipeline);
}
return;
}
if (renderer.newType)
{
renderer.clearPipeline();
}
var camMatrix = renderer._tempMatrix1;
var spriteMatrix = renderer._tempMatrix2;
var calcMatrix = renderer._tempMatrix3;
spriteMatrix.applyITRS(src.x, src.y, src.rotation, Math.abs(src.scaleX), Math.abs(src.scaleY));
camMatrix.copyFrom(camera.matrix);
if (parentMatrix)
{
// Multiply the camera by the parent matrix
camMatrix.multiplyWithOffset(parentMatrix, -camera.scrollX * src.scrollFactorX, -camera.scrollY * src.scrollFactorY);
// Undo the camera scroll
spriteMatrix.e = src.x;
spriteMatrix.f = src.y;
// Multiply by the Sprite matrix, store result in calcMatrix
camMatrix.multiply(spriteMatrix, calcMatrix);
}
else
{
spriteMatrix.e -= camera.scrollX * src.scrollFactorX;
spriteMatrix.f -= camera.scrollY * src.scrollFactorY;
// Multiply by the Sprite matrix, store result in calcMatrix
camMatrix.multiply(spriteMatrix, calcMatrix);
}
var viewportHeight = renderer.height;
skeleton.x = calcMatrix.tx;
skeleton.y = viewportHeight - calcMatrix.ty;
skeleton.scaleX = calcMatrix.scaleX;
skeleton.scaleY = calcMatrix.scaleY;
if (src.scaleX < 0)
{
skeleton.scaleX *= -1;
src.root.rotation = RadToDeg(calcMatrix.rotationNormalized);
}
else
{
// +90 degrees to account for the difference in Spine vs. Phaser rotation
src.root.rotation = Wrap(RadToDeg(CounterClockwise(calcMatrix.rotationNormalized)) + 90, 0, 360);
}
if (src.scaleY < 0)
{
skeleton.scaleY *= -1;
if (src.scaleX < 0)
{
src.root.rotation -= (RadToDeg(calcMatrix.rotationNormalized) * 2);
}
else
{
src.root.rotation += (RadToDeg(calcMatrix.rotationNormalized) * 2);
}
}
if (camera.renderToTexture)
{
skeleton.y = calcMatrix.ty;
skeleton.scaleY *= -1;
}
// Add autoUpdate option
skeleton.updateWorldTransform();
if (renderer.newType)
{
sceneRenderer.begin();
}
// Draw the current skeleton
sceneRenderer.drawSkeleton(skeleton, src.preMultipliedAlpha);
if (plugin.drawDebug || src.drawDebug)
{
// Because if we don't, the bones render positions are completely wrong (*sigh*)
var oldX = skeleton.x;
var oldY = skeleton.y;
skeleton.x = 0;
skeleton.y = 0;
sceneRenderer.drawSkeletonDebug(skeleton, src.preMultipliedAlpha);
skeleton.x = oldX;
skeleton.y = oldY;
}
if (!renderer.nextTypeMatch)
{
// The next object in the display list is not a Spine object, so we end the batch
sceneRenderer.end();
renderer.rebindPipeline(renderer.pipelines.TextureTintPipeline);
}
};
module.exports = SpineGameObjectWebGLRenderer;

View file

@ -0,0 +1,27 @@
Spine Runtimes Software License v2.5
Copyright (c) 2013-2016, Esoteric Software
All rights reserved.
You are granted a perpetual, non-exclusive, non-sublicensable, and
non-transferable license to use, install, execute, and perform the Spine
Runtimes software and derivative works solely for personal or internal
use. Without the written permission of Esoteric Software (see Section 2 of
the Spine Software License Agreement), you may not (a) modify, translate,
adapt, or develop new applications using the Spine Runtimes or otherwise
create derivative works or improvements of the Spine Runtimes or (b) remove,
delete, alter, or obscure any trademarks or any copyright, trademark, patent,
or other intellectual property or proprietary rights notices on or in the
Software, including any copy thereof. Redistributions in binary or source
form must include this license and terms.
THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,72 @@
'use strict';
const webpack = require('webpack');
const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin');
module.exports = {
mode: 'development',
context: `${__dirname}/src/`,
entry: {
'SpinePluginDebug': './SpinePlugin.js'
},
output: {
path: `${__dirname}/dist/`,
filename: '[name].js',
library: 'SpinePlugin',
libraryTarget: 'window',
sourceMapFilename: '[file].map',
devtoolModuleFilenameTemplate: 'webpack:///[resource-path]', // string
devtoolFallbackModuleFilenameTemplate: 'webpack:///[resource-path]?[hash]', // string
umdNamedDefine: true
},
performance: { hints: false },
module: {
rules: [
{
test: require.resolve('./src/runtimes/spine-both.js'),
use: 'imports-loader?this=>window'
},
{
test: require.resolve('./src/runtimes/spine-both.js'),
use: 'exports-loader?spine'
}
]
},
resolve: {
alias: {
'Spine': './runtimes/spine-both.js'
}
},
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(true)
}),
new RemovePlugin({
before: {
root: './plugins/spine/dist/',
include: [ 'SpinePluginDebug.js', 'SpinePluginDebug.js.map' ]
}
}),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
exec('node plugins/spine/copy-to-examples.js', (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
});
});
}
}
],
devtool: 'source-map'
};

View file

@ -0,0 +1,86 @@
'use strict';
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin');
module.exports = {
mode: 'production',
context: `${__dirname}/src/`,
entry: {
'SpinePlugin': './SpinePlugin.js',
'SpinePlugin.min': './SpinePlugin.js'
},
output: {
path: `${__dirname}/dist/`,
filename: '[name].js',
library: 'SpinePlugin',
libraryTarget: 'window'
},
performance: { hints: false },
module: {
rules: [
{
test: require.resolve('./src/runtimes/spine-both.js'),
use: 'imports-loader?this=>window'
},
{
test: require.resolve('./src/runtimes/spine-both.js'),
use: 'exports-loader?spine'
}
]
},
resolve: {
alias: {
'Spine': './runtimes/spine-both.js'
},
},
optimization: {
minimizer: [
new UglifyJSPlugin({
include: /\.min\.js$/,
parallel: true,
sourceMap: false,
uglifyOptions: {
compress: true,
ie8: false,
ecma: 5,
output: {comments: false},
warnings: false
},
warningsFilter: () => false
})
]
},
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(true)
}),
new RemovePlugin({
before: {
root: './plugins/spine/dist/',
include: [ 'SpinePlugin.js', 'SpinePlugin.min.js' ]
}
}),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
exec('node plugins/spine/copy-to-examples.js', (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
});
});
}
}
]
};

View file

@ -0,0 +1,74 @@
'use strict';
const webpack = require('webpack');
const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin');
module.exports = {
mode: 'development',
context: `${__dirname}/src/`,
entry: {
'SpineCanvasPluginDebug': './SpinePlugin.js'
},
output: {
path: `${__dirname}/dist/`,
filename: '[name].js',
library: 'SpinePlugin',
libraryTarget: 'window',
sourceMapFilename: '[file].map',
devtoolModuleFilenameTemplate: 'webpack:///[resource-path]', // string
devtoolFallbackModuleFilenameTemplate: 'webpack:///[resource-path]?[hash]', // string
umdNamedDefine: true
},
performance: { hints: false },
module: {
rules: [
{
test: require.resolve('./src/runtimes/spine-canvas.js'),
use: 'imports-loader?this=>window'
},
{
test: require.resolve('./src/runtimes/spine-canvas.js'),
use: 'exports-loader?spine'
}
]
},
resolve: {
alias: {
'Spine': './runtimes/spine-canvas.js'
}
},
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(false)
}),
new RemovePlugin({
before: {
before: {
root: './plugins/spine/dist/',
include: [ 'SpineCanvasPluginDebug.js', 'SpineCanvasPluginDebug.js.map' ]
}
}
}),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
exec('node plugins/spine/copy-to-examples.js', (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
});
});
}
}
],
devtool: 'source-map'
};

View file

@ -0,0 +1,88 @@
'use strict';
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin');
module.exports = {
mode: 'production',
context: `${__dirname}/src/`,
entry: {
'SpineCanvasPlugin': './SpinePlugin.js',
'SpineCanvasPlugin.min': './SpinePlugin.js'
},
output: {
path: `${__dirname}/dist/`,
filename: '[name].js',
library: 'SpinePlugin',
libraryTarget: 'window'
},
performance: { hints: false },
module: {
rules: [
{
test: require.resolve('./src/runtimes/spine-canvas.js'),
use: 'imports-loader?this=>window'
},
{
test: require.resolve('./src/runtimes/spine-canvas.js'),
use: 'exports-loader?spine'
}
]
},
resolve: {
alias: {
'Spine': './runtimes/spine-canvas.js'
}
},
optimization: {
minimizer: [
new UglifyJSPlugin({
include: /\.min\.js$/,
parallel: true,
sourceMap: false,
uglifyOptions: {
compress: true,
ie8: false,
ecma: 5,
output: {comments: false},
warnings: false
},
warningsFilter: () => false
})
]
},
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(true),
"typeof WEBGL_RENDERER": JSON.stringify(false)
}),
new RemovePlugin({
before: {
before: {
root: './plugins/spine/dist/',
include: [ 'SpineCanvasPlugin.js', 'SpineCanvasPlugin.min.js' ]
}
}
}),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
exec('node plugins/spine/copy-to-examples.js', (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
});
});
}
}
]
};

View file

@ -0,0 +1,74 @@
'use strict';
const webpack = require('webpack');
const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin');
module.exports = {
mode: 'development',
context: `${__dirname}/src/`,
entry: {
'SpineWebGLPluginDebug': './SpinePlugin.js'
},
output: {
path: `${__dirname}/dist/`,
filename: '[name].js',
library: 'SpinePlugin',
libraryTarget: 'window',
sourceMapFilename: '[file].map',
devtoolModuleFilenameTemplate: 'webpack:///[resource-path]', // string
devtoolFallbackModuleFilenameTemplate: 'webpack:///[resource-path]?[hash]', // string
umdNamedDefine: true
},
performance: { hints: false },
module: {
rules: [
{
test: require.resolve('./src/runtimes/spine-webgl.js'),
use: 'imports-loader?this=>window'
},
{
test: require.resolve('./src/runtimes/spine-webgl.js'),
use: 'exports-loader?spine'
}
]
},
resolve: {
alias: {
'Spine': './runtimes/spine-webgl.js'
},
},
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(false),
"typeof WEBGL_RENDERER": JSON.stringify(true)
}),
new RemovePlugin({
before: {
before: {
root: './plugins/spine/dist/',
include: [ 'SpineWebGLPluginDebug.js', 'SpineWebGLPluginDebug.js.map' ]
}
}
}),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
exec('node plugins/spine/copy-to-examples.js', (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
});
});
}
}
],
devtool: 'source-map'
};

View file

@ -0,0 +1,88 @@
'use strict';
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin');
module.exports = {
mode: 'production',
context: `${__dirname}/src/`,
entry: {
'SpineWebGLPlugin': './SpinePlugin.js',
'SpineWebGLPlugin.min': './SpinePlugin.js'
},
output: {
path: `${__dirname}/dist/`,
filename: '[name].js',
library: 'SpinePlugin',
libraryTarget: 'window'
},
performance: { hints: false },
module: {
rules: [
{
test: require.resolve('./src/runtimes/spine-webgl.js'),
use: 'imports-loader?this=>window'
},
{
test: require.resolve('./src/runtimes/spine-webgl.js'),
use: 'exports-loader?spine'
}
]
},
resolve: {
alias: {
'Spine': './runtimes/spine-webgl.js'
}
},
optimization: {
minimizer: [
new UglifyJSPlugin({
include: /\.min\.js$/,
parallel: true,
sourceMap: false,
uglifyOptions: {
compress: true,
ie8: false,
ecma: 5,
output: {comments: false},
warnings: false
},
warningsFilter: () => false
})
]
},
plugins: [
new webpack.DefinePlugin({
"typeof CANVAS_RENDERER": JSON.stringify(false),
"typeof WEBGL_RENDERER": JSON.stringify(true)
}),
new RemovePlugin({
before: {
before: {
root: './plugins/spine/dist/',
include: [ 'SpineWebGLPlugin.js', 'SpineWebGLPlugin.min.js' ]
}
}
}),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
exec('node plugins/spine/copy-to-examples.js', (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
});
});
}
}
]
};

View file

@ -5,8 +5,6 @@ BitmapMask.frag
BitmapMask.vert
DeferredDiffuse.frag
DeferredDiffuse.vert
FlatTint.frag
FlatTint.vert
ForwardDiffuse.frag
GBuffer.frag
TextureTint.frag

View file

@ -0,0 +1,30 @@
let fs = require('fs-extra');
let source = './build/phaser-facebook-instant-games.js';
let sourceMap = './build/phaser-facebook-instant-games.js.map';
let destFolder = '../fbtest1/lib/';
let dest = '../fbtest1/lib/phaser-facebook-instant-games.js';
let destMap = '../fbtest1/lib/phaser-facebook-instant-games.js.map';
if (fs.existsSync(destFolder))
{
fs.copy(sourceMap, destMap, function (err) {
if (err)
{
return console.error(err);
}
});
fs.copy(source, dest, function (err) {
if (err)
{
return console.error(err);
}
console.log('Build copied to ' + dest);
});
}

View file

@ -6,33 +6,38 @@ let sourceMap = './build/phaser.js.map';
let dest = '../phaser3-examples/public/build/dev.js';
let destMap = '../phaser3-examples/public/build/phaser.js.map';
let sourceCore = './build/phaser-core.js';
let sourceMapCore = './build/phaser-core.js.map';
let destCore = '../phaser3-examples/public/build/phaser-core.js';
let destMapCore = '../phaser3-examples/public/build/phaser-core.js.map';
let sourceFB = './build/phaser-facebook-instant-games.js';
let sourceFBMap = './build/phaser-facebook-instant-games.js.map';
let destFB = '../fbtest1/lib/dev.js';
let destFBMap = '../fbtest1/lib/phaser.js.map';
/*
if (fs.existsSync(destFB))
{
fs.copy(source, destFB, function (err) {
if (err)
{
return console.error(err);
}
console.log('Build copied to ' + destFB);
});
fs.copy(sourceMap, destFBMap, function (err) {
if (err)
{
return console.error(err);
}
});
}
*/
if (fs.existsSync(dest))
{
fs.copy(sourceMapCore, destMapCore, function (err) {
if (err)
{
return console.error(err);
}
});
fs.copy(sourceCore, destCore, function (err) {
if (err)
{
return console.error(err);
}
console.log('Build copied to ' + destCore);
});
fs.copy(sourceMap, destMap, function (err) {
if (err)
@ -64,5 +69,5 @@ if (fs.existsSync(dest))
}
else
{
console.log('Copy-to-Examples failed: Phaser 3 Examples not present at ../phaser3-examples');
// console.log('Copy-to-Examples failed: Phaser 3 Examples not present at ../phaser3-examples');
}

View file

@ -6,14 +6,28 @@ v.log('{bgYellow}{black} | ___/ | \\\\__ \\ / ___// __ \\_ __ \\ _(
v.log('{bgYellow}{black} | | | Y \\/ __ \\_\\___ \\\\ ___/| | \\/ / \\ ');
v.log('{bgYellow}{black} |____| |___| (____ /____ >\\___ >__| /______ / ');
v.log('{bgYellow}{black} \\/ \\/ \\/ \\/ \\/ ');
v.log('{bgYellow}{black} Available commands: ');
v.log('{bgYellow}{black} Available commands: ');
v.log('{white}* npm run {green}build {cyan} Build dev version of Phaser with Webpack');
v.log('{white}* npm run {green}watch {cyan} Build dev & put Webpack into watch mode');
v.log('{white}* npm run {green}dist {cyan} Build dist & min versions of Phaser');
v.log('{white}* npm run {green}lint {cyan} ESLint check Phaser source');
v.log('{white}* npm run {green}lintfix {cyan} ESLint check and fix Phaser source');
v.log('{white}* npm run {green}sloc {cyan} Get source code & comments line count');
v.log('{white}* npm run {green}bundleshaders {cyan} Convert vert/frag shaders to js');
v.log('{white} npm run {green}build {cyan} Build dev version of Phaser with Webpack');
v.log('{white} npm run {green}watch {cyan} Build dev & put Webpack in watch mode');
v.log('{white} npm run {green}dist {cyan} Build dist versions of Phaser');
v.log('{white} npm run {green}lint {cyan} ESLint check Phaser source');
v.log('{white} npm run {green}lintfix {cyan} ESLint check and fix Phaser source');
v.log('{white} npm run {green}sloc {cyan} Get source code & comments line count');
v.log('{white} npm run {green}bundleshaders {cyan} Convert vert/frag shaders to js');
v.log('{white} npm run {green}plugin.cam3d {cyan} Build Camera3D Plugin');
v.log('');
v.log(' {white}{bold}Facebook Instant Games{/bold}');
v.log('');
v.log('{white} npm run {green}buildfb {cyan} Build dev Phaser FB with Webpack');
v.log('{white} npm run {green}watchfb {cyan} Build FB dev in Webpack watch mode');
v.log('{white} npm run {green}distfb {cyan} Build dist versions of Phaser FB');
v.log('{white} npm run {green}distfull {cyan} Build dist versions of Phaser + FB');
v.log('');
v.log(' {white}{bold}TypeScript{/bold}');
v.log('');
v.log('{white} npm run {green}build-tsgen {cyan} Build the TypeScript Defs parser');
v.log('{white} npm run {green}tsgen {cyan} Generate the TypeScript Defs');
v.log('{white} npm run {green}test-ts {cyan} Test the TypeScript Defs');
v.log('{bgYellow}{black} https://phaser.io https://labs.phaser.io ');
v.log('{bgYellow}{black} https://phaser.io https://labs.phaser.io ');

7
scripts/tsgen/README.md Normal file
View file

@ -0,0 +1,7 @@
## TypeScript Defs Generation Tool
The TypeScript defs generation tool is called `tsgen` and is written in TypeScript. Build it by running `npm run build-tsgen`. This will compile the parser locally.
You can then run `npm run tsgen` to build the actual defs. They will replace the file located in the root `types` folder. Once the parser is built you only need use this command. Use it to re-generate the defs if you have modified the Phaser source code and wish to test your change worked.
There is also a test script available: `npm run test-ts` which will compile a test TypeScript project and output any compilation errors to `output.txt`.

503
scripts/tsgen/bin/Parser.js Normal file
View file

@ -0,0 +1,503 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const dom = require("dts-dom");
const regexEndLine = /^(.*)\r\n|\n|\r/gm;
class Parser {
constructor(docs) {
this.topLevel = [];
this.objects = {};
this.namespaces = {};
// parse doclets and create corresponding dom objects
this.parseObjects(docs);
this.resolveObjects(docs);
// removes members inherited from classes
// possibly could be avoided if mixins were defined as such before JSDoc parses them and then we could globally remove all inherited (not
// overriden) members globally from the parsed DB
this.resolveInheritance(docs);
this.resolveParents(docs);
// add integer alias
this.topLevel.push(dom.create.alias('integer', dom.type.number));
// add declare module
const phaserPkgModuleDOM = dom.create.module('phaser');
phaserPkgModuleDOM.members.push(dom.create.exportEquals('Phaser'));
this.topLevel.push(phaserPkgModuleDOM);
}
emit() {
let ignored = [];
let result = this.topLevel.reduce((out, obj) => {
// TODO: remove once stable
// if (<string>obj.kind === 'property') {
// ignored.push((<any>obj).name);
// return out;
// }
//////////////////////////
return out + dom.emit(obj);
}, '');
if (ignored.length > 0) {
console.log('ignored top level properties:');
console.log(ignored);
}
return result;
}
parseObjects(docs) {
for (let i = 0; i < docs.length; i++) {
let doclet = docs[i];
// TODO: Custom temporary rules
switch (doclet.longname) {
case 'Phaser.GameObjects.Components.Alpha':
case 'Phaser.GameObjects.Components.Animation':
case 'Phaser.GameObjects.Components.BlendMode':
case 'Phaser.GameObjects.Components.ComputedSize':
case 'Phaser.GameObjects.Components.Crop':
case 'Phaser.GameObjects.Components.Depth':
case 'Phaser.GameObjects.Components.Flip':
case 'Phaser.GameObjects.Components.GetBounds':
case 'Phaser.GameObjects.Components.Mask':
case 'Phaser.GameObjects.Components.Origin':
case 'Phaser.GameObjects.Components.PathFollower':
case 'Phaser.GameObjects.Components.Pipeline':
case 'Phaser.GameObjects.Components.ScrollFactor':
case 'Phaser.GameObjects.Components.Size':
case 'Phaser.GameObjects.Components.Texture':
case 'Phaser.GameObjects.Components.TextureCrop':
case 'Phaser.GameObjects.Components.Tint':
case 'Phaser.GameObjects.Components.ToJSON':
case 'Phaser.GameObjects.Components.Transform':
case 'Phaser.GameObjects.Components.Visible':
case 'Phaser.Renderer.WebGL.Pipelines.ModelViewProjection':
doclet.kind = 'mixin';
break;
// Because, sod you TypeScript
case 'Phaser.BlendModes':
case 'Phaser.ScaleModes':
case 'Phaser.Physics.Impact.TYPE':
case 'Phaser.Physics.Impact.COLLIDES':
case 'Phaser.Scale.Center':
case 'Phaser.Scale.Orientation':
case 'Phaser.Scale.ScaleModes':
case 'Phaser.Scale.Zoom':
case 'Phaser.Textures.FilterMode':
// console.log('Forcing enum for ' + doclet.longname);
doclet.kind = 'member';
doclet.isEnum = true;
break;
}
if ((doclet.longname.indexOf('Phaser.Physics.Arcade.Components.') == 0 || doclet.longname.indexOf('Phaser.Physics.Impact.Components.') == 0 || doclet.longname.indexOf('Phaser.Physics.Matter.Components.') == 0) && doclet.longname.indexOf('#') == -1) {
doclet.kind = 'mixin';
}
let obj;
let container = this.objects;
switch (doclet.kind) {
case 'namespace':
obj = this.createNamespace(doclet);
container = this.namespaces;
break;
case 'class':
obj = this.createClass(doclet);
break;
case 'mixin':
obj = this.createInterface(doclet);
break;
case 'member':
if (doclet.isEnum === true) {
obj = this.createEnum(doclet);
break;
}
case 'constant':
obj = this.createMember(doclet);
break;
case 'function':
obj = this.createFunction(doclet);
break;
case 'typedef':
obj = this.createTypedef(doclet);
break;
case 'event':
obj = this.createEvent(doclet);
break;
default:
console.log('Ignored doclet kind: ' + doclet.kind);
break;
}
if (obj) {
if (container[doclet.longname]) {
console.log('Warning: ignoring duplicate doc name: ' + doclet.longname);
docs.splice(i--, 1);
continue;
}
container[doclet.longname] = obj;
if (doclet.description) {
let otherDocs = obj.jsDocComment || '';
obj.jsDocComment = doclet.description.replace(regexEndLine, '$1\n') + otherDocs;
}
}
}
}
resolveObjects(docs) {
let allTypes = new Set();
for (let doclet of docs) {
let obj = doclet.kind === 'namespace' ? this.namespaces[doclet.longname] : this.objects[doclet.longname];
if (!obj) {
// TODO
console.log(`Warning: Didn't find object for ${doclet.longname}`);
continue;
}
if (!doclet.memberof) {
this.topLevel.push(obj);
}
else {
let isNamespaceMember = doclet.kind === 'class' || doclet.kind === 'typedef' || doclet.kind == 'namespace' || doclet.isEnum;
let parent = isNamespaceMember ? this.namespaces[doclet.memberof] : (this.objects[doclet.memberof] || this.namespaces[doclet.memberof]);
//TODO: this whole section should be removed once stable
if (!parent) {
console.log(`${doclet.longname} in ${doclet.meta.filename}@${doclet.meta.lineno} has parent '${doclet.memberof}' that is not defined.`);
let parts = doclet.memberof.split('.');
let newParts = [parts.pop()];
while (parts.length > 0 && this.objects[parts.join('.')] == null)
newParts.unshift(parts.pop());
parent = this.objects[parts.join('.')];
if (parent == null) {
parent = dom.create.namespace(doclet.memberof);
this.namespaces[doclet.memberof] = parent;
this.topLevel.push(parent);
}
else {
while (newParts.length > 0) {
let oldParent = parent;
parent = dom.create.namespace(newParts.shift());
parts.push(parent.name);
this.namespaces[parts.join('.')] = parent;
oldParent.members.push(parent);
parent._parent = oldParent;
}
}
}
///////////////////////////////////////////////////////
if (parent.members) {
parent.members.push(obj);
}
else {
console.log('Cannot find members array for:');
console.log(parent);
}
obj._parent = parent;
// class/interface members have methods, not functions
if ((parent.kind === 'class' || parent.kind === 'interface')
&& obj.kind === 'function')
obj.kind = 'method';
// namespace members are vars or consts, not properties
if (parent.kind === 'namespace' && obj.kind === 'property') {
if (doclet.kind == 'constant')
obj.kind = 'const';
else
obj.kind = 'var';
}
}
}
}
resolveInheritance(docs) {
for (let doclet of docs) {
let obj = doclet.kind === 'namespace' ? this.namespaces[doclet.longname] : this.objects[doclet.longname];
if (!obj) {
// TODO
console.log(`Didn't find type ${doclet.longname} ???`);
continue;
}
if (!obj._parent)
continue;
if (doclet.inherited) { // remove inherited members if they aren't from an interface
let from = this.objects[doclet.inherits];
if (!from || !from._parent)
throw `'${doclet.longname}' should inherit from '${doclet.inherits}', which is not defined.`;
if (from._parent.kind != 'interface') {
obj._parent.members.splice(obj._parent.members.indexOf(obj), 1);
obj._parent = null;
}
}
}
}
resolveParents(docs) {
for (let doclet of docs) {
let obj = this.objects[doclet.longname];
if (!obj || doclet.kind !== 'class')
continue;
let o = obj;
// resolve augments
if (doclet.augments && doclet.augments.length) {
for (let augment of doclet.augments) {
let name = this.prepareTypeName(augment);
let wrappingName = name.match(/[^<]+/s)[0]; //gets everything up to a first < (to handle augments with type parameters)
let baseType = this.objects[wrappingName];
if (!baseType) {
console.log(`ERROR: Did not find base type: ${augment} for ${doclet.longname}`);
}
else {
if (baseType.kind == 'class') {
o.baseType = dom.create.class(name);
}
else {
o.implements.push(dom.create.interface(name));
}
}
}
}
}
}
createNamespace(doclet) {
/**
namespace: { comment: '',
meta:
{ filename: 'index.js',
lineno: 10,
columnno: 0,
path: '/Users/rich/Documents/GitHub/phaser/src/tweens',
code: {} },
kind: 'namespace',
name: 'Tweens',
memberof: 'Phaser',
longname: 'Phaser.Tweens',
scope: 'static',
___id: 'T000002R034468',
___s: true }
*/
// console.log('namespace:', doclet.longname);
let obj = dom.create.namespace(doclet.name);
return obj;
}
createClass(doclet) {
let obj = dom.create.class(doclet.name);
let params = null;
if (doclet.params) {
let ctor = dom.create.constructor(null);
this.setParams(doclet, ctor);
params = ctor.parameters;
obj.members.push(ctor);
ctor._parent = obj;
}
this.processGeneric(doclet, obj, params);
if (doclet.classdesc)
doclet.description = doclet.classdesc.replace(regexEndLine, '$1\n'); // make sure docs will be added
return obj;
}
createInterface(doclet) {
return dom.create.interface(doclet.name);
}
createMember(doclet) {
let type = this.parseType(doclet);
let obj = dom.create.property(doclet.name, type);
this.processGeneric(doclet, obj, null);
this.processFlags(doclet, obj);
return obj;
}
createEvent(doclet) {
let type = this.parseType(doclet);
let obj = dom.create.const(doclet.name, type);
this.processFlags(doclet, obj);
return obj;
}
createEnum(doclet) {
let obj = dom.create.enum(doclet.name, false);
this.processFlags(doclet, obj);
return obj;
}
createFunction(doclet) {
let returnType = dom.type.void;
if (doclet.returns) {
returnType = this.parseType(doclet.returns[0]);
}
let obj = dom.create.function(doclet.name, null, returnType);
this.setParams(doclet, obj);
this.processGeneric(doclet, obj, obj.parameters);
this.processFlags(doclet, obj);
return obj;
}
createTypedef(doclet) {
const typeName = doclet.type.names[0];
let type = null;
if (doclet.type.names[0] === 'object') {
let properties = [];
for (let propDoc of doclet.properties) {
let prop = this.createMember(propDoc);
properties.push(prop);
if (propDoc.description)
prop.jsDocComment = propDoc.description.replace(regexEndLine, '$1\n');
}
type = dom.create.objectType(properties);
if (doclet.augments && doclet.augments.length) {
let intersectionTypes = [];
for (let i = 0; i < doclet.augments.length; i++) {
intersectionTypes.push(dom.create.namedTypeReference(doclet.augments[i]));
}
intersectionTypes.push(type);
type = dom.create.intersection(intersectionTypes);
}
}
else {
if (doclet.type.names[0] == "function") {
type = dom.create.functionType(null, dom.type.void);
this.setParams(doclet, type);
}
else {
type = this.parseType(doclet);
}
}
let alias = dom.create.alias(doclet.name, type);
this.processGeneric(doclet, alias, null);
return alias;
}
setParams(doclet, obj) {
let parameters = [];
if (doclet.params) {
let optional = false;
obj.jsDocComment = '';
for (let paramDoc of doclet.params) {
// TODO REMOVE TEMP FIX
if (paramDoc.name.indexOf('.') != -1) {
console.log(`Warning: ignoring param with '.' for '${doclet.longname}' in ${doclet.meta.filename}@${doclet.meta.lineno}`);
let defaultVal = paramDoc.defaultvalue !== undefined ? ` Default ${String(paramDoc.defaultvalue)}.` : '';
if (paramDoc.description)
obj.jsDocComment += `\n@param ${paramDoc.name} ${paramDoc.description.replace(regexEndLine, '$1\n')}` + defaultVal;
else if (defaultVal.length)
obj.jsDocComment += `\n@param ${paramDoc.name} ` + defaultVal;
continue;
}
///////////////////////
let param = dom.create.parameter(paramDoc.name, this.parseType(paramDoc));
parameters.push(param);
if (optional && paramDoc.optional != true) {
console.log(`Warning: correcting to optional: parameter '${paramDoc.name}' for '${doclet.longname}' in ${doclet.meta.filename}@${doclet.meta.lineno}`);
paramDoc.optional = true;
}
this.processFlags(paramDoc, param);
optional = optional || paramDoc.optional === true;
let defaultVal = paramDoc.defaultvalue !== undefined ? ` Default ${String(paramDoc.defaultvalue)}.` : '';
if (paramDoc.description)
obj.jsDocComment += `\n@param ${paramDoc.name} ${paramDoc.description.replace(regexEndLine, '$1\n')}` + defaultVal;
else if (defaultVal.length)
obj.jsDocComment += `\n@param ${paramDoc.name} ` + defaultVal;
}
}
obj.parameters = parameters;
}
parseType(typeDoc) {
if (!typeDoc || !typeDoc.type) {
return dom.type.any;
}
else {
let types = [];
for (let name of typeDoc.type.names) {
name = this.prepareTypeName(name);
let type = dom.create.namedTypeReference(this.processTypeName(name));
types.push(type);
}
if (types.length == 1)
return types[0];
else
return dom.create.union(types);
}
}
prepareTypeName(name) {
if (name.indexOf('*') != -1) {
name = name.split('*').join('any');
}
if (name.indexOf('.<') != -1 && name !== 'Array.<function()>') {
name = name.split('.<').join('<');
}
return name;
}
processTypeName(name) {
if (name === 'float')
return 'number';
if (name === 'function')
return 'Function';
if (name === 'Array.<function()>')
return 'Function[]';
if (name === 'array')
return 'any[]';
if (name.startsWith('Array<')) {
let matches = name.match(/^Array<(.*)>$/);
if (matches && matches[1]) {
return this.processTypeName(matches[1]) + '[]';
}
}
else if (name.startsWith('Object<')) {
let matches = name.match(/^Object<(.*)>$/);
if (matches && matches[1]) {
if (matches[1].indexOf(',') != -1) {
let parts = matches[1].split(',');
return `{[key: ${this.processTypeName(parts[0])}]: ${this.processTypeName(parts[1])}}`;
}
else {
return `{[key: string]: ${this.processTypeName(matches[1])}}`;
}
}
}
return name;
}
processFlags(doclet, obj) {
obj.flags = dom.DeclarationFlags.None;
if (doclet.variable === true) {
obj.flags |= dom.ParameterFlags.Rest;
let type = obj.type;
if (!type.name.endsWith('[]')) {
if (type.name != 'any')
console.log(`Warning: rest parameter should be an array type for ${doclet.longname}`);
type.name = type.name + '[]'; // Must be an array
}
}
else if (doclet.optional === true) { // Rest implies Optional no need to flag it as such
if (obj['kind'] === 'parameter')
obj.flags |= dom.ParameterFlags.Optional;
else
obj.flags |= dom.DeclarationFlags.Optional;
}
switch (doclet.access) {
case 'protected':
obj.flags |= dom.DeclarationFlags.Protected;
break;
case 'private':
obj.flags |= dom.DeclarationFlags.Private;
break;
}
if (doclet.readonly || doclet.kind === 'constant')
obj.flags |= dom.DeclarationFlags.ReadOnly;
if (doclet.scope === 'static')
obj.flags |= dom.DeclarationFlags.Static;
}
processGeneric(doclet, obj, params) {
if (doclet.tags)
for (let tag of doclet.tags) {
if (tag.originalTitle === 'generic') {
let matches = tag.value.match(/(?:(?:{)([^}]+)(?:}))?\s?([^\s]+)(?:\s?-\s?(?:\[)(.+)(?:\]))?/);
let typeParam = dom.create.typeParameter(matches[2], matches[1] == null ? null : dom.create.typeParameter(matches[1]));
obj.typeParameters.push(typeParam);
handleOverrides(matches[3], matches[2]);
}
else if (tag.originalTitle === 'genericUse') {
let matches = tag.value.match(/(?:(?:{)([^}]+)(?:}))(?:\s?-\s?(?:\[)(.+)(?:\]))?/);
let overrideType = this.prepareTypeName(matches[1]);
handleOverrides(matches[2], this.processTypeName(overrideType));
}
}
function handleOverrides(matchedString, overrideType) {
if (matchedString != null) {
let overrides = matchedString.split(',');
if (params != null) {
for (let param of params) {
if (overrides.indexOf(param.name) != -1) {
param.type = dom.create.namedTypeReference(overrideType);
}
}
}
if (overrides.indexOf('$return') != -1) { // has $return, must be a function
obj.returnType = dom.create.namedTypeReference(overrideType);
}
if (overrides.indexOf('$type') != -1) { // has $type, must be a property
obj.type = dom.create.namedTypeReference(overrideType);
}
}
}
}
}
exports.Parser = Parser;
//# sourceMappingURL=Parser.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs-extra");
const path = require("path");
const Parser_1 = require("./Parser");
function publish(data, opts) {
// remove undocumented stuff.
data({ undocumented: true }).remove();
// remove package data
data({ kind: 'package' }).remove();
// remove header comments
data({ copyright: { isString: true } }).remove();
// remove private members
data({ access: 'private' }).remove();
// remove ignored doclets
data({ ignore: true }).remove();
if (!fs.existsSync(opts.destination)) {
fs.mkdirSync(opts.destination);
}
var out = new Parser_1.Parser(data().get()).emit();
fs.writeFileSync(path.join(opts.destination, 'phaser.d.ts'), out);
}
exports.publish = publish;
;
//# sourceMappingURL=publish.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":";;AAAA,+BAA+B;AAC/B,6BAA6B;AAC7B,qCAAkC;AAElC,SAAgB,OAAO,CAAC,IAAS,EAAE,IAAS;IACxC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IACtC,sBAAsB;IACtB,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IACnC,yBAAyB;IACzB,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IACjD,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IACrC,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IAEhC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;QAClC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;KAClC;IAED,IAAI,GAAG,GAAG,IAAI,eAAM,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAE1C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,CAAC;AACtE,CAAC;AAnBD,0BAmBC;AAAA,CAAC"}

View file

@ -0,0 +1,71 @@
var logger = require('jsdoc/util/logger');
exports.handlers = {
/*
From:
"returns": [
{
"type": {
"names": [
"this"
],
"parsedType": {
"type": "NameExpression",
"name": "this",
"reservedWord": true
}
},
"description": "This Game Object instance."
}
],
To:
"returns": [
{
"type": {
"names": [
"Phaser.GameObjects.GameObject"
],
"parsedType": {
"type": "NameExpression",
"name": "Phaser.GameObjects.GameObject"
}
},
"description": "This Game Object instance."
}
],
*/
// The processingComplete event is fired after JSDoc updates the parse results to reflect inherited and borrowed symbols.
processingComplete: function (e)
{
var count = 0;
e.doclets.forEach(function(doclet) {
if (Array.isArray(doclet.returns))
{
var entry = doclet.returns[0];
if (entry.type.names[0] === 'this')
{
count++;
entry.type.names[0] = doclet.memberof;
}
if (entry.type.parsedType && entry.type.parsedType.name === 'this')
{
entry.type.parsedType.name = doclet.memberof;
entry.type.parsedType.reservedWord = false;
}
}
});
// logger.warn('converted ' + count);
}
};

View file

@ -0,0 +1,11 @@
exports.handlers = {
beforeParse: function(e) {
// replace {?[type]} with {?any}
e.source = e.source.replace(/{\?\[type\]}/g, '{any}');
// replace {[type]} with {any}
e.source = e.source.replace(/{\[type\]}/g, '{any}');
}
};

View file

@ -0,0 +1,33 @@
{
"tags": {
"allowUnknownTags": true
},
"source": {
"include": [
"../../src/",
"../../plugins/fbinstant/src"
],
"exclude": [
"../../src/phaser-arcade-physics.js",
"../../src/phaser-core.js",
"../../src/physics/matter-js/poly-decomp/",
"../../src/physics/matter-js/lib",
"../../src/polyfills"
],
"includePattern": ".+\\.js?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"plugins": ["./jsdoc-plugins/typedef"],
"opts": {
"debug": true,
"template": "./bin/",
"encoding": "utf8",
"destination": "../../types/",
"outputSourceFiles": false,
"outputSourcePath": true,
"recurse": true,
"private": false,
"lenient": true,
"sourceType": "script"
}
}

589
scripts/tsgen/src/Parser.ts Normal file
View file

@ -0,0 +1,589 @@
import * as dom from 'dts-dom';
const regexEndLine = /^(.*)\r\n|\n|\r/gm;
export class Parser {
topLevel: dom.TopLevelDeclaration[];
objects: { [key: string]: dom.DeclarationBase };
namespaces: { [key: string]: dom.NamespaceDeclaration };
constructor(docs: any[]) {
this.topLevel = [];
this.objects = {};
this.namespaces = {};
// parse doclets and create corresponding dom objects
this.parseObjects(docs);
this.resolveObjects(docs);
// removes members inherited from classes
// possibly could be avoided if mixins were defined as such before JSDoc parses them and then we could globally remove all inherited (not
// overriden) members globally from the parsed DB
this.resolveInheritance(docs);
this.resolveParents(docs);
// add integer alias
this.topLevel.push(dom.create.alias('integer', dom.type.number));
// add declare module
const phaserPkgModuleDOM = dom.create.module('phaser');
phaserPkgModuleDOM.members.push(dom.create.exportEquals('Phaser'));
this.topLevel.push(phaserPkgModuleDOM);
}
emit() {
let ignored = [];
let result = this.topLevel.reduce((out: string, obj: dom.TopLevelDeclaration) => {
// TODO: remove once stable
// if (<string>obj.kind === 'property') {
// ignored.push((<any>obj).name);
// return out;
// }
//////////////////////////
return out + dom.emit(obj);
}, '');
if (ignored.length > 0)
{
console.log('ignored top level properties:');
console.log(ignored);
}
return result;
}
private parseObjects(docs: any[]) {
for (let i = 0; i < docs.length; i++) {
let doclet = docs[i];
// TODO: Custom temporary rules
switch (doclet.longname)
{
case 'Phaser.GameObjects.Components.Alpha':
case 'Phaser.GameObjects.Components.Animation':
case 'Phaser.GameObjects.Components.BlendMode':
case 'Phaser.GameObjects.Components.ComputedSize':
case 'Phaser.GameObjects.Components.Crop':
case 'Phaser.GameObjects.Components.Depth':
case 'Phaser.GameObjects.Components.Flip':
case 'Phaser.GameObjects.Components.GetBounds':
case 'Phaser.GameObjects.Components.Mask':
case 'Phaser.GameObjects.Components.Origin':
case 'Phaser.GameObjects.Components.PathFollower':
case 'Phaser.GameObjects.Components.Pipeline':
case 'Phaser.GameObjects.Components.ScrollFactor':
case 'Phaser.GameObjects.Components.Size':
case 'Phaser.GameObjects.Components.Texture':
case 'Phaser.GameObjects.Components.TextureCrop':
case 'Phaser.GameObjects.Components.Tint':
case 'Phaser.GameObjects.Components.ToJSON':
case 'Phaser.GameObjects.Components.Transform':
case 'Phaser.GameObjects.Components.Visible':
case 'Phaser.Renderer.WebGL.Pipelines.ModelViewProjection':
doclet.kind = 'mixin';
break;
// Because, sod you TypeScript
case 'Phaser.BlendModes':
case 'Phaser.ScaleModes':
case 'Phaser.Physics.Impact.TYPE':
case 'Phaser.Physics.Impact.COLLIDES':
case 'Phaser.Scale.Center':
case 'Phaser.Scale.Orientation':
case 'Phaser.Scale.ScaleModes':
case 'Phaser.Scale.Zoom':
case 'Phaser.Textures.FilterMode':
// console.log('Forcing enum for ' + doclet.longname);
doclet.kind = 'member';
doclet.isEnum = true;
break;
}
if ((doclet.longname.indexOf('Phaser.Physics.Arcade.Components.') == 0 || doclet.longname.indexOf('Phaser.Physics.Impact.Components.') == 0 || doclet.longname.indexOf('Phaser.Physics.Matter.Components.') == 0) && doclet.longname.indexOf('#') == -1)
{
doclet.kind = 'mixin';
}
let obj: dom.DeclarationBase;
let container = this.objects;
switch (doclet.kind) {
case 'namespace':
obj = this.createNamespace(doclet);
container = this.namespaces;
break;
case 'class':
obj = this.createClass(doclet);
break;
case 'mixin':
obj = this.createInterface(doclet);
break;
case 'member':
if (doclet.isEnum === true) {
obj = this.createEnum(doclet);
break;
}
case 'constant':
obj = this.createMember(doclet);
break;
case 'function':
obj = this.createFunction(doclet);
break;
case 'typedef':
obj = this.createTypedef(doclet);
break;
case 'event':
obj = this.createEvent(doclet);
break;
default:
console.log('Ignored doclet kind: ' + doclet.kind);
break;
}
if (obj) {
if (container[doclet.longname]) {
console.log('Warning: ignoring duplicate doc name: ' + doclet.longname);
docs.splice(i--, 1);
continue;
}
container[doclet.longname] = obj;
if (doclet.description) {
let otherDocs = obj.jsDocComment || '';
obj.jsDocComment = doclet.description.replace(regexEndLine, '$1\n') + otherDocs;
}
}
}
}
private resolveObjects(docs: any[]) {
let allTypes = new Set<string>();
for (let doclet of docs) {
let obj = doclet.kind === 'namespace' ? this.namespaces[doclet.longname] : this.objects[doclet.longname];
if (!obj) {
// TODO
console.log(`Warning: Didn't find object for ${doclet.longname}`);
continue;
}
if (!doclet.memberof) {
this.topLevel.push(obj as dom.TopLevelDeclaration);
} else {
let isNamespaceMember = doclet.kind === 'class' || doclet.kind === 'typedef' || doclet.kind == 'namespace' || doclet.isEnum;
let parent = isNamespaceMember ? this.namespaces[doclet.memberof] : (this.objects[doclet.memberof] || this.namespaces[doclet.memberof]);
//TODO: this whole section should be removed once stable
if (!parent) {
console.log(`${doclet.longname} in ${doclet.meta.filename}@${doclet.meta.lineno} has parent '${doclet.memberof}' that is not defined.`);
let parts: string[] = doclet.memberof.split('.');
let newParts = [parts.pop()];
while (parts.length > 0 && this.objects[parts.join('.')] == null) newParts.unshift(parts.pop());
parent = this.objects[parts.join('.')] as dom.NamespaceDeclaration;
if (parent == null) {
parent = dom.create.namespace(doclet.memberof);
this.namespaces[doclet.memberof] = <dom.NamespaceDeclaration>parent;
this.topLevel.push(<dom.NamespaceDeclaration>parent);
} else {
while (newParts.length > 0) {
let oldParent = <dom.NamespaceDeclaration>parent;
parent = dom.create.namespace(newParts.shift());
parts.push((<dom.NamespaceDeclaration>parent).name);
this.namespaces[parts.join('.')] = <dom.NamespaceDeclaration>parent;
oldParent.members.push(<dom.NamespaceDeclaration>parent);
(<any>parent)._parent = oldParent;
}
}
}
///////////////////////////////////////////////////////
if ((<any>parent).members) {
(<any>parent).members.push(obj);
} else {
console.log('Cannot find members array for:');
console.log(parent);
}
(<any>obj)._parent = parent;
// class/interface members have methods, not functions
if (((parent as any).kind === 'class' || (parent as any).kind === 'interface')
&& (obj as any).kind === 'function')
(obj as any).kind = 'method';
// namespace members are vars or consts, not properties
if ((parent as any).kind === 'namespace' && (obj as any).kind === 'property') {
if (doclet.kind == 'constant') (obj as any).kind = 'const';
else (obj as any).kind = 'var';
}
}
}
}
private resolveInheritance(docs: any[]) {
for (let doclet of docs) {
let obj = doclet.kind === 'namespace' ? this.namespaces[doclet.longname] : this.objects[doclet.longname];
if (!obj) {
// TODO
console.log(`Didn't find type ${doclet.longname} ???`);
continue;
}
if (!(<any>obj)._parent) continue;
if (doclet.inherited) {// remove inherited members if they aren't from an interface
let from = this.objects[doclet.inherits];
if (!from || !(<any>from)._parent)
throw `'${doclet.longname}' should inherit from '${doclet.inherits}', which is not defined.`;
if ((<any>from)._parent.kind != 'interface') {
(<any>obj)._parent.members.splice((<any>obj)._parent.members.indexOf(obj), 1);
(<any>obj)._parent = null;
}
}
}
}
private resolveParents(docs: any[]) {
for (let doclet of docs) {
let obj = this.objects[doclet.longname];
if (!obj || doclet.kind !== 'class') continue;
let o = obj as dom.ClassDeclaration;
// resolve augments
if (doclet.augments && doclet.augments.length) {
for (let augment of doclet.augments) {
let name: string = this.prepareTypeName(augment);
let wrappingName = name.match(/[^<]+/s)[0];//gets everything up to a first < (to handle augments with type parameters)
let baseType = this.objects[wrappingName] as dom.ClassDeclaration | dom.InterfaceDeclaration;
if (!baseType) {
console.log(`ERROR: Did not find base type: ${augment} for ${doclet.longname}`);
} else {
if (baseType.kind == 'class') {
o.baseType = dom.create.class(name);
} else {
o.implements.push(dom.create.interface(name));
}
}
}
}
}
}
private createNamespace(doclet: any): dom.NamespaceDeclaration {
/**
namespace: { comment: '',
meta:
{ filename: 'index.js',
lineno: 10,
columnno: 0,
path: '/Users/rich/Documents/GitHub/phaser/src/tweens',
code: {} },
kind: 'namespace',
name: 'Tweens',
memberof: 'Phaser',
longname: 'Phaser.Tweens',
scope: 'static',
___id: 'T000002R034468',
___s: true }
*/
// console.log('namespace:', doclet.longname);
let obj = dom.create.namespace(doclet.name);
return obj;
}
private createClass(doclet: any): dom.ClassDeclaration {
let obj = dom.create.class(doclet.name);
let params = null;
if (doclet.params) {
let ctor = dom.create.constructor(null);
this.setParams(doclet, ctor);
params = ctor.parameters;
obj.members.push(ctor);
(<any>ctor)._parent = obj;
}
this.processGeneric(doclet, obj, params);
if (doclet.classdesc)
doclet.description = doclet.classdesc.replace(regexEndLine, '$1\n'); // make sure docs will be added
return obj;
}
private createInterface(doclet: any): dom.InterfaceDeclaration {
return dom.create.interface(doclet.name);
}
private createMember(doclet: any): dom.PropertyDeclaration {
let type = this.parseType(doclet);
let obj = dom.create.property(doclet.name, type);
this.processGeneric(doclet, obj, null);
this.processFlags(doclet, obj);
return obj;
}
private createEvent(doclet: any): dom.ConstDeclaration {
let type = this.parseType(doclet);
let obj = dom.create.const(doclet.name, type);
this.processFlags(doclet, obj);
return obj;
}
private createEnum(doclet: any): dom.EnumDeclaration {
let obj = dom.create.enum(doclet.name, false);
this.processFlags(doclet, obj);
return obj;
}
private createFunction(doclet: any): dom.FunctionDeclaration {
let returnType: dom.Type = dom.type.void;
if (doclet.returns) {
returnType = this.parseType(doclet.returns[0]);
}
let obj = dom.create.function(doclet.name, null, returnType);
this.setParams(doclet, obj);
this.processGeneric(doclet, obj, obj.parameters);
this.processFlags(doclet, obj);
return obj;
}
private createTypedef(doclet: any): dom.TypeAliasDeclaration {
const typeName = doclet.type.names[0];
let type = null;
if (doclet.type.names[0] === 'object') {
let properties = [];
for (let propDoc of doclet.properties) {
let prop = this.createMember(propDoc);
properties.push(prop);
if (propDoc.description)
prop.jsDocComment = propDoc.description.replace(regexEndLine, '$1\n');
}
type = dom.create.objectType(properties);
if (doclet.augments && doclet.augments.length) {
let intersectionTypes = [];
for (let i = 0; i < doclet.augments.length; i++) {
intersectionTypes.push(dom.create.namedTypeReference(doclet.augments[i]));
}
intersectionTypes.push(type);
type = dom.create.intersection(intersectionTypes);
}
} else {
if (doclet.type.names[0] == "function") {
type = dom.create.functionType(null, dom.type.void);
this.setParams(doclet, type);
} else {
type = this.parseType(doclet);
}
}
let alias = dom.create.alias(doclet.name, type);
this.processGeneric(doclet, alias, null);
return alias;
}
private setParams(doclet: any, obj: dom.FunctionDeclaration | dom.ConstructorDeclaration): void {
let parameters: dom.Parameter[] = [];
if (doclet.params) {
let optional = false;
obj.jsDocComment = '';
for (let paramDoc of doclet.params) {
// TODO REMOVE TEMP FIX
if (paramDoc.name.indexOf('.') != -1) {
console.log(`Warning: ignoring param with '.' for '${doclet.longname}' in ${doclet.meta.filename}@${doclet.meta.lineno}`);
let defaultVal = paramDoc.defaultvalue !== undefined ? ` Default ${String(paramDoc.defaultvalue)}.` : '';
if (paramDoc.description)
obj.jsDocComment += `\n@param ${paramDoc.name} ${paramDoc.description.replace(regexEndLine, '$1\n')}` + defaultVal;
else if (defaultVal.length)
obj.jsDocComment += `\n@param ${paramDoc.name} ` + defaultVal;
continue;
}
///////////////////////
let param = dom.create.parameter(paramDoc.name, this.parseType(paramDoc));
parameters.push(param);
if (optional && paramDoc.optional != true) {
console.log(`Warning: correcting to optional: parameter '${paramDoc.name}' for '${doclet.longname}' in ${doclet.meta.filename}@${doclet.meta.lineno}`);
paramDoc.optional = true;
}
this.processFlags(paramDoc, param);
optional = optional || paramDoc.optional === true;
let defaultVal = paramDoc.defaultvalue !== undefined ? ` Default ${String(paramDoc.defaultvalue)}.` : '';
if (paramDoc.description)
obj.jsDocComment += `\n@param ${paramDoc.name} ${paramDoc.description.replace(regexEndLine, '$1\n')}` + defaultVal;
else if (defaultVal.length)
obj.jsDocComment += `\n@param ${paramDoc.name} ` + defaultVal;
}
}
obj.parameters = parameters;
}
private parseType(typeDoc: any): dom.Type {
if (!typeDoc || !typeDoc.type) {
return dom.type.any;
} else {
let types = [];
for (let name of typeDoc.type.names) {
name = this.prepareTypeName(name);
let type = dom.create.namedTypeReference(this.processTypeName(name));
types.push(type);
}
if (types.length == 1) return types[0];
else return dom.create.union(types);
}
}
private prepareTypeName(name: string): string {
if (name.indexOf('*') != -1) {
name = (<string>name).split('*').join('any');
}
if (name.indexOf('.<') != -1 && name !== 'Array.<function()>') {
name = (<string>name).split('.<').join('<');
}
return name;
}
private processTypeName(name: string): string {
if (name === 'float') return 'number';
if (name === 'function') return 'Function';
if (name === 'Array.<function()>') return 'Function[]';
if (name === 'array') return 'any[]';
if (name.startsWith('Array<')) {
let matches = name.match(/^Array<(.*)>$/);
if (matches && matches[1]) {
return this.processTypeName(matches[1]) + '[]';
}
} else if (name.startsWith('Object<')) {
let matches = name.match(/^Object<(.*)>$/);
if (matches && matches[1]) {
if (matches[1].indexOf(',') != -1) {
let parts = matches[1].split(',');
return `{[key: ${this.processTypeName(parts[0])}]: ${this.processTypeName(parts[1])}}`;
} else {
return `{[key: string]: ${this.processTypeName(matches[1])}}`;
}
}
}
return name;
}
private processFlags(doclet: any, obj: dom.DeclarationBase | dom.Parameter) {
obj.flags = dom.DeclarationFlags.None;
if (doclet.variable === true) {
obj.flags |= dom.ParameterFlags.Rest;
let type: any = (<dom.Parameter>obj).type;
if (!type.name.endsWith('[]')) {
if (type.name != 'any')
console.log(`Warning: rest parameter should be an array type for ${doclet.longname}`);
type.name = type.name + '[]'; // Must be an array
}
} else if (doclet.optional === true) {// Rest implies Optional no need to flag it as such
if (obj['kind'] === 'parameter') obj.flags |= dom.ParameterFlags.Optional;
else obj.flags |= dom.DeclarationFlags.Optional;
}
switch (doclet.access) {
case 'protected':
obj.flags |= dom.DeclarationFlags.Protected;
break;
case 'private':
obj.flags |= dom.DeclarationFlags.Private;
break;
}
if (doclet.readonly || doclet.kind === 'constant') obj.flags |= dom.DeclarationFlags.ReadOnly;
if (doclet.scope === 'static') obj.flags |= dom.DeclarationFlags.Static;
}
private processGeneric(doclet: any, obj: dom.ClassDeclaration | dom.FunctionDeclaration | dom.PropertyDeclaration | dom.TypeAliasDeclaration, params: dom.Parameter[]) {
if (doclet.tags)
for (let tag of doclet.tags) {
if (tag.originalTitle === 'generic') {
let matches = (<string>tag.value).match(/(?:(?:{)([^}]+)(?:}))?\s?([^\s]+)(?:\s?-\s?(?:\[)(.+)(?:\]))?/);
let typeParam = dom.create.typeParameter(matches[2], matches[1] == null ? null : dom.create.typeParameter(matches[1]));
(<dom.ClassDeclaration | dom.FunctionDeclaration | dom.TypeAliasDeclaration>obj).typeParameters.push(typeParam);
handleOverrides(matches[3], matches[2]);
} else if (tag.originalTitle === 'genericUse') {
let matches = (<string>tag.value).match(/(?:(?:{)([^}]+)(?:}))(?:\s?-\s?(?:\[)(.+)(?:\]))?/);
let overrideType: string = this.prepareTypeName(matches[1]);
handleOverrides(matches[2], this.processTypeName(overrideType));
}
}
function handleOverrides(matchedString: string, overrideType: string) {
if (matchedString != null) {
let overrides = matchedString.split(',');
if (params != null) {
for (let param of params) {
if (overrides.indexOf(param.name) != -1) {
param.type = dom.create.namedTypeReference(overrideType);
}
}
}
if (overrides.indexOf('$return') != -1) {// has $return, must be a function
(<dom.FunctionDeclaration>obj).returnType = dom.create.namedTypeReference(overrideType);
}
if (overrides.indexOf('$type') != -1) {// has $type, must be a property
(<dom.PropertyDeclaration>obj).type = dom.create.namedTypeReference(overrideType);
}
}
}
}
}

View file

@ -0,0 +1,24 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import { Parser } from './Parser';
export function publish(data: any, opts: any) {
// remove undocumented stuff.
data({ undocumented: true }).remove();
// remove package data
data({ kind: 'package' }).remove();
// remove header comments
data({ copyright: { isString: true } }).remove();
// remove private members
data({ access: 'private' }).remove();
// remove ignored doclets
data({ ignore: true }).remove();
if (!fs.existsSync(opts.destination)) {
fs.mkdirSync(opts.destination);
}
var out = new Parser(data().get()).emit();
fs.writeFileSync(path.join(opts.destination, 'phaser.d.ts'), out);
};

View file

@ -0,0 +1,39 @@
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var MyScene = /** @class */ (function (_super) {
__extends(MyScene, _super);
function MyScene() {
return _super !== null && _super.apply(this, arguments) || this;
}
MyScene.prototype.preload = function () {
this.load.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
};
MyScene.prototype.create = function () {
var sprite = this.add.sprite(400, 300, 'cards', 'clubs3');
sprite.setInteractive();
this.input.on('pointerdown', function () {
sprite.setFrame('hearts4');
});
};
return MyScene;
}(Phaser.Scene));
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: MyScene
};
var game = new Phaser.Game(config);
//# sourceMappingURL=game.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"game.js","sourceRoot":"","sources":["../src/game.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA;IAAsB,2BAAY;IAAlC;;IAoBA,CAAC;IAlBU,yBAAO,GAAd;QAEI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,CAAC,CAAC;IAClF,CAAC;IAEM,wBAAM,GAAb;QAEI,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE1D,MAAM,CAAC,cAAc,EAAE,CAAC;QAExB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,EAAE;YAEzB,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAE/B,CAAC,CAAC,CAAC;IACP,CAAC;IAEL,cAAC;AAAD,CAAC,AApBD,CAAsB,MAAM,CAAC,KAAK,GAoBjC;AAED,IAAI,MAAM,GAAG;IACT,IAAI,EAAE,MAAM,CAAC,IAAI;IACjB,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IACX,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF,IAAI,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC"}

View file

Some files were not shown because too many files have changed in this diff Show more