Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Class called Time to control frame rate of MovieClips #322

Closed
wants to merge 3 commits into from
Closed

New Class called Time to control frame rate of MovieClips #322

wants to merge 3 commits into from

Conversation

mikkoh
Copy link
Contributor

@mikkoh mikkoh commented Sep 10, 2013

This one is a bit controversial. (Sorry)

But I was reading some blog posts and the biggest issue people had with Pixi was that there was nothing to control the frame rate of MovieClips.

I created a Class called Time which will control the frame rate of all MovieClips by multiplying the animationSpeed of MovieClips by Time.timeScale.

Time.timeScale is set according to the target frame rate. So for instance if your sprite sheets were exported at 30fps your target frame rate should be set to 30fps. You can access targetFrameRate via Time. targetFrameRate.

Time.timeScale is updated every time the render function of renderers is called. By doing PIXI.Time.update();

There is also a property called Time.minFrameRate which makes sure if the renderer doesn't render for a long time that animations wont "jump" many frames ahead because the developer has chosen not to render for awhile. An example of when this could happen is if DOM based UI comes up and you don't want the renderer to run.

Time.timeScale can also be used to do programmatic animations independent of frame rate. So for instance if at 60fps you wanted to move an item by 10px per frame you'd do something like:

mySprite.position.x += 10;

But lets say the frame rate drops to 30fps we'll actually be moving much slower across the screen. So the above can be changed to be:

mySprite.position.x += 10 * PIXI.Time.timeScale;

Anyway like I said this is a bit controversial cause it limits ALL MovieClips but in some way it's nice to have a global way to do this. Putting it out there for discussion.

…amerate independent animations and also will limit the framerate of MovieClips
@englercj
Copy link
Member

I would argue that each movieclip should manage it's own time based animation, similar to how animated sprites were handled in grapefruit before pixi. The delta since the last update was added to a tracker of the current time spent on a frame, and when that time was over how long we should spend on a frame we moved to the next one.

I don't like the idea of having a global limit to all aniamtions, and I am wary about reinventing Tween in this library. I think what might be more beneficial is having a Timer or Clock class to normalize timing things, and then using that to perform animation timings withing the class itself.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 10, 2013

The only good thing about having a global calculation for time step is that it's more performant. I agree if each MovieClip managed their own frame rate then it would be better however from a production stand point very rarely will you ever have movieclips exported at different frame rates. If they were exported at different frame rates and you had a global class that managed time step you could offset that timescale by changing the animationSpeed in MovieClip. So for instance you could set Time to run at 30fps and then if there are animations that were created at 12fps you could change the animation speed to be 12/30. Do you know what I mean?

In no way do I want to create yet another Tweening engine however it is important functionality especially for games to have a timeScale that changes depending on the duration of the render loop. Especially since mobile will at like 30fps and desktop will run at 60fps.

I got very used to this with Unity3D: http://docs.unity3d.com/Documentation/ScriptReference/Time.html

@englercj
Copy link
Member

@mikkoh I'm not disagreeing that it can be useful in certain areas, i'm just dubious whether it should be put into a rendering engine. That is why I think having a timer class is useful because it empowers users to do what you are talking about, and what I am talking about instead of us forcing a single way on them.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 11, 2013

Sorry which part do you feel shouldn't be put into a rendering engine?

I think we agree that there needs to be a way to limit how fast a sprite sheet/MovieClip progresses through the animation.

You're basically saying that each MovieClip should calculate it's own timeScale right? And I'm saying that perhaps it's better to define it globally so that there's less calculations. For instance in my current project I could have had potentially 60 MovieClips at one time. (thank goodness we scaled down for tablets sake) That's 60x more calculations that need to be done if each MovieClip calculates how much they should update by during each update call.

I guess a better example would be if we had a particle engine and the particle engine was emitting MovieClips. It's a lot more overhead if each MovieClip calculates how much it should update by.

What I mention about developers being able to access Time.timeScale is just a bonus. Plus if you wanted to create something like https://github.com/mrdoob/stats.js/ to be able to get render statistics having a class like Time could be valuable.

I have no issues writing the code to make each MovieClip calculate it's update amount. I just want to see your reasoning behind going this route.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 11, 2013

BTW just want to clarify that I think animationSpeed should still be in there. (as you can see from my code)

I think there are some neat things you could do with that. For example perhaps you could make it -1 to make the animation play in reverse. (I've actually implemented that feature already just want to test some more https://github.com/Jam3/pixi.js/tree/movieAniSpeed)

@englercj
Copy link
Member

I don't think animated sprites should be in the core rendering engine, I think that is game engine code or a plugin at best.

My point was that you can do only 1 calculation of time, but have each movieclip track its own frame time. Like I mentioned in my earlier link the update method passes along the delta time to each of the animated sprites; so the time calculation is done only once. Then each sprite adds that value to its instance tracker to see when to move frames, that is exactly what it does now except delta time based instead of delta frame based.

Basically, we don't need a global single time calculator that everyone grabs off the pixi object. Just a timer class that can be used to pass the deltas to the update transform if we want to. Having a global timer is not a good idea, but having an instance of a timer on the renderer makes sense.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 11, 2013

Ok I get what you're saying now.

Basically you're saying that you're not sure which is absolutely the best way of doing this if there's a new architecture. (it all makes sense after reading #307).

What do you feel is the best plan of attack for me and our time as we're actively working on projects that use Pixi and I believe we'll be using it more and more as time goes on? Of course we want to give back and help shape where things are headed.

Also I guess it would be good to know what would constitute Pixi cutting down Pixi so it's a straight up render engine. I mean would it just have PIXI.Sprite in it? Would we even have PIXI.DisplayObjectContainer?

@englercj
Copy link
Member

@mikkoh My vision is anything that is not 100% required for rendering is removed from the core. That means that the core module is basically the renderers, DisplayObject, Sprite, and Texture. Everything else is sugar on top of that and can be done in a separate, optionally included, module or in userland.

@GoodBoyDigital
Copy link
Member

Hey guys! Good to see a nice healthy discussion about this!

@mikkoh I use exactly the same mechanic as you do (I have a Time Class very similar to your own) and I quite like the idea of a single place where time steps are calculated as its nice and efficient. I find myself using Time based Animation on pretty much all projects at the mo too.

As it stands it could be useful for movieClips but also for the interactionManager as that works at a different frame rate to the rendering ( no point hit testing at 60fps :/ )

I see where you are coming from too @englercj keeping the renderer core down to a minimum will be incredibly useful for people who simply want to use the bare bones of pixi as a rendering component.

The way I'm leaning at the moment is to still have movieclips / text / interaction and stuff in the main pixi engine as people coming from flash / create.js world will find it quite helpful, especially when we consider lots of people are using it to create content other than games.

Once we are "feature complete" me and @englercj will take a look at separating the core out for users who require a more streamlined pixi too.

Although definitely up for a discussion on all of this!

@englercj
Copy link
Member

I do want to mention we are not thinking of destroying any code here, just making certain features optional. The default build would be a "full" build that has all modules (movieclip, text, etc) in it.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 11, 2013

@englercj yeah I assumed that features wouldn't be destroyed. Figured you guys wanted to do something like Tweenlite Plugins.

@GoodBoyDigital yeah frame independent is pretty important especially in the html/js world.

I'd almost suggest that if there's every such things as Physics, Particle, Plugins for Pixi something like this would be pretty vital especially when targeting low power devices and desktops.

But yeah. I can totally understand not adding features to PIXI til everything is consolidated to its basics. I think it's also a good idea because then it's easier to make sure that performance is good as possible with the renderer without being hindered by other things. You could even write Unit tests to check the frame rate of the renderers and ensure that performance is getting better and not worse.

@siwonia
Copy link

siwonia commented Sep 12, 2013

Hey guys,

Great to read this long overdue discussion. First of all good work @mikkoh! I also use a kind of the same code to have a better control of PIXI.MovieClip's frame rate. In addition, I absolutely agree with @englercj that we should keep the renderer core down to a minimum.

Nevertheless, I think that this feature will be used by many more than just @GoodBoyDigital, @mikkoh and me, and we should include it necessarily. I think @Makio64 was searching for the same: #237.

@englercj
Copy link
Member

Handling frames by time instead of rate is something we will definately support, the only point of contention here is whether the timer should be global or not.

@GoodBoyDigital
Copy link
Member

Cool beans, I think we are actually all on the same page?

One global class to manage the time:

// in render function..
PIXI.Time.update();

and then each movieclip would have a line like this:

// in movie update function..
this.currentFrame += this.animationSpeed * PIXI.Time.DeltaTime;

Sound good?

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 12, 2013

@GoodBoyDigital I believe that's exactly what I'm doing in this merge. So glad to see you're cool with this.

@englercj One global singleton controlled by the renderers render call + each MovieClip has their own Time also???

Might be not the greatest thing to do what I just mentioned above for the future. Let's say theres a new way of animating something then those things also have to have Time in them. IMO it's just a waste of the CPU and memory. So I'm still for a global time.

But perhaps you could do something like this:

var clip = new PIXI.MovieClip( textures, 12 );

Where 12 is the fps this clip should run at. If frame rate is passed in then it'll create it's own Time instance in MovieClip to calculate TimeStep. I think generally this feature wouldn't be used but might be handy if you have animations designed at different frame rates.

@englercj
Copy link
Member

I never said a movieclip should have it's own time instance. I said the renderer should have a time instance on it, and pass the delta via updateTransform to the the children; this is actually the third time I have said it now.

If it is being controlled by the renderer's render call (which it should) then it should be a property of the renderer. Having multiple renderers call to a global singleton to track time will collide. I'm saying it should be something like this:

CanvasRenderer.prototype.render = function() {
    var delta = this.timer.getDelta();

    //...

    children.updateTransform(delta);
};

That is how most (all I have seen) renderers handle timing, see Clock in grapefruit.

@Makio64
Copy link

Makio64 commented Sep 12, 2013

@GoodBoyDigital : It's exactly what I used on my last project.

Another cases is when you work with animator : some frames are more long than others.

In this case you should made something like :

update = function(dt) {
    this.time += dt;
    while (this.time >= this.currentFrame.duration) {
        this.time -= this.currentFrame.duration;
        this.nextFrame();
    }
}

@englercj
Copy link
Member

That is exactly what I had said @Makio64, I even linked some code that does exactly that. Where gf.game._delta was the latest delta from an instance of a timer class on the running game (this was before I started passing the delta via a param).

@GoodBoyDigital
Copy link
Member

sweet - looks like we are definitely all talking about the same method :)

@englercj
Copy link
Member

@GoodBoyDigital I want to make sure we are, because this:

One global class to manage the time:

// in render function..
PIXI.Time.update();

Is not what I am saying, I am saying there should not be a gloal singleton to manage the time. Each renderer should instantiate its own timer.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 12, 2013

@englercj My bad I forgot you had said that. I remember know you had said that renderer should have it's own time. My bad sorry.

But still I was just trying to work through a solution for when you have some clips that are rendered out at 12fps, some at 24fps, etc. in one project.

So #322 (comment) solution should prob work.

I think in the end if that instance lives inside renderer people would end up trying to access it for programmatic animations (such as physics engines) and it would be annoying to have to pass around an instance of the renderer to be able to access Time.

@GoodBoyDigital
Copy link
Member

@englercj Oh, I see! I think there should be? Its a perfect candidate for a singleton in my mind? No need passing it through all the display objects as it would only be used by movieclips and the interaction manager at this stage?

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 12, 2013

@englercj I think the only time a global singleton PIXI.Time would fail if you had two renderers in one page. How often would that happen?

@drkibitz
Copy link
Contributor

-1 to singleton

On Thursday, September 12, 2013, Mikko Haapoja wrote:

@englercj https://github.com/englercj I think the only time a global
singleton Time would fail if you had two renderers in one page. How often
would that happen?


Reply to this email directly or view it on GitHubhttps://github.com//pull/322#issuecomment-24328983
.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 12, 2013

@drkibitz What's you're reasoning?

@drkibitz
Copy link
Contributor

From Past experience singletons always prevent an architecture from becoming more, that is until the singleton is removed. If the plan is to eventually allow multiple renderers, I see adding a singleton as the opposite direction of progress. I would say dependency injection is the solution to this, as well as the solution to the other current issues in pixi preventing multiple renderers.

@mikkoh
Copy link
Contributor Author

mikkoh commented Sep 12, 2013

Yeah I mean technically every time renderer.render() is called you could update which Time was associated to the static variable. This way devs would have the ease of use having a singleton and multiple renderers.

I'm just thinking that the likelihood of having multiple renderers on a page is pretty minimal. Like I can't think of an instance where you'd do that. Perhaps having multiple banners on one page???

@englercj
Copy link
Member

@englercj I think the only time a global singleton PIXI.Time would fail if you had two renderers in one page. How often would that happen?

Quite often, anytime pixi is used to create a "reusable widget" (such as in a graphing library) there will be multiple renderers, it is a case we must consider and handle because people are doing it right now. We've gotten a few bugs about multiple webgl renderers meaning people are already doing it. We have to remember that this is a rendering engine, and that doesn't limit to just games. Lots of people are using this for rendering outside of gaming.

The burden of writing a reusable library, or platform, is that you cannot dismiss edge cases even when they seem unlikely. A library that dismisses edge cases will be unsuccessful, and soon replaced.

There are downsides to making it a singleton, and no downside to having each renderer have their own instance. Passing a parameter that isn't used has no (significant) performance impact.

A singleton is for something where you must enforce having only one. That is to say, having many of them is either an error, or will cause fatal issues. That isn't the case here.

@englercj Oh, I see! I think there should be? Its a perfect candidate for a singleton in my mind? No need passing it through all the display objects as it would only be used by movieclips and the interaction manager at this stage?

Consider also things that inherit our classes though, someone could conceivably need this delta in a class inheriting from DisplayObject. If it is already passed down then they can use it in their updateTransform even if the base class doesn't.

@GoodBoyDigital
Copy link
Member

Good point, I appreciate what you are saying about catering for edge cases. I still don't think its something that should be passed down through update transform though as its too much of an edge case to warrant passing a variable that 90% of the time wont be used.
Each element is given a stage reference so we could add the a time class to each stage instance? That way we are win win. Easy to access the time class: this.root.time.deltatTime and also its tucked away from everything unless its required.. Could do the trick i reckon.

@englercj
Copy link
Member

@GoodBoyDigital I think that is the best compromise at the moment. In this case would the stage call this.timer.update() in it's update transform?

@lock
Copy link

lock bot commented Feb 26, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked and limited conversation to collaborators Feb 26, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants