Merge pull request #1 from photonstorm/master

update with base
This commit is contained in:
Jason Nicholls 2018-12-23 09:17:19 +02:00 committed by GitHub
commit 57a1326b84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
153 changed files with 4820 additions and 1252 deletions

View file

@ -2,17 +2,170 @@
## Version 3.16.0 - Ishikawa - in development ## Version 3.16.0 - Ishikawa - in development
### Facebook Instant Games Updates and Fixes
* Added the `Leaderboard.getConnectedScores` method, to get a list of scores from player connected entries.
* The `loadPlayerPhoto` function in the Instant Games plugin now listens for the updated Loader event correctly, causing the `photocomplete` event to fire properly.
* `Leaderboard.setScore` now emits the LeaderboardScore object with the `setscore` event, as the documentation said it did.
* `Leaderboard.getPlayerScore` now only populates the `playerScore` property if the entry isn't `null`.
* If the `setScore` or `getPlayerScore` calls fail, it will return `null` as the score instance, instead of causing a run-time error.
* You can now pass an object or a string to `setScore` and objects will be automatically stringified.
* The `preloadAds` method will now only create an AdInstance object if the interstitial `loadSync` promise resolves.
* The `preloadVideoAds` method will now only create an AdInstance object if the interstitial `loadSync` promise resolves.
* The `preloadAds` method will now emit the `adsnofill` event, if there are no ads in the inventory to load.
* The `preloadVideoAds` method will now emit the `adsnofill` event, if there are no ads in the inventory to load.
* The `showAd` method will now emit the `adsnotloaded` event, if there are no ads loaded matching the given Placement ID.
* The `showVideo` method will now emit the `adsnotloaded` event, if there are no ads loaded matching the given Placement ID.
* Showing an ad will emit the `adfinished` event when the ad is closed, previously this event was called `showad` but the new name better reflects what has happened.
* The Facebook Plugin is now available in the `Phaser.Scene` class template under the `facebook` property (thanks @bryanwood)
### Keyboard Input - New Features
The specificity of the Keyboard events has been changed to allow you more control over event handling. Previously, the Keyboard Plugin would emit the global `keydown_CODE` event first (where CODE was a keycode string, like `keydown_A`), then it would emit the global `keydown` event. In previous versions, `Key` objects, created via `this.input.keyboard.addKey()`, didn't emit events.
The `Key` class now extends EventEmitter and emits two new events directly: `down` and `up`. This means you can listen for an event from a Key you've created, i.e.: `yourKey.on('up', handler)`.
The order has also now changed. If it exists, the Key object will dispatch its `down` event first. Then the Keyboard Plugin will dispatch `keydown_CODE` and finally the least specific of them all, `keydown` will be dispatched.
You also now have the ability to cancel this at any stage either on a local or global level. All events handlers are sent an event object which you can call `event.stopImmediatePropagation()` on. This will immediately stop any further listeners from being invoked in the current Scene. Therefore, if you call `stopImmediatePropagation()` in the `Key.on` handler, then the Keyboard Plugin will not emit either the `keydown_CODE` or `keydown` global events. You can also call `stopImmediatePropagation()` during the `keydown_CODE` handler, to stop it reaching the global `keydown` handler. As `keydown` is last, calling it there has no effect.
There is also the `stopPropagation()` function. This works in the same way as `stopImmediatePropagation` but instead of being local, it works across all of the Scenes in your game. For example, if you had 3 active Scenes (A, B and C, with A at the top of the Scene list), all listening for the same key, calling `stopPropagation()` in Scene A would stop the event from reaching any handlers in Scenes B or C. Remember that events flow down the Scene list from top to bottom. So, the top-most rendering Scene in the Scene list has priority over any Scene below it.
All the above also works for `keyup` events.
New in 3.16 is the ability to receive a global `keydown` or `keyup` event from any key on the keyboard. Previously, it would only emit the event if it came from one of the keys listed in the KeyCodes file. Now, those global events will fire for any key, regardless of location.
#### Keyboard Captures
Key capturing is the way in which you stop a keyboard DOM event from activating anything else in the browser by calling `preventDefault` on it. For example, in tall web pages, pressing the SPACE BAR causes the page to scroll down. Obviously, if this is also the fire or jump button in your game, you don't want this to happen. So the key needs to be 'captured' to prevent it. Equally, you may wish to also capture the arrow keys, for similar reasons. Key capturing is done on a global level. If you set-up the capture of a key in one Scene, it will be captured globally across the whole game.
In 3.16 you now do this using the new `KeyboardPlugin.addCapture` method. This takes keycodes as its argument. You can either pass in a single key code (i.e. 32 for the Space Bar), an array of key codes, or a comma-delimited string - in which case the string is parsed and each code it can work out is captured.
To remove a capture you can use the `KeyboardPlugin.removeCapture` method, which takes the same style arguments as adding captures. To clear all captures call `KeyboardPlugin.clearCaptures`. Again, remember that these actions are global.
You can also temporarily enable and disable capturing using `KeyboardPlugin.enableGlobalCapture` and `KeyboardPlugin.disableGlobalCapture`. This means if you set-up a bunch of key captures, but then need to disable them all for a while (perhaps you swap focus to a DOM text field), you can call `disableGlobalCapture` to do this, and when finished in the DOM you can enable captures again with `enableGlobalCapture`, without having to clear and re-create them all.
Default captures can be defined in the Game Config in the `input.keyboard.captures` object. The captures are actually stored in the `KeyboardManager` class. The `KeyboardPlugin` is just a proxy to methods in the Keyboard Manager, but is how you should interface with it.
* `KeyboardPlugin.addCapture` is a new method that allows you to define a set of keycodes to have the default browser behaviors disabled on.
* `KeyboardPlugin.removeCapture` is a new method that removes specific previously set key captures.
* `KeyboardPlugin.clearCaptures` is a new method that removes all key captures.
* `KeyboardPlugin.getCaptures` is a new method that returns an array of all current key captures.
* `KeyboardPlugin.enableGlobalCapture` is a new method that enables any key captures that have been created.
* `KeyboardPlugin.disableGlobalCapture` is a new method that disables any key captures that have been created, without removing them from the captures list.
* `KeyboardPlugin.addKey` has a new boolean argument `enableCapture`, which is true by default, that will add a key capture for the Key being created.
* `KeyboardPlugin.addKeys` has a new boolean argument `enableCapture`, which is true by default, that will add a key capture for any Key created by the method.
#### Other Keyboard Updates and Fixes
* There is a new class called `KeyboardManager`. This class is created by the global Input Manager, if keyboard access has been enabled in the Game config. It's responsible for handling all browser keyboard events. Previously, the `KeyboardPlugin` did this. Which meant that every Scene that had its own Keyboard Plugin was binding more native keyboard events. This was causing problems with parallel Scenes when needing to capture keys. the `KeyboardPlugin` class still exists, and is still the main point of interface when you call `this.input.keyboard` in a Scene, but DOM event handling responsibility has been taken away from it. This means there's no only
one set of bindings ever created, which makes things a lot cleaner.
* There is a new Game and Scene Config setting `input.keyboard.capture` which is an array of KeyCodes that the Keyboard Plugin will capture all non-modified key events on. By default it is empty. You can populate it in the config, or use the new capture methods.
* The Keyboard Manager will now call `preventDefault` only on non-modified key presses, stopping the keyboard event from hitting the browser. Previously, capturing the R key, for example, would block a CTRL+R page reload, but it now ignores it because of the key modifier.
* `Key.emitOnRepeat` is a new boolean property that controls if the Key will continuously emit a `down` event while being held down (true), or emit the event just once, on first press, and then skip future events (false).
* `Key.setEmitOnRepeat` is a new chainable method for setting the `emitOnRepeat` property.
* The `KeyboardPlugin.addKeys` method has a new optional boolean `emitOnRepeat` which sets that property on all Key objects it creates as part of the call. It defaults to `false`.
* The `KeyboardPlugin.addKey` method has a new optional boolean `emitOnRepeat` which sets that property on the Key object it creates. It defaults to `false`.
* The `Key` class now extends EventEmitter and emits two events directly: `down` and `up`. This means you can listen for an event from a Key you've created, i.e.: `yourKey.on('up', handler)`.
* The following Key Codes have been added, which include some missing alphabet letters in Persian and Arabic: `SEMICOLON_FIREFOX`, `COLON`, `COMMA_FIREFOX_WINDOWS`, `COMMA_FIREFOX`, `BRACKET_RIGHT_FIREFOX` and `BRACKET_LEFT_FIREFOX` (thanks @wmateam)
* `Key.onDown` is a new method that handles the Key being pressed down, including down repeats.
* `Key.onUp` is a new method that handles the Key being released.
* `Key.destroy` is a new method that handles Key instance destruction. It is called automatically in `KeyboardPlugin.destroy`.
* The `Key.preventDefault` property has been removed. This is now handled by the global keyboard capture methods.
* `Key.metaKey` is a new boolean property which indicates if the Meta Key was held down when the Key was pressed. On a Mac the Meta Key is Command. On a Windows keyboard, it's the Windows key.
* `InputManager.keyboard` is a new property that instantiates the global Keyboard Manager, if enabled in the game config.
* The `KeyboardPlugin.addKey` method has a new boolean property `enableCapture` which automatically prevents default on the Key being created.
* The `KeyboardPlugin.addKeys` method has a new boolean property `enableCapture` which automatically prevents default on Keys being created.
* `Phaser.Input.Keyboard.ProcessKeyDown` has been removed as it's no longer required, `Key.onDown` handles it instead.
* `Phaser.Input.Keyboard.ProcessKeyUp` has been removed as it's no longer required, `Key.onUp` handles it instead.
* The Keyboard Manager has a property called `captures` which is an array of keycodes, as populated by the Game Config. Any key code in the array will have `preventDefault` called on it if pressed.
* `KeyboardPlugin.manager` is a new property that references the Keyboard Manager and is used internally.
* `KeyboardPlugin.target` has been removed as it's no longer used by the class.
* `KeyboardPlugin.queue` has been removed as it's no longer used by the class.
* `KeyboardPlugin.onKeyHandler` has been removed as it's no longer used by the class.
### Mouse and Touch Input - New Features, Updates and Fixes
* The Mouse Manager class has been updated to remove some commented out code and refine the `startListeners` method.
* When enabling a Game Object for input it will now use the `width` and `height` properties of the Game Object first, falling back to the frame size if not found. This stops a bug when enabling BitmapText objects for input and it using the font texture as the hit area size, rather than the text itself.
* `Pointer.smoothFactor` is a float-value that allows you to automatically apply smoothing to the Pointer position as it moves. This is ideal when you want something smoothly tracking a pointer in a game, or are need a smooth drawing motion for an art package. The default value is zero, meaning disabled. Set to a small number, such as 0.2, to enable.
* `Config.inputSmoothFactor` is a new property that allows you to set the smoothing factor for all Pointers the game creators. The default value is zero, which is disabled. Set in the game config as `input: { smoothFactor: value }`.
* `InputManager.transformPointer` has a new boolean argument `wasMove`, which controls if the pointer is being transformed after a move or up/down event.
* `Pointer.velocity` is a new Vector2 that contains the velocity of the Pointer, based on the current and previous positions. The velocity is smoothed out each frame, according to the `Pointer.motionFactor` property. This is done for more accurate gesture recognition. The velocity is updated based on Pointer movement, it doesn't require a button to be pressed first.
* `Pointer.angle` is a new property that contains the angle of the Pointer, in radians, based on the current and previous positions. The angle is smoothed out each frame, according to the `Pointer.motionFactor` property. This is done for more accurate gesture recognition. The angle is updated based on Pointer movement, it doesn't require a button to be pressed first.
* `Pointer.distance` is a new property that contains the distance of the Pointer, in radians, based on the current and previous positions. The distance is smoothed out each frame, according to the `Pointer.motionFactor` property. This is done for more accurate gesture recognition. The distance is updated based on Pointer movement, it doesn't require a button to be pressed first.
* `Pointer.motionFactor` is a new property that controls how much smoothing to apply to the Pointer positions each frame. This value is passed to the Smooth Step Interpolation that is used to calculate the velocity, angle and distance of the Pointer. It's applied every frame, until the midPoint reaches the current position of the Pointer. The default value is 0.2.
* The Input Plugin was emitting a `preUpdate` event, with the capital U, instead of `preupdate`. This has now been corrected. Fix #4185 (thanks @gadelan)
* `Pointer.updateMotion` is a new method that is called automatically, each step, by the Input Manager. It's responsible for calculating the Pointer velocity, angle and distance properties.
* `Pointer.time` is a new property that holds the time the Pointer was last updated by the Game step.
* `Pointer.getDistance` has been updated. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions.
* `Pointer.getDistanceX` is a new method that will return the horizontal distance between the Pointer's previous and current coordinates. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions.
* `Pointer.getDistanceY` is a new method that will return the horizontal distance between the Pointer's previous and current coordinates. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions.
* `Pointer.getDuration` is a new method that will return the duration the Pointer was held down for. If the Pointer has a button pressed down at the time this method is called, it will return the duration since the Pointer's was pressed down. If no button is held down, it will return the last recorded duration, based on the time the Pointer button was released.
* `Pointer.getAngle` is a new method that will return the angle between the Pointer coordinates. If the Pointer has a button pressed down at the time this method is called, it will return the angle between the Pointer's `downX` and `downY` values and the current position. If no button is held down, it will return the last recorded angle, based on where the Pointer was when the button was released.
* In previous versions, the VisibilityHandler would create a `mousedown` listener for the game canvas and then call `window.focus` when detected (assuming the game config `autoFocus` property was `true`). Responsibility for this has now been handled to the Mouse Manager `onMouseDown` handler.
* In previous versions, the VisibilityHandler would create a `mouseout` listener for the game canvas and then set `game.isOver` when detected. Responsibility for this has now been handled to the Mouse Manager, which sets the new Input Manager `isOver` property directly.
* In previous versions, the VisibilityHandler would create a `mouseover` listener for the game canvas and then set `game.isOver` when detected. Responsibility for this has now been handled to the Mouse Manager, which sets the new Input Manager `isOver` property directly.
* The `Phaser.Game.isOver` property has been moved. You can now find it in the Input Manager and it's also accessible via the Input Plugin, which means you can do `this.input.isOver` from within a Scene. This makes more sense as it's input related and not a game level property.
* The Input Plugin has a new event you can listen to: `gameover`, which is triggered whenever the mouse or a pointer is moved over the Game canvas. Listen to it with `this.input.on('gameover')` from within a Scene.
* The Input Plugin has a new event you can listen to: `gameout`, which is triggered whenever the mouse or a pointer leaves the Game canvas. Listen to it with `this.input.on('gameout')` from within a Scene.
* The Game used to emit a `mouseover` event when the mouse entered the game canvas. This is no longer emitted by the Game itself and can instead be listened for using the new Input Plugin event `gameover`.
* The Game used to emit a `mouseout` event when the mouse left the game canvas. This is no longer emitted by the Game itself and can instead be listened for using the new Input Plugin event `gameout`.
* If the `window` object exists (which it will in normal browser environments) new `mouseup` and `touchend` event listeners are bound to it and trigger the normal `mouseup` or `touchend` events within the internal input system. This means if you will now get a `pointerup` event from the Input Plugin even if the pointer is released outside of the game canvas. Pointers will also no longer think they are still 'down' if released outside the canvas and then moved inside again in their new state.
* The window will now have focus called on it by the Touch Manager, as well as the Mouse Manager, is the `autoFocus` game config property is enabled.
* The Input Plugin has a new event you can listen to: `pointerdownoutside`, which is triggered whenever the mouse or a pointer is pressed down while outside of the Game canvas. Listen to it with `this.input.on('pointerdownoutside')` from within a Scene.
* The Input Plugin has a new event you can listen to: `pointerupoutside`, which is triggered whenever the mouse or a pointer is released while outside of the Game canvas. Listen to it with `this.input.on('pointerupoutside')` from within a Scene.
* `Pointer.downElement` is a new property that holds the target of the DOM Event that triggered when the Pointer was pressed down. If this is within the game, this will be the game canvas element.
* `Pointer.upElement` is a new property that holds the target of the DOM Event that triggered when the Pointer was released. If this is within the game, this will be the game canvas element.
### New Features ### New Features
* You can now load external Scene files using the new `load.sceneFile` method. This allows you to dynamically load a Scene into the Scene Manager of your game, and swap to it at will. Please see the documentation and examples for further details.
* The data object being sent to the Dynamic Bitmap Text callback now has a new property `parent`, which is a reference to the Bitmap Text instance that owns the data object (thanks ornyth) * The data object being sent to the Dynamic Bitmap Text callback now has a new property `parent`, which is a reference to the Bitmap Text instance that owns the data object (thanks ornyth)
* The WebGL Renderer has a new method `clearPipeline`, which will clear down the current pipeline and reset the blend mode, ready for the context to be passed to a 3rd party library. * The WebGL Renderer has a new method `clearPipeline`, which will clear down the current pipeline and reset the blend mode, ready for the context to be passed to a 3rd party library.
* The WebGL Renderer has a new method `rebindPipeline`, which will rebind the given pipeline instance, reset the blank texture and reset the blend mode. Which is useful for recovering from 3rd party libs that have modified the gl context. * The WebGL Renderer has a new method `rebindPipeline`, which will rebind the given pipeline instance, reset the blank texture and reset the blend mode. Which is useful for recovering from 3rd party libs that have modified the gl context.
* Game Objects have a new property called `state`. Use this to track the state of a Game Object during its lifetime. For example, it could move from a state of 'moving', to 'attacking', to 'dead'. Phaser itself will never set this property, although plugins are allowed to. * Game Objects have a new property called `state`. Use this to track the state of a Game Object during its lifetime. For example, it could move from a state of 'moving', to 'attacking', to 'dead'. Phaser itself will never set this property, although plugins are allowed to.
* Game Objects have a new method called `setState` which will set the state property in a chainable call.
* `BlendModes.ERASE` is a new blend mode that will erase the object being drawn. When used in conjunction with a Render Texture it allows for effects that let you erase parts of the texture, in either Canvas or WebGL. When used with a transparent game canvas, it allows you to erase parts of the canvas, showing the web page background through.
* `BlendModes.SOURCE_IN` is a new Canvas-only blend mode, that allows you to use the `source-in` composite operation when rendering Game Objects.
* `BlendModes.SOURCE_OUT` is a new Canvas-only blend mode, that allows you to use the `source-out` composite operation when rendering Game Objects.
* `BlendModes.SOURCE_ATOP` is a new Canvas-only blend mode, that allows you to use the `source-atop` composite operation when rendering Game Objects.
* `BlendModes.DESTINATION_OVER` is a new Canvas-only blend mode, that allows you to use the `destination-over` composite operation when rendering Game Objects.
* `BlendModes.DESTINATION_IN` is a new Canvas-only blend mode, that allows you to use the `destination-in` composite operation when rendering Game Objects.
* `BlendModes.DESTINATION_OUT` is a new Canvas-only blend mode, that allows you to use the `destination-out` composite operation when rendering Game Objects.
* `BlendModes.DESTINATION_ATOP` is a new Canvas-only blend mode, that allows you to use the `destination-atop` composite operation when rendering Game Objects.
* `BlendModes.LIGHTER` is a new Canvas-only blend mode, that allows you to use the `lighter` composite operation when rendering Game Objects.
* `BlendModes.COPY` is a new Canvas-only blend mode, that allows you to use the `copy` composite operation when rendering Game Objects.
* `BlendModes.XOR` is a new Canvas-only blend mode, that allows you to use the `xor` composite operation when rendering Game Objects.
* `RenderTexture.erase` is a new method that will take an object, or array of objects, and draw them to the Render Texture using an ERASE blend mode, resulting in them being removed from the Render Texture. This is really handy for making a bitmap masked texture in Canvas or WebGL (without using an actual mask), or for 'cutting away' part of a texture.
* There is a new boolean Game Config property called `customEnvironment`. If set to `true` it will skip the internal Feature checks when working out which type of renderer to create, allowing you to run Phaser under non-native web environments. If using this value, you _must_ set an explicit `renderType` of either CANVAS or WEBGL. It cannot be left as AUTO. Fix #4166 (thanks @jcyuan)
* `Animation.nextFrame` will advance an animation to the next frame in the sequence instantly, regardless of the animation time or state. You can call this on a Sprite: `sprite.anims.nextFrame()` (thanks rgk25)
* `Animation.previousFrame` will set an animation to the previous frame in the sequence instantly, regardless of the animation time or state. You can call this on a Sprite: `sprite.anims.previousFrame()` (thanks rgk25)
* `Geom.Intersects.PointToLine` has a new optional argument `lineThickness` (which defaults to 1). This allows you to determine if the point intersects a line of a given thickness, where the line-ends are circular (not square).
* `Geom.Line.GetNearestPoint` is a new static method that will return the nearest point on a line to the given point.
* `Geom.Line.GetShortestDistance` is a new static method that will return the shortest distance from a line to the given point.
* `Camera.getBounds` is a new method that will return a rectangle containing the bounds of the camera.
* `Camera.centerOnX` will move the camera horizontally to be centered on the given coordinate, without changing its vertical placement.
* `Camera.centerOnY` will move the camera vertically to be centered on the given coordinate, without changing its horizontally placement.
* `AnimationManager.exists` is a new method that will check to see if an Animation using the given key already exists or not and returns a boolean.
* `animationstart-key` is a new Animation key specific event emitted by a Game Object. For example, if you had an animation with a key of 'explode' you can now listen for `animationstart-explode`.
* `animationrestart-key` is a new Animation key specific event emitted by a Game Object. For example, if you had an animation with a key of 'explode' you can now listen for `animationrestart-explode`.
* `animationcomplete-key` is a new Animation key specific event emitted by a Game Object. For example, if you had an animation with a key of 'explode' you can now listen for `animationcomplete-explode`.
* `animationupdate-key` is a new Animation key specific event emitted by a Game Object. For example, if you had an animation with a key of 'explode' you can now listen for `animationupdate-explode`.
* The Animation class now extends the Event Emitter and dispatches events itself. This allows you to listen for events from a specific Animation, rather than via a Game Object. This is handy, for example, if you had an explosion animation that you wanted to trigger a sound effect when it started. You can now listen for the events from the Animation object directly.
* The Animation class now emits the `start` event when played (either forward, or in reverse) by any Game Object.
* The Animation class now emits the `restart` event when it restarts playing on any Game Object.
* The Animation class now emits the `complete` event when it finishes playing on any Game Object.
* The Animation Component has a new method called `chain` which allows you to line-up another animation to start playing as soon as the current one stops, no matter how it stops (either by reaching its natural end, or directly by having stop called on it). You can chain a new animation at any point, including before the current one starts playing, during it, or when it ends (via its `animationcomplete` callback). Chained animations are specific to a Game Object, meaning different Game Objects can have different chained animations without impacting the global animation they're playing.
* `CanvasTexture.drawFrame` is a new method that allows you to draw a texture frame to the CanvasTexture based on the texture key and frame given.
* `CanvasTexture.getIndex` is a new method that will take an x/y coordinate and return the Image Data index offset used to retrieve the pixel values.
* `CanvasTexture.getPixels` is a new method that will take a region as an x/y and width/height and return all of the pixels in that region from the CanvasTexture.
* `CanvasTexture.setPixel` is a new method that sets the given pixel in the CanvasTexture to the color and alpha values provided.
* `CanvasTexture.getData` is a new method that will extract an ImageData block from the CanvasTexture from the region given.
* `CanvasTexture.putData` is a new method that will put an ImageData block at the given coordinates in a CanvasTexture.
### Updates ### Updates
* The Mouse Manager class has been updated to remove some commented out code and refine the `startListeners` method.
* The following Key Codes have been added, which include some missing alphabet letters in Persian and Arabic: `SEMICOLON_FIREFOX`, `COLON`, `COMMA_FIREFOX_WINDOWS`, `COMMA_FIREFOX`, `BRACKET_RIGHT_FIREFOX` and `BRACKET_LEFT_FIREFOX` (thanks @wmateam)
* You can now modify `this.physics.world.debugGraphic.defaultStrokeWidth` to set the stroke width of any debug drawn body, previously it was always 1 (thanks @samme) * You can now modify `this.physics.world.debugGraphic.defaultStrokeWidth` to set the stroke width of any debug drawn body, previously it was always 1 (thanks @samme)
* `TextStyle.setFont` has a new optional argument `updateText` which will sets if the text should be automatically updated or not (thanks @DotTheGreat) * `TextStyle.setFont` has a new optional argument `updateText` which will sets if the text should be automatically updated or not (thanks @DotTheGreat)
* `ProcessQueue.destroy` now sets the internal `toProcess` counter to zero. * `ProcessQueue.destroy` now sets the internal `toProcess` counter to zero.
@ -26,12 +179,36 @@
* The method `DisplayList.sortGameObjects` has been removed. It has thrown a runtime error since v3.3.0! which no-one even spotted, a good indication of how little the method is used. The display list is automatically sorted anyway, so if you need to sort a small section of it, just use the standard JavaScript Array sort method (thanks ornyth) * The method `DisplayList.sortGameObjects` has been removed. It has thrown a runtime error since v3.3.0! which no-one even spotted, a good indication of how little the method is used. The display list is automatically sorted anyway, so if you need to sort a small section of it, just use the standard JavaScript Array sort method (thanks ornyth)
* The method `DisplayList.getTopGameObject` has been removed. It has thrown a runtime error since v3.3.0! which no-one even spotted, a good indication of how little the method is used (thanks ornyth) * The method `DisplayList.getTopGameObject` has been removed. It has thrown a runtime error since v3.3.0! which no-one even spotted, a good indication of how little the method is used (thanks ornyth)
* `WebGLRenderer.setFramebuffer` has a new optional boolean argument `updateScissor`, which will reset the scissor to match the framebuffer size, or clear it. * `WebGLRenderer.setFramebuffer` has a new optional boolean argument `updateScissor`, which will reset the scissor to match the framebuffer size, or clear it.
* `WebAudioSoundManager.onFocus` will not try to resume the Audio Context if it's still locked.
* `WebAudioSoundManager.onBlur` will not try to suspend the Audio Context if it's still locked.
* When using `ScenePlugin.add`, to add a new Scene to the Scene Manager, it didn't allow you to include the optional Scene data object. You can now pass this in the call (thanks @kainage)
* `Graphics.stroke` is a new alias for the `strokePath` method, to keep the calls consistent with the Canvas Rendering Context API.
* `Graphics.fill` is a new alias for the `fillPath` method, to keep the calls consistent with the Canvas Rendering Context API.
* `LoaderPlugin.sceneManager` is a new property that is a reference to the global Scene Manager, useful for Plugins.
* Whenever `Camera.roundPixels` was enabled it would use a bitwise operation to truncate the float (`x |= 0`) - this has been replaced across all files that used it, with a call to `Math.round` instead. This gives far better results when zooming cameras both in and out of a Scene, stopping thin gaps appearing between closely packed Game Objects.
* `AnimationManager.create` will now return a boolean `false` if the given key is invalid (i.e. undefined or falsey).
* `AnimationManager.create` will no longer raise a console warning if the animation key is already in use. Instead, it will return the animation belonging to that key. A brand new animation will only be created if the key isn't already in use. When this happens, the `add` event is emitted by the Animation Manager. If no event is emitted, the animation already existed.
* `ArcadePhysics.Body.destroy` will now only add itself to the World `pendingDestroy` list if the world property exists. This prevents `Cannot read property 'pendingDestroy' of undefined` errors if you try to delete a physics body in a callback and then immediately change Scene (which tells the physics work to also delete all bodies)
* The Animation Component `restart` method has had is sole `key` argument removed. Previously, you had to pass in the key of the animation you wished to reverse, but now you can just call the method directly, and as long as there is an animation playing, it will automatically start playing in reverse, without the nee for a key (the way it should have been originally)
* `Animation.play` and `playReverse` will now accept either a string-based key of the animation to play (like before), or you can pass in an Animation instance, and it will play that animation.
* `CanvasTexture.clear` now has 4 new optional arguments: `x, y, width, height` which allow you to define the region of the texture to be cleared. If not provided it will clear the whole texture, which is the same behavior as before.
* EarCut, the polygon triangulation library used by the Graphics and WebGL classes, has been upgraded from 2.1.1 to 2.1.4. 2.1.2 fixed a few race conditions where bad input would cause an error. 2.1.3 improved performance for bigger inputs (5-12%) and 2.1.4 fixed a race condition that could lead to a freeze on degenerate input.
* `TextureTintPipeline.batchQuad` and `batchTri` have two new optional arguments `texture` and `unit` which are used to re-set the batch texture should the method cause a batch flush.
* `TextureTintPipeline.requireTextureBatch` is a new internal method that helps speed-up the creation of texture batches. It is used in conjunction with `setTexture2D` and `pushBatch`.
* `TextureTintPipeline.flush` and `TextureTintPipeline.pushBatch` have been optimized to handle zero based texture units as priority. They've also been refactored to avoid creation of empty texture batches.
* The `WebGLRenderer.setTexture2D` method has a new optional argument `flush` which controls if the pipeline is flushed if the given texture is new, or not. This is used internally to skip flushing during an existing flush.
* The Tilemap Layer `width` and `height` properties are now based on the tilemap tile sizes multiplied by the layer dimensions. This corrects an issue with layer sizes being wrong if you called `setBaseTileSize` on a Map.
* The WebGLRenderer will now clear the framebuffer at the start of every render.
* `WebGLRenderer.setScissor` now has a new optional argument `drawingBufferHeight` which allows you to specify the drawing buffer height, rather than use the renderers default value.
* `WebGLRenderer.pushScissor` now has a new optional argument `drawingBufferHeight` which allows you to specify the drawing buffer height, rather than use the renderers default value.
* `WebGLRenderer.preRender` now calls `gl.clearColor` in order to restore the background clear color in case something, like a Render Texture, has changed it.
* `Map.set` will now update an existing value if you provide it with a key that already exists within the Map. Previously, if you tried to set the value of a key that existed it would be skipped.
### Bug Fixes ### Bug Fixes
* The `loadPlayerPhoto` function in the Instant Games plugin now listens for the updated Loader event correctly, causing the `photocomplete` event to fire properly.
* The Rectangle Shape object wouldn't render if it didn't have a stroke, or any other objects on the display list (thanks mliko) * The Rectangle Shape object wouldn't render if it didn't have a stroke, or any other objects on the display list (thanks mliko)
* When using a font string instead of setting `fontFamily`, `fontSize` and `fontStyle` in either `Text.setStyle` or `setFont`, the style properties wouldn't get set. This isn't a problem while creating the text object, only if modifying it later (thanks @DotTheGreat) * When using a font string instead of setting `fontFamily`, `fontSize` and `fontStyle` in either `Text.setStyle` or `setFont`, the style properties wouldn't get set. This isn't a problem while creating the text object, only if modifying it later (thanks @DotTheGreat)
* `Text.toJSON` wasn't saving the font style when using the "font" shorthand to create it. It now saves it correctly. Fix #4141 (thanks @divillysausages)
* Disabling camera bounds and then moving the camera to an area in a Tilemap that did not have any tile information would throw an `Uncaught Reference error` as it tried to access tiles that did not exist (thanks @Siyalatas) * Disabling camera bounds and then moving the camera to an area in a Tilemap that did not have any tile information would throw an `Uncaught Reference error` as it tried to access tiles that did not exist (thanks @Siyalatas)
* Fixed an issue where Sprite Sheets being extracted from a texture atlas would fail if the sheet was either just a single column or single row of sprites. Fix #4096 (thanks @Cirras) * Fixed an issue where Sprite Sheets being extracted from a texture atlas would fail if the sheet was either just a single column or single row of sprites. Fix #4096 (thanks @Cirras)
* If you created an Arcade Physics Group without passing a configuration object, and passing an array of non-standard children, it would throw a classType runtime error. It now creates a default config object correctly (thanks @pierpo) * If you created an Arcade Physics Group without passing a configuration object, and passing an array of non-standard children, it would throw a classType runtime error. It now creates a default config object correctly (thanks @pierpo)
@ -43,21 +220,53 @@
* Starting with version 3.13 in the Canvas Renderer, it was possible for long-running scripts to start to get bogged-down in `fillRect` calls if the game had a background color set. The context is now saved properly to avoid this. Fix #4056 (thanks @Aveyder) * Starting with version 3.13 in the Canvas Renderer, it was possible for long-running scripts to start to get bogged-down in `fillRect` calls if the game had a background color set. The context is now saved properly to avoid this. Fix #4056 (thanks @Aveyder)
* Render Textures created larger than the size of the default canvas would be automatically clipped when drawn to in WebGL. They now reset the gl scissor and drawing height property in order to draw to their full size, regardless of the canvas size. Fix #4139 (thanks @chaoyang805 @iamchristopher) * Render Textures created larger than the size of the default canvas would be automatically clipped when drawn to in WebGL. They now reset the gl scissor and drawing height property in order to draw to their full size, regardless of the canvas size. Fix #4139 (thanks @chaoyang805 @iamchristopher)
* The `cameraFilter` property of a Game Object will now allow full bitmasks to be set (a value of -1), instead of just those > 0 (thanks @stuartkeith) * The `cameraFilter` property of a Game Object will now allow full bitmasks to be set (a value of -1), instead of just those > 0 (thanks @stuartkeith)
* When using a Particle Emitter, the array of dead particles (`this.dead`) wasn't being filled correctly. Dead particles are now moved to it as they should be (thanks @Waclaw-I) * The `PathFollower.startFollow` method now properly uses the `startAt` argument to the method, so you can start a follower off at any point along the path. Fix #3688 (thanks @DannyT @diteix)
* Static Circular Arcade Physics Bodies now render as circles in the debug display, instead of showing their rectangle bounds (thanks @maikthomas)
* Changing the mute flag on an `HTML5AudioSound` instance, via the `mute` setter, now works, as it does via the Sound Manager (thanks @Waclaw-I @neon-dev)
* Changing the volume on an `HTML5AudioSound` instance, via the `volume` setter, now works, as it does via the Sound Manager (thanks @Waclaw-I)
* The Dynamic Tilemap Layer WebGL renderer was drawing tiles at the incorrect position if the layer was scaled. Fix #4104 (thanks @the-realest-stu)
* `Tile.tileset` now returns the specific Tileset associated with the tile, rather than an array of them. Fix #4095 (thanks @quadrupleslap)
* `Tile.getCollisionGroup` wouldn't return the correct Group after the change to support multiple Tilesets. It now returns the group properly (thanks @jbpuryear)
* `Tile.getTileData` wouldn't return the correct data after the change to support multiple Tilesets. It now returns the tile data properly (thanks @jbpuryear)
* The `GetTileAt` and `RemoveTileAt` components would error with "Cannot read property 'index' of undefined" if the tile was undefined rather than null. It now handles both cases (thanks @WaSa42)
* Changing `TileSprite.width` or `TileSprite.height` will now flag the texture as dirty and call `updateDisplayOrigin`, allowing you to resize TileSprites dynamically in both Canvas and WebGL.
* `RandomDataGenerator.shuffle` has been fixed to use the proper modifier in the calculation, allowing for a more even distribution (thanks wayfinder)
* The Particle Emitter was not recycling dead particles correctly, so it was creating new objects every time it emitted (the old particles were then left to the browsers gc to clear up). This has now been recoded, so the emitter will properly keep track of dead particles and re-use them (thanks @Waclaw-I for the initial PR)
* `ParticleEmitter.indexSortCallback` has been removed as it's no longer required.
* `Particle.index` has been removed, as it's no longer required. Particles don't need to keep track of their index any more.
* The Particle Emitter no longer needs to call the StableSort.inplace during its preUpdate, saving cpu.
* `Particle.resetPosition` is a new method that is called when a particle dies, preparing it ready for firing again in the future.
* The Canvas `SetTransform` method would save the context state, but it wasn't restored at the end in the following Game Objects: Dynamic Bitmap Text, Graphics, Arc, Curve, Ellipse, Grid, IsoBox, IsoTriangle, Line, Polygon, Rectangle, Star and Triangle. These now all restore the context, meaning if you're using non-canvas sized cameras in Canvas mode, it will now render beyond just the first custom camera.
* `Utils.Array.MoveUp` wouldn't let you move an array element to the top-most index in the array. This also impacted `Container.moveUp`.
* The Texture Tint Pipeline had a logic error that would cause every 2001st quad to either be invisible, or pick-up the texture of the 2000th quad by mistake. The `batchQuad` and `batchTri` methods how handle re-assigning the batch texture if they cause a batch flush as part of their process.
* Rotating Sprites that used a Normal Map wouldn't rotate the normal map with it, causing the lighting effects to become irregular. The normal map vectors are now rotated correctly (thanks @sercant for the PR and @fazzamatazz and @ysraelJMM for the report)
* Changing `scaleX` or `scaleY` on a `MatterImage` or `MatterSprite` would cause the body scale to become distorted as the setters didn't use the correct factor when resetting the initial scale. Fix #4206 (thanks @YannCaron)
* `StaticBody.reset` in Arcade Physics would ignore the `x` and `y` values given to it. If given, they're now used to reset the parent Game Object before the body is updated. Fix #4224 (thanks @samme)
* Static Tilemap Layers wouldn't render correctly if the layer used a tileset with a different size to the base map data (set via `setBaseTileSize`). They now render correctly in WebGL and Canvas, regardless of the base tile size.
* When using `RenderTexture.fill`, the `alpha` argument would be ignored in Canvas mode. It's now used when filling the RenderTexture.
* Fixed an issue in `WebGLRenderer.setScissor` where it was possible to try and compare the scissor size to a non-current scissor, if called outside of the render loop (i.e. from `RenderTexture.fill`) (thanks @hackhat)
* `RenderTexture.fill` in WebGL would use `gl.clear` and a clear color to try and fill the Render Texture. This only worked for full-canvas sized RenderTextures that didn't have a camera zoom applied. It has now been swapped to use the `drawFillRect` method of the Texture Tint Pipeline, allowing it to work properly regardless of camera zoom or size.
* `Container.getFirst` was using an incorrect Array Utils function `GetFirstElement`, when it should have been using `GetFirst`. It now uses the correct function. Fix #4244 (thanks @miran248)
* `List.getFirst` was using an incorrect Array Utils function `GetFirstElement`, when it should have been using `GetFirst`. It now uses the correct function. Fix #4244 (thanks @miran248)
* Fixed an issue where changing the viewport or size of a Camera belonging to a RenderTexture, it wouldn't impact the rendering and objects will still render outside of the viewport range. It's now converted to a proper gl scissor rect by the renderer, meaning you can limit the area rendered to by adjusting the internal Render Texture cameras viewport. Fix #4243 (thanks @hackhat)
* `CanvasTexture.destroy` is a new method that specifically handles the destruction of the CanvasTexture and all of its associated typed arrays. This prevents a memory leak when creating and destroying lots of RenderTextures (which are CanvasTexture backed). Fix #4239 (thanks @sjb933)
* The Alpha, Flip and Origin components have been removed from the Mesh Game Object (and by extension, Quad as well) as they are not used in the renderer and should be manipulated via the Mesh properties. Fix #4188 (thanks @enriqueto)
### Examples and TypeScript ### Examples and TypeScript
Thanks to the following for helping with the Phaser 3 Examples and TypeScript definitions, either by reporting errors, or even better, fixing them: Thanks to the following for helping with the Phaser 3 Examples and TypeScript definitions, either by reporting errors, or even better, fixing them:
@guilhermehto @samvieten @darkwebdev @guilhermehto @samvieten @darkwebdev @RoryO @snowbillr @slothyrulez @jcyuan @jestarray
### Phaser Doc Jam ### Phaser Doc Jam
The [Phaser Doc Jam](http://docjam.phaser.io) is an on-going effort to ensure that the Phaser 3 API has 100% documentation coverage. Thanks to the monumental effort of myself and the following people we're now really close to that goal! My thanks to: The Phaser Doc Jam was a community-backed effort to try and get the Phaser 3 API documentation to 100% coverage. The Doc Jam is now over and I offer my thanks to the following who helped with docs in this release:
16patsle - @icbat - @gurungrahul2 - @samme - @telinc1 - anandu pavanan - blackhawx - candelibas - Diego Romero - Elliott Wallace - eric - Georges Gabereau - Haobo Zhang - henriacle - madclaws - marc136 - Mihail Ilinov - naum303 - NicolasRoehm - rejacobson - Robert Kowalski - rootasjey - scottwestover - stetso - therealsamf - Tigran - willblackmore - zenwaichi 16patsle - @gurungrahul2 - @icbat - @samme - @telinc1 - anandu pavanan - blackhawx - candelibas - Diego Romero - doronlinder - Elliott Wallace - eric - Georges Gabereau - Haobo Zhang - henriacle - jak6jak - Jake Jensen - James Van Roose - JamesSkemp - joelahoover - Joey - madclaws - marc136 - Mihail Ilinov - naum303 - NicolasRoehm - nuane - rejacobson - Robert Kowalski - rodri042 - rootasjey - sawamara - scottwestover - sir13tommy - stetso - therealsamf - Tigran - willblackmore - zenwaichi
If you'd like to help finish off the last parts of documentation then take a look at the [Doc Jam site](http://docjam.phaser.io). Also, the following helped with the docs outside of the Doc Jam:
@bryanwood @jestarray @matosummer @tfelix @imilo
## Version 3.15.1 - Batou - 16th October 2018 ## Version 3.15.1 - Batou - 16th October 2018

View file

@ -157,7 +157,7 @@ Also, please subscribe to the [Phaser World](https://phaser.io/community/newslet
### Facebook Instant Games ### Facebook Instant Games
Phaser 3.13 introduced the new [Facebook Instant Games](https://developers.facebook.com/docs/games/instant-games) 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. 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: The plugin offers the following features:
@ -177,6 +177,12 @@ The plugin offers the following features:
* 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. * 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. * 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: 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 ```html
@ -382,8 +388,8 @@ All rights reserved.
"Above all, video games are meant to be just one thing: fun. Fun for everyone." - Satoru Iwata "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-js]: https://github.com/photonstorm/phaser/releases/download/v3.15.1/phaser.js
[get-minjs]: https://github.com/photonstorm/phaser/releases/download/v3.11/phaser.min.js [get-minjs]: https://github.com/photonstorm/phaser/releases/download/v3.15.1/phaser.min.js
[clone-http]: https://github.com/photonstorm/phaser.git [clone-http]: https://github.com/photonstorm/phaser.git
[clone-ssh]: git@github.com:photonstorm/phaser.git [clone-ssh]: git@github.com:photonstorm/phaser.git
[clone-ghwin]: github-windows://openRepo/https://github.com/photonstorm/phaser [clone-ghwin]: github-windows://openRepo/https://github.com/photonstorm/phaser
@ -392,4 +398,4 @@ All rights reserved.
[issues]: https://github.com/photonstorm/phaser/issues [issues]: https://github.com/photonstorm/phaser/issues
[examples]: https://github.com/photonstorm/phaser3-examples [examples]: https://github.com/photonstorm/phaser3-examples
[contribute]: https://github.com/photonstorm/phaser/blob/master/.github/CONTRIBUTING.md [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

@ -13,11 +13,11 @@
* @property {boolean} video - Is this a video ad? * @property {boolean} video - Is this a video ad?
*/ */
var AdInstance = function (instance, video) var AdInstance = function (placementID, instance, video)
{ {
return { return {
instance: instance, instance: instance,
placementID: instance.getPlacementID(), placementID: placementID,
shown: false, shown: false,
video: video video: video
}; };

View file

@ -1871,6 +1871,10 @@ var FacebookInstantGamesPlugin = new Class({
* *
* If the instance is created successfully then the ad is preloaded ready for display in-game via the method `showAd()`. * If the instance is created successfully then the ad is preloaded ready for display in-game via the method `showAd()`.
* *
* If the ad loads it will emit the `adloaded` event, passing the AdInstance as the only parameter.
*
* If the ad cannot be displayed because there was no inventory to fill it, it will emit the `adsnofill` event.
*
* @method Phaser.FacebookInstantGamesPlugin#preloadAds * @method Phaser.FacebookInstantGamesPlugin#preloadAds
* @since 3.13.0 * @since 3.13.0
* *
@ -1912,18 +1916,36 @@ var FacebookInstantGamesPlugin = new Class({
for (i = 0; i < placementID.length; i++) for (i = 0; i < placementID.length; i++)
{ {
var id = placementID[i]; var id = placementID[i];
var data;
FBInstant.getInterstitialAdAsync(id).then(function (data) FBInstant.getInterstitialAdAsync(id).then(function (interstitial)
{ {
var ad = AdInstance(data, true); data = interstitial;
return interstitial.loadAsync();
}).then(function ()
{
var ad = AdInstance(id, data, false);
_this.ads.push(ad); _this.ads.push(ad);
return ad.loadAsync(); _this.emit('adloaded', ad);
}).catch(function (e) }).catch(function (e)
{ {
console.warn(e); if (e.code === 'ADS_NO_FILL')
{
_this.emit('adsnofill', id);
}
else if (e.code === 'ADS_FREQUENT_LOAD')
{
_this.emit('adsfrequentload', id);
}
else
{
console.warn(e);
}
}); });
} }
@ -1931,10 +1953,14 @@ var FacebookInstantGamesPlugin = new Class({
}, },
/** /**
* Attempt to create an instance of an interstitial video ad. * Attempt to create an instance of an rewarded video ad.
* *
* If the instance is created successfully then the ad is preloaded ready for display in-game via the method `showVideo()`. * If the instance is created successfully then the ad is preloaded ready for display in-game via the method `showVideo()`.
* *
* If the ad loads it will emit the `adloaded` event, passing the AdInstance as the only parameter.
*
* If the ad cannot be displayed because there was no inventory to fill it, it will emit the `adsnofill` event.
*
* @method Phaser.FacebookInstantGamesPlugin#preloadVideoAds * @method Phaser.FacebookInstantGamesPlugin#preloadVideoAds
* @since 3.13.0 * @since 3.13.0
* *
@ -1976,18 +2002,36 @@ var FacebookInstantGamesPlugin = new Class({
for (i = 0; i < placementID.length; i++) for (i = 0; i < placementID.length; i++)
{ {
var id = placementID[i]; var id = placementID[i];
var data;
FBInstant.getRewardedVideoAsync(id).then(function (data) FBInstant.getRewardedVideoAsync(id).then(function (reward)
{ {
var ad = AdInstance(data, true); data = reward;
return reward.loadAsync();
}).then(function ()
{
var ad = AdInstance(id, data, true);
_this.ads.push(ad); _this.ads.push(ad);
return ad.loadAsync(); _this.emit('adloaded', ad);
}).catch(function (e) }).catch(function (e)
{ {
console.warn(e); if (e.code === 'ADS_NO_FILL')
{
_this.emit('adsnofill', id);
}
else if (e.code === 'ADS_FREQUENT_LOAD')
{
_this.emit('adsfrequentload', id);
}
else
{
console.warn(e);
}
}); });
} }
@ -1997,9 +2041,9 @@ var FacebookInstantGamesPlugin = new Class({
/** /**
* Displays a previously loaded interstitial ad. * Displays a previously loaded interstitial ad.
* *
* If the ad is successfully displayed this plugin will emit the `showad` event, with the AdInstance object as its parameter. * If the ad is successfully displayed this plugin will emit the `adfinished` event, with the AdInstance object as its parameter.
* *
* If the ad cannot be displayed because there was no inventory to fill it, it will emit the `adsnofill` event. * If the ad cannot be displayed, it will emit the `adsnotloaded` event.
* *
* @method Phaser.FacebookInstantGamesPlugin#showAd * @method Phaser.FacebookInstantGamesPlugin#showAd
* @since 3.13.0 * @since 3.13.0
@ -2022,18 +2066,20 @@ var FacebookInstantGamesPlugin = new Class({
{ {
ad.shown = true; ad.shown = true;
_this.emit('showad', ad); _this.emit('adfinished', ad);
}).catch(function (e) }).catch(function (e)
{ {
if (e.code === 'ADS_NO_FILL') if (e.code === 'ADS_NOT_LOADED')
{ {
_this.emit('adsnofill'); _this.emit('adsnotloaded', ad);
} }
else else if (e.code === 'RATE_LIMITED')
{ {
console.warn(e); _this.emit('adratelimited', ad);
} }
_this.emit('adshowerror', e, ad);
}); });
} }
} }
@ -2044,9 +2090,9 @@ var FacebookInstantGamesPlugin = new Class({
/** /**
* Displays a previously loaded interstitial video ad. * Displays a previously loaded interstitial video ad.
* *
* If the ad is successfully displayed this plugin will emit the `showad` event, with the AdInstance object as its parameter. * If the ad is successfully displayed this plugin will emit the `adfinished` event, with the AdInstance object as its parameter.
* *
* If the ad cannot be displayed because there was no inventory to fill it, it will emit the `adsnofill` event. * If the ad cannot be displayed, it will emit the `adsnotloaded` event.
* *
* @method Phaser.FacebookInstantGamesPlugin#showVideo * @method Phaser.FacebookInstantGamesPlugin#showVideo
* @since 3.13.0 * @since 3.13.0
@ -2069,18 +2115,20 @@ var FacebookInstantGamesPlugin = new Class({
{ {
ad.shown = true; ad.shown = true;
_this.emit('showad', ad); _this.emit('adfinished', ad);
}).catch(function (e) }).catch(function (e)
{ {
if (e.code === 'ADS_NO_FILL') if (e.code === 'ADS_NOT_LOADED')
{ {
_this.emit('adsnofill'); _this.emit('adsnotloaded', ad);
} }
else else if (e.code === 'RATE_LIMITED')
{ {
console.warn(e); _this.emit('adratelimited', ad);
} }
_this.emit('adshowerror', e, ad);
}); });
} }
} }

View file

@ -139,13 +139,15 @@ var Leaderboard = new Class({
* *
* The data is requested in an async call, so the result isn't available immediately. * 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 score, any extra data and the name of the Leaderboard. * 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.FacebookInstantGamesPlugin.Leaderboard#setScore * @method Phaser.FacebookInstantGamesPlugin.Leaderboard#setScore
* @since 3.13.0 * @since 3.13.0
* *
* @param {integer} score - The new score for the player. Must be a 64-bit integer number. * @param {integer} score - The new score for the player. Must be a 64-bit integer number.
* @param {string} [data] - Metadata to associate with the stored score. Must be less than 2KB in size. * @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. * @return {this} This Leaderboard instance.
*/ */
@ -153,11 +155,27 @@ var Leaderboard = new Class({
{ {
if (data === undefined) { data = ''; } if (data === undefined) { data = ''; }
if (typeof data === 'object')
{
data = JSON.stringify(data);
}
var _this = this; var _this = this;
this.ref.setScoreAsync(score, data).then(function (entry) this.ref.setScoreAsync(score, data).then(function (entry)
{ {
_this.emit('setscore', entry.getScore(), entry.getExtraData(), _this.name); 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) }).catch(function (e)
{ {
@ -174,6 +192,8 @@ var Leaderboard = new Class({
* *
* When the call completes this Leaderboard will emit the `getplayerscore` event along with the score and the name of the Leaderboard. * 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.FacebookInstantGamesPlugin.Leaderboard#getPlayerScore * @method Phaser.FacebookInstantGamesPlugin.Leaderboard#getPlayerScore
* @since 3.13.0 * @since 3.13.0
* *
@ -185,11 +205,18 @@ var Leaderboard = new Class({
this.ref.getPlayerEntryAsync().then(function (entry) this.ref.getPlayerEntryAsync().then(function (entry)
{ {
var score = LeaderboardScore(entry); if (entry)
{
var score = LeaderboardScore(entry);
_this.playerScore = score; _this.playerScore = score;
_this.emit('getplayerscore', score, _this.name); _this.emit('getplayerscore', score, _this.name);
}
else
{
_this.emit('getplayerscore', null, _this.name);
}
}).catch(function (e) }).catch(function (e)
{ {
@ -204,7 +231,7 @@ var Leaderboard = new Class({
* *
* The data is requested in an async call, so the result isn't available immediately. * 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. * 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.FacebookInstantGamesPlugin.Leaderboard#getScores * @method Phaser.FacebookInstantGamesPlugin.Leaderboard#getScores
* @since 3.13.0 * @since 3.13.0
@ -237,6 +264,47 @@ var Leaderboard = new Class({
console.warn(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.FacebookInstantGamesPlugin.Leaderboard#getConnectedScores
* @since 3.16.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.
*/
getConnectedScores: function (count, offset)
{
if (count === undefined) { count = 10; }
if (offset === undefined) { offset = 0; }
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; return this;
} }

View file

@ -6,6 +6,7 @@
var Clamp = require('../math/Clamp'); var Clamp = require('../math/Clamp');
var Class = require('../utils/Class'); var Class = require('../utils/Class');
var EventEmitter = require('eventemitter3');
var FindClosestInSorted = require('../utils/array/FindClosestInSorted'); var FindClosestInSorted = require('../utils/array/FindClosestInSorted');
var Frame = require('./AnimationFrame'); var Frame = require('./AnimationFrame');
var GetValue = require('../utils/object/GetValue'); var GetValue = require('../utils/object/GetValue');
@ -65,6 +66,7 @@ var GetValue = require('../utils/object/GetValue');
* *
* @class Animation * @class Animation
* @memberof Phaser.Animations * @memberof Phaser.Animations
* @extends Phaser.Events.EventEmitter
* @constructor * @constructor
* @since 3.0.0 * @since 3.0.0
* *
@ -74,10 +76,14 @@ var GetValue = require('../utils/object/GetValue');
*/ */
var Animation = new Class({ var Animation = new Class({
Extends: EventEmitter,
initialize: initialize:
function Animation (manager, key, config) function Animation (manager, key, config)
{ {
EventEmitter.call(this);
/** /**
* A reference to the global Animation Manager * A reference to the global Animation Manager
* *
@ -797,7 +803,14 @@ var Animation = new Class({
component.pendingRepeat = false; component.pendingRepeat = false;
component.parent.emit('animationrepeat', this, component.currentFrame, component.repeatCounter, component.parent); var frame = component.currentFrame;
var parent = component.parent;
this.emit('repeat', this, frame);
parent.emit('animationrepeat-' + this.key, this, frame, component.repeatCounter, parent);
parent.emit('animationrepeat', this, frame, component.repeatCounter, parent);
} }
} }
}, },
@ -939,6 +952,8 @@ var Animation = new Class({
*/ */
destroy: function () destroy: function ()
{ {
this.removeAllListeners();
this.manager.off('pauseall', this.pause, this); this.manager.off('pauseall', this.pause, this);
this.manager.off('resumeall', this.resume, this); this.manager.off('resumeall', this.resume, this);

View file

@ -19,8 +19,7 @@ var Class = require('../utils/Class');
* A single frame in an Animation sequence. * A single frame in an Animation sequence.
* *
* An AnimationFrame consists of a reference to the Texture it uses for rendering, references to other * An AnimationFrame consists of a reference to the Texture it uses for rendering, references to other
* frames in the animation, and index data. It also has the ability to fire its own `onUpdate` callback * frames in the animation, and index data. It also has the ability to modify the animation timing.
* and modify the animation timing.
* *
* AnimationFrames are generated automatically by the Animation class. * AnimationFrames are generated automatically by the Animation class.
* *

View file

@ -154,33 +154,64 @@ var AnimationManager = new Class({
return this; return this;
}, },
/**
* Checks to see if the given key is already in use within the Animation Manager or not.
*
* Animations are global. Keys created in one scene can be used from any other Scene in your game. They are not Scene specific.
*
* @method Phaser.Animations.AnimationManager#exists
* @since 3.16.0
*
* @param {string} key - The key of the Animation to check.
*
* @return {boolean} `true` if the Animation already exists in the Animation Manager, or `false` if the key is available.
*/
exists: function (key)
{
return this.anims.has(key);
},
/** /**
* Creates a new Animation and adds it to the Animation Manager. * Creates a new Animation and adds it to the Animation Manager.
* *
* Animations are global. Once created, you can use them in any Scene in your game. They are not Scene specific.
*
* If an invalid key is given this method will return `false`.
*
* If you pass the key of an animation that already exists in the Animation Manager, that animation will be returned.
*
* A brand new animation is only created if the key is valid and not already in use.
*
* If you wish to re-use an existing key, call `AnimationManager.remove` first, then this method.
*
* @method Phaser.Animations.AnimationManager#create * @method Phaser.Animations.AnimationManager#create
* @fires AddAnimationEvent * @fires AddAnimationEvent
* @since 3.0.0 * @since 3.0.0
* *
* @param {AnimationConfig} config - The configuration settings for the Animation. * @param {AnimationConfig} config - The configuration settings for the Animation.
* *
* @return {Phaser.Animations.Animation} The Animation that was created. * @return {(Phaser.Animations.Animation|false)} The Animation that was created, or `false` is the key is already in use.
*/ */
create: function (config) create: function (config)
{ {
var key = config.key; var key = config.key;
if (!key || this.anims.has(key)) var anim = false;
if (key)
{ {
console.warn('Invalid Animation Key, or Key already in use: ' + key); anim = this.get(key);
return;
if (!anim)
{
anim = new Animation(this, key, config);
this.anims.set(key, anim);
this.emit('add', key, anim);
}
} }
var anim = new Animation(this, key, config);
this.anims.set(key, anim);
this.emit('add', key, anim);
return anim; return anim;
}, },
@ -421,7 +452,7 @@ var AnimationManager = new Class({
* @param {string} key - The key of the animation to load. * @param {string} key - The key of the animation to load.
* @param {(string|integer)} [startFrame] - The name of a start frame to set on the loaded animation. * @param {(string|integer)} [startFrame] - The name of a start frame to set on the loaded animation.
* *
* @return {Phaser.GameObjects.GameObject} [description] * @return {Phaser.GameObjects.GameObject} The Game Object with the animation loaded into it.
*/ */
load: function (child, key, startFrame) load: function (child, key, startFrame)
{ {
@ -576,7 +607,7 @@ var AnimationManager = new Class({
}, },
/** /**
* [description] * Get the animation data as javascript object by giving key, or get the data of all animations as array of objects, if key wasn't provided.
* *
* @method Phaser.Animations.AnimationManager#toJSON * @method Phaser.Animations.AnimationManager#toJSON
* @since 3.0.0 * @since 3.0.0
@ -608,7 +639,8 @@ var AnimationManager = new Class({
}, },
/** /**
* [description] * Destroy this Animation Manager and clean up animation definitions and references to other objects.
* This method should not be called directly. It will be called automatically as a response to a `destroy` event from the Phaser.Game instance.
* *
* @method Phaser.Animations.AnimationManager#destroy * @method Phaser.Animations.AnimationManager#destroy
* @since 3.0.0 * @since 3.0.0

View file

@ -10,8 +10,7 @@ var Device = require('../device');
var GetFastValue = require('../utils/object/GetFastValue'); var GetFastValue = require('../utils/object/GetFastValue');
var GetValue = require('../utils/object/GetValue'); var GetValue = require('../utils/object/GetValue');
var IsPlainObject = require('../utils/object/IsPlainObject'); var IsPlainObject = require('../utils/object/IsPlainObject');
var MATH = require('../math/const'); var PhaserMath = require('../math/');
var RND = require('../math/random-data-generator/RandomDataGenerator');
var NOOP = require('../utils/NOOP'); var NOOP = require('../utils/NOOP');
var DefaultPlugins = require('../plugins/DefaultPlugins'); var DefaultPlugins = require('../plugins/DefaultPlugins');
var ValueToColor = require('../display/color/ValueToColor'); var ValueToColor = require('../display/color/ValueToColor');
@ -31,15 +30,13 @@ var ValueToColor = require('../display/color/ValueToColor');
/** /**
* Config object containing various sound settings. * Config object containing various sound settings.
* *
* @typedef {object} SoundConfig * @typedef {object} AudioConfig
* *
* @property {boolean} [mute=false] - Boolean indicating whether the sound should be muted or not. * @property {boolean} [disableWebAudio=false] - Use HTML5 Audio instead of Web Audio.
* @property {number} [volume=1] - A value between 0 (silence) and 1 (full volume). * @property {AudioContext} [context] - An existing Web Audio context.
* @property {number} [rate=1] - Defines the speed at which the sound should be played. * @property {boolean} [noAudio=false] - Disable all audio output.
* @property {number} [detune=0] - Represents detuning of sound in [cents](https://en.wikipedia.org/wiki/Cent_%28music%29). *
* @property {number} [seek=0] - Position of playback for this sound, in seconds. * @see Phaser.Sound.SoundManagerCreator
* @property {boolean} [loop=false] - Whether or not the sound or current sound marker should loop.
* @property {number} [delay=0] - Time, in seconds, that should elapse before the sound actually starts its playback.
*/ */
/** /**
@ -50,19 +47,21 @@ var ValueToColor = require('../display/color/ValueToColor');
* @property {(boolean|TouchInputConfig)} [touch=true] - Touch input configuration. `true` uses the default configuration and `false` disables touch input. * @property {(boolean|TouchInputConfig)} [touch=true] - Touch input configuration. `true` uses the default configuration and `false` disables touch input.
* @property {(boolean|GamepadInputConfig)} [gamepad=false] - Gamepad input configuration. `true` enables gamepad input. * @property {(boolean|GamepadInputConfig)} [gamepad=false] - Gamepad input configuration. `true` enables gamepad input.
* @property {integer} [activePointers=1] - The maximum number of touch pointers. See {@link Phaser.Input.InputManager#pointers}. * @property {integer} [activePointers=1] - The maximum number of touch pointers. See {@link Phaser.Input.InputManager#pointers}.
* @property {number} [smoothFactor=0] - The smoothing factor to apply during Pointer movement. See {@link Phaser.Input.Pointer#smoothFactor}.
*/ */
/** /**
* @typedef {object} MouseInputConfig * @typedef {object} MouseInputConfig
* *
* @property {*} [target=null] - Where the Mouse Manager listens for mouse input events. The default is the game canvas. * @property {*} [target=null] - Where the Mouse Manager listens for mouse input events. The default is the game canvas.
* @property {boolean} [capture=true] - Whether mouse input events have preventDefault() called on them. * @property {boolean} [capture=true] - Whether mouse input events have `preventDefault` called on them.
*/ */
/** /**
* @typedef {object} KeyboardInputConfig * @typedef {object} KeyboardInputConfig
* *
* @property {*} [target=window] - Where the Keyboard Manager listens for keyboard input events. * @property {*} [target=window] - Where the Keyboard Manager listens for keyboard input events.
* @property {?integer} [capture] - `preventDefault` will be called on every non-modified key which has a key code in this array. By default it is empty.
*/ */
/** /**
@ -179,6 +178,7 @@ var ValueToColor = require('../display/color/ValueToColor');
* @property {boolean} [start] - Whether the plugin should be started automatically. * @property {boolean} [start] - Whether the plugin should be started automatically.
* @property {string} [systemKey] - For a scene plugin, add the plugin to the scene's systems object under this key (`this.sys.KEY`, from the scene). * @property {string} [systemKey] - For a scene plugin, add the plugin to the scene's systems object under this key (`this.sys.KEY`, from the scene).
* @property {string} [sceneKey] - For a scene plugin, add the plugin to the scene object under this key (`this.KEY`, from the scene). * @property {string} [sceneKey] - For a scene plugin, add the plugin to the scene object under this key (`this.KEY`, from the scene).
* @property {string} [mapping] - If this plugin is to be injected into the Scene Systems, this is the property key map used.
* @property {*} [data] - Arbitrary data passed to the plugin's init() method. * @property {*} [data] - Arbitrary data passed to the plugin's init() method.
* *
* @example * @example
@ -355,6 +355,11 @@ var Config = new Class({
*/ */
this.canvasStyle = GetValue(config, 'canvasStyle', null); this.canvasStyle = GetValue(config, 'canvasStyle', null);
/**
* @const {boolean} Phaser.Boot.Config#customEnvironment - Is Phaser running under a custom (non-native web) environment? If so, set this to `true` to skip internal Feature detection. If `true` the `renderType` cannot be left as `AUTO`.
*/
this.customEnvironment = GetValue(config, 'customEnvironment', false);
/** /**
* @const {?object} Phaser.Boot.Config#sceneConfig - The default Scene configuration object. * @const {?object} Phaser.Boot.Config#sceneConfig - The default Scene configuration object.
*/ */
@ -365,9 +370,7 @@ var Config = new Class({
*/ */
this.seed = GetValue(config, 'seed', [ (Date.now() * Math.random()).toString() ]); this.seed = GetValue(config, 'seed', [ (Date.now() * Math.random()).toString() ]);
MATH.RND = new RND(); PhaserMath.RND = new PhaserMath.RandomDataGenerator(this.seed);
MATH.RND.init(this.seed);
/** /**
* @const {string} Phaser.Boot.Config#gameTitle - The title of the game. * @const {string} Phaser.Boot.Config#gameTitle - The title of the game.
@ -413,6 +416,11 @@ var Config = new Class({
*/ */
this.inputKeyboardEventTarget = GetValue(config, 'input.keyboard.target', window); this.inputKeyboardEventTarget = GetValue(config, 'input.keyboard.target', window);
/**
* @const {?integer[]} Phaser.Boot.Config#inputKeyboardCapture - `preventDefault` will be called on every non-modified key which has a key code in this array. By default, it is empty.
*/
this.inputKeyboardCapture = GetValue(config, 'input.keyboard.capture', []);
/** /**
* @const {(boolean|object)} Phaser.Boot.Config#inputMouse - Enable the Mouse Plugin. This can be disabled in games that don't need mouse input. * @const {(boolean|object)} Phaser.Boot.Config#inputMouse - Enable the Mouse Plugin. This can be disabled in games that don't need mouse input.
*/ */
@ -448,6 +456,11 @@ var Config = new Class({
*/ */
this.inputActivePointers = GetValue(config, 'input.activePointers', 1); this.inputActivePointers = GetValue(config, 'input.activePointers', 1);
/**
* @const {integer} Phaser.Boot.Config#inputSmoothFactor - The smoothing factor to apply during Pointer movement. See {@link Phaser.Input.Pointer#smoothFactor}.
*/
this.inputSmoothFactor = GetValue(config, 'input.smoothFactor', 0);
/** /**
* @const {boolean} Phaser.Boot.Config#inputGamepad - Enable the Gamepad Plugin. This can be disabled in games that don't need gamepad input. * @const {boolean} Phaser.Boot.Config#inputGamepad - Enable the Gamepad Plugin. This can be disabled in games that don't need gamepad input.
*/ */
@ -464,7 +477,7 @@ var Config = new Class({
this.disableContextMenu = GetValue(config, 'disableContextMenu', false); this.disableContextMenu = GetValue(config, 'disableContextMenu', false);
/** /**
* @const {SoundConfig} Phaser.Boot.Config#audio - The Audio Configuration object. * @const {AudioConfig} Phaser.Boot.Config#audio - The Audio Configuration object.
*/ */
this.audio = GetValue(config, 'audio'); this.audio = GetValue(config, 'audio');
@ -569,7 +582,7 @@ var Config = new Class({
var bgc = GetValue(config, 'backgroundColor', 0); var bgc = GetValue(config, 'backgroundColor', 0);
/** /**
* @const {Phaser.Display.Color} Phaser.Boot.Config#backgroundColor - The background color of the game canvas. The default is black. * @const {Phaser.Display.Color} Phaser.Boot.Config#backgroundColor - The background color of the game canvas. The default is black. This value is ignored if `transparent` is set to `true`.
*/ */
this.backgroundColor = ValueToColor(bgc); this.backgroundColor = ValueToColor(bgc);

View file

@ -23,10 +23,13 @@ var CreateRenderer = function (game)
{ {
var config = game.config; var config = game.config;
// Game either requested Canvas, if ((config.customEnvironment || config.canvas) && config.renderType === CONST.AUTO)
// or requested AUTO or WEBGL but the browser doesn't support it, so fall back to Canvas {
throw new Error('Must set explicit renderType in custom environment');
}
if (config.renderType !== CONST.HEADLESS) // Not a custom environment, didn't provide their own canvas and not headless, so determine the renderer:
if (!config.customEnvironment && !config.canvas && config.renderType !== CONST.HEADLESS)
{ {
if (config.renderType === CONST.CANVAS || (config.renderType !== CONST.CANVAS && !Features.webGL)) if (config.renderType === CONST.CANVAS || (config.renderType !== CONST.CANVAS && !Features.webGL))
{ {

View file

@ -329,17 +329,6 @@ var Game = new Class({
*/ */
this.hasFocus = false; this.hasFocus = false;
/**
* Is the mouse pointer currently over the game canvas or not?
* This is modified by the VisibilityHandler.
*
* @name Phaser.Game#isOver
* @type {boolean}
* @readonly
* @since 3.10.0
*/
this.isOver = true;
// Wait for the DOM Ready event, then call boot. // Wait for the DOM Ready event, then call boot.
DOMContentLoaded(this.boot.bind(this)); DOMContentLoaded(this.boot.bind(this));
}, },
@ -743,6 +732,34 @@ var Game = new Class({
this.events.emit('resize', width, height); this.events.emit('resize', width, height);
}, },
/**
* Returns the current game frame.
* When the game starts running, the frame is incremented every time Request Animation Frame, or Set Timeout, fires.
*
* @method Phaser.Game#getFrame
* @since 3.16.0
*
* @return {number} The current game frame.
*/
getFrame: function ()
{
return this.loop.frame;
},
/**
* Returns the current game timestamp.
* When the game starts running, the frame is incremented every time Request Animation Frame, or Set Timeout, fires.
*
* @method Phaser.Game#getTime
* @since 3.16.0
*
* @return {number} The current game timestamp.
*/
getTime: function ()
{
return this.loop.frame.time;
},
/** /**
* Game Destroy event. * Game Destroy event.
* *

View file

@ -450,8 +450,6 @@ var TimeStep = new Class({
*/ */
step: function (time) step: function (time)
{ {
this.frame++;
var before = time - this.lastTime; var before = time - this.lastTime;
if (before < 0) if (before < 0)
@ -561,6 +559,8 @@ var TimeStep = new Class({
// Shift time value over // Shift time value over
this.lastTime = time; this.lastTime = time;
this.frame++;
}, },
/** /**
@ -607,7 +607,7 @@ var TimeStep = new Class({
} }
else if (seamless) else if (seamless)
{ {
this.startTime += -this.lastTime + (this.lastTime = window.performance.now()); this.startTime += -this.lastTime + (this.lastTime + window.performance.now());
} }
this.raf.start(this.step.bind(this), this.useRAF); this.raf.start(this.step.bind(this), this.useRAF);

View file

@ -109,26 +109,6 @@ var VisibilityHandler = function (game)
if (window.focus && game.config.autoFocus) if (window.focus && game.config.autoFocus)
{ {
window.focus(); window.focus();
game.canvas.addEventListener('mousedown', function ()
{
window.focus();
}, { passive: true });
}
if (game.canvas)
{
game.canvas.onmouseout = function ()
{
game.isOver = false;
eventEmitter.emit('mouseout');
};
game.canvas.onmouseover = function ()
{
game.isOver = true;
eventEmitter.emit('mouseover');
};
} }
}; };

View file

@ -568,7 +568,7 @@ var BaseCamera = new Class({
* @param {number} y - The vertical coordinate to center on. * @param {number} y - The vertical coordinate to center on.
* @param {Phaser.Math.Vector2} [out] - A Vec2 to store the values in. If not given a new Vec2 is created. * @param {Phaser.Math.Vector2} [out] - A Vec2 to store the values in. If not given a new Vec2 is created.
* *
* @return {Phaser.Math.Vector2} The scroll coordinates stored in the `x` abd `y` properties. * @return {Phaser.Math.Vector2} The scroll coordinates stored in the `x` and `y` properties.
*/ */
getScroll: function (x, y, out) getScroll: function (x, y, out)
{ {
@ -589,6 +589,60 @@ var BaseCamera = new Class({
return out; return out;
}, },
/**
* Moves the Camera horizontally so that it is centered on the given x coordinate, bounds allowing.
* Calling this does not change the scrollY value.
*
* @method Phaser.Cameras.Scene2D.BaseCamera#centerOnX
* @since 3.16.0
*
* @param {number} x - The horizontal coordinate to center on.
*
* @return {Phaser.Cameras.Scene2D.BaseCamera} This Camera instance.
*/
centerOnX: function (x)
{
var originX = this.width * 0.5;
this.midPoint.x = x;
this.scrollX = x - originX;
if (this.useBounds)
{
this.scrollX = this.clampX(this.scrollX);
}
return this;
},
/**
* Moves the Camera vertically so that it is centered on the given y coordinate, bounds allowing.
* Calling this does not change the scrollX value.
*
* @method Phaser.Cameras.Scene2D.BaseCamera#centerOnY
* @since 3.16.0
*
* @param {number} y - The vertical coordinate to center on.
*
* @return {Phaser.Cameras.Scene2D.BaseCamera} This Camera instance.
*/
centerOnY: function (y)
{
var originY = this.height * 0.5;
this.midPoint.y = y;
this.scrollY = y - originY;
if (this.useBounds)
{
this.scrollY = this.clampY(this.scrollY);
}
return this;
},
/** /**
* Moves the Camera so that it is centered on the given coordinates, bounds allowing. * Moves the Camera so that it is centered on the given coordinates, bounds allowing.
* *
@ -602,19 +656,8 @@ var BaseCamera = new Class({
*/ */
centerOn: function (x, y) centerOn: function (x, y)
{ {
var originX = this.width * 0.5; this.centerOnX(x);
var originY = this.height * 0.5; this.centerOnY(y);
this.midPoint.set(x, y);
this.scrollX = x - originX;
this.scrollY = y - originY;
if (this.useBounds)
{
this.scrollX = this.clampX(this.scrollX);
this.scrollY = this.clampY(this.scrollY);
}
return this; return this;
}, },
@ -1079,12 +1122,14 @@ var BaseCamera = new Class({
* @param {integer} y - The top-left y coordinate of the bounds. * @param {integer} y - The top-left y coordinate of the bounds.
* @param {integer} width - The width of the bounds, in pixels. * @param {integer} width - The width of the bounds, in pixels.
* @param {integer} height - The height of the bounds, in pixels. * @param {integer} height - The height of the bounds, in pixels.
* @param {boolean} [centerOn] - If `true` the Camera will automatically be centered on the new bounds. * @param {boolean} [centerOn=false] - If `true` the Camera will automatically be centered on the new bounds.
* *
* @return {Phaser.Cameras.Scene2D.BaseCamera} This Camera instance. * @return {Phaser.Cameras.Scene2D.BaseCamera} This Camera instance.
*/ */
setBounds: function (x, y, width, height, centerOn) setBounds: function (x, y, width, height, centerOn)
{ {
if (centerOn === undefined) { centerOn = false; }
this._bounds.setTo(x, y, width, height); this._bounds.setTo(x, y, width, height);
this.dirty = true; this.dirty = true;
@ -1103,6 +1148,31 @@ var BaseCamera = new Class({
return this; return this;
}, },
/**
* Returns a rectangle containing the bounds of the Camera.
*
* If the Camera does not have any bounds the rectangle will be empty.
*
* The rectangle is a copy of the bounds, so is safe to modify.
*
* @method Phaser.Cameras.Scene2D.BaseCamera#getBounds
* @since 3.16.0
*
* @param {Phaser.Geom.Rectangle} [out] - An optional Rectangle to store the bounds in. If not given, a new Rectangle will be created.
*
* @return {Phaser.Geom.Rectangle} A rectangle containing the bounds of this Camera.
*/
getBounds: function (out)
{
if (out === undefined) { out = new Rectangle(); }
var source = this._bounds;
out.setTo(source.x, source.y, source.width, source.height);
return out;
},
/** /**
* Sets the name of this Camera. * Sets the name of this Camera.
* This value is for your own use and isn't used internally. * This value is for your own use and isn't used internally.
@ -1701,7 +1771,7 @@ var BaseCamera = new Class({
}, },
/** /**
* The x position of the center of the Camera's viewport, relative to the top-left of the game canvas. * The horizontal position of the center of the Camera's viewport, relative to the left of the game canvas.
* *
* @name Phaser.Cameras.Scene2D.BaseCamera#centerX * @name Phaser.Cameras.Scene2D.BaseCamera#centerX
* @type {number} * @type {number}
@ -1718,7 +1788,7 @@ var BaseCamera = new Class({
}, },
/** /**
* The y position of the center of the Camera's viewport, relative to the top-left of the game canvas. * The vertical position of the center of the Camera's viewport, relative to the top of the game canvas.
* *
* @name Phaser.Cameras.Scene2D.BaseCamera#centerY * @name Phaser.Cameras.Scene2D.BaseCamera#centerY
* @type {number} * @type {number}

View file

@ -270,8 +270,8 @@ var Shake = new Class({
if (this.camera.roundPixels) if (this.camera.roundPixels)
{ {
this._offsetX |= 0; this._offsetX = Math.round(this._offsetX);
this._offsetY |= 0; this._offsetY = Math.round(this._offsetY);
} }
} }
else else

View file

@ -20,7 +20,7 @@ var CONST = {
* @type {string} * @type {string}
* @since 3.0.0 * @since 3.0.0
*/ */
VERSION: '3.16.0 Beta 1', VERSION: '3.16.0 Beta 4',
BlendModes: require('./renderer/BlendModes'), BlendModes: require('./renderer/BlendModes'),

View file

@ -9,15 +9,15 @@ var Vector2 = require('../../math/Vector2');
/** /**
* @classdesc * @classdesc
* [description] * A MoveTo Curve is a very simple curve consisting of only a single point. Its intended use is to move the ending point in a Path.
* *
* @class MoveTo * @class MoveTo
* @memberof Phaser.Curves * @memberof Phaser.Curves
* @constructor * @constructor
* @since 3.0.0 * @since 3.0.0
* *
* @param {number} [x] - [description] * @param {number} [x] - `x` pixel coordinate.
* @param {number} [y] - [description] * @param {number} [y] - `y` pixel coordinate.
*/ */
var MoveTo = new Class({ var MoveTo = new Class({
@ -28,7 +28,7 @@ var MoveTo = new Class({
// Skip length calcs in paths // Skip length calcs in paths
/** /**
* [description] * Denotes that this Curve does not influence the bounds, points, and drawing of its parent Path. Must be `false` or some methods in the parent Path will throw errors.
* *
* @name Phaser.Curves.MoveTo#active * @name Phaser.Curves.MoveTo#active
* @type {boolean} * @type {boolean}
@ -38,7 +38,7 @@ var MoveTo = new Class({
this.active = false; this.active = false;
/** /**
* [description] * The lone point which this curve consists of.
* *
* @name Phaser.Curves.MoveTo#p0 * @name Phaser.Curves.MoveTo#p0
* @type {Phaser.Math.Vector2} * @type {Phaser.Math.Vector2}
@ -68,17 +68,17 @@ var MoveTo = new Class({
}, },
/** /**
* [description] * Retrieves the point at given position in the curve. This will always return this curve's only point.
* *
* @method Phaser.Curves.MoveTo#getPointAt * @method Phaser.Curves.MoveTo#getPointAt
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Math.Vector2} O - [out,$return] * @generic {Phaser.Math.Vector2} O - [out,$return]
* *
* @param {number} u - [description] * @param {number} u - The position in the path to retrieve, between 0 and 1. Not used.
* @param {Phaser.Math.Vector2} [out] - [description] * @param {Phaser.Math.Vector2} [out] - An optional vector in which to store the point.
* *
* @return {Phaser.Math.Vector2} [description] * @return {Phaser.Math.Vector2} The modified `out` vector, or a new `Vector2` if none was provided.
*/ */
getPointAt: function (u, out) getPointAt: function (u, out)
{ {
@ -112,12 +112,12 @@ var MoveTo = new Class({
}, },
/** /**
* [description] * Converts this curve into a JSON-serializable object.
* *
* @method Phaser.Curves.MoveTo#toJSON * @method Phaser.Curves.MoveTo#toJSON
* @since 3.0.0 * @since 3.0.0
* *
* @return {JSONCurve} [description] * @return {JSONCurve} A primitive object with the curve's type and only point.
*/ */
toJSON: function () toJSON: function ()
{ {

View file

@ -92,16 +92,19 @@ var RequestAnimationFrame = new Class({
* @type {FrameRequestCallback} * @type {FrameRequestCallback}
* @since 3.0.0 * @since 3.0.0
*/ */
this.step = function step (timestamp) this.step = function step ()
{ {
// Because we cannot trust the time passed to this callback from the browser and need it kept in sync with event times
var timestamp = window.performance.now();
// DOMHighResTimeStamp // DOMHighResTimeStamp
_this.lastTime = _this.tick; _this.lastTime = _this.tick;
_this.tick = timestamp; _this.tick = timestamp;
_this.timeOutID = window.requestAnimationFrame(step);
_this.callback(timestamp); _this.callback(timestamp);
_this.timeOutID = window.requestAnimationFrame(step);
}; };
/** /**
@ -122,9 +125,9 @@ var RequestAnimationFrame = new Class({
_this.tick = d; _this.tick = d;
_this.timeOutID = window.setTimeout(stepTimeout, delay);
_this.callback(d); _this.callback(d);
_this.timeOutID = window.setTimeout(stepTimeout, delay);
}; };
}, },

View file

@ -7,8 +7,10 @@
var Class = require('../utils/Class'); var Class = require('../utils/Class');
var CONST = require('./const'); var CONST = require('./const');
var NOOP = require('../utils/NOOP'); var NOOP = require('../utils/NOOP');
var Vec2 = require('../math/Vector2');
var Rectangle = require('../geom/rectangle/Rectangle'); var Rectangle = require('../geom/rectangle/Rectangle');
var Size = require('../structs/Size');
var SnapFloor = require('../math/snap/SnapFloor');
var Vec2 = require('../math/Vector2');
/** /**
* @classdesc * @classdesc
@ -41,19 +43,25 @@ var ScaleManager = new Class({
this.scaleMode = 0; this.scaleMode = 0;
// The base game size, as requested in the game config // The base game size, as requested in the game config
this.width = 0; this.gameSize = new Size();
this.height = 0;
// The canvas size, which is the base game size * zoom * resolution // The canvas size, which is the base game size * zoom * resolution
this.canvasWidth = 0; this.canvasSize = new Size();
this.canvasHeight = 0;
// this.width = 0;
// this.height = 0;
// this.canvasWidth = 0;
// this.canvasHeight = 0;
this.resolution = 1; this.resolution = 1;
this.zoom = 1; this.zoom = 1;
// The actual displayed canvas size (after refactoring in CSS depending on the scale mode, parent, etc) // The actual displayed canvas size (after refactoring in CSS depending on the scale mode, parent, etc)
this.displayWidth = 0; this.displaySize = new Size();
this.displayHeight = 0;
// this.displayWidth = 0;
// this.displayHeight = 0;
// The scale factor between the base game size and the displayed size // The scale factor between the base game size and the displayed size
this.scale = new Vec2(1); this.scale = new Vec2(1);
@ -71,6 +79,8 @@ var ScaleManager = new Class({
this.allowFullScreen = false; this.allowFullScreen = false;
this.snap = new Vec2(1, 1);
this.listeners = { this.listeners = {
orientationChange: NOOP, orientationChange: NOOP,

View file

@ -141,9 +141,9 @@ var EventEmitter = new Class({
* @since 3.0.0 * @since 3.0.0
* *
* @param {(string|symbol)} event - The event name. * @param {(string|symbol)} event - The event name.
* @param {function} fn - Only remove the listeners that match this function. * @param {function} [fn] - Only remove the listeners that match this function.
* @param {*} context - Only remove the listeners that have this context. * @param {*} [context] - Only remove the listeners that have this context.
* @param {boolean} once - Only remove one-time listeners. * @param {boolean} [once] - Only remove one-time listeners.
* *
* @return {Phaser.Events.EventEmitter} `this`. * @return {Phaser.Events.EventEmitter} `this`.
*/ */
@ -155,9 +155,9 @@ var EventEmitter = new Class({
* @since 3.0.0 * @since 3.0.0
* *
* @param {(string|symbol)} event - The event name. * @param {(string|symbol)} event - The event name.
* @param {function} fn - Only remove the listeners that match this function. * @param {function} [fn] - Only remove the listeners that match this function.
* @param {*} context - Only remove the listeners that have this context. * @param {*} [context] - Only remove the listeners that have this context.
* @param {boolean} once - Only remove one-time listeners. * @param {boolean} [once] - Only remove one-time listeners.
* *
* @return {Phaser.Events.EventEmitter} `this`. * @return {Phaser.Events.EventEmitter} `this`.
*/ */

View file

@ -61,8 +61,8 @@ var GameObject = new Class({
* Phaser itself will never modify this value, although plugins may do so. * Phaser itself will never modify this value, although plugins may do so.
* *
* Use this property to track the state of a Game Object during its lifetime. For example, it could move from * Use this property to track the state of a Game Object during its lifetime. For example, it could move from
* a state of 'moving', to 'attacking', to 'dead'. The state value should typically be an integer (ideally mapped to a constant * a state of 'moving', to 'attacking', to 'dead'. The state value should be an integer (ideally mapped to a constant
* in your game code), but could also be a string, or any other data-type. It is recommended to keep it light and simple. * in your game code), or a string. These are recommended to keep it light and simple, with fast comparisons.
* If you need to store complex data about your Game Object, look at using the Data Component instead. * If you need to store complex data about your Game Object, look at using the Data Component instead.
* *
* @name Phaser.GameObjects.GameObject#state * @name Phaser.GameObjects.GameObject#state
@ -227,6 +227,30 @@ var GameObject = new Class({
return this; return this;
}, },
/**
* Sets the current state of this Game Object.
*
* Phaser itself will never modify the State of a Game Object, although plugins may do so.
*
* For example, a Game Object could change from a state of 'moving', to 'attacking', to 'dead'.
* The state value should typically be an integer (ideally mapped to a constant
* in your game code), but could also be a string. It is recommended to keep it light and simple.
* If you need to store complex data about your Game Object, look at using the Data Component instead.
*
* @method Phaser.GameObjects.GameObject#setState
* @since 3.16.0
*
* @param {(integer|string)} value - The state of the Game Object.
*
* @return {this} This GameObject.
*/
setState: function (value)
{
this.state = value;
return this;
},
/** /**
* Adds a Data Manager component to this Game Object. * Adds a Data Manager component to this Game Object.
* *

View file

@ -72,7 +72,6 @@ var DynamicBitmapTextCanvasRenderer = function (renderer, src, interpolationPerc
if (src.cropWidth > 0 && src.cropHeight > 0) if (src.cropWidth > 0 && src.cropHeight > 0)
{ {
ctx.save();
ctx.beginPath(); ctx.beginPath();
ctx.rect(0, 0, src.cropWidth, src.cropHeight); ctx.rect(0, 0, src.cropWidth, src.cropHeight);
ctx.clip(); ctx.clip();
@ -145,8 +144,8 @@ var DynamicBitmapTextCanvasRenderer = function (renderer, src, interpolationPerc
if (camera.roundPixels) if (camera.roundPixels)
{ {
x |= 0; x = Math.round(x);
y |= 0; y = Math.round(y);
} }
ctx.save(); ctx.save();
@ -167,11 +166,7 @@ var DynamicBitmapTextCanvasRenderer = function (renderer, src, interpolationPerc
lastCharCode = charCode; lastCharCode = charCode;
} }
if (src.cropWidth > 0 && src.cropHeight > 0) // Restore the context saved in SetTransform
{
ctx.restore();
}
ctx.restore(); ctx.restore();
}; };

View file

@ -273,20 +273,20 @@ var DynamicBitmapTextWebGLRenderer = function (renderer, src, interpolationPerce
if (roundPixels) if (roundPixels)
{ {
tx0 |= 0; tx0 = Math.round(tx0);
ty0 |= 0; ty0 = Math.round(ty0);
tx1 |= 0; tx1 = Math.round(tx1);
ty1 |= 0; ty1 = Math.round(ty1);
tx2 |= 0; tx2 = Math.round(tx2);
ty2 |= 0; ty2 = Math.round(ty2);
tx3 |= 0; tx3 = Math.round(tx3);
ty3 |= 0; ty3 = Math.round(ty3);
} }
pipeline.batchQuad(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect); pipeline.batchQuad(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect, texture, 0);
} }
if (crop) if (crop)

View file

@ -68,12 +68,12 @@ var Render = require('./BitmapTextRender');
* *
* To create a BitmapText data files you need a 3rd party app such as: * To create a BitmapText data files you need a 3rd party app such as:
* *
* BMFont (Windows, free): http://www.angelcode.com/products/bmfont/ * BMFont (Windows, free): {@link http://www.angelcode.com/products/bmfont/|http://www.angelcode.com/products/bmfont/}
* Glyph Designer (OS X, commercial): http://www.71squared.com/en/glyphdesigner * Glyph Designer (OS X, commercial): {@link http://www.71squared.com/en/glyphdesigner|http://www.71squared.com/en/glyphdesigner}
* Littera (Web-based, free): http://kvazars.com/littera/ * Littera (Web-based, free): {@link http://kvazars.com/littera/|http://kvazars.com/littera/}
* *
* For most use cases it is recommended to use XML. If you wish to use JSON, the formatting should be equal to the result of * For most use cases it is recommended to use XML. If you wish to use JSON, the formatting should be equal to the result of
* converting a valid XML file through the popular X2JS library. An online tool for conversion can be found here: http://codebeautify.org/xmltojson * converting a valid XML file through the popular X2JS library. An online tool for conversion can be found here: {@link http://codebeautify.org/xmltojson|http://codebeautify.org/xmltojson}
* *
* @class BitmapText * @class BitmapText
* @extends Phaser.GameObjects.GameObject * @extends Phaser.GameObjects.GameObject

View file

@ -148,8 +148,8 @@ var BitmapTextCanvasRenderer = function (renderer, src, interpolationPercentage,
if (roundPixels) if (roundPixels)
{ {
x |= 0; x = Math.round(x);
y |= 0; y = Math.round(y);
} }
ctx.save(); ctx.save();

View file

@ -203,20 +203,20 @@ var BitmapTextWebGLRenderer = function (renderer, src, interpolationPercentage,
if (roundPixels) if (roundPixels)
{ {
tx0 |= 0; tx0 = Math.round(tx0);
ty0 |= 0; ty0 = Math.round(ty0);
tx1 |= 0; tx1 = Math.round(tx1);
ty1 |= 0; ty1 = Math.round(ty1);
tx2 |= 0; tx2 = Math.round(tx2);
ty2 |= 0; ty2 = Math.round(ty2);
tx3 |= 0; tx3 = Math.round(tx3);
ty3 |= 0; ty3 = Math.round(ty3);
} }
pipeline.batchQuad(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect); pipeline.batchQuad(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect, texture, 0);
} }
}; };

View file

@ -78,8 +78,8 @@ var BlitterCanvasRenderer = function (renderer, src, interpolationPercentage, ca
{ {
if (roundPixels) if (roundPixels)
{ {
dx |= 0; dx = Math.round(dx);
dy |= 0; dy = Math.round(dy);
} }
ctx.drawImage( ctx.drawImage(

View file

@ -106,15 +106,15 @@ var BlitterWebGLRenderer = function (renderer, src, interpolationPercentage, cam
if (roundPixels) if (roundPixels)
{ {
tx0 |= 0; tx0 = Math.round(tx0);
ty0 |= 0; ty0 = Math.round(ty0);
tx1 |= 0; tx1 = Math.round(tx1);
ty1 |= 0; ty1 = Math.round(ty1);
} }
// TL x/y, BL x/y, BR x/y, TR x/y // TL x/y, BL x/y, BR x/y, TR x/y
if (pipeline.batchQuad(tx0, ty0, tx0, ty1, tx1, ty1, tx1, ty0, frame.u0, frame.v0, frame.u1, frame.v1, tint, tint, tint, tint, tintEffect)) if (pipeline.batchQuad(tx0, ty0, tx0, ty1, tx1, ty1, tx1, ty0, frame.u0, frame.v0, frame.u1, frame.v1, tint, tint, tint, tint, tintEffect, frame.glTexture, 0))
{ {
prevTextureSourceIndex = -1; prevTextureSourceIndex = -1;
} }

View file

@ -4,6 +4,7 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/ */
var BaseAnimation = require('../../animations/Animation');
var Class = require('../../utils/Class'); var Class = require('../../utils/Class');
/** /**
@ -11,6 +12,11 @@ var Class = require('../../utils/Class');
* *
* Listen for it on the Game Object: `sprite.on('animationstart', listener)` * Listen for it on the Game Object: `sprite.on('animationstart', listener)`
* *
* You can also listen for a specific animation by appending a hyphen and its key to the event name. For example,
* if you have an animation called `explode`, you can listen for `sprite.on('animationstart-explode', listener)`.
*
* You can also listen for the `start` event from the Animation itself: `animation.on('start', listener)`.
*
* @event Phaser.GameObjects.Components.Animation#onStartEvent * @event Phaser.GameObjects.Components.Animation#onStartEvent
* @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation. * @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation.
* @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame. * @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame.
@ -22,6 +28,11 @@ var Class = require('../../utils/Class');
* *
* Listen for it on the Game Object: `sprite.on('animationrestart', listener)` * Listen for it on the Game Object: `sprite.on('animationrestart', listener)`
* *
* You can also listen for a specific animation by appending a hyphen and its key to the event name. For example,
* if you have an animation called `explode`, you can listen for `sprite.on('animationrestart-explode', listener)`.
*
* You can also listen for the `restart` event from the Animation itself: `animation.on('restart', listener)`.
*
* @event Phaser.GameObjects.Components.Animation#onRestartEvent * @event Phaser.GameObjects.Components.Animation#onRestartEvent
* @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation. * @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation.
* @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame. * @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame.
@ -33,6 +44,9 @@ var Class = require('../../utils/Class');
* *
* Listen for it on the Game Object: `sprite.on('animationrepeat', listener)` * Listen for it on the Game Object: `sprite.on('animationrepeat', listener)`
* *
* You can also listen for a specific animation by appending a hyphen and its key to the event name. For example,
* if you have an animation called `explode`, you can listen for `sprite.on('animationrepeat-explode', listener)`.
*
* @event Phaser.GameObjects.Components.Animation#onRepeatEvent * @event Phaser.GameObjects.Components.Animation#onRepeatEvent
* @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation. * @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation.
* @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame. * @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame.
@ -46,6 +60,9 @@ var Class = require('../../utils/Class');
* *
* Listen for it on the Game Object: `sprite.on('animationupdate', listener)` * Listen for it on the Game Object: `sprite.on('animationupdate', listener)`
* *
* You can also listen for a specific animation by appending a hyphen and its key to the event name. For example,
* if you have an animation called `explode`, you can listen for `sprite.on('animationupdate-explode', listener)`.
*
* @event Phaser.GameObjects.Components.Animation#onUpdateEvent * @event Phaser.GameObjects.Components.Animation#onUpdateEvent
* @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation. * @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation.
* @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame. * @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame.
@ -57,6 +74,11 @@ var Class = require('../../utils/Class');
* *
* Listen for it on the Game Object: `sprite.on('animationcomplete', listener)` * Listen for it on the Game Object: `sprite.on('animationcomplete', listener)`
* *
* You can also listen for a specific animation by appending a hyphen and its key to the event name. For example,
* if you have an animation called `explode`, you can listen for `sprite.on('animationcomplete-explode', listener)`.
*
* You can also listen for the `complete` event from the Animation itself: `animation.on('complete', listener)`.
*
* @event Phaser.GameObjects.Components.Animation#onCompleteEvent * @event Phaser.GameObjects.Components.Animation#onCompleteEvent
* @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation. * @param {Phaser.Animations.Animation} animation - Reference to the currently playing animation.
* @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame. * @param {Phaser.Animations.AnimationFrame} frame - Reference to the current Animation Frame.
@ -132,6 +154,16 @@ var Animation = new Class({
*/ */
this.currentFrame = null; this.currentFrame = null;
/**
* The key of the next Animation to be loaded into this Animation Controller when the current animation completes.
*
* @name Phaser.GameObjects.Components.Animation#nextAnim
* @type {?string}
* @default null
* @since 3.16.0
*/
this.nextAnim = null;
/** /**
* Time scale factor. * Time scale factor.
* *
@ -339,6 +371,37 @@ var Animation = new Class({
this._pendingStopValue; this._pendingStopValue;
}, },
/**
* Sets an animation to be played immediately after the current one completes.
*
* The current animation must enter a 'completed' state for this to happen, i.e. finish all of its repeats, delays, etc, or have the `stop` method called directly on it.
*
* An animation set to repeat forever will never enter a completed state.
*
* You can chain a new animation at any point, including before the current one starts playing, during it, or when it ends (via its `animationcomplete` callback).
* Chained animations are specific to a Game Object, meaning different Game Objects can have different chained animations without impacting the global animation they're playing.
*
* Call this method with no arguments to reset the chained animation.
*
* @method Phaser.GameObjects.Components.Animation#chain
* @since 3.16.0
*
* @param {(string|Phaser.Animations.Animation)} [key] - The string-based key of the animation to play next, as defined previously in the Animation Manager. Or an Animation instance.
*
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
*/
chain: function (key)
{
if (key instanceof BaseAnimation)
{
key = key.key;
}
this.nextAnim = key;
return this.parent;
},
/** /**
* Sets the amount of time, in milliseconds, that the animation will be delayed before starting playback. * Sets the amount of time, in milliseconds, that the animation will be delayed before starting playback.
* *
@ -508,13 +571,15 @@ var Animation = new Class({
}, },
/** /**
* Plays an Animation on the Game Object that owns this Animation Component. * Plays an Animation on a Game Object that has the Animation component, such as a Sprite.
*
* Animations are stored in the global Animation Manager and are referenced by a unique string-based key.
* *
* @method Phaser.GameObjects.Components.Animation#play * @method Phaser.GameObjects.Components.Animation#play
* @fires Phaser.GameObjects.Components.Animation#onStartEvent * @fires Phaser.GameObjects.Components.Animation#onStartEvent
* @since 3.0.0 * @since 3.0.0
* *
* @param {string} key - The string-based key of the animation to play, as defined previously in the Animation Manager. * @param {(string|Phaser.Animations.Animation)} key - The string-based key of the animation to play, as defined previously in the Animation Manager. Or an Animation instance.
* @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call. * @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
* @param {integer} [startFrame=0] - Optionally start the animation playing from this frame index. * @param {integer} [startFrame=0] - Optionally start the animation playing from this frame index.
* *
@ -525,6 +590,11 @@ var Animation = new Class({
if (ignoreIfPlaying === undefined) { ignoreIfPlaying = false; } if (ignoreIfPlaying === undefined) { ignoreIfPlaying = false; }
if (startFrame === undefined) { startFrame = 0; } if (startFrame === undefined) { startFrame = 0; }
if (key instanceof BaseAnimation)
{
key = key.key;
}
if (ignoreIfPlaying && this.isPlaying && this.currentAnim.key === key) if (ignoreIfPlaying && this.isPlaying && this.currentAnim.key === key)
{ {
return this.parent; return this.parent;
@ -543,7 +613,7 @@ var Animation = new Class({
* @fires Phaser.GameObjects.Components.Animation#onStartEvent * @fires Phaser.GameObjects.Components.Animation#onStartEvent
* @since 3.12.0 * @since 3.12.0
* *
* @param {string} key - The string-based key of the animation to play, as defined previously in the Animation Manager. * @param {(string|Phaser.Animations.Animation)} key - The string-based key of the animation to play, as defined previously in the Animation Manager. Or an Animation instance.
* @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call. * @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
* @param {integer} [startFrame=0] - Optionally start the animation playing from this frame index. * @param {integer} [startFrame=0] - Optionally start the animation playing from this frame index.
* *
@ -554,6 +624,11 @@ var Animation = new Class({
if (ignoreIfPlaying === undefined) { ignoreIfPlaying = false; } if (ignoreIfPlaying === undefined) { ignoreIfPlaying = false; }
if (startFrame === undefined) { startFrame = 0; } if (startFrame === undefined) { startFrame = 0; }
if (key instanceof BaseAnimation)
{
key = key.key;
}
if (ignoreIfPlaying && this.isPlaying && this.currentAnim.key === key) if (ignoreIfPlaying && this.isPlaying && this.currentAnim.key === key)
{ {
return this.parent; return this.parent;
@ -566,8 +641,7 @@ var Animation = new Class({
}, },
/** /**
* Load an Animation and fires 'onStartEvent' event, * Load an Animation and fires 'onStartEvent' event, extracted from 'play' method.
* extracted from 'play' method
* *
* @method Phaser.GameObjects.Components.Animation#_startAnimation * @method Phaser.GameObjects.Components.Animation#_startAnimation
* @fires Phaser.GameObjects.Components.Animation#onStartEvent * @fires Phaser.GameObjects.Components.Animation#onStartEvent
@ -598,26 +672,33 @@ var Animation = new Class({
gameObject.visible = true; gameObject.visible = true;
} }
gameObject.emit('animationstart', this.currentAnim, this.currentFrame, gameObject); var frame = this.currentFrame;
anim.emit('start', anim, frame);
gameObject.emit('animationstart-' + key, anim, frame, gameObject);
gameObject.emit('animationstart', anim, frame, gameObject);
return gameObject; return gameObject;
}, },
/** /**
* Reverse an Animation that is already playing on the Game Object. * Reverse the Animation that is already playing on the Game Object.
* *
* @method Phaser.GameObjects.Components.Animation#reverse * @method Phaser.GameObjects.Components.Animation#reverse
* @since 3.12.0 * @since 3.12.0
* *
* @param {string} key - The string-based key of the animation to play, as defined previously in the Animation Manager.
*
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component. * @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
*/ */
reverse: function (key) reverse: function ()
{ {
if (!this.isPlaying || this.currentAnim.key !== key) { return this.parent; } if (this.isPlaying)
this._reverse = !this._reverse; {
this.forward = !this.forward; this._reverse = !this._reverse;
this.forward = !this.forward;
}
return this.parent; return this.parent;
}, },
@ -774,7 +855,9 @@ var Animation = new Class({
{ {
if (includeDelay === undefined) { includeDelay = false; } if (includeDelay === undefined) { includeDelay = false; }
this.currentAnim.getFirstTick(this, includeDelay); var anim = this.currentAnim;
anim.getFirstTick(this, includeDelay);
this.forward = true; this.forward = true;
this.isPlaying = true; this.isPlaying = true;
@ -782,11 +865,16 @@ var Animation = new Class({
this._paused = false; this._paused = false;
// Set frame // Set frame
this.updateFrame(this.currentAnim.frames[0]); this.updateFrame(anim.frames[0]);
var gameObject = this.parent; var gameObject = this.parent;
var frame = this.currentFrame;
gameObject.emit('animationrestart', this.currentAnim, this.currentFrame, gameObject); anim.emit('restart', anim, frame);
gameObject.emit('animationrestart-' + anim.key, anim, frame, gameObject);
gameObject.emit('animationrestart', anim, frame, gameObject);
return this.parent; return this.parent;
}, },
@ -794,6 +882,10 @@ var Animation = new Class({
/** /**
* Immediately stops the current animation from playing and dispatches the `animationcomplete` event. * Immediately stops the current animation from playing and dispatches the `animationcomplete` event.
* *
* If no animation is set, no event will be dispatched.
*
* If there is another animation queued (via the `chain` method) then it will start playing immediately.
*
* @method Phaser.GameObjects.Components.Animation#stop * @method Phaser.GameObjects.Components.Animation#stop
* @fires Phaser.GameObjects.Components.Animation#onCompleteEvent * @fires Phaser.GameObjects.Components.Animation#onCompleteEvent
* @since 3.0.0 * @since 3.0.0
@ -807,8 +899,26 @@ var Animation = new Class({
this.isPlaying = false; this.isPlaying = false;
var gameObject = this.parent; var gameObject = this.parent;
var anim = this.currentAnim;
var frame = this.currentFrame;
gameObject.emit('animationcomplete', this.currentAnim, this.currentFrame, gameObject); if (anim)
{
anim.emit('complete', anim, frame);
gameObject.emit('animationcomplete-' + anim.key, anim, frame, gameObject);
gameObject.emit('animationcomplete', anim, frame, gameObject);
}
if (this.nextAnim)
{
var key = this.nextAnim;
this.nextAnim = null;
this.play(key);
}
return gameObject; return gameObject;
}, },
@ -856,7 +966,7 @@ var Animation = new Class({
* @fires Phaser.GameObjects.Components.Animation#onCompleteEvent * @fires Phaser.GameObjects.Components.Animation#onCompleteEvent
* @since 3.4.0 * @since 3.4.0
* *
* @param {Phaser.Animations.AnimationFrame} delay - The frame to check before stopping this animation. * @param {Phaser.Animations.AnimationFrame} frame - The frame to check before stopping this animation.
* *
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component. * @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
*/ */
@ -1010,6 +1120,8 @@ var Animation = new Class({
var anim = this.currentAnim; var anim = this.currentAnim;
gameObject.emit('animationupdate-' + anim.key, anim, animationFrame, gameObject);
gameObject.emit('animationupdate', anim, animationFrame, gameObject); gameObject.emit('animationupdate', anim, animationFrame, gameObject);
if (this._pendingStop === 3 && this._pendingStopValue === animationFrame) if (this._pendingStop === 3 && this._pendingStopValue === animationFrame)
@ -1019,6 +1131,50 @@ var Animation = new Class({
} }
}, },
/**
* Advances the animation to the next frame, regardless of the time or animation state.
* If the animation is set to repeat, or yoyo, this will still take effect.
*
* Calling this does not change the direction of the animation. I.e. if it was currently
* playing in reverse, calling this method doesn't then change the direction to forwards.
*
* @method Phaser.GameObjects.Components.Animation#nextFrame
* @since 3.16.0
*
* @return {Phaser.GameObjects.GameObject} The Game Object this Animation Component belongs to.
*/
nextFrame: function ()
{
if (this.currentAnim)
{
this.currentAnim.nextFrame(this);
}
return this.parent;
},
/**
* Advances the animation to the previous frame, regardless of the time or animation state.
* If the animation is set to repeat, or yoyo, this will still take effect.
*
* Calling this does not change the direction of the animation. I.e. if it was currently
* playing in forwards, calling this method doesn't then change the direction to backwards.
*
* @method Phaser.GameObjects.Components.Animation#previousFrame
* @since 3.16.0
*
* @return {Phaser.GameObjects.GameObject} The Game Object this Animation Component belongs to.
*/
previousFrame: function ()
{
if (this.currentAnim)
{
this.currentAnim.previousFrame(this);
}
return this.parent;
},
/** /**
* Sets if the current Animation will yoyo when it reaches the end. * Sets if the current Animation will yoyo when it reaches the end.
* A yoyo'ing animation will play through consecutively, and then reverse-play back to the start again. * A yoyo'ing animation will play through consecutively, and then reverse-play back to the start again.

View file

@ -37,6 +37,7 @@ var BlendMode = {
* * ADD * * ADD
* * MULTIPLY * * MULTIPLY
* * SCREEN * * SCREEN
* * ERASE
* *
* Canvas has more available depending on browser support. * Canvas has more available depending on browser support.
* *
@ -85,6 +86,7 @@ var BlendMode = {
* * ADD * * ADD
* * MULTIPLY * * MULTIPLY
* * SCREEN * * SCREEN
* * ERASE (only works when rendering to a framebuffer, like a Render Texture)
* *
* Canvas has more available depending on browser support. * Canvas has more available depending on browser support.
* *
@ -92,7 +94,7 @@ var BlendMode = {
* *
* Blend modes have different effects under Canvas and WebGL, and from browser to browser, depending * Blend modes have different effects under Canvas and WebGL, and from browser to browser, depending
* on support. Blend Modes also cause a WebGL batch flush should it encounter a new blend mode. For these * on support. Blend Modes also cause a WebGL batch flush should it encounter a new blend mode. For these
* reasons try to be careful about the construction of your Scene and the frequency of which blend modes * reasons try to be careful about the construction of your Scene and the frequency in which blend modes
* are used. * are used.
* *
* @method Phaser.GameObjects.Components.BlendMode#setBlendMode * @method Phaser.GameObjects.Components.BlendMode#setBlendMode

View file

@ -610,7 +610,7 @@ var Container = new Class({
*/ */
getFirst: function (property, value, startIndex, endIndex) getFirst: function (property, value, startIndex, endIndex)
{ {
return ArrayUtils.GetFirstElement(this.list, property, value, startIndex, endIndex); return ArrayUtils.GetFirst(this.list, property, value, startIndex, endIndex);
}, },
/** /**

View file

@ -61,14 +61,16 @@ var Extern = new Class({
GameObject.call(this, scene, 'Extern'); GameObject.call(this, scene, 'Extern');
}, },
preUpdate: function (time, delta) preUpdate: function ()
{ {
// override this! // override this!
// Arguments: time, delta
}, },
render: function (renderer, camera, calcMatrix) render: function ()
{ {
// override this! // override this!
// Arguments: renderer, camera, calcMatrix
} }
}); });

View file

@ -506,6 +506,26 @@ var Graphics = new Class({
return this; return this;
}, },
/**
* Fill the current path.
*
* This is an alias for `Graphics.fillPath` and does the same thing.
* It was added to match the CanvasRenderingContext 2D API.
*
* @method Phaser.GameObjects.Graphics#fill
* @since 3.16.0
*
* @return {Phaser.GameObjects.Graphics} This Game Object.
*/
fill: function ()
{
this.commandBuffer.push(
Commands.FILL_PATH
);
return this;
},
/** /**
* Stroke the current path. * Stroke the current path.
* *
@ -523,6 +543,26 @@ var Graphics = new Class({
return this; return this;
}, },
/**
* Stroke the current path.
*
* This is an alias for `Graphics.strokePath` and does the same thing.
* It was added to match the CanvasRenderingContext 2D API.
*
* @method Phaser.GameObjects.Graphics#stroke
* @since 3.16.0
*
* @return {Phaser.GameObjects.Graphics} This Game Object.
*/
stroke: function ()
{
this.commandBuffer.push(
Commands.STROKE_PATH
);
return this;
},
/** /**
* Fill the given circle. * Fill the given circle.
* *

View file

@ -45,8 +45,6 @@ var GraphicsCanvasRenderer = function (renderer, src, interpolationPercentage, c
var green = 0; var green = 0;
var blue = 0; var blue = 0;
ctx.save();
// Reset any currently active paths // Reset any currently active paths
ctx.beginPath(); ctx.beginPath();
@ -239,6 +237,7 @@ var GraphicsCanvasRenderer = function (renderer, src, interpolationPercentage, c
} }
} }
// Restore the context saved in SetTransform
ctx.restore(); ctx.restore();
}; };

View file

@ -28,7 +28,7 @@ var Sprite = require('../sprite/Sprite');
/** /**
* @typedef {object} GroupConfig * @typedef {object} GroupConfig
* *
* @property {?object} [classType=Sprite] - Sets {@link Phaser.GameObjects.Group#classType}. * @property {?GroupClassTypeConstructor} [classType=Sprite] - Sets {@link Phaser.GameObjects.Group#classType}.
* @property {?boolean} [active=true] - Sets {@link Phaser.GameObjects.Group#active}. * @property {?boolean} [active=true] - Sets {@link Phaser.GameObjects.Group#active}.
* @property {?number} [maxSize=-1] - Sets {@link Phaser.GameObjects.Group#maxSize}. * @property {?number} [maxSize=-1] - Sets {@link Phaser.GameObjects.Group#maxSize}.
* @property {?string} [defaultKey=null] - Sets {@link Phaser.GameObjects.Group#defaultKey}. * @property {?string} [defaultKey=null] - Sets {@link Phaser.GameObjects.Group#defaultKey}.
@ -52,7 +52,7 @@ var Sprite = require('../sprite/Sprite');
* *
* `key` is required. {@link Phaser.GameObjects.Group#defaultKey} is not used. * `key` is required. {@link Phaser.GameObjects.Group#defaultKey} is not used.
* *
* @property {?object} [classType] - The class of each new Game Object. * @property {?GroupClassTypeConstructor} [classType] - The class of each new Game Object.
* @property {string} [key] - The texture key of each new Game Object. * @property {string} [key] - The texture key of each new Game Object.
* @property {?(string|integer)} [frame=null] - The texture frame of each new Game Object. * @property {?(string|integer)} [frame=null] - The texture frame of each new Game Object.
* @property {?boolean} [visible=true] - The visible state of each new Game Object. * @property {?boolean} [visible=true] - The visible state of each new Game Object.
@ -93,6 +93,18 @@ var Sprite = require('../sprite/Sprite');
* @see Phaser.Utils.Array.Range * @see Phaser.Utils.Array.Range
*/ */
/**
* A constructor function (class) that can be assigned to `classType`.
* @callback GroupClassTypeConstructor
* @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time.
* @param {number} x - The horizontal position of this Game Object in the world.
* @param {number} y - The vertical position of this Game Object in the world.
* @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.
*
* @see Phaser.GameObjects.Group#classType
*/
/** /**
* @classdesc A Group is a way for you to create, manipulate, or recycle similar Game Objects. * @classdesc A Group is a way for you to create, manipulate, or recycle similar Game Objects.
* *
@ -185,7 +197,7 @@ var Group = new Class({
* The class to create new group members from. * The class to create new group members from.
* *
* @name Phaser.GameObjects.Group#classType * @name Phaser.GameObjects.Group#classType
* @type {object} * @type {GroupClassTypeConstructor}
* @since 3.0.0 * @since 3.0.0
* @default Phaser.GameObjects.Sprite * @default Phaser.GameObjects.Sprite
*/ */

View file

@ -15,7 +15,7 @@ var Group = require('./Group');
* @method Phaser.GameObjects.GameObjectCreator#group * @method Phaser.GameObjects.GameObjectCreator#group
* @since 3.0.0 * @since 3.0.0
* *
* @param {GroupConfig} config - The configuration object this Game Object will use to create itself. * @param {GroupConfig|GroupCreateConfig} config - The configuration object this Game Object will use to create itself.
* *
* @return {Phaser.GameObjects.Group} The Game Object that was created. * @return {Phaser.GameObjects.Group} The Game Object that was created.
*/ */

View file

@ -16,7 +16,7 @@ var GameObjectFactory = require('../GameObjectFactory');
* @since 3.0.0 * @since 3.0.0
* *
* @param {(Phaser.GameObjects.GameObject[]|GroupConfig|GroupConfig[])} [children] - Game Objects to add to this Group; or the `config` argument. * @param {(Phaser.GameObjects.GameObject[]|GroupConfig|GroupConfig[])} [children] - Game Objects to add to this Group; or the `config` argument.
* @param {GroupConfig} [config] - A Group Configuration object. * @param {GroupConfig|GroupCreateConfig} [config] - A Group Configuration object.
* *
* @return {Phaser.GameObjects.Group} The Game Object that was created. * @return {Phaser.GameObjects.Group} The Game Object that was created.
*/ */

View file

@ -20,13 +20,10 @@ var MeshRender = require('./MeshRender');
* @webglOnly * @webglOnly
* @since 3.0.0 * @since 3.0.0
* *
* @extends Phaser.GameObjects.Components.Alpha
* @extends Phaser.GameObjects.Components.BlendMode * @extends Phaser.GameObjects.Components.BlendMode
* @extends Phaser.GameObjects.Components.Depth * @extends Phaser.GameObjects.Components.Depth
* @extends Phaser.GameObjects.Components.Flip
* @extends Phaser.GameObjects.Components.GetBounds * @extends Phaser.GameObjects.Components.GetBounds
* @extends Phaser.GameObjects.Components.Mask * @extends Phaser.GameObjects.Components.Mask
* @extends Phaser.GameObjects.Components.Origin
* @extends Phaser.GameObjects.Components.Pipeline * @extends Phaser.GameObjects.Components.Pipeline
* @extends Phaser.GameObjects.Components.ScaleMode * @extends Phaser.GameObjects.Components.ScaleMode
* @extends Phaser.GameObjects.Components.Size * @extends Phaser.GameObjects.Components.Size
@ -50,13 +47,10 @@ var Mesh = new Class({
Extends: GameObject, Extends: GameObject,
Mixins: [ Mixins: [
Components.Alpha,
Components.BlendMode, Components.BlendMode,
Components.Depth, Components.Depth,
Components.Flip,
Components.GetBounds, Components.GetBounds,
Components.Mask, Components.Mask,
Components.Origin,
Components.Pipeline, Components.Pipeline,
Components.ScaleMode, Components.ScaleMode,
Components.Size, Components.Size,
@ -157,7 +151,6 @@ var Mesh = new Class({
this.setTexture(texture, frame); this.setTexture(texture, frame);
this.setPosition(x, y); this.setPosition(x, y);
this.setSizeToFrame(); this.setSizeToFrame();
this.setOrigin();
this.initPipeline(); this.initPipeline();
} }

View file

@ -67,7 +67,7 @@ var MeshWebGLRenderer = function (renderer, src, interpolationPercentage, camera
var meshVerticesLength = vertices.length; var meshVerticesLength = vertices.length;
var vertexCount = Math.floor(meshVerticesLength * 0.5); var vertexCount = Math.floor(meshVerticesLength * 0.5);
if (pipeline.vertexCount + vertexCount >= pipeline.vertexCapacity) if (pipeline.vertexCount + vertexCount > pipeline.vertexCapacity)
{ {
pipeline.flush(); pipeline.flush();
} }
@ -92,8 +92,8 @@ var MeshWebGLRenderer = function (renderer, src, interpolationPercentage, camera
if (camera.roundPixels) if (camera.roundPixels)
{ {
tx |= 0; tx = Math.round(tx);
ty |= 0; ty = Math.round(ty);
} }
vertexViewF32[++vertexOffset] = tx; vertexViewF32[++vertexOffset] = tx;

View file

@ -47,16 +47,6 @@ var Particle = new Class({
*/ */
this.frame = null; this.frame = null;
/**
* The position of this Particle within its Emitter's particle pool.
*
* @name Phaser.GameObjects.Particles.Particle#index
* @type {number}
* @default 0
* @since 3.0.0
*/
this.index = 0;
/** /**
* The x coordinate of this Particle. * The x coordinate of this Particle.
* *
@ -276,6 +266,18 @@ var Particle = new Class({
return (this.lifeCurrent > 0); return (this.lifeCurrent > 0);
}, },
/**
* Resets the position of this particle back to zero.
*
* @method Phaser.GameObjects.Particles.Particle#resetPosition
* @since 3.16.0
*/
resetPosition: function ()
{
this.x = 0;
this.y = 0;
},
/** /**
* Starts this Particle from the given coordinates. * Starts this Particle from the given coordinates.
* *
@ -382,8 +384,6 @@ var Particle = new Class({
this.alpha = emitter.alpha.onEmit(this, 'alpha'); this.alpha = emitter.alpha.onEmit(this, 'alpha');
this.tint = emitter.tint.onEmit(this, 'tint'); this.tint = emitter.tint.onEmit(this, 'tint');
this.index = emitter.alive.length;
}, },
/** /**

View file

@ -2012,13 +2012,9 @@ var ParticleEmitter = new Class({
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var particle; var particle = dead.pop();
if (dead.length > 0) if (!particle)
{
particle = dead.pop();
}
else
{ {
particle = new this.particleClass(this); particle = new this.particleClass(this);
} }
@ -2073,47 +2069,49 @@ var ParticleEmitter = new Class({
var processors = this.manager.getProcessors(); var processors = this.manager.getProcessors();
var particles = this.alive; var particles = this.alive;
var dead = this.dead;
var i = 0;
var rip = [];
var length = particles.length; var length = particles.length;
for (var index = 0; index < length; index++) for (i = 0; i < length; i++)
{ {
var particle = particles[index]; var particle = particles[i];
// update returns `true` if the particle is now dead (lifeStep < 0) // update returns `true` if the particle is now dead (lifeCurrent <= 0)
if (particle.update(delta, step, processors)) if (particle.update(delta, step, processors))
{ {
// Moves the dead particle to the end of the particles array (ready for splicing out later) rip.push({ index: i, particle: particle });
var last = particles[length - 1];
particles[length - 1] = particle;
particles[index] = last;
index -= 1;
length -= 1;
} }
} }
// Move dead particles to the dead array // Move dead particles to the dead array
var deadLength = particles.length - length; length = rip.length;
if (deadLength > 0) if (length > 0)
{ {
var rip = particles.splice(particles.length - deadLength, deadLength);
var deathCallback = this.deathCallback; var deathCallback = this.deathCallback;
var deathCallbackScope = this.deathCallbackScope; var deathCallbackScope = this.deathCallbackScope;
if (deathCallback) for (i = length - 1; i >= 0; i--)
{ {
for (var i = 0; i < rip.length; i++) var entry = rip[i];
// Remove from particles array
particles.splice(entry.index, 1);
// Add to dead array
dead.push(entry.particle);
// Callback
if (deathCallback)
{ {
deathCallback.call(deathCallbackScope, rip[i]); deathCallback.call(deathCallbackScope, entry.particle);
} }
entry.particle.resetPosition();
} }
this.dead = this.dead.concat(rip);
StableSort.inplace(particles, this.indexSortCallback);
} }
if (!this.on) if (!this.on)
@ -2153,22 +2151,6 @@ var ParticleEmitter = new Class({
depthSortCallback: function (a, b) depthSortCallback: function (a, b)
{ {
return a.y - b.y; return a.y - b.y;
},
/**
* Calculates the difference of two particles, for sorting them by index.
*
* @method Phaser.GameObjects.Particles.ParticleEmitter#indexSortCallback
* @since 3.0.0
*
* @param {object} a - The first particle.
* @param {object} b - The second particle.
*
* @return {integer} The difference of a and b's `index` properties.
*/
indexSortCallback: function (a, b)
{
return a.index - b.index;
} }
}); });

View file

@ -99,8 +99,8 @@ var ParticleManagerCanvasRenderer = function (renderer, emitterManager, interpol
if (roundPixels) if (roundPixels)
{ {
x |= 0; x = Math.round(x);
y |= 0; y = Math.round(y);
} }
ctx.drawImage(frame.source.image, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height); ctx.drawImage(frame.source.image, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height);

View file

@ -118,25 +118,22 @@ var ParticleManagerWebGLRenderer = function (renderer, emitterManager, interpola
if (roundPixels) if (roundPixels)
{ {
tx0 |= 0; tx0 = Math.round(tx0);
ty0 |= 0; ty0 = Math.round(ty0);
tx1 |= 0; tx1 = Math.round(tx1);
ty1 |= 0; ty1 = Math.round(ty1);
tx2 |= 0; tx2 = Math.round(tx2);
ty2 |= 0; ty2 = Math.round(ty2);
tx3 |= 0; tx3 = Math.round(tx3);
ty3 |= 0; ty3 = Math.round(ty3);
} }
var tint = getTint(particle.tint, alpha); var tint = getTint(particle.tint, alpha);
if (pipeline.batchQuad(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, frame.u0, frame.v0, frame.u1, frame.v1, tint, tint, tint, tint, tintEffect)) pipeline.batchQuad(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, frame.u0, frame.v0, frame.u1, frame.v1, tint, tint, tint, tint, tintEffect, texture, 0);
{
pipeline.setTexture2D(texture, 0);
}
} }
} }
}; };

View file

@ -23,6 +23,7 @@ var Vector2 = require('../../math/Vector2');
* @property {boolean} [positionOnPath=false] - Whether to position the PathFollower on the Path using its path offset. * @property {boolean} [positionOnPath=false] - Whether to position the PathFollower on the Path using its path offset.
* @property {boolean} [rotateToPath=false] - Should the PathFollower automatically rotate to point in the direction of the Path? * @property {boolean} [rotateToPath=false] - Should the PathFollower automatically rotate to point in the direction of the Path?
* @property {number} [rotationOffset=0] - If the PathFollower is rotating to match the Path, this value is added to the rotation value. This allows you to rotate objects to a path but control the angle of the rotation as well. * @property {number} [rotationOffset=0] - If the PathFollower is rotating to match the Path, this value is added to the rotation value. This allows you to rotate objects to a path but control the angle of the rotation as well.
* @property {number} [startAt=0] - Current start position of the path follow, between 0 and 1.
*/ */
/** /**
@ -253,6 +254,22 @@ var PathFollower = new Class({
this.rotateToPath = GetBoolean(config, 'rotateToPath', false); this.rotateToPath = GetBoolean(config, 'rotateToPath', false);
this.pathRotationOffset = GetValue(config, 'rotationOffset', 0); this.pathRotationOffset = GetValue(config, 'rotationOffset', 0);
// This works, but it's not an ideal way of doing it as the follower jumps position
var seek = GetValue(config, 'startAt', startAt);
if (seek)
{
config.onStart = function (tween)
{
var tweenData = tween.data[0];
tweenData.progress = seek;
tweenData.elapsed = tweenData.duration * seek;
var v = tweenData.ease(tweenData.progress);
tweenData.current = tweenData.start + ((tweenData.end - tweenData.start) * v);
tweenData.target[tweenData.key] = tweenData.current;
};
}
this.pathTween = this.scene.sys.tweens.addCounter(config); this.pathTween = this.scene.sys.tweens.addCounter(config);
// The starting point of the path, relative to this follower // The starting point of the path, relative to this follower

View file

@ -13,6 +13,7 @@ var CONST = require('../../const');
var Frame = require('../../textures/Frame'); var Frame = require('../../textures/Frame');
var GameObject = require('../GameObject'); var GameObject = require('../GameObject');
var Render = require('./RenderTextureRender'); var Render = require('./RenderTextureRender');
var Utils = require('../../renderer/webgl/Utils');
var UUID = require('../../utils/string/UUID'); var UUID = require('../../utils/string/UUID');
/** /**
@ -409,25 +410,31 @@ var RenderTexture = new Class({
{ {
if (alpha === undefined) { alpha = 1; } if (alpha === undefined) { alpha = 1; }
var ur = ((rgb >> 16)|0) & 0xff; var r = ((rgb >> 16) | 0) & 0xff;
var ug = ((rgb >> 8)|0) & 0xff; var g = ((rgb >> 8) | 0) & 0xff;
var ub = (rgb|0) & 0xff; var b = (rgb | 0) & 0xff;
if (this.gl) var gl = this.gl;
if (gl)
{ {
this.renderer.setFramebuffer(this.framebuffer, true); var renderer = this.renderer;
var gl = this.gl; var bounds = this.getBounds();
gl.clearColor(ur / 255.0, ug / 255.0, ub / 255.0, alpha); renderer.setFramebuffer(this.framebuffer, true);
gl.clear(gl.COLOR_BUFFER_BIT); renderer.pipelines.TextureTintPipeline.drawFillRect(
bounds.x, bounds.y, bounds.right, bounds.bottom,
Utils.getTintFromFloats(r / 255, g / 255, b / 255, 1),
alpha
);
this.renderer.setFramebuffer(null, true); renderer.setFramebuffer(null, true);
} }
else else
{ {
this.context.fillStyle = 'rgb(' + ur + ',' + ug + ',' + ub + ')'; this.context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
} }
@ -446,17 +453,18 @@ var RenderTexture = new Class({
{ {
if (this.dirty) if (this.dirty)
{ {
if (this.gl) var gl = this.gl;
{
this.renderer.setFramebuffer(this.framebuffer, true);
var gl = this.gl; if (gl)
{
var renderer = this.renderer;
renderer.setFramebuffer(this.framebuffer, true);
gl.clearColor(0, 0, 0, 0); gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT);
this.renderer.setFramebuffer(null, true); renderer.setFramebuffer(null, true);
} }
else else
{ {
@ -610,11 +618,18 @@ var RenderTexture = new Class({
var gl = this.gl; var gl = this.gl;
this.camera.preRender(1, 1, 1); this.camera.preRender(1, 1);
if (gl) if (gl)
{ {
this.renderer.setFramebuffer(this.framebuffer, true); var cx = this.camera._cx;
var cy = this.camera._cy;
var cw = this.camera._cw;
var ch = this.camera._ch;
this.renderer.setFramebuffer(this.framebuffer, false);
this.renderer.pushScissor(cx, cy, cw, ch, ch);
var pipeline = this.pipeline; var pipeline = this.pipeline;
@ -624,7 +639,9 @@ var RenderTexture = new Class({
pipeline.flush(); pipeline.flush();
this.renderer.setFramebuffer(null, true); this.renderer.setFramebuffer(null, false);
this.renderer.popScissor();
pipeline.projOrtho(0, pipeline.width, pipeline.height, 0, -1000.0, 1000.0); pipeline.projOrtho(0, pipeline.width, pipeline.height, 0, -1000.0, 1000.0);
} }
@ -692,11 +709,18 @@ var RenderTexture = new Class({
if (textureFrame) if (textureFrame)
{ {
this.camera.preRender(1, 1, 1); this.camera.preRender(1, 1);
if (gl) if (gl)
{ {
this.renderer.setFramebuffer(this.framebuffer, true); var cx = this.camera._cx;
var cy = this.camera._cy;
var cw = this.camera._cw;
var ch = this.camera._ch;
this.renderer.setFramebuffer(this.framebuffer, false);
this.renderer.pushScissor(cx, cy, cw, ch, ch);
var pipeline = this.pipeline; var pipeline = this.pipeline;
@ -706,7 +730,9 @@ var RenderTexture = new Class({
pipeline.flush(); pipeline.flush();
this.renderer.setFramebuffer(null, true); this.renderer.setFramebuffer(null, false);
this.renderer.popScissor();
pipeline.projOrtho(0, pipeline.width, pipeline.height, 0, -1000.0, 1000.0); pipeline.projOrtho(0, pipeline.width, pipeline.height, 0, -1000.0, 1000.0);
} }
@ -853,11 +879,23 @@ var RenderTexture = new Class({
var prevX = gameObject.x; var prevX = gameObject.x;
var prevY = gameObject.y; var prevY = gameObject.y;
if (this._eraseMode)
{
var blendMode = gameObject.blendMode;
gameObject.blendMode = BlendModes.ERASE;
}
gameObject.setPosition(x, y); gameObject.setPosition(x, y);
gameObject.renderCanvas(this.renderer, gameObject, 0, this.camera, null); gameObject.renderCanvas(this.renderer, gameObject, 0, this.camera, null);
gameObject.setPosition(prevX, prevY); gameObject.setPosition(prevX, prevY);
if (this._eraseMode)
{
gameObject.blendMode = blendMode;
}
}, },
/** /**
@ -905,19 +943,7 @@ var RenderTexture = new Class({
if (this.gl) if (this.gl)
{ {
if (this._eraseMode)
{
var blendMode = this.renderer.currentBlendMode;
this.renderer.setBlendMode(BlendModes.ERASE);
}
this.pipeline.batchTextureFrame(textureFrame, x, y, tint, alpha, this.camera.matrix, null); this.pipeline.batchTextureFrame(textureFrame, x, y, tint, alpha, this.camera.matrix, null);
if (this._eraseMode)
{
this.renderer.setBlendMode(blendMode);
}
} }
else else
{ {
@ -954,6 +980,12 @@ var RenderTexture = new Class({
} }
this.texture.destroy(); this.texture.destroy();
this.camera.destroy();
this.canvas = null;
this.context = null;
this.framebuffer = null;
this.texture = null;
} }
} }

View file

@ -61,6 +61,9 @@ var ArcCanvasRenderer = function (renderer, src, interpolationPercentage, camera
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -73,6 +73,9 @@ var CurveCanvasRenderer = function (renderer, src, interpolationPercentage, came
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -70,6 +70,9 @@ var EllipseCanvasRenderer = function (renderer, src, interpolationPercentage, ca
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -59,6 +59,9 @@ var RectangleCanvasRenderer = function (renderer, src, interpolationPercentage,
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -86,6 +86,9 @@ var IsoBoxCanvasRenderer = function (renderer, src, interpolationPercentage, cam
ctx.fill(); ctx.fill();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -99,6 +99,9 @@ var IsoTriangleCanvasRenderer = function (renderer, src, interpolationPercentage
ctx.fill(); ctx.fill();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -42,6 +42,9 @@ var LineCanvasRenderer = function (renderer, src, interpolationPercentage, camer
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -70,6 +70,9 @@ var PolygonCanvasRenderer = function (renderer, src, interpolationPercentage, ca
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -59,6 +59,9 @@ var RectangleCanvasRenderer = function (renderer, src, interpolationPercentage,
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -70,6 +70,9 @@ var StarCanvasRenderer = function (renderer, src, interpolationPercentage, camer
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -60,6 +60,9 @@ var TriangleCanvasRenderer = function (renderer, src, interpolationPercentage, c
ctx.stroke(); ctx.stroke();
} }
// Restore the context saved in SetTransform
ctx.restore();
} }
}; };

View file

@ -537,9 +537,9 @@ var TextStyle = new Class({
var i = 0; var i = 0;
this.fontStyle = (fontSplit.length > 2) ? fontSplit[i++] : ''; fontStyle = (fontSplit.length > 2) ? fontSplit[i++] : '';
this.fontSize = fontSplit[i++] || '16px'; fontSize = fontSplit[i++] || '16px';
this.fontFamily = fontSplit[i++] || 'Courier'; fontFamily = fontSplit[i++] || 'Courier';
} }
if (fontFamily !== this.fontFamily || fontSize !== this.fontSize || fontStyle !== this.fontStyle) if (fontFamily !== this.fontFamily || fontSize !== this.fontSize || fontStyle !== this.fontStyle)

View file

@ -470,6 +470,9 @@ var TileSprite = new Class({
canvas.height = this.height; canvas.height = this.height;
this.frame.setSize(this.width, this.height); this.frame.setSize(this.width, this.height);
this.updateDisplayOrigin();
this.dirty = true;
} }
if (!this.dirty || this.renderer && this.renderer.gl) if (!this.dirty || this.renderer && this.renderer.gl)

View file

@ -1,23 +1,64 @@
/** /**
* @author Richard Davey <rich@photonstorm.com> * @author Richard Davey <rich@photonstorm.com>
* @author Florian Mertens
* @copyright 2018 Photon Storm Ltd. * @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/ */
/** /**
* Checks if the given point falls between the two end-points of the line segment. * Checks if the a Point falls between the two end-points of a Line, based on the given line thickness.
*
* Assumes that the line end points are circular, not square.
* *
* @function Phaser.Geom.Intersects.PointToLine * @function Phaser.Geom.Intersects.PointToLine
* @since 3.0.0 * @since 3.0.0
* *
* @param {(Phaser.Geom.Point|any)} point - The point, or point-like object to check. * @param {(Phaser.Geom.Point|any)} point - The point, or point-like object to check.
* @param {Phaser.Geom.Line} line - The line segment to test for intersection on. * @param {Phaser.Geom.Line} line - The line segment to test for intersection on.
* @param {number} [lineThickness=1] - The line thickness. Assumes that the line end points are circular.
* *
* @return {boolean} `true` if the two objects intersect, otherwise `false`. * @return {boolean} `true` if the Point falls on the Line, otherwise `false`.
*/ */
var PointToLine = function (point, line) var PointToLine = function (point, line, lineThickness)
{ {
return ((point.x - line.x1) * (line.y2 - line.y1) === (line.x2 - line.x1) * (point.y - line.y1)); if (lineThickness === undefined) { lineThickness = 1; }
var x1 = line.x1;
var y1 = line.y1;
var x2 = line.x2;
var y2 = line.y2;
var px = point.x;
var py = point.y;
var L2 = (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
if (L2 === 0)
{
return false;
}
var r = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) / L2;
// Assume line thickness is circular
if (r < 0)
{
// Outside line1
return (Math.sqrt(((x1 - px) * (x1 - px)) + ((y1 - py) * (y1 - py))) <= lineThickness);
}
else if ((r >= 0) && (r <= 1))
{
// On the line segment
var s = (((y1 - py) * (x2 - x1)) - ((x1 - px) * (y2 - y1))) / L2;
return (Math.abs(s) * Math.sqrt(L2) <= lineThickness);
}
else
{
// Outside line2
return (Math.sqrt(((x2 - px) * (x2 - px)) + ((y2 - py) * (y2 - py))) <= lineThickness);
}
}; };
module.exports = PointToLine; module.exports = PointToLine;

View file

@ -0,0 +1,49 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Florian Mertens
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Point = require('../point/Point');
/**
* Get the nearest point on a line perpendicular to the given point.
*
* @function Phaser.Geom.Line.GetNearestPoint
* @since 3.16.0
*
* @generic {Phaser.Geom.Point} O - [out,$return]
*
* @param {Phaser.Geom.Line} line - The line to get the nearest point on.
* @param {(Phaser.Geom.Point|object)} point - The point to get the nearest point to.
* @param {(Phaser.Geom.Point|object)} [out] - An optional point, or point-like object, to store the coordinates of the nearest point on the line.
*
* @return {(Phaser.Geom.Point|object)} The nearest point on the line.
*/
var GetNearestPoint = function (line, point, out)
{
if (out === undefined) { out = new Point(); }
var x1 = line.x1;
var y1 = line.y1;
var x2 = line.x2;
var y2 = line.y2;
var L2 = (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
if (L2 === 0)
{
return out;
}
var r = (((point.x - x1) * (x2 - x1)) + ((point.y - y1) * (y2 - y1))) / L2;
out.x = x1 + (r * (x2 - x1));
out.y = y1 + (r * (y2 - y1));
return out;
};
module.exports = GetNearestPoint;

View file

@ -0,0 +1,41 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Florian Mertens
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Get the shortest distance from a Line to the given Point.
*
* @function Phaser.Geom.Line.GetShortestDistance
* @since 3.16.0
*
* @generic {Phaser.Geom.Point} O - [out,$return]
*
* @param {Phaser.Geom.Line} line - The line to get the distance from.
* @param {(Phaser.Geom.Point|object)} point - The point to get the shortest distance to.
*
* @return {number} The shortest distance from the line to the point.
*/
var GetShortestDistance = function (line, point)
{
var x1 = line.x1;
var y1 = line.y1;
var x2 = line.x2;
var y2 = line.y2;
var L2 = (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
if (L2 === 0)
{
return false;
}
var s = (((y1 - point.y) * (x2 - x1)) - ((x1 - point.x) * (y2 - y1))) / L2;
return Math.abs(s) * Math.sqrt(L2);
};
module.exports = GetShortestDistance;

View file

@ -8,14 +8,15 @@ var MATH_CONST = require('../../math/const');
var Angle = require('./Angle'); var Angle = require('./Angle');
/** /**
* [description] * The Y value of the normal of the given line.
* The normal of a line is a vector that points perpendicular from it.
* *
* @function Phaser.Geom.Line.NormalY * @function Phaser.Geom.Line.NormalY
* @since 3.0.0 * @since 3.0.0
* *
* @param {Phaser.Geom.Line} line - [description] * @param {Phaser.Geom.Line} line - The line to calculate the normal of.
* *
* @return {number} [description] * @return {number} The Y value of the normal of the Line.
*/ */
var NormalY = function (line) var NormalY = function (line)
{ {

View file

@ -13,9 +13,11 @@ Line.Clone = require('./Clone');
Line.CopyFrom = require('./CopyFrom'); Line.CopyFrom = require('./CopyFrom');
Line.Equals = require('./Equals'); Line.Equals = require('./Equals');
Line.GetMidPoint = require('./GetMidPoint'); Line.GetMidPoint = require('./GetMidPoint');
Line.GetNearestPoint = require('./GetNearestPoint');
Line.GetNormal = require('./GetNormal'); Line.GetNormal = require('./GetNormal');
Line.GetPoint = require('./GetPoint'); Line.GetPoint = require('./GetPoint');
Line.GetPoints = require('./GetPoints'); Line.GetPoints = require('./GetPoints');
Line.GetShortestDistance = require('./GetShortestDistance');
Line.Height = require('./Height'); Line.Height = require('./Height');
Line.Length = require('./Length'); Line.Length = require('./Length');
Line.NormalAngle = require('./NormalAngle'); Line.NormalAngle = require('./NormalAngle');

View file

@ -7,7 +7,8 @@
var Point = require('./Point'); var Point = require('./Point');
/** /**
* [description] * Get the centroid or geometric center of a plane figure (the arithmetic mean position of all the points in the figure).
* Informally, it is the point at which a cutout of the shape could be perfectly balanced on the tip of a pin.
* *
* @function Phaser.Geom.Point.GetCentroid * @function Phaser.Geom.Point.GetCentroid
* @since 3.0.0 * @since 3.0.0

View file

@ -7,17 +7,17 @@
var GetMagnitude = require('./GetMagnitude'); var GetMagnitude = require('./GetMagnitude');
/** /**
* [description] * Changes the magnitude (length) of a two-dimensional vector without changing its direction.
* *
* @function Phaser.Geom.Point.SetMagnitude * @function Phaser.Geom.Point.SetMagnitude
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Point} O - [point,$return] * @generic {Phaser.Geom.Point} O - [point,$return]
* *
* @param {Phaser.Geom.Point} point - [description] * @param {Phaser.Geom.Point} point - The Point to treat as the end point of the vector.
* @param {number} magnitude - [description] * @param {number} magnitude - The new magnitude of the vector.
* *
* @return {Phaser.Geom.Point} [description] * @return {Phaser.Geom.Point} The modified Point.
*/ */
var SetMagnitude = function (point, magnitude) var SetMagnitude = function (point, magnitude)
{ {

View file

@ -7,14 +7,14 @@
var Polygon = require('./Polygon'); var Polygon = require('./Polygon');
/** /**
* [description] * Create a new polygon which is a copy of the specified polygon
* *
* @function Phaser.Geom.Polygon.Clone * @function Phaser.Geom.Polygon.Clone
* @since 3.0.0 * @since 3.0.0
* *
* @param {Phaser.Geom.Polygon} polygon - [description] * @param {Phaser.Geom.Polygon} polygon - The polygon to create a clone of
* *
* @return {Phaser.Geom.Polygon} [description] * @return {Phaser.Geom.Polygon} A new separate Polygon cloned from the specified polygon, based on the same points.
*/ */
var Clone = function (polygon) var Clone = function (polygon)
{ {

View file

@ -4,37 +4,30 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/ */
// 2.1.1 (Mar 17, 2016) // Earcut 2.1.4 (December 4th 2018)
/* /*
ISC License * ISC License
*
Copyright (c) 2016, Mapbox * Copyright (c) 2016, Mapbox
*
Permission to use, copy, modify, and/or distribute this software for any purpose * Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice * with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies. * and this permission notice appear in all copies.
*
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE. * THIS SOFTWARE.
*/ */
'use strict'; 'use strict';
module.exports = earcut; module.exports = earcut;
/*
vertices is a flat array of vertice coordinates like [x0,y0, x1,y1, x2,y2, ...].
holes is an array of hole indices if any (e.g. [5, 8] for a 12-vertice input would mean one hole with vertices 57 and another with 811).
dimensions is the number of coordinates per vertice in the input array (2 by default).
Each group of three vertice indices in the resulting array forms a triangle.
*/
function earcut(data, holeIndices, dim) { function earcut(data, holeIndices, dim) {
dim = dim || 2; dim = dim || 2;
@ -44,9 +37,9 @@ function earcut(data, holeIndices, dim) {
outerNode = linkedList(data, 0, outerLen, dim, true), outerNode = linkedList(data, 0, outerLen, dim, true),
triangles = []; triangles = [];
if (!outerNode) return triangles; if (!outerNode || outerNode.next === outerNode.prev) return triangles;
var minX, minY, maxX, maxY, x, y, size; var minX, minY, maxX, maxY, x, y, invSize;
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
@ -64,11 +57,12 @@ function earcut(data, holeIndices, dim) {
if (y > maxY) maxY = y; if (y > maxY) maxY = y;
} }
// minX, minY and size are later used to transform coords into integers for z-order calculation // minX, minY and invSize are later used to transform coords into integers for z-order calculation
size = Math.max(maxX - minX, maxY - minY); invSize = Math.max(maxX - minX, maxY - minY);
invSize = invSize !== 0 ? 1 / invSize : 0;
} }
earcutLinked(outerNode, triangles, dim, minX, minY, size); earcutLinked(outerNode, triangles, dim, minX, minY, invSize);
return triangles; return triangles;
} }
@ -104,7 +98,7 @@ function filterPoints(start, end) {
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
removeNode(p); removeNode(p);
p = end = p.prev; p = end = p.prev;
if (p === p.next) return null; if (p === p.next) break;
again = true; again = true;
} else { } else {
@ -116,11 +110,11 @@ function filterPoints(start, end) {
} }
// main ear slicing loop which triangulates a polygon (given as a linked list) // main ear slicing loop which triangulates a polygon (given as a linked list)
function earcutLinked(ear, triangles, dim, minX, minY, size, pass) { function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
if (!ear) return; if (!ear) return;
// interlink polygon nodes in z-order // interlink polygon nodes in z-order
if (!pass && size) indexCurve(ear, minX, minY, size); if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
var stop = ear, var stop = ear,
prev, next; prev, next;
@ -130,7 +124,7 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
prev = ear.prev; prev = ear.prev;
next = ear.next; next = ear.next;
if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) { if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
// cut off the triangle // cut off the triangle
triangles.push(prev.i / dim); triangles.push(prev.i / dim);
triangles.push(ear.i / dim); triangles.push(ear.i / dim);
@ -138,7 +132,7 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
removeNode(ear); removeNode(ear);
// skipping the next vertice leads to less sliver triangles // skipping the next vertex leads to less sliver triangles
ear = next.next; ear = next.next;
stop = next.next; stop = next.next;
@ -151,16 +145,16 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
if (ear === stop) { if (ear === stop) {
// try filtering points and slicing again // try filtering points and slicing again
if (!pass) { if (!pass) {
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1); earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
// if this didn't work, try curing all small self-intersections locally // if this didn't work, try curing all small self-intersections locally
} else if (pass === 1) { } else if (pass === 1) {
ear = cureLocalIntersections(ear, triangles, dim); ear = cureLocalIntersections(ear, triangles, dim);
earcutLinked(ear, triangles, dim, minX, minY, size, 2); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
// as a last resort, try splitting the remaining polygon into two // as a last resort, try splitting the remaining polygon into two
} else if (pass === 2) { } else if (pass === 2) {
splitEarcut(ear, triangles, dim, minX, minY, size); splitEarcut(ear, triangles, dim, minX, minY, invSize);
} }
break; break;
@ -188,7 +182,7 @@ function isEar(ear) {
return true; return true;
} }
function isEarHashed(ear, minX, minY, size) { function isEarHashed(ear, minX, minY, invSize) {
var a = ear.prev, var a = ear.prev,
b = ear, b = ear,
c = ear.next; c = ear.next;
@ -202,22 +196,26 @@ function isEarHashed(ear, minX, minY, size) {
maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y); maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);
// z-order range for the current triangle bbox; // z-order range for the current triangle bbox;
var minZ = zOrder(minTX, minTY, minX, minY, size), var minZ = zOrder(minTX, minTY, minX, minY, invSize),
maxZ = zOrder(maxTX, maxTY, minX, minY, size); maxZ = zOrder(maxTX, maxTY, minX, minY, invSize);
// first look for points inside the triangle in increasing z-order var p = ear.prevZ,
var p = ear.nextZ; n = ear.nextZ;
while (p && p.z <= maxZ) { // look for points inside the triangle in both directions
while (p && p.z >= minZ && n && n.z <= maxZ) {
if (p !== ear.prev && p !== ear.next && if (p !== ear.prev && p !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
area(p.prev, p, p.next) >= 0) return false; area(p.prev, p, p.next) >= 0) return false;
p = p.nextZ; p = p.prevZ;
if (n !== ear.prev && n !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
area(n.prev, n, n.next) >= 0) return false;
n = n.nextZ;
} }
// then look for points in decreasing z-order // look for remaining points in decreasing z-order
p = ear.prevZ;
while (p && p.z >= minZ) { while (p && p.z >= minZ) {
if (p !== ear.prev && p !== ear.next && if (p !== ear.prev && p !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
@ -225,6 +223,14 @@ function isEarHashed(ear, minX, minY, size) {
p = p.prevZ; p = p.prevZ;
} }
// look for remaining points in increasing z-order
while (n && n.z <= maxZ) {
if (n !== ear.prev && n !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
area(n.prev, n, n.next) >= 0) return false;
n = n.nextZ;
}
return true; return true;
} }
@ -254,7 +260,7 @@ function cureLocalIntersections(start, triangles, dim) {
} }
// try splitting polygon into two and triangulate them independently // try splitting polygon into two and triangulate them independently
function splitEarcut(start, triangles, dim, minX, minY, size) { function splitEarcut(start, triangles, dim, minX, minY, invSize) {
// look for a valid diagonal that divides the polygon into two // look for a valid diagonal that divides the polygon into two
var a = start; var a = start;
do { do {
@ -269,8 +275,8 @@ function splitEarcut(start, triangles, dim, minX, minY, size) {
c = filterPoints(c, c.next); c = filterPoints(c, c.next);
// run earcut on each half // run earcut on each half
earcutLinked(a, triangles, dim, minX, minY, size); earcutLinked(a, triangles, dim, minX, minY, invSize);
earcutLinked(c, triangles, dim, minX, minY, size); earcutLinked(c, triangles, dim, minX, minY, invSize);
return; return;
} }
b = b.next; b = b.next;
@ -376,10 +382,10 @@ function findHoleBridge(hole, outerNode) {
} }
// interlink polygon nodes in z-order // interlink polygon nodes in z-order
function indexCurve(start, minX, minY, size) { function indexCurve(start, minX, minY, invSize) {
var p = start; var p = start;
do { do {
if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size); if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize);
p.prevZ = p.prev; p.prevZ = p.prev;
p.nextZ = p.next; p.nextZ = p.next;
p = p.next; p = p.next;
@ -444,11 +450,11 @@ function sortLinked(list) {
return list; return list;
} }
// z-order of a point given coords and size of the data bounding box // z-order of a point given coords and inverse of the longer side of data bbox
function zOrder(x, y, minX, minY, size) { function zOrder(x, y, minX, minY, invSize) {
// coords are transformed into non-negative 15-bit integer range // coords are transformed into non-negative 15-bit integer range
x = 32767 * (x - minX) / size; x = 32767 * (x - minX) * invSize;
y = 32767 * (y - minY) / size; y = 32767 * (y - minY) * invSize;
x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F; x = (x | (x << 4)) & 0x0F0F0F0F;
@ -590,14 +596,14 @@ function removeNode(p) {
} }
function Node(i, x, y) { function Node(i, x, y) {
// vertice index in coordinates array // vertex index in coordinates array
this.i = i; this.i = i;
// vertex coordinates // vertex coordinates
this.x = x; this.x = x;
this.y = y; this.y = y;
// previous and next vertice nodes in a polygon ring // previous and next vertex nodes in a polygon ring
this.prev = null; this.prev = null;
this.next = null; this.next = null;

View file

@ -10,14 +10,26 @@ var GetPoints = require('./GetPoints');
/** /**
* @classdesc * @classdesc
* [description] * A Polygon object
*
* The polygon is a closed shape consists of a series of connected straight lines defined by list of ordered points.
* Several formats are supported to define the list of points, check the setTo method for details.
* This is a geometry object allowing you to define and inspect the shape.
* It is not a Game Object, in that you cannot add it to the display list, and it has no texture.
* To render a Polygon you should look at the capabilities of the Graphics class.
* *
* @class Polygon * @class Polygon
* @memberof Phaser.Geom * @memberof Phaser.Geom
* @constructor * @constructor
* @since 3.0.0 * @since 3.0.0
* *
* @param {Phaser.Geom.Point[]} [points] - [description] * @param {Phaser.Geom.Point[]} [points] - List of points defining the perimeter of this Polygon. Several formats are supported:
* - A string containing paired x y values separated by a single space: `'40 0 40 20 100 20 100 80 40 80 40 100 0 50'`
* - An array of Point objects: `[new Phaser.Point(x1, y1), ...]`
* - An array of objects with public x y properties: `[obj1, obj2, ...]`
* - An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]`
* - An array of arrays with two elements representing x/y coordinates: `[[x1, y1], [x2, y2], ...]`
*/ */
var Polygon = new Class({ var Polygon = new Class({
@ -82,7 +94,7 @@ var Polygon = new Class({
* @method Phaser.Geom.Polygon#setTo * @method Phaser.Geom.Polygon#setTo
* @since 3.0.0 * @since 3.0.0
* *
* @param {array} points - [description] * @param {array} points - Points defining the perimeter of this polygon. Please check function description above for the different supported formats.
* *
* @return {Phaser.Geom.Polygon} This Polygon object. * @return {Phaser.Geom.Polygon} This Polygon object.
*/ */

View file

@ -5,16 +5,16 @@
*/ */
/** /**
* [description] * Reverses the order of the points of a Polygon.
* *
* @function Phaser.Geom.Polygon.Reverse * @function Phaser.Geom.Polygon.Reverse
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Polygon} O - [polygon,$return] * @generic {Phaser.Geom.Polygon} O - [polygon,$return]
* *
* @param {Phaser.Geom.Polygon} polygon - [description] * @param {Phaser.Geom.Polygon} polygon - The Polygon to modify.
* *
* @return {Phaser.Geom.Polygon} [description] * @return {Phaser.Geom.Polygon} The modified Polygon.
*/ */
var Reverse = function (polygon) var Reverse = function (polygon)
{ {

View file

@ -5,14 +5,14 @@
*/ */
/** /**
* [description] * Calculates the area of the given Rectangle object.
* *
* @function Phaser.Geom.Rectangle.Area * @function Phaser.Geom.Rectangle.Area
* @since 3.0.0 * @since 3.0.0
* *
* @param {Phaser.Geom.Rectangle} rect - [description] * @param {Phaser.Geom.Rectangle} rect - The rectangle to calculate the area of.
* *
* @return {number} [description] * @return {number} The area of the Rectangle object.
*/ */
var Area = function (rect) var Area = function (rect)
{ {

View file

@ -6,22 +6,22 @@
var GetAspectRatio = require('./GetAspectRatio'); var GetAspectRatio = require('./GetAspectRatio');
// Fits the target rectangle into the source rectangle.
// Preserves aspect ratio.
// Scales and centers the target rectangle to the source rectangle
/** /**
* [description] * Adjusts the target rectangle, changing its width, height and position,
* so that it fits inside the area of the source rectangle, while maintaining its original
* aspect ratio.
*
* Unlike the `FitOutside` function, there may be some space inside the source area not covered.
* *
* @function Phaser.Geom.Rectangle.FitInside * @function Phaser.Geom.Rectangle.FitInside
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Rectangle} O - [target,$return] * @generic {Phaser.Geom.Rectangle} O - [target,$return]
* *
* @param {Phaser.Geom.Rectangle} target - [description] * @param {Phaser.Geom.Rectangle} target - The target rectangle to adjust.
* @param {Phaser.Geom.Rectangle} source - [description] * @param {Phaser.Geom.Rectangle} source - The source rectangle to envlope the target in.
* *
* @return {Phaser.Geom.Rectangle} [description] * @return {Phaser.Geom.Rectangle} The modified target rectangle instance.
*/ */
var FitInside = function (target, source) var FitInside = function (target, source)
{ {

View file

@ -6,22 +6,22 @@
var GetAspectRatio = require('./GetAspectRatio'); var GetAspectRatio = require('./GetAspectRatio');
// Fits the target rectangle around the source rectangle.
// Preserves aspect ration.
// Scales and centers the target rectangle to the source rectangle
/** /**
* [description] * Adjusts the target rectangle, changing its width, height and position,
* so that it fully covers the area of the source rectangle, while maintaining its original
* aspect ratio.
*
* Unlike the `FitInside` function, the target rectangle may extend further out than the source.
* *
* @function Phaser.Geom.Rectangle.FitOutside * @function Phaser.Geom.Rectangle.FitOutside
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Rectangle} O - [target,$return] * @generic {Phaser.Geom.Rectangle} O - [target,$return]
* *
* @param {Phaser.Geom.Rectangle} target - [description] * @param {Phaser.Geom.Rectangle} target - The target rectangle to adjust.
* @param {Phaser.Geom.Rectangle} source - [description] * @param {Phaser.Geom.Rectangle} source - The source rectangle to envlope the target in.
* *
* @return {Phaser.Geom.Rectangle} [description] * @return {Phaser.Geom.Rectangle} The modified target rectangle instance.
*/ */
var FitOutside = function (target, source) var FitOutside = function (target, source)
{ {

View file

@ -5,16 +5,16 @@
*/ */
/** /**
* [description] * Rounds down (floors) the top left X and Y co-ordinates of the given Rectangle to the largest integer less than or equal to them
* *
* @function Phaser.Geom.Rectangle.Floor * @function Phaser.Geom.Rectangle.Floor
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Rectangle} O - [rect,$return] * @generic {Phaser.Geom.Rectangle} O - [rect,$return]
* *
* @param {Phaser.Geom.Rectangle} rect - [description] * @param {Phaser.Geom.Rectangle} rect - The rectangle to floor the top left X and Y co-ordinates of
* *
* @return {Phaser.Geom.Rectangle} [description] * @return {Phaser.Geom.Rectangle} The rectangle that was passed to this function with its co-ordinates floored.
*/ */
var Floor = function (rect) var Floor = function (rect)
{ {

View file

@ -19,8 +19,8 @@ var Perimeter = require('./Perimeter');
* @generic {Phaser.Geom.Point[]} O - [out,$return] * @generic {Phaser.Geom.Point[]} O - [out,$return]
* *
* @param {Phaser.Geom.Rectangle} rectangle - The Rectangle object to get the points from. * @param {Phaser.Geom.Rectangle} rectangle - The Rectangle object to get the points from.
* @param {number} step - [description] * @param {number} step - Step between points. Used to calculate the number of points to return when quantity is falsy. Ignored if quantity is positive.
* @param {integer} quantity - [description] * @param {integer} quantity - The number of evenly spaced points from the rectangles perimeter to return. If falsy, step param will be used to calculate the number of points.
* @param {(array|Phaser.Geom.Point[])} [out] - An optional array to store the points in. * @param {(array|Phaser.Geom.Point[])} [out] - An optional array to store the points in.
* *
* @return {(array|Phaser.Geom.Point[])} An array of Points from the perimeter of the rectangle. * @return {(array|Phaser.Geom.Point[])} An array of Points from the perimeter of the rectangle.

View file

@ -5,15 +5,15 @@
*/ */
/** /**
* [description] * Checks if two Rectangles overlap. If a Rectangle is within another Rectangle, the two will be considered overlapping. Thus, the Rectangles are treated as "solid".
* *
* @function Phaser.Geom.Rectangle.Overlaps * @function Phaser.Geom.Rectangle.Overlaps
* @since 3.0.0 * @since 3.0.0
* *
* @param {Phaser.Geom.Rectangle} rectA - [description] * @param {Phaser.Geom.Rectangle} rectA - The first Rectangle to check.
* @param {Phaser.Geom.Rectangle} rectB - [description] * @param {Phaser.Geom.Rectangle} rectB - The second Rectangle to check.
* *
* @return {boolean} [description] * @return {boolean} `true` if the two Rectangles overlap, `false` otherwise.
*/ */
var Overlaps = function (rectA, rectB) var Overlaps = function (rectA, rectB)
{ {

View file

@ -9,17 +9,17 @@ var Circle = require('../circle/Circle');
// Adapted from https://gist.github.com/mutoo/5617691 // Adapted from https://gist.github.com/mutoo/5617691
/** /**
* [description] * Finds the circumscribed circle (circumcircle) of a Triangle object. The circumcircle is the circle which touches all of the triangle's vertices.
* *
* @function Phaser.Geom.Triangle.CircumCircle * @function Phaser.Geom.Triangle.CircumCircle
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Circle} O - [out,$return] * @generic {Phaser.Geom.Circle} O - [out,$return]
* *
* @param {Phaser.Geom.Triangle} triangle - [description] * @param {Phaser.Geom.Triangle} triangle - The Triangle to use as input.
* @param {Phaser.Geom.Circle} [out] - [description] * @param {Phaser.Geom.Circle} [out] - An optional Circle to store the result in.
* *
* @return {Phaser.Geom.Circle} [description] * @return {Phaser.Geom.Circle} The updated `out` Circle, or a new Circle if none was provided.
*/ */
var CircumCircle = function (triangle, out) var CircumCircle = function (triangle, out)
{ {

View file

@ -19,17 +19,17 @@ function getLength (x1, y1, x2, y2)
} }
/** /**
* [description] * Calculates the position of the incenter of a Triangle object. This is the point where its three angle bisectors meet and it's also the center of the incircle, which is the circle inscribed in the triangle.
* *
* @function Phaser.Geom.Triangle.InCenter * @function Phaser.Geom.Triangle.InCenter
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Point} O - [out,$return] * @generic {Phaser.Geom.Point} O - [out,$return]
* *
* @param {Phaser.Geom.Triangle} triangle - [description] * @param {Phaser.Geom.Triangle} triangle - The Triangle to find the incenter of.
* @param {Phaser.Geom.Point} [out] - [description] * @param {Phaser.Geom.Point} [out] - An optional Point in which to store the coordinates.
* *
* @return {Phaser.Geom.Point} [description] * @return {Phaser.Geom.Point} Point (x, y) of the center pixel of the triangle.
*/ */
var InCenter = function (triangle, out) var InCenter = function (triangle, out)
{ {

View file

@ -5,18 +5,18 @@
*/ */
/** /**
* [description] * Moves each point (vertex) of a Triangle by a given offset, thus moving the entire Triangle by that offset.
* *
* @function Phaser.Geom.Triangle.Offset * @function Phaser.Geom.Triangle.Offset
* @since 3.0.0 * @since 3.0.0
* *
* @generic {Phaser.Geom.Triangle} O - [triangle,$return] * @generic {Phaser.Geom.Triangle} O - [triangle,$return]
* *
* @param {Phaser.Geom.Triangle} triangle - [description] * @param {Phaser.Geom.Triangle} triangle - The Triangle to move.
* @param {number} x - [description] * @param {number} x - The horizontal offset (distance) by which to move each point. Can be positive or negative.
* @param {number} y - [description] * @param {number} y - The vertical offset (distance) by which to move each point. Can be positive or negative.
* *
* @return {Phaser.Geom.Triangle} [description] * @return {Phaser.Geom.Triangle} The modified Triangle.
*/ */
var Offset = function (triangle, x, y) var Offset = function (triangle, x, y)
{ {

View file

@ -7,6 +7,7 @@
var Class = require('../utils/Class'); var Class = require('../utils/Class');
var CONST = require('./const'); var CONST = require('./const');
var EventEmitter = require('eventemitter3'); var EventEmitter = require('eventemitter3');
var Keyboard = require('./keyboard/KeyboardManager');
var Mouse = require('./mouse/MouseManager'); var Mouse = require('./mouse/MouseManager');
var Pointer = require('./Pointer'); var Pointer = require('./Pointer');
var Rectangle = require('../geom/rectangle/Rectangle'); var Rectangle = require('../geom/rectangle/Rectangle');
@ -63,10 +64,10 @@ var InputManager = new Class({
this.canvas; this.canvas;
/** /**
* The Input Configuration object, as set in the Game Config. * The Game Configuration object, as set during the game boot.
* *
* @name Phaser.Input.InputManager#config * @name Phaser.Input.InputManager#config
* @type {object} * @type {Phaser.Boot.Config}
* @since 3.0.0 * @since 3.0.0
*/ */
this.config = config; this.config = config;
@ -110,6 +111,27 @@ var InputManager = new Class({
*/ */
this.domCallbacks = { up: [], down: [], move: [], upOnce: [], downOnce: [], moveOnce: [] }; this.domCallbacks = { up: [], down: [], move: [], upOnce: [], downOnce: [], moveOnce: [] };
/**
* Are any mouse or touch pointers currently over the game canvas?
* This is updated automatically by the canvas over and out handlers.
*
* @name Phaser.Input.InputManager#isOver
* @type {boolean}
* @readonly
* @since 3.16.0
*/
this.isOver = true;
/**
* isOver state change property.
*
* @name Phaser.Input.InputManager#_emitIsOverEvent
* @type {boolean}
* @private
* @since 3.16.0
*/
this._emitIsOverEvent = false;
/** /**
* Are there any up callbacks defined? * Are there any up callbacks defined?
* *
@ -175,6 +197,15 @@ var InputManager = new Class({
*/ */
this.defaultCursor = ''; this.defaultCursor = '';
/**
* A reference to the Keyboard Manager class, if enabled via the `input.keyboard` Game Config property.
*
* @name Phaser.Input.InputManager#keyboard
* @type {?Phaser.Input.Keyboard.KeyboardManager}
* @since 3.16.0
*/
this.keyboard = (config.inputKeyboard) ? new Keyboard(this) : null;
/** /**
* A reference to the Mouse Manager class, if enabled via the `input.mouse` Game Config property. * A reference to the Mouse Manager class, if enabled via the `input.mouse` Game Config property.
* *
@ -226,7 +257,11 @@ var InputManager = new Class({
for (var i = 0; i <= this.pointersTotal; i++) for (var i = 0; i <= this.pointersTotal; i++)
{ {
this.pointers.push(new Pointer(this, i)); var pointer = new Pointer(this, i);
pointer.smoothFactor = config.inputSmoothFactor;
this.pointers.push(pointer);
} }
/** /**
@ -397,7 +432,6 @@ var InputManager = new Class({
*/ */
resize: function () resize: function ()
{ {
/*
this.updateBounds(); this.updateBounds();
// Game config size // Game config size
@ -411,7 +445,38 @@ var InputManager = new Class({
// Scale factor // Scale factor
this.scale.x = gw / bw; this.scale.x = gw / bw;
this.scale.y = gh / bh; this.scale.y = gh / bh;
*/ },
/**
* Internal canvas state change, called automatically by the Mouse Manager.
*
* @method Phaser.Input.InputManager#setCanvasOver
* @private
* @since 3.16.0
*
* @param {number} event - The DOM Event.
*/
setCanvasOver: function (event)
{
this.isOver = true;
this._emitIsOverEvent = event;
},
/**
* Internal canvas state change, called automatically by the Mouse Manager.
*
* @method Phaser.Input.InputManager#setCanvasOut
* @private
* @since 3.16.0
*
* @param {number} event - The DOM Event.
*/
setCanvasOut: function (event)
{
this.isOver = false;
this._emitIsOverEvent = event;
}, },
/** /**
@ -441,11 +506,16 @@ var InputManager = new Class({
for (i = 0; i < this.pointersTotal; i++) for (i = 0; i < this.pointersTotal; i++)
{ {
pointers[i].reset(); pointers[i].reset(time);
} }
if (!this.enabled || len === 0) if (!this.enabled || len === 0)
{ {
for (i = 0; i < this.pointersTotal; i++)
{
pointers[i].updateMotion();
}
return; return;
} }
@ -502,6 +572,11 @@ var InputManager = new Class({
break; break;
} }
} }
for (i = 0; i < this.pointersTotal; i++)
{
pointers[i].updateMotion();
}
}, },
/** /**
@ -521,6 +596,9 @@ var InputManager = new Class({
{ {
this.canvas.style.cursor = this.defaultCursor; this.canvas.style.cursor = this.defaultCursor;
} }
// Reset the isOver event
this._emitIsOverEvent = null;
}, },
/** /**
@ -768,6 +846,8 @@ var InputManager = new Class({
var pointer = new Pointer(this, id); var pointer = new Pointer(this, id);
pointer.smoothFactor = this.config.inputSmoothFactor;
this.pointers.push(pointer); this.pointers.push(pointer);
this.pointersTotal++; this.pointersTotal++;
@ -1310,15 +1390,35 @@ var InputManager = new Class({
* @param {Phaser.Input.Pointer} pointer - The Pointer to transform the values for. * @param {Phaser.Input.Pointer} pointer - The Pointer to transform the values for.
* @param {number} pageX - The Page X value. * @param {number} pageX - The Page X value.
* @param {number} pageY - The Page Y value. * @param {number} pageY - The Page Y value.
* @param {boolean} wasMove - Are we transforming the Pointer from a move event, or an up / down event?
*/ */
transformPointer: function (pointer, pageX, pageY) transformPointer: function (pointer, pageX, pageY, wasMove)
{ {
// Store the previous position var p0 = pointer.position;
pointer.prevPosition.x = pointer.x; var p1 = pointer.prevPosition;
pointer.prevPosition.y = pointer.y;
pointer.x = (pageX - this.bounds.left) * this.scale.x; // Store previous position
pointer.y = (pageY - this.bounds.top) * this.scale.y; p1.x = p0.x;
p1.y = p0.y;
// Translate coordinates
var x = (pageX - this.bounds.left) * this.scale.x;
var y = (pageY - this.bounds.top) * this.scale.y;
var a = pointer.smoothFactor;
if (!wasMove || a === 0)
{
// Set immediately
p0.x = x;
p0.y = y;
}
else
{
// Apply smoothing
p0.x = x * a + p1.x * (1 - a);
p0.y = y * a + p1.y * (1 - a);
}
}, },
/** /**
@ -1415,6 +1515,11 @@ var InputManager = new Class({
{ {
this.events.removeAllListeners(); this.events.removeAllListeners();
if (this.keyboard)
{
this.keyboard.destroy();
}
if (this.mouse) if (this.mouse)
{ {
this.mouse.destroy(); this.mouse.destroy();

View file

@ -406,7 +406,7 @@ var InputPlugin = new Class({
preUpdate: function () preUpdate: function ()
{ {
// Registered input plugins listen for this // Registered input plugins listen for this
this.pluginEvents.emit('preUpdate'); this.pluginEvents.emit('preupdate');
var removeList = this._pendingRemoval; var removeList = this._pendingRemoval;
var insertList = this._pendingInsertion; var insertList = this._pendingInsertion;
@ -476,16 +476,23 @@ var InputPlugin = new Class({
return; return;
} }
this.pluginEvents.emit('update', time, delta);
var manager = this.manager; var manager = this.manager;
this.pluginEvents.emit('update', time, delta);
// Another Scene above this one has already consumed the input events, or we're in transition // Another Scene above this one has already consumed the input events, or we're in transition
if (manager.globalTopOnly && manager.ignoreEvents) if (manager.globalTopOnly && manager.ignoreEvents)
{ {
return; return;
} }
if (manager._emitIsOverEvent)
{
var event = (manager.isOver) ? 'gameover' : 'gameout';
this.emit(event, time, manager._emitIsOverEvent);
}
var runUpdate = (manager.dirty || this.pollRate === 0); var runUpdate = (manager.dirty || this.pollRate === 0);
if (this.pollRate > -1) if (this.pollRate > -1)
@ -548,16 +555,16 @@ var InputPlugin = new Class({
total += this.processDownEvents(pointer); total += this.processDownEvents(pointer);
} }
if (pointer.justUp)
{
total += this.processUpEvents(pointer);
}
if (pointer.justMoved) if (pointer.justMoved)
{ {
total += this.processMoveEvents(pointer); total += this.processMoveEvents(pointer);
} }
if (pointer.justUp)
{
total += this.processUpEvents(pointer);
}
if (total > 0 && manager.globalTopOnly) if (total > 0 && manager.globalTopOnly)
{ {
// We interacted with an event in this Scene, so block any Scenes below us from doing the same this frame // We interacted with an event in this Scene, so block any Scenes below us from doing the same this frame
@ -751,6 +758,7 @@ var InputPlugin = new Class({
* @fires Phaser.GameObjects.GameObject#pointerdownEvent * @fires Phaser.GameObjects.GameObject#pointerdownEvent
* @fires Phaser.Input.InputPlugin#gameobjectdownEvent * @fires Phaser.Input.InputPlugin#gameobjectdownEvent
* @fires Phaser.Input.InputPlugin#pointerdownEvent * @fires Phaser.Input.InputPlugin#pointerdownEvent
* @fires Phaser.Input.InputPlugin#pointerdownoutsideEvent
* @since 3.0.0 * @since 3.0.0
* *
* @param {Phaser.Input.Pointer} pointer - The Pointer being tested. * @param {Phaser.Input.Pointer} pointer - The Pointer being tested.
@ -798,10 +806,13 @@ var InputPlugin = new Class({
} }
} }
// Contains ALL Game Objects currently over in the array // If they released outside the canvas, but pressed down inside it, we'll still dispatch the event.
if (!aborted) if (!aborted)
{ {
this.emit('pointerdown', pointer, currentlyOver); var type = (pointer.downElement === this.manager.game.canvas) ? 'pointerdown' : 'pointerdownoutside';
// Contains ALL Game Objects currently up in the array
this.emit(type, pointer, currentlyOver);
} }
return total; return total;
@ -1337,6 +1348,7 @@ var InputPlugin = new Class({
* @private * @private
* @fires Phaser.GameObjects.GameObject#pointerupEvent * @fires Phaser.GameObjects.GameObject#pointerupEvent
* @fires Phaser.Input.InputPlugin#gameobjectupEvent * @fires Phaser.Input.InputPlugin#gameobjectupEvent
* @fires Phaser.Input.InputPlugin#gameobjectupoutsideEvent
* @since 3.0.0 * @since 3.0.0
* *
* @param {Phaser.Input.Pointer} pointer - The pointer to check for events against. * @param {Phaser.Input.Pointer} pointer - The pointer to check for events against.
@ -1364,8 +1376,6 @@ var InputPlugin = new Class({
continue; continue;
} }
// pointerupoutside
gameObject.emit('pointerup', pointer, gameObject.input.localX, gameObject.input.localY, _eventContainer); gameObject.emit('pointerup', pointer, gameObject.input.localX, gameObject.input.localY, _eventContainer);
if (_eventData.cancelled) if (_eventData.cancelled)
@ -1383,10 +1393,13 @@ var InputPlugin = new Class({
} }
} }
// If they released outside the canvas, but pressed down inside it, we'll still dispatch the event.
if (!aborted) if (!aborted)
{ {
var type = (pointer.upElement === this.manager.game.canvas) ? 'pointerup' : 'pointerupoutside';
// Contains ALL Game Objects currently up in the array // Contains ALL Game Objects currently up in the array
this.emit('pointerup', pointer, currentlyOver); this.emit(type, pointer, currentlyOver);
} }
return currentlyOver.length; return currentlyOver.length;
@ -1704,20 +1717,20 @@ var InputPlugin = new Class({
var width = 0; var width = 0;
var height = 0; var height = 0;
if (frame) if (gameObject.width)
{
width = frame.realWidth;
height = frame.realHeight;
}
else if (gameObject.width)
{ {
width = gameObject.width; width = gameObject.width;
height = gameObject.height; height = gameObject.height;
} }
else if (frame)
{
width = frame.realWidth;
height = frame.realHeight;
}
if (gameObject.type === 'Container' && (width === 0 || height === 0)) if (gameObject.type === 'Container' && (width === 0 || height === 0))
{ {
console.warn('Container.setInteractive() must specify a Shape or call setSize() first'); console.warn('Container.setInteractive must specify a Shape or call setSize() first');
continue; continue;
} }
@ -2297,6 +2310,23 @@ var InputPlugin = new Class({
}, },
/**
* Are any mouse or touch pointers currently over the game canvas?
*
* @name Phaser.Input.InputPlugin#isOver
* @type {boolean}
* @readonly
* @since 3.16.0
*/
isOver: {
get: function ()
{
return this.manager.isOver;
}
},
/** /**
* The mouse has its own unique Pointer object, which you can reference directly if making a _desktop specific game_. * The mouse has its own unique Pointer object, which you can reference directly if making a _desktop specific game_.
* If you are supporting both desktop and touch devices then do not use this property, instead use `activePointer` * If you are supporting both desktop and touch devices then do not use this property, instead use `activePointer`
@ -2675,4 +2705,14 @@ module.exports = InputPlugin;
* @event Phaser.Input.InputPlugin#pointerupEvent * @event Phaser.Input.InputPlugin#pointerupEvent
* @param {Phaser.Input.Pointer} pointer - The Pointer. * @param {Phaser.Input.Pointer} pointer - The Pointer.
* @param {Phaser.GameObjects.GameObject[]} currentlyOver - All the Game Objects currently under the Pointer. * @param {Phaser.GameObjects.GameObject[]} currentlyOver - All the Game Objects currently under the Pointer.
*
* A Pointer was pressed down outside of the game canvas.
* @event Phaser.Input.InputPlugin#pointerdownoutsideEvent
* @param {Phaser.Input.Pointer} pointer - The Pointer.
* @param {Phaser.GameObjects.GameObject[]} currentlyOver - All the Game Objects currently under the Pointer.
*
* A Pointer was released outside of the game canvas.
* @event Phaser.Input.InputPlugin#pointerupoutsideEvent
* @param {Phaser.Input.Pointer} pointer - The Pointer.
* @param {Phaser.GameObjects.GameObject[]} currentlyOver - All the Game Objects currently under the Pointer.
*/ */

View file

@ -4,8 +4,10 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/ */
var Angle = require('../math/angle/Between');
var Class = require('../utils/Class'); var Class = require('../utils/Class');
var Distance = require('../math/distance/DistanceBetween'); var Distance = require('../math/distance/DistanceBetween');
var FuzzyEqual = require('../math/fuzzy/Equal');
var SmoothStepInterpolation = require('../math/interpolation/SmoothStepInterpolation'); var SmoothStepInterpolation = require('../math/interpolation/SmoothStepInterpolation');
var Vector2 = require('../math/Vector2'); var Vector2 = require('../math/Vector2');
@ -66,6 +68,26 @@ var Pointer = new Class({
*/ */
this.event; this.event;
/**
* The DOM element the Pointer was pressed down on, taken from the DOM event.
*
* @name Phaser.Input.Pointer#downElement
* @type {any}
* @readonly
* @since 3.16.0
*/
this.downElement;
/**
* The DOM element the Pointer was released on, taken from the DOM event.
*
* @name Phaser.Input.Pointer#upElement
* @type {any}
* @readonly
* @since 3.16.0
*/
this.upElement;
/** /**
* The camera the Pointer interacted with during its last update. * The camera the Pointer interacted with during its last update.
* *
@ -102,6 +124,7 @@ var Pointer = new Class({
* *
* @name Phaser.Input.Pointer#position * @name Phaser.Input.Pointer#position
* @type {Phaser.Math.Vector2} * @type {Phaser.Math.Vector2}
* @readonly
* @since 3.0.0 * @since 3.0.0
*/ */
this.position = new Vector2(); this.position = new Vector2();
@ -111,15 +134,109 @@ var Pointer = new Class({
* *
* The old x and y values are stored in here during the InputManager.transformPointer call. * The old x and y values are stored in here during the InputManager.transformPointer call.
* *
* You can use it to track how fast the pointer is moving, or to smoothly interpolate between the old and current position. * Use the properties `velocity`, `angle` and `distance` to create your own gesture recognition.
* See the `Pointer.getInterpolatedPosition` method to assist in this.
* *
* @name Phaser.Input.Pointer#prevPosition * @name Phaser.Input.Pointer#prevPosition
* @type {Phaser.Math.Vector2} * @type {Phaser.Math.Vector2}
* @readonly
* @since 3.11.0 * @since 3.11.0
*/ */
this.prevPosition = new Vector2(); this.prevPosition = new Vector2();
/**
* An internal vector used for calculations of the pointer speed and angle.
*
* @name Phaser.Input.Pointer#midPoint
* @type {Phaser.Math.Vector2}
* @private
* @since 3.16.0
*/
this.midPoint = new Vector2(-1, -1);
/**
* The current velocity of the Pointer, based on its current and previous positions.
*
* This value is smoothed out each frame, according to the `motionFactor` property.
*
* This property is updated whenever the Pointer moves, regardless of any button states. In other words,
* it changes based on movement alone - a button doesn't have to be pressed first.
*
* @name Phaser.Input.Pointer#velocity
* @type {Phaser.Math.Vector2}
* @readonly
* @since 3.16.0
*/
this.velocity = new Vector2();
/**
* The current angle the Pointer is moving, in radians, based on its previous and current position.
*
* The angle is based on the old position facing to the current position.
*
* This property is updated whenever the Pointer moves, regardless of any button states. In other words,
* it changes based on movement alone - a button doesn't have to be pressed first.
*
* @name Phaser.Input.Pointer#angle
* @type {number}
* @readonly
* @since 3.16.0
*/
this.angle = 0;
/**
* The distance the Pointer has moved, based on its previous and current position.
*
* This value is smoothed out each frame, according to the `motionFactor` property.
*
* This property is updated whenever the Pointer moves, regardless of any button states. In other words,
* it changes based on movement alone - a button doesn't have to be pressed first.
*
* If you need the total distance travelled since the primary buttons was pressed down,
* then use the `Pointer.getDistance` method.
*
* @name Phaser.Input.Pointer#distance
* @type {number}
* @readonly
* @since 3.16.0
*/
this.distance = 0;
/**
* The smoothing factor to apply to the Pointer position.
*
* Due to their nature, pointer positions are inherently noisy. While this is fine for lots of games, if you need cleaner positions
* then you can set this value to apply an automatic smoothing to the positions as they are recorded.
*
* The default value of zero means 'no smoothing'.
* Set to a small value, such as 0.2, to apply an average level of smoothing between positions. You can do this by changing this
* value directly, or by setting the `input.smoothFactor` property in the Game Config.
*
* Positions are only smoothed when the pointer moves. If the primary button on this Pointer enters an Up or Down state, then the position
* is always precise, and not smoothed.
*
* @name Phaser.Input.Pointer#smoothFactor
* @type {number}
* @default 0
* @since 3.16.0
*/
this.smoothFactor = 0;
/**
* The factor applied to the motion smoothing each frame.
*
* This value is passed to the Smooth Step Interpolation that is used to calculate the velocity,
* angle and distance of the Pointer. It's applied every frame, until the midPoint reaches the current
* position of the Pointer. 0.2 provides a good average but can be increased if you need a
* quicker update and are working in a high performance environment. Never set this value to
* zero.
*
* @name Phaser.Input.Pointer#motionFactor
* @type {number}
* @default 0.2
* @since 3.16.0
*/
this.motionFactor = 0.2;
/** /**
* The x position of this Pointer, translated into the coordinate space of the most recent Camera it interacted with. * The x position of this Pointer, translated into the coordinate space of the most recent Camera it interacted with.
* *
@ -140,6 +257,16 @@ var Pointer = new Class({
*/ */
this.worldY = 0; this.worldY = 0;
/**
* Time when this Pointer was most recently moved (regardless of the state of its buttons, if any)
*
* @name Phaser.Input.Pointer#moveTime
* @type {number}
* @default 0
* @since 3.0.0
*/
this.moveTime = 0;
/** /**
* X coordinate of the Pointer when Button 1 (left button), or Touch, was pressed, used for dragging objects. * X coordinate of the Pointer when Button 1 (left button), or Touch, was pressed, used for dragging objects.
* *
@ -344,6 +471,15 @@ var Pointer = new Class({
* @since 3.10.0 * @since 3.10.0
*/ */
this.active = (id === 0) ? true : false; this.active = (id === 0) ? true : false;
/**
* Time when this Pointer was most recently updated by the Game step.
*
* @name Phaser.Input.Pointer#time
* @type {number}
* @since 3.16.0
*/
this.time = 0;
}, },
/** /**
@ -365,13 +501,13 @@ var Pointer = new Class({
/** /**
* Resets the temporal properties of this Pointer. * Resets the temporal properties of this Pointer.
* Called automatically by the Input Plugin each update. * This method is called automatically each frame by the Input Manager.
* *
* @method Phaser.Input.Pointer#reset * @method Phaser.Input.Pointer#reset
* @private * @private
* @since 3.0.0 * @since 3.0.0
*/ */
reset: function () reset: function (time)
{ {
this.dirty = false; this.dirty = false;
@ -379,10 +515,60 @@ var Pointer = new Class({
this.justUp = false; this.justUp = false;
this.justMoved = false; this.justMoved = false;
this.time = time;
this.movementX = 0; this.movementX = 0;
this.movementY = 0; this.movementY = 0;
}, },
/**
* Calculates the motion of this Pointer, including its velocity and angle of movement.
* This method is called automatically each frame by the Input Manager.
*
* @method Phaser.Input.Pointer#updateMotion
* @private
* @since 3.16.0
*/
updateMotion: function ()
{
var cx = this.position.x;
var cy = this.position.y;
var mx = this.midPoint.x;
var my = this.midPoint.y;
if (cx === mx && cy === my)
{
// Nothing to do here
return;
}
// Moving towards our goal ...
var vx = SmoothStepInterpolation(this.motionFactor, mx, cx);
var vy = SmoothStepInterpolation(this.motionFactor, my, cy);
if (FuzzyEqual(vx, cx, 0.1))
{
vx = cx;
}
if (FuzzyEqual(vy, cy, 0.1))
{
vy = cy;
}
this.midPoint.set(vx, vy);
var dx = cx - vx;
var dy = cy - vy;
this.velocity.set(dx, dy);
this.angle = Angle(vx, vy, cx, cy);
this.distance = Math.sqrt(dx * dx + dy * dy);
},
/** /**
* Internal method to handle a Mouse Up Event. * Internal method to handle a Mouse Up Event.
* *
@ -402,8 +588,10 @@ var Pointer = new Class({
this.event = event; this.event = event;
this.upElement = event.target;
// Sets the local x/y properties // Sets the local x/y properties
this.manager.transformPointer(this, event.pageX, event.pageY); this.manager.transformPointer(this, event.pageX, event.pageY, false);
// 0: Main button pressed, usually the left button or the un-initialized state // 0: Main button pressed, usually the left button or the un-initialized state
if (event.button === 0) if (event.button === 0)
@ -441,8 +629,10 @@ var Pointer = new Class({
this.event = event; this.event = event;
this.downElement = event.target;
// Sets the local x/y properties // Sets the local x/y properties
this.manager.transformPointer(this, event.pageX, event.pageY); this.manager.transformPointer(this, event.pageX, event.pageY, false);
// 0: Main button pressed, usually the left button or the un-initialized state // 0: Main button pressed, usually the left button or the un-initialized state
if (event.button === 0) if (event.button === 0)
@ -471,7 +661,7 @@ var Pointer = new Class({
* @param {MouseEvent} event - The Mouse Event to process. * @param {MouseEvent} event - The Mouse Event to process.
* @param {integer} time - The current timestamp as generated by the Request Animation Frame or SetTimeout. * @param {integer} time - The current timestamp as generated by the Request Animation Frame or SetTimeout.
*/ */
move: function (event) move: function (event, time)
{ {
if (event.buttons) if (event.buttons)
{ {
@ -481,7 +671,7 @@ var Pointer = new Class({
this.event = event; this.event = event;
// Sets the local x/y properties // Sets the local x/y properties
this.manager.transformPointer(this, event.pageX, event.pageY); this.manager.transformPointer(this, event.pageX, event.pageY, true);
if (this.manager.mouse.locked) if (this.manager.mouse.locked)
{ {
@ -492,6 +682,8 @@ var Pointer = new Class({
this.justMoved = true; this.justMoved = true;
this.moveTime = time;
this.dirty = true; this.dirty = true;
this.wasTouch = false; this.wasTouch = false;
@ -522,8 +714,10 @@ var Pointer = new Class({
this.event = event; this.event = event;
this.downElement = event.target;
// Sets the local x/y properties // Sets the local x/y properties
this.manager.transformPointer(this, event.pageX, event.pageY); this.manager.transformPointer(this, event.pageX, event.pageY, false);
this.primaryDown = true; this.primaryDown = true;
this.downX = this.x; this.downX = this.x;
@ -549,15 +743,17 @@ var Pointer = new Class({
* @param {TouchEvent} event - The Touch Event to process. * @param {TouchEvent} event - The Touch Event to process.
* @param {integer} time - The current timestamp as generated by the Request Animation Frame or SetTimeout. * @param {integer} time - The current timestamp as generated by the Request Animation Frame or SetTimeout.
*/ */
touchmove: function (event) touchmove: function (event, time)
{ {
this.event = event; this.event = event;
// Sets the local x/y properties // Sets the local x/y properties
this.manager.transformPointer(this, event.pageX, event.pageY); this.manager.transformPointer(this, event.pageX, event.pageY, true);
this.justMoved = true; this.justMoved = true;
this.moveTime = time;
this.dirty = true; this.dirty = true;
this.wasTouch = true; this.wasTouch = true;
@ -579,8 +775,10 @@ var Pointer = new Class({
this.event = event; this.event = event;
this.upElement = event.target;
// Sets the local x/y properties // Sets the local x/y properties
this.manager.transformPointer(this, event.pageX, event.pageY); this.manager.transformPointer(this, event.pageX, event.pageY, false);
this.primaryDown = false; this.primaryDown = false;
this.upX = this.x; this.upX = this.x;
@ -705,17 +903,131 @@ var Pointer = new Class({
}, },
/** /**
* Returns the distance between the Pointer's current position and where it was * If the Pointer has a button pressed down at the time this method is called, it will return the
* first pressed down (the `downX` and `downY` properties) * distance between the Pointer's `downX` and `downY` values and the current position.
*
* If no button is held down, it will return the last recorded distance, based on where
* the Pointer was when the button was released.
*
* If you wish to get the distance being travelled currently, based on the velocity of the Pointer,
* then see the `Pointer.distance` property.
* *
* @method Phaser.Input.Pointer#getDistance * @method Phaser.Input.Pointer#getDistance
* @since 3.13.0 * @since 3.13.0
* *
* @return {number} The distance the Pointer has moved since being pressed down. * @return {number} The distance the Pointer moved.
*/ */
getDistance: function () getDistance: function ()
{ {
return Distance(this.downX, this.downY, this.x, this.y); if (this.isDown)
{
return Distance(this.downX, this.downY, this.x, this.y);
}
else
{
return Distance(this.downX, this.downY, this.upX, this.upY);
}
},
/**
* If the Pointer has a button pressed down at the time this method is called, it will return the
* horizontal distance between the Pointer's `downX` and `downY` values and the current position.
*
* If no button is held down, it will return the last recorded horizontal distance, based on where
* the Pointer was when the button was released.
*
* @method Phaser.Input.Pointer#getDistanceX
* @since 3.16.0
*
* @return {number} The horizontal distance the Pointer moved.
*/
getDistanceX: function ()
{
if (this.isDown)
{
return Math.abs(this.downX - this.x);
}
else
{
return Math.abs(this.downX - this.upX);
}
},
/**
* If the Pointer has a button pressed down at the time this method is called, it will return the
* vertical distance between the Pointer's `downX` and `downY` values and the current position.
*
* If no button is held down, it will return the last recorded vertical distance, based on where
* the Pointer was when the button was released.
*
* @method Phaser.Input.Pointer#getDistanceY
* @since 3.16.0
*
* @return {number} The vertical distance the Pointer moved.
*/
getDistanceY: function ()
{
if (this.isDown)
{
return Math.abs(this.downY - this.y);
}
else
{
return Math.abs(this.downY - this.upY);
}
},
/**
* If the Pointer has a button pressed down at the time this method is called, it will return the
* duration since the Pointer's was pressed down.
*
* If no button is held down, it will return the last recorded duration, based on the time
* the Pointer button was released.
*
* @method Phaser.Input.Pointer#getDuration
* @since 3.16.0
*
* @return {number} The duration the Pointer was held down for in milliseconds.
*/
getDuration: function ()
{
if (this.isDown)
{
return (this.time - this.downTime);
}
else
{
return (this.upTime - this.downTime);
}
},
/**
* If the Pointer has a button pressed down at the time this method is called, it will return the
* angle between the Pointer's `downX` and `downY` values and the current position.
*
* If no button is held down, it will return the last recorded angle, based on where
* the Pointer was when the button was released.
*
* The angle is based on the old position facing to the current position.
*
* If you wish to get the current angle, based on the velocity of the Pointer, then
* see the `Pointer.angle` property.
*
* @method Phaser.Input.Pointer#getAngle
* @since 3.16.0
*
* @return {number} The angle between the Pointer's coordinates in radians.
*/
getAngle: function ()
{
if (this.isDown)
{
return Angle(this.downX, this.downY, this.x, this.y);
}
else
{
return Angle(this.downX, this.downY, this.upX, this.upY);
}
}, },
/** /**

View file

@ -0,0 +1,432 @@
/**
* @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 ArrayRemove = require('../../utils/array/Remove');
var Class = require('../../utils/Class');
var KeyCodes = require('../../input/keyboard/keys/KeyCodes');
var NOOP = require('../../utils/Class');
/**
* @classdesc
* The Keyboard Manager is a helper class that belongs to the global Input Manager.
*
* Its role is to listen for native DOM Keyboard Events and then store them for further processing by the Keyboard Plugin.
*
* You do not need to create this class directly, the Input Manager will create an instance of it automatically if keyboard
* input has been enabled in the Game Config.
*
* @class KeyboardManager
* @memberof Phaser.Input.Keyboard
* @constructor
* @since 3.16.0
*
* @param {Phaser.Input.InputManager} inputManager - A reference to the Input Manager.
*/
var KeyboardManager = new Class({
initialize:
function KeyboardManager (inputManager)
{
/**
* A reference to the Input Manager.
*
* @name Phaser.Input.Keyboard.KeyboardManager#manager
* @type {Phaser.Input.InputManager}
* @since 3.16.0
*/
this.manager = inputManager;
/**
* An internal event queue.
*
* @name Phaser.Input.Keyboard.KeyboardManager#queue
* @type {KeyboardEvent[]}
* @private
* @since 3.16.0
*/
this.queue = [];
/**
* A flag that controls if the non-modified keys, matching those stored in the `captures` array,
* have `preventDefault` called on them or not.
*
* A non-modified key is one that doesn't have a modifier key held down with it. The modifier keys are
* shift, control, alt and the meta key (Command on a Mac, the Windows Key on Windows).
* Therefore, if the user presses shift + r, it won't prevent this combination, because of the modifier.
* However, if the user presses just the r key on its own, it will have its event prevented.
*
* If you wish to stop capturing the keys, for example switching out to a DOM based element, then
* you can toggle this property at run-time.
*
* @name Phaser.Input.Keyboard.KeyboardManager#preventDefault
* @type {boolean}
* @since 3.16.0
*/
this.preventDefault = true;
/**
* An array of Key Code values that will automatically have `preventDefault` called on them,
* as long as the `KeyboardManager.preventDefault` boolean is set to `true`.
*
* By default the array contains: The Space Key, the Cursor Keys, 0 to 9 and A to Z.
*
* The key must be non-modified when pressed in order to be captured.
*
* A non-modified key is one that doesn't have a modifier key held down with it. The modifier keys are
* shift, control, alt and the meta key (Command on a Mac, the Windows Key on Windows).
* Therefore, if the user presses shift + r, it won't prevent this combination, because of the modifier.
* However, if the user presses just the r key on its own, it will have its event prevented.
*
* If you wish to stop capturing the keys, for example switching out to a DOM based element, then
* you can toggle the `KeyboardManager.preventDefault` boolean at run-time.
*
* If you need more specific control, you can create Key objects and set the flag on each of those instead.
*
* This array can be populated via the Game Config by setting the `input.keyboard.capture` array, or you
* can call the `addCapture` method. See also `removeCapture` and `clearCaptures`.
*
* @name Phaser.Input.Keyboard.KeyboardManager#captures
* @type {integer[]}
* @since 3.16.0
*/
this.captures = [];
/**
* A boolean that controls if the Keyboard Manager is enabled or not.
* Can be toggled on the fly.
*
* @name Phaser.Input.Keyboard.KeyboardManager#enabled
* @type {boolean}
* @default false
* @since 3.16.0
*/
this.enabled = false;
/**
* The Keyboard Event target, as defined in the Game Config.
* Typically the window in which the game is rendering, but can be any interactive DOM element.
*
* @name Phaser.Input.Keyboard.KeyboardManager#target
* @type {any}
* @since 3.16.0
*/
this.target;
/**
* The Key Down Event handler.
* This function is sent the native DOM KeyEvent.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Keyboard.KeyboardManager#onKeyDown
* @type {function}
* @since 3.16.00
*/
this.onKeyDown = NOOP;
/**
* The Key Up Event handler.
* This function is sent the native DOM KeyEvent.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Keyboard.KeyboardManager#onKeyUp
* @type {function}
* @since 3.16.00
*/
this.onKeyUp = NOOP;
inputManager.events.once('boot', this.boot, this);
},
/**
* The Keyboard Manager boot process.
*
* @method Phaser.Input.Keyboard.KeyboardManager#boot
* @private
* @since 3.16.0
*/
boot: function ()
{
var config = this.manager.config;
this.enabled = config.inputKeyboard;
this.target = config.inputKeyboardEventTarget;
this.addCapture(config.inputKeyboardCapture);
if (!this.target && window)
{
this.target = window;
}
if (this.enabled && this.target)
{
this.startListeners();
}
this.manager.game.events.on('poststep', this.postUpdate, this);
},
/**
* Starts the Keyboard Event listeners running.
* This is called automatically and does not need to be manually invoked.
*
* @method Phaser.Input.Keyboard.KeyboardManager#startListeners
* @since 3.16.0
*/
startListeners: function ()
{
var _this = this;
this.onKeyDown = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.queue.push(event);
var modified = (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey);
if (_this.preventDefault && !modified && _this.captures.indexOf(event.keyCode) > -1)
{
event.preventDefault();
}
};
this.onKeyUp = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.queue.push(event);
var modified = (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey);
if (_this.preventDefault && !modified && _this.captures.indexOf(event.keyCode) > -1)
{
event.preventDefault();
}
};
var target = this.target;
if (target)
{
target.addEventListener('keydown', this.onKeyDown, false);
target.addEventListener('keyup', this.onKeyUp, false);
this.enabled = true;
}
},
/**
* Stops the Key Event listeners.
* This is called automatically and does not need to be manually invoked.
*
* @method Phaser.Input.Keyboard.KeyboardManager#stopListeners
* @since 3.16.0
*/
stopListeners: function ()
{
var target = this.target;
target.removeEventListener('keydown', this.onKeyDown, false);
target.removeEventListener('keyup', this.onKeyUp, false);
this.enabled = false;
},
/**
* Clears the event queue.
* Called automatically by the Input Manager.
*
* @method Phaser.Input.Keyboard.KeyboardManager#postUpdate
* @private
* @since 3.16.0
*/
postUpdate: function ()
{
this.queue = [];
},
/**
* By default when a key is pressed Phaser will not stop the event from propagating up to the browser.
* There are some keys this can be annoying for, like the arrow keys or space bar, which make the browser window scroll.
*
* This `addCapture` method enables consuming keyboard event for specific keys so it doesn't bubble up to the the browser
* and cause the default browser behavior.
*
* Please note that keyboard captures are global. This means that if you call this method from within a Scene, to say prevent
* the SPACE BAR from triggering a page scroll, then it will prevent it for any Scene in your game, not just the calling one.
*
* You can pass in a single key code value, or an array of key codes, or a string:
*
* ```javascript
* this.input.keyboard.addCapture(62);
* ```
*
* An array of key codes:
*
* ```javascript
* this.input.keyboard.addCapture([ 62, 63, 64 ]);
* ```
*
* Or a string:
*
* ```javascript
* this.input.keyboard.addCapture('W,S,A,D');
* ```
*
* To use non-alpha numeric keys, use a string, such as 'UP', 'SPACE' or 'LEFT'.
*
* You can also provide an array mixing both strings and key code integers.
*
* If there are active captures after calling this method, the `preventDefault` property is set to `true`.
*
* @method Phaser.Input.Keyboard.KeyboardManager#addCapture
* @since 3.16.0
*
* @param {(string|integer|integer[]|any[])} keycode - The Key Codes to enable capture for, preventing them reaching the browser.
*/
addCapture: function (keycode)
{
if (typeof keycode === 'string')
{
keycode = keycode.split(',');
}
if (!Array.isArray(keycode))
{
keycode = [ keycode ];
}
var captures = this.captures;
for (var i = 0; i < keycode.length; i++)
{
var code = keycode[i];
if (typeof code === 'string')
{
code = KeyCodes[code.toUpperCase()];
}
if (captures.indexOf(code) === -1)
{
captures.push(code);
}
}
this.preventDefault = captures.length > 0;
},
/**
* Removes an existing key capture.
*
* Please note that keyboard captures are global. This means that if you call this method from within a Scene, to remove
* the capture of a key, then it will remove it for any Scene in your game, not just the calling one.
*
* You can pass in a single key code value, or an array of key codes, or a string:
*
* ```javascript
* this.input.keyboard.removeCapture(62);
* ```
*
* An array of key codes:
*
* ```javascript
* this.input.keyboard.removeCapture([ 62, 63, 64 ]);
* ```
*
* Or a string:
*
* ```javascript
* this.input.keyboard.removeCapture('W,S,A,D');
* ```
*
* To use non-alpha numeric keys, use a string, such as 'UP', 'SPACE' or 'LEFT'.
*
* You can also provide an array mixing both strings and key code integers.
*
* If there are no captures left after calling this method, the `preventDefault` property is set to `false`.
*
* @method Phaser.Input.Keyboard.KeyboardManager#removeCapture
* @since 3.16.0
*
* @param {(string|integer|integer[]|any[])} keycode - The Key Codes to disable capture for, allowing them reaching the browser again.
*/
removeCapture: function (keycode)
{
if (typeof keycode === 'string')
{
keycode = keycode.split(',');
}
if (!Array.isArray(keycode))
{
keycode = [ keycode ];
}
var captures = this.captures;
for (var i = 0; i < keycode.length; i++)
{
var code = keycode[i];
if (typeof code === 'string')
{
code = KeyCodes[code.toUpperCase()];
}
ArrayRemove(captures, code);
}
this.preventDefault = captures.length > 0;
},
/**
* Removes all keyboard captures and sets the `preventDefault` property to `false`.
*
* @method Phaser.Input.Keyboard.KeyboardManager#clearCaptures
* @since 3.16.0
*/
clearCaptures: function ()
{
this.captures = [];
this.preventDefault = false;
},
/**
* Destroys this Keyboard Manager instance.
*
* @method Phaser.Input.Keyboard.KeyboardManager#destroy
* @since 3.16.0
*/
destroy: function ()
{
this.stopListeners();
this.clearCaptures();
this.queue = [];
this.manager.game.events.off('poststep', this.postUpdate, this);
this.target = null;
this.enabled = false;
this.manager = null;
}
});
module.exports = KeyboardManager;

View file

@ -12,8 +12,6 @@ var Key = require('./keys/Key');
var KeyCodes = require('./keys/KeyCodes'); var KeyCodes = require('./keys/KeyCodes');
var KeyCombo = require('./combo/KeyCombo'); var KeyCombo = require('./combo/KeyCombo');
var KeyMap = require('./keys/KeyMap'); var KeyMap = require('./keys/KeyMap');
var ProcessKeyDown = require('./keys/ProcessKeyDown');
var ProcessKeyUp = require('./keys/ProcessKeyUp');
var SnapFloor = require('../../math/snap/SnapFloor'); var SnapFloor = require('../../math/snap/SnapFloor');
/** /**
@ -42,6 +40,10 @@ var SnapFloor = require('../../math/snap/SnapFloor');
* var spaceBar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE); * var spaceBar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
* ``` * ```
* *
* If you have multiple parallel Scenes, each trying to get keyboard input, be sure to disable capture on them to stop them from
* stealing input from another Scene in the list. You can do this with `this.input.keyboard.enabled = false` within the
* Scene to stop all input, or `this.input.keyboard.preventDefault = false` to stop a Scene halting input on another Scene.
*
* _Note_: Many keyboards are unable to process certain combinations of keys due to hardware limitations known as ghosting. * _Note_: Many keyboards are unable to process certain combinations of keys due to hardware limitations known as ghosting.
* See http://www.html5gamedevs.com/topic/4876-impossible-to-use-more-than-2-keyboard-input-buttons-at-the-same-time/ for more details. * See http://www.html5gamedevs.com/topic/4876-impossible-to-use-more-than-2-keyboard-input-buttons-at-the-same-time/ for more details.
* *
@ -95,7 +97,16 @@ var KeyboardPlugin = new Class({
this.sceneInputPlugin = sceneInputPlugin; this.sceneInputPlugin = sceneInputPlugin;
/** /**
* A boolean that controls if the Keyboard Plugin is enabled or not. * A reference to the global Keyboard Manager.
*
* @name Phaser.Input.Keyboard.KeyboardPlugin#manager
* @type {Phaser.Input.InputPlugin}
* @since 3.16.0
*/
this.manager = sceneInputPlugin.manager.keyboard;
/**
* A boolean that controls if this Keyboard Plugin is enabled or not.
* Can be toggled on the fly. * Can be toggled on the fly.
* *
* @name Phaser.Input.Keyboard.KeyboardPlugin#enabled * @name Phaser.Input.Keyboard.KeyboardPlugin#enabled
@ -105,16 +116,6 @@ var KeyboardPlugin = new Class({
*/ */
this.enabled = true; this.enabled = true;
/**
* The Keyboard Event target, as defined in the Scene or Game Config.
* Typically the browser window, but can be any interactive DOM element.
*
* @name Phaser.Input.Keyboard.KeyboardPlugin#target
* @type {any}
* @since 3.10.0
*/
this.target;
/** /**
* An array of Key objects to process. * An array of Key objects to process.
* *
@ -133,26 +134,6 @@ var KeyboardPlugin = new Class({
*/ */
this.combos = []; this.combos = [];
/**
* An internal event queue.
*
* @name Phaser.Input.Keyboard.KeyboardPlugin#queue
* @type {KeyboardEvent[]}
* @private
* @since 3.10.0
*/
this.queue = [];
/**
* Internal event handler.
*
* @name Phaser.Input.Keyboard.KeyboardPlugin#onKeyHandler
* @type {function}
* @private
* @since 3.10.0
*/
this.onKeyHandler;
/** /**
* Internal time value. * Internal time value.
* *
@ -178,10 +159,15 @@ var KeyboardPlugin = new Class({
boot: function () boot: function ()
{ {
var settings = this.settings.input; var settings = this.settings.input;
var config = this.scene.sys.game.config;
this.enabled = GetValue(settings, 'keyboard', config.inputKeyboard); this.enabled = GetValue(settings, 'keyboard', true);
this.target = GetValue(settings, 'keyboard.target', config.inputKeyboardEventTarget);
var captures = GetValue(settings, 'keyboard.capture', null);
if (captures)
{
this.addCaptures(captures);
}
this.sceneInputPlugin.pluginEvents.once('destroy', this.destroy, this); this.sceneInputPlugin.pluginEvents.once('destroy', this.destroy, this);
}, },
@ -197,10 +183,7 @@ var KeyboardPlugin = new Class({
*/ */
start: function () start: function ()
{ {
if (this.enabled) this.startListeners();
{
this.startListeners();
}
this.sceneInputPlugin.pluginEvents.once('shutdown', this.shutdown, this); this.sceneInputPlugin.pluginEvents.once('shutdown', this.shutdown, this);
}, },
@ -228,33 +211,6 @@ var KeyboardPlugin = new Class({
*/ */
startListeners: function () startListeners: function ()
{ {
var _this = this;
var handler = function (event)
{
if (event.defaultPrevented || !_this.isActive())
{
// Do nothing if event already handled
return;
}
_this.queue.push(event);
var key = _this.keys[event.keyCode];
if (key && key.preventDefault)
{
event.preventDefault();
}
};
this.onKeyHandler = handler;
this.target.addEventListener('keydown', handler, false);
this.target.addEventListener('keyup', handler, false);
// Finally, listen for an update event from the Input Plugin
this.sceneInputPlugin.pluginEvents.on('update', this.update, this); this.sceneInputPlugin.pluginEvents.on('update', this.update, this);
}, },
@ -268,12 +224,159 @@ var KeyboardPlugin = new Class({
*/ */
stopListeners: function () stopListeners: function ()
{ {
this.target.removeEventListener('keydown', this.onKeyHandler);
this.target.removeEventListener('keyup', this.onKeyHandler);
this.sceneInputPlugin.pluginEvents.off('update', this.update); this.sceneInputPlugin.pluginEvents.off('update', this.update);
}, },
/**
* By default when a key is pressed Phaser will not stop the event from propagating up to the browser.
* There are some keys this can be annoying for, like the arrow keys or space bar, which make the browser window scroll.
*
* This `addCapture` method enables consuming keyboard events for specific keys, so they don't bubble up the browser
* and cause the default behaviors.
*
* Please note that keyboard captures are global. This means that if you call this method from within a Scene, to say prevent
* the SPACE BAR from triggering a page scroll, then it will prevent it for any Scene in your game, not just the calling one.
*
* You can pass a single key code value:
*
* ```javascript
* this.input.keyboard.addCapture(62);
* ```
*
* An array of key codes:
*
* ```javascript
* this.input.keyboard.addCapture([ 62, 63, 64 ]);
* ```
*
* Or, a comma-delimited string:
*
* ```javascript
* this.input.keyboard.addCapture('W,S,A,D');
* ```
*
* To use non-alpha numeric keys, use a string, such as 'UP', 'SPACE' or 'LEFT'.
*
* You can also provide an array mixing both strings and key code integers.
*
* @method Phaser.Input.Keyboard.KeyboardPlugin#addCapture
* @since 3.16.0
*
* @param {(string|integer|integer[]|any[])} keycode - The Key Codes to enable event capture for.
*
* @return {Phaser.Input.Keyboard.KeyboardPlugin} This KeyboardPlugin object.
*/
addCapture: function (keycode)
{
this.manager.addCapture(keycode);
return this;
},
/**
* Removes an existing key capture.
*
* Please note that keyboard captures are global. This means that if you call this method from within a Scene, to remove
* the capture of a key, then it will remove it for any Scene in your game, not just the calling one.
*
* You can pass a single key code value:
*
* ```javascript
* this.input.keyboard.removeCapture(62);
* ```
*
* An array of key codes:
*
* ```javascript
* this.input.keyboard.removeCapture([ 62, 63, 64 ]);
* ```
*
* Or, a comma-delimited string:
*
* ```javascript
* this.input.keyboard.removeCapture('W,S,A,D');
* ```
*
* To use non-alpha numeric keys, use a string, such as 'UP', 'SPACE' or 'LEFT'.
*
* You can also provide an array mixing both strings and key code integers.
*
* @method Phaser.Input.Keyboard.KeyboardPlugin#removeCapture
* @since 3.16.0
*
* @param {(string|integer|integer[]|any[])} keycode - The Key Codes to disable event capture for.
*
* @return {Phaser.Input.Keyboard.KeyboardPlugin} This KeyboardPlugin object.
*/
removeCapture: function (keycode)
{
this.manager.removeCapture(keycode);
return this;
},
/**
* Returns an array that contains all of the keyboard captures currently enabled.
*
* @method Phaser.Input.Keyboard.KeyboardPlugin#getCaptures
* @since 3.16.0
*
* @return {integer[]} An array of all the currently capturing key codes.
*/
getCaptures: function ()
{
return this.manager.captures;
},
/**
* Allows Phaser to prevent any key captures you may have defined from bubbling up the browser.
* You can use this to re-enable event capturing if you had paused it via `disableGlobalCapture`.
*
* @method Phaser.Input.Keyboard.KeyboardPlugin#enableGlobalCapture
* @since 3.16.0
*
* @return {Phaser.Input.Keyboard.KeyboardPlugin} This KeyboardPlugin object.
*/
enableGlobalCapture: function ()
{
this.manager.preventDefault = true;
return this;
},
/**
* Disables Phaser from preventing any key captures you may have defined, without actually removing them.
* You can use this to temporarily disable event capturing if, for example, you swap to a DOM element.
*
* @method Phaser.Input.Keyboard.KeyboardPlugin#disableGlobalCapture
* @since 3.16.0
*
* @return {Phaser.Input.Keyboard.KeyboardPlugin} This KeyboardPlugin object.
*/
disableGlobalCapture: function ()
{
this.manager.preventDefault = false;
return this;
},
/**
* Removes all keyboard captures.
*
* Note that this is a global change. It will clear all event captures across your game, not just for this specific Scene.
*
* @method Phaser.Input.Keyboard.KeyboardPlugin#clearCaptures
* @since 3.16.0
*
* @return {Phaser.Input.Keyboard.KeyboardPlugin} This KeyboardPlugin object.
*/
clearCaptures: function ()
{
this.manager.clearCaptures();
return this;
},
/** /**
* @typedef {object} CursorKeys * @typedef {object} CursorKeys
* @memberof Phaser.Input.Keyboard * @memberof Phaser.Input.Keyboard
@ -331,11 +434,16 @@ var KeyboardPlugin = new Class({
* @since 3.10.0 * @since 3.10.0
* *
* @param {(object|string)} keys - An object containing Key Codes, or a comma-separated string. * @param {(object|string)} keys - An object containing Key Codes, or a comma-separated string.
* @param {boolean} [enableCapture=true] - Automatically call `preventDefault` on the native DOM browser event for the key codes being added.
* @param {boolean} [emitOnRepeat=false] - Controls if the Key will continuously emit a 'down' event while being held down (true), or emit the event just once (false, the default).
* *
* @return {object} An object containing Key objects mapped to the input properties. * @return {object} An object containing Key objects mapped to the input properties.
*/ */
addKeys: function (keys) addKeys: function (keys, enableCapture, emitOnRepeat)
{ {
if (enableCapture === undefined) { enableCapture = true; }
if (emitOnRepeat === undefined) { emitOnRepeat = false; }
var output = {}; var output = {};
if (typeof keys === 'string') if (typeof keys === 'string')
@ -348,7 +456,7 @@ var KeyboardPlugin = new Class({
if (currentKey) if (currentKey)
{ {
output[currentKey] = this.addKey(currentKey); output[currentKey] = this.addKey(currentKey, enableCapture, emitOnRepeat);
} }
} }
} }
@ -356,7 +464,7 @@ var KeyboardPlugin = new Class({
{ {
for (var key in keys) for (var key in keys)
{ {
output[key] = this.addKey(keys[key]); output[key] = this.addKey(keys[key], enableCapture, emitOnRepeat);
} }
} }
@ -374,11 +482,16 @@ var KeyboardPlugin = new Class({
* @since 3.10.0 * @since 3.10.0
* *
* @param {(Phaser.Input.Keyboard.Key|string|integer)} key - Either a Key object, a string, such as `A` or `SPACE`, or a key code value. * @param {(Phaser.Input.Keyboard.Key|string|integer)} key - Either a Key object, a string, such as `A` or `SPACE`, or a key code value.
* @param {boolean} [enableCapture=true] - Automatically call `preventDefault` on the native DOM browser event for the key codes being added.
* @param {boolean} [emitOnRepeat=false] - Controls if the Key will continuously emit a 'down' event while being held down (true), or emit the event just once (false, the default).
* *
* @return {Phaser.Input.Keyboard.Key} The newly created Key object, or a reference to it if it already existed in the keys array. * @return {Phaser.Input.Keyboard.Key} The newly created Key object, or a reference to it if it already existed in the keys array.
*/ */
addKey: function (key) addKey: function (key, enableCapture, emitOnRepeat)
{ {
if (enableCapture === undefined) { enableCapture = true; }
if (emitOnRepeat === undefined) { emitOnRepeat = false; }
var keys = this.keys; var keys = this.keys;
if (key instanceof Key) if (key instanceof Key)
@ -394,6 +507,13 @@ var KeyboardPlugin = new Class({
keys[key.keyCode] = key; keys[key.keyCode] = key;
} }
if (enableCapture)
{
this.addCapture(key.keyCode);
}
key.setEmitOnRepeat(emitOnRepeat);
return key; return key;
} }
@ -405,6 +525,13 @@ var KeyboardPlugin = new Class({
if (!keys[key]) if (!keys[key])
{ {
keys[key] = new Key(key); keys[key] = new Key(key);
if (enableCapture)
{
this.addCapture(key);
}
keys[key].setEmitOnRepeat(emitOnRepeat);
} }
return keys[key]; return keys[key];
@ -419,6 +546,8 @@ var KeyboardPlugin = new Class({
* @since 3.10.0 * @since 3.10.0
* *
* @param {(Phaser.Input.Keyboard.Key|string|integer)} key - Either a Key object, a string, such as `A` or `SPACE`, or a key code value. * @param {(Phaser.Input.Keyboard.Key|string|integer)} key - Either a Key object, a string, such as `A` or `SPACE`, or a key code value.
*
* @return {Phaser.Input.Keyboard.KeyboardPlugin} This KeyboardPlugin object.
*/ */
removeKey: function (key) removeKey: function (key)
{ {
@ -442,6 +571,8 @@ var KeyboardPlugin = new Class({
{ {
keys[key] = undefined; keys[key] = undefined;
} }
return this;
}, },
/** /**
@ -522,7 +653,7 @@ var KeyboardPlugin = new Class({
}, },
/** /**
* Internal update handler called by the Input Manager, which is in turn invoked by the Game step. * Internal update handler called by the Input Plugin, which is in turn invoked by the Game step.
* *
* @method Phaser.Input.Keyboard.KeyboardPlugin#update * @method Phaser.Input.Keyboard.KeyboardPlugin#update
* @private * @private
@ -534,17 +665,14 @@ var KeyboardPlugin = new Class({
{ {
this.time = time; this.time = time;
var len = this.queue.length; var queue = this.manager.queue;
var len = queue.length;
if (!this.enabled || len === 0) if (!this.isActive() || len === 0)
{ {
return; return;
} }
// Clears the queue array, and also means we don't work on array data that could potentially
// be modified during the processing phase
var queue = this.queue.splice(0, len);
var keys = this.keys; var keys = this.keys;
// Process the event queue, dispatching all of the events that have stored up // Process the event queue, dispatching all of the events that have stored up
@ -552,33 +680,87 @@ var KeyboardPlugin = new Class({
{ {
var event = queue[i]; var event = queue[i];
var code = event.keyCode; var code = event.keyCode;
var key = keys[code];
var repeat = false;
// Override the default functions (it's too late for the browser to use them anyway, so we may as well)
if (event.cancelled === undefined)
{
// Event allowed to flow across all handlers in this Scene, and any other Scene in the Scene list
event.cancelled = 0;
// Won't reach any more local (Scene level) handlers
event.stopImmediatePropagation = function ()
{
event.cancelled = 1;
};
// Won't reach any more handlers in any Scene further down the Scene list
event.stopPropagation = function ()
{
event.cancelled = -1;
};
}
if (event.cancelled === -1)
{
// This event has been stopped from broadcasting to any other Scene, so abort.
continue;
}
if (event.type === 'keydown') if (event.type === 'keydown')
{ {
if (KeyMap[code] && (keys[code] === undefined || keys[code].isDown === false)) // Key specific callback first
if (key)
{ {
// Will emit a keyboard or keyup event repeat = key.isDown;
this.emit(event.type, event);
this.emit('keydown_' + KeyMap[code], event); key.onDown(event);
} }
if (keys[code]) if (!event.cancelled && (!key || !repeat))
{ {
ProcessKeyDown(keys[code], event); // keydown_code event
if (KeyMap[code])
{
this.emit('keydown_' + KeyMap[code], event);
}
if (!event.cancelled)
{
// keydown event
this.emit(event.type, event);
}
} }
} }
else else
{ {
// Will emit a keyboard or keyup event // Key specific callback first
this.emit(event.type, event); if (key)
this.emit('keyup_' + KeyMap[code], event);
if (keys[code])
{ {
ProcessKeyUp(keys[code], event); key.onUp(event);
} }
if (!event.cancelled)
{
// keyup_code event
if (KeyMap[code])
{
this.emit('keyup_' + KeyMap[code], event);
}
if (!event.cancelled)
{
// keyup event
this.emit(event.type, event);
}
}
}
// Reset the cancel state for other Scenes to use
if (event.cancelled === 1)
{
event.cancelled = 0;
} }
} }
}, },
@ -593,6 +775,8 @@ var KeyboardPlugin = new Class({
* *
* @method Phaser.Input.Keyboard.KeyboardPlugin#resetKeys * @method Phaser.Input.Keyboard.KeyboardPlugin#resetKeys
* @since 3.15.0 * @since 3.15.0
*
* @return {Phaser.Input.Keyboard.KeyboardPlugin} This KeyboardPlugin object.
*/ */
resetKeys: function () resetKeys: function ()
{ {
@ -643,6 +827,17 @@ var KeyboardPlugin = new Class({
{ {
this.shutdown(); this.shutdown();
var keys = this.keys;
for (var i = 0; i < keys.length; i++)
{
// Because it's a sparsely populated array
if (keys[i])
{
keys[i].destroy();
}
}
this.keys = []; this.keys = [];
this.combos = []; this.combos = [];
this.queue = []; this.queue = [];
@ -650,7 +845,7 @@ var KeyboardPlugin = new Class({
this.scene = null; this.scene = null;
this.settings = null; this.settings = null;
this.sceneInputPlugin = null; this.sceneInputPlugin = null;
this.target = null; this.manager = null;
} }
}); });

View file

@ -10,6 +10,7 @@
module.exports = { module.exports = {
KeyboardManager: require('./KeyboardManager'),
KeyboardPlugin: require('./KeyboardPlugin'), KeyboardPlugin: require('./KeyboardPlugin'),
Key: require('./keys/Key'), Key: require('./keys/Key'),

View file

@ -5,6 +5,7 @@
*/ */
var Class = require('../../../utils/Class'); var Class = require('../../../utils/Class');
var EventEmitter = require('eventemitter3');
/** /**
* @classdesc * @classdesc
@ -12,6 +13,7 @@ var Class = require('../../../utils/Class');
* keycode must be an integer * keycode must be an integer
* *
* @class Key * @class Key
* @extends Phaser.Events.EventEmitter
* @memberof Phaser.Input.Keyboard * @memberof Phaser.Input.Keyboard
* @constructor * @constructor
* @since 3.0.0 * @since 3.0.0
@ -20,10 +22,14 @@ var Class = require('../../../utils/Class');
*/ */
var Key = new Class({ var Key = new Class({
Extends: EventEmitter,
initialize: initialize:
function Key (keyCode) function Key (keyCode)
{ {
EventEmitter.call(this);
/** /**
* The keycode of this key. * The keycode of this key.
* *
@ -42,16 +48,6 @@ var Key = new Class({
*/ */
this.originalEvent = undefined; this.originalEvent = undefined;
/**
* Should this Key prevent event propagation?
*
* @name Phaser.Input.Keyboard.Key#preventDefault
* @type {boolean}
* @default true
* @since 3.0.0
*/
this.preventDefault = true;
/** /**
* Can this Key be processed? * Can this Key be processed?
* *
@ -112,6 +108,17 @@ var Key = new Class({
*/ */
this.shiftKey = false; this.shiftKey = false;
/**
* The down state of the Meta key, if pressed at the same time as this key.
* On a Mac the Meta Key is the Command key. On Windows keyboards, it's the Windows key.
*
* @name Phaser.Input.Keyboard.Key#metaKey
* @type {boolean}
* @default false
* @since 3.16.0
*/
this.metaKey = false;
/** /**
* The location of the modifier key. 0 for standard (or unknown), 1 for left, 2 for right, 3 for numpad. * The location of the modifier key. 0 for standard (or unknown), 1 for left, 2 for right, 3 for numpad.
* *
@ -152,6 +159,19 @@ var Key = new Class({
*/ */
this.timeUp = 0; this.timeUp = 0;
/**
* When a key is held down should it continuously fire the `down` event each time it repeats?
*
* By default it will emit the `down` event just once, but if you wish to receive the event
* for each repeat as well, enable this property.
*
* @name Phaser.Input.Keyboard.Key#emitOnRepeat
* @type {boolean}
* @default false
* @since 3.16.0
*/
this.emitOnRepeat = false;
/** /**
* If a key is held down this holds down the number of times the key has 'repeated'. * If a key is held down this holds down the number of times the key has 'repeated'.
* *
@ -195,10 +215,102 @@ var Key = new Class({
this._tick = -1; this._tick = -1;
}, },
/**
* Controls if this Key will continuously emit a `down` event while being held down (true),
* or emit the event just once, on first press, and then skip future events (false).
*
* @method Phaser.Input.Keyboard.Key#setEmitOnRepeat
* @since 3.16.0
*
* @param {boolean} value - Emit `down` events on repeated key down actions, or just once?
*
* @return {Phaser.Input.Keyboard.Key} This Key instance.
*/
setEmitOnRepeat: function (value)
{
this.emitOnRepeat = value;
return this;
},
/**
* Processes the Key Down action for this Key.
* Called automatically by the Keyboard Plugin.
*
* @method Phaser.Input.Keyboard.Key#onDown
* @since 3.16.0
*
* @param {KeyboardEvent} event - The native DOM Keyboard event.
*/
onDown: function (event)
{
this.originalEvent = event;
if (!this.enabled)
{
return;
}
this.altKey = event.altKey;
this.ctrlKey = event.ctrlKey;
this.shiftKey = event.shiftKey;
this.metaKey = event.metaKey;
this.location = event.location;
this.repeats++;
if (!this.isDown)
{
this.isDown = true;
this.isUp = false;
this.timeDown = event.timeStamp;
this.duration = 0;
this._justDown = true;
this._justUp = false;
this.emit('down', this, event);
}
else if (this.emitOnRepeat)
{
this.emit('down', this, event);
}
},
/**
* Processes the Key Up action for this Key.
* Called automatically by the Keyboard Plugin.
*
* @method Phaser.Input.Keyboard.Key#onUp
* @since 3.16.0
*
* @param {KeyboardEvent} event - The native DOM Keyboard event.
*/
onUp: function (event)
{
this.originalEvent = event;
if (!this.enabled)
{
return;
}
this.isDown = false;
this.isUp = true;
this.timeUp = event.timeStamp;
this.duration = this.timeUp - this.timeDown;
this.repeats = 0;
this._justDown = false;
this._justUp = true;
this._tick = -1;
this.emit('up', this, event);
},
/** /**
* Resets this Key object back to its default un-pressed state. * Resets this Key object back to its default un-pressed state.
* *
* @method Phaser.Input.Keyboard.Key.reset * @method Phaser.Input.Keyboard.Key#reset
* @since 3.6.0 * @since 3.6.0
* *
* @return {Phaser.Input.Keyboard.Key} This Key instance. * @return {Phaser.Input.Keyboard.Key} This Key instance.
@ -212,6 +324,7 @@ var Key = new Class({
this.altKey = false; this.altKey = false;
this.ctrlKey = false; this.ctrlKey = false;
this.shiftKey = false; this.shiftKey = false;
this.metaKey = false;
this.timeDown = 0; this.timeDown = 0;
this.duration = 0; this.duration = 0;
this.timeUp = 0; this.timeUp = 0;
@ -221,6 +334,19 @@ var Key = new Class({
this._tick = -1; this._tick = -1;
return this; return this;
},
/**
* Removes any bound event handlers and removes local references.
*
* @method Phaser.Input.Keyboard.Key#destroy
* @since 3.16.0
*/
destroy: function ()
{
this.removeAllListeners();
this.originalEvent = null;
} }
}); });

View file

@ -1,53 +0,0 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Used internally by the Keyboard Plugin.
*
* @function Phaser.Input.Keyboard.ProcessKeyDown
* @private
* @since 3.0.0
*
* @param {Phaser.Input.Keyboard.Key} key - The Key to process the event for.
* @param {KeyboardEvent} event - The native Keyboard event.
*
* @return {Phaser.Input.Keyboard.Key} The Key that was processed.
*/
var ProcessKeyDown = function (key, event)
{
key.originalEvent = event;
if (key.preventDefault)
{
event.preventDefault();
}
if (!key.enabled)
{
return;
}
key.altKey = event.altKey;
key.ctrlKey = event.ctrlKey;
key.shiftKey = event.shiftKey;
key.location = event.location;
if (key.isDown === false)
{
key.isDown = true;
key.isUp = false;
key.timeDown = event.timeStamp;
key.duration = 0;
key._justDown = true;
key._justUp = false;
}
key.repeats++;
return key;
};
module.exports = ProcessKeyDown;

View file

@ -1,46 +0,0 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Used internally by the Keyboard Plugin.
*
* @function Phaser.Input.Keyboard.ProcessKeyUp
* @private
* @since 3.0.0
*
* @param {Phaser.Input.Keyboard.Key} key - The Key to process the event for.
* @param {KeyboardEvent} event - The native Keyboard event.
*
* @return {Phaser.Input.Keyboard.Key} The Key that was processed.
*/
var ProcessKeyUp = function (key, event)
{
key.originalEvent = event;
if (key.preventDefault)
{
event.preventDefault();
}
if (!key.enabled)
{
return;
}
key.isDown = false;
key.isUp = true;
key.timeUp = event.timeStamp;
key.duration = key.timeUp - key.timeDown;
key.repeats = 0;
key._justDown = false;
key._justUp = true;
key._tick = -1;
return key;
};
module.exports = ProcessKeyUp;

View file

@ -115,6 +115,28 @@ var MouseManager = new Class({
*/ */
this.onMouseUp = NOOP; this.onMouseUp = NOOP;
/**
* The Mouse Over Event handler.
* This function is sent the native DOM MouseEvent.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Mouse.MouseManager#onMouseOver
* @type {function}
* @since 3.16.0
*/
this.onMouseOver = NOOP;
/**
* The Mouse Out Event handler.
* This function is sent the native DOM MouseEvent.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Mouse.MouseManager#onMouseOut
* @type {function}
* @since 3.16.0
*/
this.onMouseOut = NOOP;
/** /**
* Internal pointerLockChange handler. * Internal pointerLockChange handler.
* This function is sent the native DOM MouseEvent. * This function is sent the native DOM MouseEvent.
@ -238,6 +260,8 @@ var MouseManager = new Class({
startListeners: function () startListeners: function ()
{ {
var _this = this; var _this = this;
var canvas = this.manager.canvas;
var autoFocus = (window && window.focus && this.manager.game.config.autoFocus);
this.onMouseMove = function (event) this.onMouseMove = function (event)
{ {
@ -257,6 +281,11 @@ var MouseManager = new Class({
this.onMouseDown = function (event) this.onMouseDown = function (event)
{ {
if (autoFocus)
{
window.focus();
}
if (event.defaultPrevented || !_this.enabled || !_this.manager) if (event.defaultPrevented || !_this.enabled || !_this.manager)
{ {
// Do nothing if event already handled // Do nothing if event already handled
@ -265,7 +294,7 @@ var MouseManager = new Class({
_this.manager.queueMouseDown(event); _this.manager.queueMouseDown(event);
if (_this.capture) if (_this.capture && event.target === canvas)
{ {
event.preventDefault(); event.preventDefault();
} }
@ -281,12 +310,34 @@ var MouseManager = new Class({
_this.manager.queueMouseUp(event); _this.manager.queueMouseUp(event);
if (_this.capture) if (_this.capture && event.target === canvas)
{ {
event.preventDefault(); event.preventDefault();
} }
}; };
this.onMouseOver = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.manager.setCanvasOver(event);
};
this.onMouseOut = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.manager.setCanvasOut(event);
};
var target = this.target; var target = this.target;
if (!target) if (!target)
@ -297,17 +348,16 @@ var MouseManager = new Class({
var passive = { passive: true }; var passive = { passive: true };
var nonPassive = { passive: false }; var nonPassive = { passive: false };
if (this.capture) target.addEventListener('mousemove', this.onMouseMove, (this.capture) ? nonPassive : passive);
target.addEventListener('mousedown', this.onMouseDown, (this.capture) ? nonPassive : passive);
target.addEventListener('mouseup', this.onMouseUp, (this.capture) ? nonPassive : passive);
target.addEventListener('mouseover', this.onMouseOver, (this.capture) ? nonPassive : passive);
target.addEventListener('mouseout', this.onMouseOut, (this.capture) ? nonPassive : passive);
if (window)
{ {
target.addEventListener('mousemove', this.onMouseMove, nonPassive); window.addEventListener('mousedown', this.onMouseDown, nonPassive);
target.addEventListener('mousedown', this.onMouseDown, nonPassive); window.addEventListener('mouseup', this.onMouseUp, nonPassive);
target.addEventListener('mouseup', this.onMouseUp, nonPassive);
}
else
{
target.addEventListener('mousemove', this.onMouseMove, passive);
target.addEventListener('mousedown', this.onMouseDown, passive);
target.addEventListener('mouseup', this.onMouseUp, passive);
} }
if (Features.pointerLock) if (Features.pointerLock)
@ -343,6 +393,14 @@ var MouseManager = new Class({
target.removeEventListener('mousemove', this.onMouseMove); target.removeEventListener('mousemove', this.onMouseMove);
target.removeEventListener('mousedown', this.onMouseDown); target.removeEventListener('mousedown', this.onMouseDown);
target.removeEventListener('mouseup', this.onMouseUp); target.removeEventListener('mouseup', this.onMouseUp);
target.removeEventListener('mouseover', this.onMouseOver);
target.removeEventListener('mouseout', this.onMouseOut);
if (window)
{
window.removeEventListener('mousedown', this.onMouseDown);
window.removeEventListener('mouseup', this.onMouseUp);
}
if (Features.pointerLock) if (Features.pointerLock)
{ {

View file

@ -112,6 +112,26 @@ var TouchManager = new Class({
*/ */
this.onTouchCancel = NOOP; this.onTouchCancel = NOOP;
/**
* The Touch Over event handler function.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Touch.TouchManager#onTouchOver
* @type {function}
* @since 3.16.0
*/
this.onTouchOver = NOOP;
/**
* The Touch Out event handler function.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Touch.TouchManager#onTouchOut
* @type {function}
* @since 3.16.0
*/
this.onTouchOut = NOOP;
inputManager.events.once('boot', this.boot, this); inputManager.events.once('boot', this.boot, this);
}, },
@ -154,9 +174,16 @@ var TouchManager = new Class({
startListeners: function () startListeners: function ()
{ {
var _this = this; var _this = this;
var canvas = this.manager.canvas;
var autoFocus = (window && window.focus && this.manager.game.config.autoFocus);
this.onTouchStart = function (event) this.onTouchStart = function (event)
{ {
if (autoFocus)
{
window.focus();
}
if (event.defaultPrevented || !_this.enabled || !_this.manager) if (event.defaultPrevented || !_this.enabled || !_this.manager)
{ {
// Do nothing if event already handled // Do nothing if event already handled
@ -165,7 +192,7 @@ var TouchManager = new Class({
_this.manager.queueTouchStart(event); _this.manager.queueTouchStart(event);
if (_this.capture) if (_this.capture && event.target === canvas)
{ {
event.preventDefault(); event.preventDefault();
} }
@ -197,7 +224,7 @@ var TouchManager = new Class({
_this.manager.queueTouchEnd(event); _this.manager.queueTouchEnd(event);
if (_this.capture) if (_this.capture && event.target === canvas)
{ {
event.preventDefault(); event.preventDefault();
} }
@ -219,6 +246,28 @@ var TouchManager = new Class({
} }
}; };
this.onTouchOver = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.manager.setCanvasOver(event);
};
this.onTouchOut = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.manager.setCanvasOut(event);
};
var target = this.target; var target = this.target;
if (!target) if (!target)
@ -229,18 +278,17 @@ var TouchManager = new Class({
var passive = { passive: true }; var passive = { passive: true };
var nonPassive = { passive: false }; var nonPassive = { passive: false };
if (this.capture) target.addEventListener('touchstart', this.onTouchStart, (this.capture) ? nonPassive : passive);
target.addEventListener('touchmove', this.onTouchMove, (this.capture) ? nonPassive : passive);
target.addEventListener('touchend', this.onTouchEnd, (this.capture) ? nonPassive : passive);
target.addEventListener('touchcancel', this.onTouchCancel, (this.capture) ? nonPassive : passive);
target.addEventListener('touchover', this.onTouchOver, (this.capture) ? nonPassive : passive);
target.addEventListener('touchout', this.onTouchOut, (this.capture) ? nonPassive : passive);
if (window)
{ {
target.addEventListener('touchstart', this.onTouchStart, nonPassive); window.addEventListener('touchstart', this.onTouchStart, nonPassive);
target.addEventListener('touchmove', this.onTouchMove, nonPassive); window.addEventListener('touchend', this.onTouchEnd, nonPassive);
target.addEventListener('touchend', this.onTouchEnd, nonPassive);
target.addEventListener('touchcancel', this.onTouchCancel, nonPassive);
}
else
{
target.addEventListener('touchstart', this.onTouchStart, passive);
target.addEventListener('touchmove', this.onTouchMove, passive);
target.addEventListener('touchend', this.onTouchEnd, passive);
} }
this.enabled = true; this.enabled = true;
@ -261,6 +309,14 @@ var TouchManager = new Class({
target.removeEventListener('touchmove', this.onTouchMove); target.removeEventListener('touchmove', this.onTouchMove);
target.removeEventListener('touchend', this.onTouchEnd); target.removeEventListener('touchend', this.onTouchEnd);
target.removeEventListener('touchcancel', this.onTouchCancel); target.removeEventListener('touchcancel', this.onTouchCancel);
target.removeEventListener('touchover', this.onTouchOver);
target.removeEventListener('touchout', this.onTouchOut);
if (window)
{
window.removeEventListener('touchstart', this.onTouchStart);
window.removeEventListener('touchend', this.onTouchEnd);
}
}, },
/** /**

View file

@ -65,7 +65,6 @@ var LoaderPlugin = new Class({
* *
* @name Phaser.Loader.LoaderPlugin#scene * @name Phaser.Loader.LoaderPlugin#scene
* @type {Phaser.Scene} * @type {Phaser.Scene}
* @protected
* @since 3.0.0 * @since 3.0.0
*/ */
this.scene = scene; this.scene = scene;
@ -75,7 +74,6 @@ var LoaderPlugin = new Class({
* *
* @name Phaser.Loader.LoaderPlugin#systems * @name Phaser.Loader.LoaderPlugin#systems
* @type {Phaser.Scenes.Systems} * @type {Phaser.Scenes.Systems}
* @protected
* @since 3.0.0 * @since 3.0.0
*/ */
this.systems = scene.sys; this.systems = scene.sys;
@ -85,7 +83,6 @@ var LoaderPlugin = new Class({
* *
* @name Phaser.Loader.LoaderPlugin#cacheManager * @name Phaser.Loader.LoaderPlugin#cacheManager
* @type {Phaser.Cache.CacheManager} * @type {Phaser.Cache.CacheManager}
* @protected
* @since 3.7.0 * @since 3.7.0
*/ */
this.cacheManager = scene.sys.cache; this.cacheManager = scene.sys.cache;
@ -95,11 +92,20 @@ var LoaderPlugin = new Class({
* *
* @name Phaser.Loader.LoaderPlugin#textureManager * @name Phaser.Loader.LoaderPlugin#textureManager
* @type {Phaser.Textures.TextureManager} * @type {Phaser.Textures.TextureManager}
* @protected
* @since 3.7.0 * @since 3.7.0
*/ */
this.textureManager = scene.sys.textures; this.textureManager = scene.sys.textures;
/**
* A reference to the global Scene Manager.
*
* @name Phaser.Loader.LoaderPlugin#sceneManager
* @type {Phaser.Scenes.SceneManager}
* @protected
* @since 3.16.0
*/
this.sceneManager = scene.sys.game.scene;
// Inject the available filetypes into the Loader // Inject the available filetypes into the Loader
FileTypesManager.install(this); FileTypesManager.install(this);
@ -1089,6 +1095,7 @@ var LoaderPlugin = new Class({
this.systems = null; this.systems = null;
this.textureManager = null; this.textureManager = null;
this.cacheManager = null; this.cacheManager = null;
this.sceneManager = null;
} }
}); });

View file

@ -0,0 +1,221 @@
/**
* @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('../../utils/Class');
var CONST = require('../const');
var File = require('../File');
var FileTypesManager = require('../FileTypesManager');
var GetFastValue = require('../../utils/object/GetFastValue');
var IsPlainObject = require('../../utils/object/IsPlainObject');
/**
* @typedef {object} Phaser.Loader.FileTypes.SceneFileConfig
*
* @property {string} key - The key of the file. Must be unique within both the Loader and the Text Cache.
* @property {string} [url] - The absolute or relative URL to load the file from.
* @property {string} [extension='txt'] - The default file extension to use if no url is provided.
* @property {XHRSettingsObject} [xhrSettings] - Extra XHR Settings specifically for this file.
*/
/**
* @classdesc
* An external Scene JavaScript File suitable for loading by the Loader.
*
* These are created when you use the Phaser.Loader.LoaderPlugin#sceneFile method and are not typically created directly.
*
* For documentation about what all the arguments and configuration options mean please see Phaser.Loader.LoaderPlugin#sceneFile.
*
* @class SceneFile
* @extends Phaser.Loader.File
* @memberof Phaser.Loader.FileTypes
* @constructor
* @since 3.16.0
*
* @param {Phaser.Loader.LoaderPlugin} loader - A reference to the Loader that is responsible for this file.
* @param {(string|Phaser.Loader.FileTypes.SceneFileConfig)} key - The key to use for this file, or a file configuration object.
* @param {string} [url] - The absolute or relative URL to load this file from. If undefined or `null` it will be set to `<key>.js`, i.e. if `key` was "alien" then the URL will be "alien.js".
* @param {XHRSettingsObject} [xhrSettings] - Extra XHR Settings specifically for this file.
*/
var SceneFile = new Class({
Extends: File,
initialize:
function SceneFile (loader, key, url, xhrSettings)
{
var extension = 'js';
if (IsPlainObject(key))
{
var config = key;
key = GetFastValue(config, 'key');
url = GetFastValue(config, 'url');
xhrSettings = GetFastValue(config, 'xhrSettings');
extension = GetFastValue(config, 'extension', extension);
}
var fileConfig = {
type: 'text',
cache: loader.cacheManager.text,
extension: extension,
responseType: 'text',
key: key,
url: url,
xhrSettings: xhrSettings
};
File.call(this, loader, fileConfig);
},
/**
* Called automatically by Loader.nextFile.
* This method controls what extra work this File does with its loaded data.
*
* @method Phaser.Loader.FileTypes.SceneFile#onProcess
* @since 3.16.0
*/
onProcess: function ()
{
this.state = CONST.FILE_PROCESSING;
this.data = this.xhrLoader.responseText;
this.onProcessComplete();
},
/**
* Adds this file to its target cache upon successful loading and processing.
*
* @method Phaser.Loader.FileTypes.SceneFile#addToCache
* @since 3.16.0
*/
addToCache: function ()
{
var code = this.data.concat('(function(){\n' + 'return new ' + this.key + '();\n' + '}).call(this);');
this.loader.sceneManager.add(this.key, eval(code));
this.complete = true;
}
});
/**
* Adds an external Scene file, or array of Scene files, to the current load queue.
*
* You can call this method from within your Scene's `preload`, along with any other files you wish to load:
*
* ```javascript
* function preload ()
* {
* this.load.sceneFile('Level1', 'src/Level1.js');
* }
* ```
*
* The file is **not** loaded right away. It is added to a queue ready to be loaded either when the loader starts,
* or if it's already running, when the next free load slot becomes available. This happens automatically if you
* are calling this from within the Scene's `preload` method, or a related callback. Because the file is queued
* it means you cannot use the file immediately after calling this method, but must wait for the file to complete.
* The typical flow for a Phaser Scene is that you load assets in the Scene's `preload` method and then when the
* Scene's `create` method is called you are guaranteed that all of those assets are ready for use and have been
* loaded.
*
* The key must be a unique String. It is used to add the file to the global Scene Manager upon a successful load.
*
* For a Scene File it's vitally important that the key matches the class name in the JavaScript file.
*
* For example here is the source file:
*
* ```javascript
* class ExternalScene extends Phaser.Scene {
*
* constructor ()
* {
* super('myScene');
* }
*
* }
* ```
*
* Because the class is called `ExternalScene` that is the exact same key you must use when loading it:
*
* ```javascript
* function preload ()
* {
* this.load.sceneFile('ExternalScene', 'src/yourScene.js');
* }
* ```
*
* The key that is used within the Scene Manager can either be set to the same, or you can override it in the Scene
* constructor, as we've done in the example above, where the Scene key was changed to `myScene`.
*
* The key should be unique both in terms of files being loaded and Scenes already present in the Scene Manager.
* Loading a file using a key that is already taken will result in a warning. If you wish to replace an existing file
* then remove it from the Scene Manager first, before loading a new one.
*
* Instead of passing arguments you can pass a configuration object, such as:
*
* ```javascript
* this.load.sceneFile({
* key: 'Level1',
* url: 'src/Level1.js'
* });
* ```
*
* See the documentation for `Phaser.Loader.FileTypes.SceneFileConfig` for more details.
*
* Once the file has finished loading it will be added to the Scene Manager.
*
* ```javascript
* this.load.sceneFile('Level1', 'src/Level1.js');
* // and later in your game ...
* this.scene.start('Level1');
* ```
*
* If you have specified a prefix in the loader, via `Loader.setPrefix` then this value will be prepended to this files
* key. For example, if the prefix was `WORLD1.` and the key was `Story` the final key will be `WORLD1.Story` and
* this is what you would use to retrieve the text from the Scene Manager.
*
* The URL can be relative or absolute. If the URL is relative the `Loader.baseURL` and `Loader.path` values will be prepended to it.
*
* If the URL isn't specified the Loader will take the key and create a filename from that. For example if the key is "story"
* and no URL is given then the Loader will set the URL to be "story.js". It will always add `.js` as the extension, although
* this can be overridden if using an object instead of method arguments. If you do not desire this action then provide a URL.
*
* Note: The ability to load this type of file will only be available if the Scene File type has been built into Phaser.
* It is available in the default build but can be excluded from custom builds.
*
* @method Phaser.Loader.LoaderPlugin#sceneFile
* @fires Phaser.Loader.LoaderPlugin#addFileEvent
* @since 3.16.0
*
* @param {(string|Phaser.Loader.FileTypes.SceneFileConfig|Phaser.Loader.FileTypes.SceneFileConfig[])} key - The key to use for this file, or a file configuration object, or array of them.
* @param {string} [url] - The absolute or relative URL to load this file from. If undefined or `null` it will be set to `<key>.js`, i.e. if `key` was "alien" then the URL will be "alien.js".
* @param {XHRSettingsObject} [xhrSettings] - An XHR Settings configuration object. Used in replacement of the Loaders default XHR Settings.
*
* @return {Phaser.Loader.LoaderPlugin} The Loader instance.
*/
FileTypesManager.register('sceneFile', function (key, url, xhrSettings)
{
if (Array.isArray(key))
{
for (var i = 0; i < key.length; i++)
{
// If it's an array it has to be an array of Objects, so we get everything out of the 'key' object
this.addFile(new SceneFile(this, key[i]));
}
}
else
{
this.addFile(new SceneFile(this, key, url, xhrSettings));
}
return this;
});
module.exports = SceneFile;

View file

@ -26,6 +26,7 @@ module.exports = {
MultiAtlasFile: require('./MultiAtlasFile'), MultiAtlasFile: require('./MultiAtlasFile'),
PackFile: require('./PackFile'), PackFile: require('./PackFile'),
PluginFile: require('./PluginFile'), PluginFile: require('./PluginFile'),
SceneFile: require('./SceneFile'),
ScenePluginFile: require('./ScenePluginFile'), ScenePluginFile: require('./ScenePluginFile'),
ScriptFile: require('./ScriptFile'), ScriptFile: require('./ScriptFile'),
SpriteSheetFile: require('./SpriteSheetFile'), SpriteSheetFile: require('./SpriteSheetFile'),

View file

@ -5,7 +5,7 @@
*/ */
/** /**
* [description] * Rotate a `point` around `x` and `y` by the given `angle` and `distance`.
* *
* @function Phaser.Math.RotateAroundDistance * @function Phaser.Math.RotateAroundDistance
* @since 3.0.0 * @since 3.0.0
@ -14,7 +14,7 @@
* @param {number} x - The horizontal coordinate to rotate around. * @param {number} x - The horizontal coordinate to rotate around.
* @param {number} y - The vertical coordinate to rotate around. * @param {number} y - The vertical coordinate to rotate around.
* @param {number} angle - The angle of rotation in radians. * @param {number} angle - The angle of rotation in radians.
* @param {number} distance - [description] * @param {number} distance - The distance from (x, y) to place the point at.
* *
* @return {Phaser.Geom.Point} The given point. * @return {Phaser.Geom.Point} The given point.
*/ */

View file

@ -53,6 +53,7 @@ var MATH_CONST = {
/** /**
* An instance of the Random Number Generator. * An instance of the Random Number Generator.
* This is not set until the Game boots.
* *
* @name Phaser.Math.RND * @name Phaser.Math.RND
* @type {Phaser.Math.RandomDataGenerator} * @type {Phaser.Math.RandomDataGenerator}

View file

@ -28,13 +28,14 @@ var LinearInterpolation = function (v, k)
{ {
return Linear(v[0], v[1], f); return Linear(v[0], v[1], f);
} }
else if (k > 1)
if (k > 1)
{ {
return Linear(v[m], v[m - 1], m - f); return Linear(v[m], v[m - 1], m - f);
} }
else
return Linear(v[i], v[(i + 1 > m) ? m : i + 1], f - i); {
return Linear(v[i], v[(i + 1 > m) ? m : i + 1], f - i);
}
}; };
module.exports = LinearInterpolation; module.exports = LinearInterpolation;

View file

@ -478,7 +478,7 @@ var RandomDataGenerator = new Class({
for (var i = len; i > 0; i--) for (var i = len; i > 0; i--)
{ {
var randomIndex = Math.floor(this.frac() * (len + 1)); var randomIndex = Math.floor(this.frac() * (i + 1));
var itemAtIndex = array[randomIndex]; var itemAtIndex = array[randomIndex];
array[randomIndex] = array[i]; array[randomIndex] = array[i];

View file

@ -10,12 +10,10 @@ var Image = require('../../gameobjects/image/Image');
/** /**
* @classdesc * @classdesc
* An Arcade Physics Image Game Object. * An Arcade Physics Image is an Image with an Arcade Physics body and related components.
* The body can be dynamic or static.
* *
* An Image is a light-weight Game Object useful for the display of static images in your game, * The main difference between an Arcade Image and an Arcade Sprite is that you cannot animate an Arcade Image.
* such as logos, backgrounds, scenery or other non-animated elements. Images can have input
* events and physics bodies, or be tweened, tinted or scrolled. The main difference between an
* Image and a Sprite is that you cannot animate an Image as they do not have the Animation component.
* *
* @class Image * @class Image
* @extends Phaser.GameObjects.Image * @extends Phaser.GameObjects.Image

View file

@ -10,15 +10,11 @@ var Sprite = require('../../gameobjects/sprite/Sprite');
/** /**
* @classdesc * @classdesc
* An Arcade Physics Sprite Game Object. * An Arcade Physics Sprite is a Sprite with an Arcade Physics body and related components.
* The body can be dynamic or static.
* *
* A Sprite Game Object is used for the display of both static and animated images in your game. * The main difference between an Arcade Sprite and an Arcade Image is that you cannot animate an Arcade Image.
* Sprites can have input events and physics bodies. They can also be tweened, tinted, scrolled * If you do not require animation then you can safely use Arcade Images instead of Arcade Sprites.
* and animated.
*
* The main difference between a Sprite and an Image Game Object is that you cannot animate Images.
* As such, Sprites take a fraction longer to process and have a larger API footprint due to the Animation
* Component. If you do not require animation then you can safely use Images to replace Sprites in all cases.
* *
* @class Sprite * @class Sprite
* @extends Phaser.GameObjects.Sprite * @extends Phaser.GameObjects.Sprite

View file

@ -623,7 +623,7 @@ var Body = new Class({
this.overlapR = 0; this.overlapR = 0;
/** /**
* Whether this Body is overlapped with another and both have zero velocity. * Whether this Body is overlapped with another and both are not moving.
* *
* @name Phaser.Physics.Arcade.Body#embedded * @name Phaser.Physics.Arcade.Body#embedded
* @type {boolean} * @type {boolean}
@ -718,6 +718,7 @@ var Body = new Class({
* @name Phaser.Physics.Arcade.Body#physicsType * @name Phaser.Physics.Arcade.Body#physicsType
* @type {integer} * @type {integer}
* @readonly * @readonly
* @default Phaser.Physics.Arcade.DYNAMIC_BODY
* @since 3.0.0 * @since 3.0.0
*/ */
this.physicsType = CONST.DYNAMIC_BODY; this.physicsType = CONST.DYNAMIC_BODY;
@ -787,7 +788,8 @@ var Body = new Class({
}, },
/** /**
* Updates this Body's transform, dimensions, and position from its Game Object. * Updates the Body's `transform`, `width`, `height`, and `center` from its Game Object.
* The Body's `position` isn't changed.
* *
* @method Phaser.Physics.Arcade.Body#updateBounds * @method Phaser.Physics.Arcade.Body#updateBounds
* @since 3.0.0 * @since 3.0.0
@ -874,7 +876,7 @@ var Body = new Class({
* @fires Phaser.Physics.Arcade.World#worldbounds * @fires Phaser.Physics.Arcade.World#worldbounds
* @since 3.0.0 * @since 3.0.0
* *
* @param {number} delta - The delta time, in ms, elapsed since the last frame. * @param {number} delta - The delta time, in seconds, elapsed since the last frame.
*/ */
update: function (delta) update: function (delta)
{ {
@ -1395,7 +1397,10 @@ var Body = new Class({
{ {
this.enable = false; this.enable = false;
this.world.pendingDestroy.set(this); if (this.world)
{
this.world.pendingDestroy.set(this);
}
}, },
/** /**

View file

@ -40,7 +40,7 @@ var Collider = new Class({
this.world = world; this.world = world;
/** /**
* The name of the collider (unused by phaser). * The name of the collider (unused by Phaser).
* *
* @name Phaser.Physics.Arcade.Collider#name * @name Phaser.Physics.Arcade.Collider#name
* @type {string} * @type {string}

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