Animations got slower!

Why should Phaser limit framerate? The faster the better. You can use things like suggested or desired Fps, but basically it’s up to the user to decide how to use the frames.
It would make faster monitors pretty useless…

Re. phaser-test.js:

This is expected.

Particles not visible at all? Or something else?

This is expected. When game.forceSingleUpdate === true (the default), the update rate is variable and tracks the browser’s animation frame rate. So in that case you need to use game.time.delta when you update the angle. Or you can set game.forceSingleUpdate = false and the update rate will be fixed and you won’t need to use the delta value.

Tweens should run at the same speed in any case.

What’s happening in your game logic there?

Milton:
Yes, running at a faster frame rate would be great, but only if the game still works properly, of course. It seems from Phaser’s own documentation that it was never intended to run faster than 60fps, and it appears that it was never tested at monitor refresh rates faster than 60hz. Furthermore, looking through other threads on this forum, one can see that game designers sometimes ask for a way to limit the frame rate for game design reasons. A constant frame rate can simplify physics calculations and ensure consistency, and can be critical if your game is based around hand-drawn frame-based animations that need to play at a certain speed.

I don’t personally require a limited frame rate in my game, but the animation engine is broken on monitors with a high refresh rate. One way to solve the problem would be if Phaser could limit the frame rate to 60fps, so the game would perform the same across both older and newer monitors. Reading the code, it certainly seems like that was the intent in Phaser’s design.

Samme:
Yes, the #1 thing stopping phaser-test.js from being a usable solution for me is that when I use it, particles are not visible at all.

A secondary, lesser problem with phaser-test.js is the flickering behavior I get with visibility changes on 240hz. I notice it the most with my text boxes (well, technically it’s a few text boxes that I reuse a lot by hiding and showing them and changing the text). To hide a text box, I set the visibility of its parent group to false, and I set the height of the box to 0. (The reason I set the height to 0 is that sometimes, for an animating effect, I’ll tween the height to 0 with a callback that sets visibility to false at the end of the callback. It’s important that my instant-hide and animating-hide leave the text box in the same state, for ease of reuse.) To show the text box, I set the visibility of its parent group to true and restore the height to its full height (and sometimes, for an animating effect, I’ll turn on visibility and tween the height back to its full height). I’m not precisely sure which of these specific things is causing the flickering effect. Sometimes I think what I’m seeing is remnants of the older text flashing a couple frames after it was supposed to be immediately changed. Is it caused by the changing of the text in the text box? Is it caused by doing the hide and then show logic in one frame? Other times, right before the animating tween from 0 to full height, I think I see a flash of the box at full height before the tween begins.

I don’t see these problems at 60hz, so something subtle is going on where the visual updates are happening so quickly that it’s catching things in a “dirty” state.

When I tried to solve the problem in 2.6.2 by setting game.tweens.frameBased to true, the flickering of hidden objects was way more extreme.

I don’t care about the preloader animation spinning faster, that was just an observation I was noting to help track the differences in behavior between the candidate solutions.

I don’t consider the audio issue with phaser-test.js to be a show-stopper, but I definitely prefer the behavior from 2.6.2 where audio before the first click is simply dropped. It’s weird when the sounds get queued up and play late, when the user first clicks. I know you say that’s the intended behavior, but 2.6.2’s behavior makes more sense to me.

1 Like

Thanks for testing these. I don’t have a superfast display so I can’t tell what works or not myself.

If you set game.forceSingleUpdate = false (which was originally the default earlier in Phaser 2), then logic updates and renders are limited to 60fps. game.time.fps is actually the animation frame rate from the browser, not Phaser’s update rate, so it can be larger. Phaser CE shows game.time.ups and game.time.rps as well, they should be close to 60: https://codepen.io/samme/full/RZKMMw

They’re working for me so I wonder if this is some other Phaser CE incompatibility, but I don’t think there are any.

Can you tell me more about what changes you made to Phaser CE to create phaser-test.js? Maybe I can apply the changes to Phaser 2.6.2, and get the benefits of your fixes without giving up the particle animations.

Tonight I tried combining game.tweens.frameBased = true with game.forceSingleUpdate = false while leaving desiredFps at its default. My hope was that setting frameBased to true would solve the slow animation (it did) and then setting forceSingleUpdate to false would force logic updates and renders to run in lockstep at 60fps, thus cleaning up the flickering artifacts on the 240hz monitor (it didn’t).

In fact, I don’t see any difference with forceSingleUpdate set to false. Either way, lots of graphic elements that are supposed to be invisible flash on the screen for an instant. It seems like this must be caused by rendering happening at a faster rate than the update loop, and from your description of forceSingleUpdate, I would expect that to solve the problem. Is there a way I can dig deeper into this, and prove to myself one way or another whether Phaser is actually respecting this property’s value, and if it isn’t, figure out why not?

In case it is helpful, here is the result of your benchmark app running on the 240 hz monitor:

I can’t help but wonder if the elapsedMS being 0 is a potential problem.

1 Like

I’ve spent a bit more time analyzing the flashing artifacts. I think it’s happening when there’s a hidden object and in the same frame I make it visible and move it. At 60hz, it appears, as expected in the new location. At 240hz, I see a flash of the object becoming visible at the original location, before it moves to the new location. Effectively, although the visibility change and motion should be happening simultaneously before the render, at 240hz the changes are no longer happening as an “atomic transaction”.

At least, I think that’s what I’m seeing.

As for the missing particle effects, I wonder whether the API for emitters changed at all between 2.6.2 and CE. Perhaps the 2.6.2 way of writing emitters doesn’t work in the newer edition without some modification.

phaser-test.js should fix all the timing problems. It uses a proper delta step for particles, tweens, timers, and physics. When forceSingleUpdate is on (the default), it makes one logic update and one render per animation frame, with a nonfixed delta. When forceSingleUpdate is off, it steps at the fixed delta given by desiredFps.

time.physicsElapsed, time.physicsElapsedMS and frameBased are removed. time.elapsed and time.elapsedMS are still there but they’re not used for any game timing.

If this is using phaser-test.js, you should set game.forceSingleRender = false as well (and I will make that the default later). Then verify that game.time.rps is around 60.

My experiments last night were with Phaser 2.6.2. I still haven’t figured out how to regain particle effects if I’m using phaser-test.

I compared the emitter code and there were some small changes to the API, but I haven’t found anything yet different enough to explain why I’m seeing no particles with phaser-test.

Are all the changes you made in phaser-test isolated to a few specific methods that I could analyze and try porting to 2.6.2, or are they spread out across the library?

There are changes, but I can’t think of anything that would fail. All the v2.6.2 particle examples still work in Phaser CE. I can take a look at your game if you like.

In Phaser v2.6.2, when forceSingleUpdate is off, logic updates are restricted to desiredFps but renders are not. There will probably be 240 renders per second. But I’m still not sure if that can cause problems.

I don’t know if your design allows it, but you might try avoiding setting any display properties (height, scale, alpha, visible) on the parent/group objects.

They are pretty big changes. Probably more fruitful to work on the particle problem. I can try to debug it if you send a link or code.

Thanks for offering to take a look at my particle code, to see if anything jumps out at you as incompatible with CE.

My game is written in Clojurescript, a language that compiles to javascript. Here’s the code, and then I’ll explain what it translates to:

(defn make-emitter [map-data parent node1 node2]
  (let [node-data (:nodes map-data)
        [node1-x node1-y] (get node-data node1)
        [node2-x node2-y] (get node-data node2)
        group (add-child parent node1-x node1-y)
        emitter (ocall game "add.emitter" 0 0 200)
        rotation (- (ocall game "math.angleBetween" node1-x node1-y node2-x node2-y)
                    (ocall game "math.degToRad" 90))]
    (doto emitter
      (oset! "particleClass" (ElectroParticle [node1-x node1-y] [node2-x node2-y]))
      (ocall "makeParticles")
      (ocall "setRotation" 0 0)
      (ocall "setAlpha" 0.5 0.8)
      (ocall "setScale" 0.1 0.25 0.1 0.25)
      (ocall "setXSpeed" 0 0)
      (ocall "setYSpeed" 100 200)
      (oset! "gravity" 100)
      (oset! "width" 5))
    (ocall group "add" emitter)
    (oset! group "rotation" rotation)
    (ocall emitter "start" false 3000 10)))

So, I create a group object called group and then I create an emitter with game.add.emitter(0,0,200). I then do some math calls to compute the rotation of the group (which the emitter will be added to). This allows me to build the emitter so it only emits along the y-axis, and the group is rotated so that this points in the correct direction. The remaining calls correspond to:

emitter.particleClass = my custom class
emitter.makeParticles()
emitter.setRotation(0,0)
emitter.setAlpha(0.5,0.8)
emitter.setScale(0.1,0.25,0.1,0.25)
emitter.setXSpeed(0,0)
emitter.setYSpeed(100,200)
emitter.gravity = 100
emitter.width = 5
group.add(emitter)
group.rotation = rotation
emitter.start(false,3000,10)

See anything suspect in there? I didn’t.

The other possibility is that it is the custom particle class where the incompatibility lies, but I’m not doing anything too sophisticated there. Essentially, I’ve overriden the Particle class so that the particle dies after it has traversed a certain distance (the emitter is positioned inside a group that is rotated so from the emitter’s perspective, the particles only travel along the y-axis, so I simply kill the particle when the y value exceeds the desired length).

This is the actual Clojurescript code, which may look a little complicated because of the machinery involved with customizing the Particle class, but the customizations are fairly straightforward.

(defn ElectroParticle [[src-x src-y] [dest-x dest-y]]
  (let [dist-x (Math.abs (- src-x dest-x))
        dist-y (Math.abs (- src-y dest-y))
        length (Math.sqrt (+ (* dist-x dist-x) (* dist-y dist-y)))
        particle-class (fn [game x y key frame]
                         (this-as this
                           (.call (gget "Phaser.Particle") this game x y
                                  "assets" 0)))]
    (oset! particle-class "prototype" (js/Object.create (gget "Phaser.Particle.prototype")))
    (oset! particle-class "prototype.update"
           (fn [] (this-as this
                    (when (> (oget this "y") length) (ocall this "kill"))
                    (gcall "Phaser.Particle.prototype.update" this))))
    (oset! particle-class "prototype.onEmit"
           (fn [] (this-as this
                    (oset! this "frameName" (rand-nth colors)))))
    particle-class))

What’s happening here is that ElectroParticle is a function that dynamically constructs a Particle subclass using two points as the input. First, I compute the length between those points. Then, I create a class based off of the Particle class, changing its update method to kill the particle when its y value exceeds that length. I also override its onEmit method so that each particle emitted is randomly set to one of several colored particles, using frameName.

Unless the Particle class constructor or its update and onEmit methods have changed from 2.6.2 to CE, or unless the way that the Particle class accesses its key/frame has changed, I don’t see how this code could be the problem either.

Make sure you’ve added the group with this.add.group() or else added it to the stage.

Yes, the group containing the emitter is added as a child to another group (a particle layer containing all the various emitter groups, so that I can fade them out simultaneously by tweening the alpha on this parent group). I make it a child using the particle layer group’s addChild method. The particle layer is in turn a child of another group and so on up to the stage.

I ran the game with phaser-test again, with forceSingleRender set to false this time, and it did indeed clean up the flashing/flickering problem. So the main remaining problem I have with phaser-test is that it causes my particles to disappear.

While I had phaser-test running, I opened up the browser’s debugger console and verified that the emitter actually existed. I copied the properties of the emitter at runtime from the console into a text file, which I am pasting below. Do you see anything strange here that would explain why the emitter’s particles are not visible in phaser-test? To me, the thing that looks fishy is that the children is an array of size 200, but it appears to be empty and the counts object shows that it the emitter has produced 0 particles. Phaser 2.6.2 doesn’t have this counts object, so I don’t really have anything to compare it to, but it seems wrong that it is all 0.

This suggests to me that the emitter isn’t actually getting "start"ed, but the start method in 2.6.2 and CE looks identical, with the exception that the name was changed on of one the args from quantity to total, which shouldn’t have affected my code in any way.

emitters: {…}  // the array of emitters
0: {…} // We're inspecting the first one in the array, i.e., emitters[0]
_bounds: Object { x: 0, y: 0, width: 0, … }
_cacheAsBitmap: false
_cacheIsDirty: false
_counter: 0
_cr: 1
_currentBounds: null
_explode: true
_flowQuantity: 0
_flowTotal: 0
_frames: 0
_gravity: Object { x: 0, y: 100, type: 25 }
_id: 0
_mask: null
_maxParticleScale: Object { x: 0.25, y: 0.25, type: 25 }
_minParticleScale: Object { x: 0.1, y: 0.1, type: 25 }
_sortProperty: "z"
_sr: 0
_timer: 41323.999999999716
_total: 0
alive: true
alpha: 1
alphaData: null
angularDrag: 0
area: Object { x: 0, y: 0, width: 5, … }
autoAlpha: false
autoScale: false
blendMode: 0
bounce: Object { x: 0, y: 0, type: 25 }
cameraOffset: Object { x: 0, y: 0, type: 25 }
children: Array(200) [ {…}, {…}, {…}, … ]
classType: function Sprite(game, x, y, key, frame)
counts: Object { emitted: 0, failed: 0, totalEmitted: 0, … }
cursor: Object { type: 0, physicsType: 0, rotation: 0, … }
cursorIndex: 0
emitX: 0
emitY: 0
enableBody: false
enableBodyDebug: false
exists: true
filterArea: null
fixedToCamera: false
frequency: 10
game: Object { id: 0, parent: "app", width: 960, … }
hash: Array(200) [ {…}, {…}, {…}, … ]
hitArea: null
ignoreChildInput: false
ignoreDestroy: false
inputEnableChildren: false
lifespan: 3000
maxAngle: null
maxParticleAlpha: 0.8
maxParticleScale: 1
maxParticleSpeed: Object { x: 0, y: 200, type: 25 }
maxParticles: 200
maxRotation: 0
maxSpeed: 100
minAngle: null
minParticleAlpha: 0.5
minParticleScale: 1
minParticleSpeed: Object { x: 0, y: 100, type: 25 }
minRotation: 0
minSpeed: 0
name: "emitter0"
on: true
onChildInputDown: Object {  }
onChildInputOut: Object {  }
onChildInputOver: Object {  }
onChildInputUp: Object {  }
onDestroy: Object {  }
parent: Object { name: "group", z: 0, rotation: -1.5707963267948966, … }
particleAnchor: Object { x: 0.5, y: 0.5, type: 25 }
particleBringToTop: false
particleClass: function p(a, b, c)
articleDrag: Object { x: 0, y: 0, type: 25 }
particleSendToBack: false
pendingDestroy: false
physicsBodyType: 0
physicsSortDirection: null
physicsType: 7
pivot: Object { x: 0, y: 0, type: 25 }
position: Object { x: 0, y: 0, type: 25 }
renderable: false
rotation: 0
scale: Object { x: 1, y: 1, type: 25 }
scaleData: null
type: 11
updateOnlyExistingChildren: false
visible: true
worldAlpha: 1
worldPosition: Object { x: 467, y: 560, type: 25 }
worldRotation: -1.5707963267948966
worldScale: Object { x: 1, y: 1, type: 25 }
worldTransform: Object { a: 6.123233995736766e-17, b: -1, c: 1, … }
z: 0
<prototype>: Object { constructor: Emitter(game, x, y, maxParticles), update: update(), makeParticles: makeParticles(keys, frames, quantity, collide, collideWorldBounds, particleArguments)
, … }

The emitter’s start method is definitely getting called, because the lifespan and frequency properties have clearly been set, and that’s where that happens. So the emitter has been created, but nothing is flowing? That suggests to me that the game’s time loop isn’t actually calling the emitter to emit.

Try putting a breakpoint on Phaser.Particles.Arcade.Emitter#update and see if it’s getting called.

I checked with breakpoints:

Phaser.Particles.Arcade.Emitter#start is getting called.
Phaser.Particles.Arcade.Emitter#update is not getting called.

And I verified that if you follow the chain of parents from emitter you eventually get to the stage.