Skip to content

Commit 21479ac

Browse files
committed
Sprite.autoCull now properly works if the camera moves around the world.
Sprite.inCamera uses a much faster check if auto culling or world bounds checks are enabled and properly adjusts for camera position.
1 parent 60aa21d commit 21479ac

5 files changed

Lines changed: 80 additions & 42 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ The proxy methods are generated one-time dynamically but only when needed.
157157
* The Phaser.Device class has been made into a singleton and removed it's dependancy on Phaser.Game (thanks @pnstickne #1328)
158158
* ArrayList has been renamed to `ArraySet` (as it's actually a data set implementation) and moved from the `core` folder to the `utils` folder (thanks @pnstickne)
159159
* If you are reloading a Phaser Game on a page that never properly refreshes (such as in an AngularJS project) then you will quickly run out of AudioContext nodes. If this is the case create a global var called `PhaserGlobal` on the window object before creating the game. The active AudioContext will then be saved to `window.PhaserGlobal.audioContext` when the Phaser game is destroyed, and re-used when it starts again (#1233)
160+
* Camera.screenView is now deprecated. All Camera culling checks are made against Camera.view now instead.
160161

161162
### Bug Fixes
162163

@@ -179,6 +180,8 @@ The proxy methods are generated one-time dynamically but only when needed.
179180
This fixes a bug in FF where it would use the default DOMMouseWheel (thanks @pnstickne #1313)
180181
* Stage.smoothed needed to modify the value of PIXI.scaleMode.DEFAULT instead of PIXI.scaleMode.LINEAR (thanks @pixelpicosean #1322)
181182
* Newly created Groups always had zero z index (thanks @spayton #1291)
183+
* Sprite.autoCull now properly works if the camera moves around the world.
184+
* Sprite.inCamera uses a much faster check if auto culling or world bounds checks are enabled and properly adjusts for camera position.
182185

183186
### Pixi 2.1.0 New Features
184187

src/core/Camera.js

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ Phaser.Camera = function (game, id, x, y, width, height) {
3939
* Camera view.
4040
* The view into the world we wish to render (by default the game dimensions).
4141
* The x/y values are in world coordinates, not screen coordinates, the width/height is how many pixels to render.
42-
* Objects outside of this view are not rendered if set to camera cull.
42+
* Sprites outside of this view are not rendered if Sprite.autoCull is set to `true`. Otherwise they are always rendered.
4343
* @property {Phaser.Rectangle} view
4444
*/
4545
this.view = new Phaser.Rectangle(x, y, width, height);
4646

4747
/**
4848
* @property {Phaser.Rectangle} screenView - Used by Sprites to work out Camera culling.
49+
* @deprecated No longer used for camera culling. Uses Camera.view instead.
4950
*/
5051
this.screenView = new Phaser.Rectangle(x, y, width, height);
5152

@@ -86,20 +87,6 @@ Phaser.Camera = function (game, id, x, y, width, height) {
8687
*/
8788
this.target = null;
8889

89-
/**
90-
* @property {number} edge - Edge property.
91-
* @private
92-
* @default
93-
*/
94-
this._edge = 0;
95-
96-
/**
97-
* @property {Phaser.Point} position - Current position of the camera in world.
98-
* @private
99-
* @default
100-
*/
101-
this._position = new Phaser.Point();
102-
10390
/**
10491
* @property {PIXI.DisplayObject} displayObject - The display object to which all game objects are added. Set by World.boot
10592
*/
@@ -110,11 +97,31 @@ Phaser.Camera = function (game, id, x, y, width, height) {
11097
*/
11198
this.scale = null;
11299

100+
/**
101+
* @property {number} totalInView - The total number of Sprites with `autoCull` set to `true` that are visible by this Camera.
102+
* @readOnly
103+
*/
104+
this.totalInView = 0;
105+
113106
/**
114107
* @property {Phaser.Point} _targetPosition - Internal point used to calculate target position
115108
*/
116109
this._targetPosition = new Phaser.Point();
117110

111+
/**
112+
* @property {number} edge - Edge property.
113+
* @private
114+
* @default
115+
*/
116+
this._edge = 0;
117+
118+
/**
119+
* @property {Phaser.Point} position - Current position of the camera in world.
120+
* @private
121+
* @default
122+
*/
123+
this._position = new Phaser.Point();
124+
118125
};
119126

120127
/**
@@ -143,6 +150,12 @@ Phaser.Camera.FOLLOW_TOPDOWN_TIGHT = 3;
143150

144151
Phaser.Camera.prototype = {
145152

153+
preUpdate: function () {
154+
155+
this.totalInView = 0;
156+
157+
},
158+
146159
/**
147160
* Tells this camera which sprite to follow.
148161
* @method Phaser.Camera#follow
@@ -253,12 +266,12 @@ Phaser.Camera.prototype = {
253266
*/
254267
updateTarget: function () {
255268

256-
this._targetPosition
257-
.copyFrom(this.target)
258-
.multiply(
259-
this.target.parent ? this.target.parent.worldTransform.a : 1,
260-
this.target.parent ? this.target.parent.worldTransform.d : 1
261-
);
269+
this._targetPosition.copyFrom(this.target);
270+
271+
if (this.target.parent)
272+
{
273+
this._targetPosition.multiply(this.target.parent.worldTransform.a, this.target.parent.worldTransform.d);
274+
}
262275

263276
if (this.deadzone)
264277
{

src/gameobjects/Sprite.js

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -261,32 +261,43 @@ Phaser.Sprite.prototype.preUpdate = function() {
261261
if (this.autoCull || this.checkWorldBounds)
262262
{
263263
this._bounds.copyFrom(this.getBounds());
264-
}
265264

266-
if (this.autoCull)
267-
{
268-
// Won't get rendered but will still get its transform updated
269-
this.renderable = this.game.world.camera.screenView.intersects(this._bounds);
270-
}
265+
this._bounds.x += this.game.camera.view.x;
266+
this._bounds.y += this.game.camera.view.y;
271267

272-
if (this.checkWorldBounds)
273-
{
274-
// The Sprite is already out of the world bounds, so let's check to see if it has come back again
275-
if (this._cache[5] === 1 && this.game.world.bounds.intersects(this._bounds))
268+
if (this.autoCull)
276269
{
277-
this._cache[5] = 0;
278-
this.events.onEnterBounds.dispatch(this);
270+
// Won't get rendered but will still get its transform updated
271+
if (this.game.world.camera.view.intersects(this._bounds))
272+
{
273+
this.renderable = true;
274+
this.game.world.camera.totalInView++;
275+
}
276+
else
277+
{
278+
this.renderable = false;
279+
}
279280
}
280-
else if (this._cache[5] === 0 && !this.game.world.bounds.intersects(this._bounds))
281-
{
282-
// The Sprite WAS in the screen, but has now left.
283-
this._cache[5] = 1;
284-
this.events.onOutOfBounds.dispatch(this);
285281

286-
if (this.outOfBoundsKill)
282+
if (this.checkWorldBounds)
283+
{
284+
// The Sprite is already out of the world bounds, so let's check to see if it has come back again
285+
if (this._cache[5] === 1 && this.game.world.bounds.intersects(this._bounds))
286+
{
287+
this._cache[5] = 0;
288+
this.events.onEnterBounds.dispatch(this);
289+
}
290+
else if (this._cache[5] === 0 && !this.game.world.bounds.intersects(this._bounds))
287291
{
288-
this.kill();
289-
return false;
292+
// The Sprite WAS in the screen, but has now left.
293+
this._cache[5] = 1;
294+
this.events.onOutOfBounds.dispatch(this);
295+
296+
if (this.outOfBoundsKill)
297+
{
298+
this.kill();
299+
return false;
300+
}
290301
}
291302
}
292303
}
@@ -1065,7 +1076,14 @@ Object.defineProperty(Phaser.Sprite.prototype, "inCamera", {
10651076

10661077
get: function() {
10671078

1068-
return this.game.world.camera.screenView.intersects(this.getBounds());
1079+
if (!this.autoCull && !this.checkWorldBounds)
1080+
{
1081+
this._bounds.copyFrom(this.getBounds());
1082+
this._bounds.x += this.game.camera.view.x;
1083+
this._bounds.y += this.game.camera.view.y;
1084+
}
1085+
1086+
return this.game.world.camera.view.intersects(this._bounds);
10691087

10701088
}
10711089

src/utils/Debug.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ Phaser.Utils.Debug.prototype = {
278278
}
279279

280280
this.line('View x: ' + camera.view.x + ' Y: ' + camera.view.y + ' w: ' + camera.view.width + ' h: ' + camera.view.height);
281+
// this.line('Screen View x: ' + camera.screenView.x + ' Y: ' + camera.screenView.y + ' w: ' + camera.screenView.width + ' h: ' + camera.screenView.height);
282+
this.line('Total in view: ' + camera.totalInView);
281283
this.stop();
282284

283285
},
@@ -474,6 +476,7 @@ Phaser.Utils.Debug.prototype = {
474476
this.line('x: ' + sprite.x.toFixed(1) + ' y: ' + sprite.y.toFixed(1));
475477
this.line('angle: ' + sprite.angle.toFixed(1) + ' rotation: ' + sprite.rotation.toFixed(1));
476478
this.line('visible: ' + sprite.visible + ' in camera: ' + sprite.inCamera);
479+
this.line('bounds x: ' + sprite._bounds.x + ' y: ' + sprite._bounds.y + ' w: ' + sprite._bounds.width + ' h: ' + sprite._bounds.height);
477480

478481
this.stop();
479482

typescript/phaser.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ declare module Phaser {
458458
scale: Phaser.Point;
459459
screenView: Phaser.Rectangle;
460460
target: Phaser.Sprite;
461+
totalInView: number;
461462
view: Phaser.Rectangle;
462463
visible: boolean;
463464
width: number;

0 commit comments

Comments
 (0)