Coder Social home page Coder Social logo

icecreamyou / mainloop.js Goto Github PK

View Code? Open in Web Editor NEW
513.0 513.0 54.0 1.41 MB

Provides a well-constructed main loop useful for JavaScript games and other animated or time-dependent applications.

License: MIT License

JavaScript 100.00%
animation frame-rate game-loop games javascript javascript-games javascript-library loop mainloop physics time webgl

mainloop.js's People

Contributors

dependabot[bot] avatar icecreamyou avatar jamesplease avatar lukasdrgon avatar thomwright avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mainloop.js's Issues

Calling MainLoop.stop() from within animate() doesn't work

Basically, MainLoop.stop() stops the loop by canceling the frame referenced by the rafHandle variable. However, inside of animate() (which calls the begin/draw/update/end functions), rafHandle points to the current frame, so canceling it doesn't do anything. Afterwards, requestAnimationFrame() still gets called again at the end of animate(), setting a new rafHandle for the next frame.

The solution to this is to simply move the requestAnimationFrame() call from the bottom of animate() to the top. As far as I can tell, this has no negative effects. It has one additional positive effect, which is that if we have to fall back to the setTimeout()-based polyfill for requestAnimationFrame(), the frames will be called slightly closer to on-time.

Remove "global" library state

This library only supports one instance per page due to the way it is written with global library state. A more idiomatic approach would be to expose a constructor that makes a new MainLoop. For instance,

var mainLoop = new MainLoop(options);

mainLoop.setEnd(cb);

// You can easily make another one
var mainLoopTwo = new MainLoop(options);

Given how well-organized the code is, I think it'd be relatively simple to refactor it to use this system instead.

If this is something you're interested in, let me know and I'd be happy to put together a PR for you to review.

setInterval/setTimeout vs requestAnimationFrame?

Just trying to understand the approach a bit better before porting it-

Would it work to call update(simulationTimestep) and updating all the related variables inside setInterval/setTimeout - even if render() is scheduled with rAF? Talking about browsers only and assuming that the callback can call performance.now()

In other words, I'm trying to understand why physics updates are also done in rAF which, as you've mentioned is tied to the display rate and can be delayed when switching tabs etc.

Thanks!

Using a class method as function for "Update" or "Draw"

Hello !

I'm having a problem with the Update function.
Let's say I have a class named "Game" which has a method named "update"
Normally, I could do this :

let game = new Game();
game.update();

and the method could be like this :
update() {
this.objects.doStuff();
}

But when I try to call the method with the Update call, I can't access "this"
I get the following error : this is undefined

Is it due to the plugin implementation or is this simple javascript behaviour that I'm not aware of ?

Thank you very much.

Designing a networked (Node.js) main loop for multiplayer capability

Hey Isaac!

Thanks a lot for this awesome repository. How would you suggest going about a Node.js implementation? I can't quite figure out the best way to do this.

I'm currently running an interval on the server-side, which streams out data to the client-side, which will then render the game (as for now). But as you probably know, this is not the best solution.

Basically I need to figure out a way to stream updates from server-side to client-side, and then render it in the most efficient way.

Any input is much appreciated!

mainloop server-side not working because of "window"

Hello, the documentation says that it can be used in Node.js with require('mainloop')
But when I tested, it simply isn't possible because of the use of "window', which obviously isn't available server-side...

stop() on setTimeout fallback.

I haven't tested this yet personally but I noticed in stop() that you call cancelAnimationFrame(rafHandle). If the RAF polyfill falls back to setTimeout, wouldn't stop() fail to actually stop the loop from continuing since you would need to use clearTimeout() instead?

Update library to Node Module

Since last commit was 2 years ago is a nice idea update the library to fully work with the node module system (a.k.a. export default/import), once this feature is more mature now.

port to Rust / WASM

would love to have this for rust/wasm projects...

If I go for it, how should I handle the license? (happy for it to be MIT too)

of course it would mostly be copy/paste and I want to give you full credit for all the hard work you put into it - but Rust/WASM has its own set of problems that need some original solutions too.

FPS Calculation

There might be some minor issues with the FPS calculation:

  1. FPS < 1 is currently not possible, but this might happen in test environments.
    Proposed solution: Update the EMA FPS value n times in each loop with the value frameCount / n, where n is the number of seconds passed since the last update.
  2. FPS update intervals are currently a little longer than 1 second, because:
    timestamp > lastFpsUpdate + 1000.
    So currently it does not calculate:
    frames / second
    but:
    frames / (second + time until next frame starts).
    This could be corrected this with:
    while (lastFpsUpdate < timestamp - 1000) lastFpsUpdate += 1000;
    instead of:
    lastFpsUpdate = timestamp;

Allow specifying a different requestAnimationFrame function

This library currently uses window.requestAnimationFrame if available and falls back to setTimeout otherwise. The upcoming WebVR API will use display.requestAnimationFrame to schedule frames on attached VR displays. This library should support such loops.

How I can avoid calling setEnd 60 times per second?

I would like to only execute "setEnd" 20 times per second, because updating updateGamestate 60 times per second over the network would be too much expensive.

This code is kind of working for me but it looks like it gives performance issues.

const nus = 20 // Number of updates per second - 1 min, 60 max
let emit = true
setInterval(() => (emit = true), 1000 / nus)

mainloop.setEnd(() => {
	if (emit) {
		io.emit('gameState', updateGamestate(bodies))
		emit = false
	}
})

There is a proper way to limit the number of calls to setEnd?

Deep call stack

This runs great, but the recursive call to requestAnimation causes the call stack to become pretty deep. I'm only using the loop for update(), so maybe the issue is with how I integrated it?

I've integrated this into my N64 javascript emu at http://1964js.com.

Making loop run at an accelerated rate

Hi,

I've been trying to force the loop to execute more quickly so that I can test my game code at a faster rate (game runs twice as fast for example). I've tried updating the simulated timestep and/or max FPS but I think there's a throttle built into MainLoop which is actively preventing me from running too quickly.

Do you have any tips on what I can do?

Many thanks!
Andy

publish new version to npm registry

Hi!

I see you added "main" in package.json about 2 months ago, but you have not published a new package to the npm registry.

So after doing "npm install mainloop.js" I still had to add main to use your lib.

Could you please publish a newer version of this package to the npm registry?

Thanks.

Where is the good place to have Websocket send() calls with the library?

Hello!

Your article on game time step helped me so much when I was making a small game. Thank you for the article and this awesome library!

I am trying to make a multiplayer game with Websocket and I'm not sure where would be the ideal place to put the socket.send() calls. The game updates at fixed time step, and draws at 60fps. I would like to send the player inputs to the authoritative server at 30hz instead of 60hz. Is this possible to do with this library?

Thank you for your help!

bower install MainLoop doesn't work

Minor nitpick, but "bower install MainLoop" doesn't actually work (at least, not in git bash) but..

"bower install mainloop" DOES work. Is this an issue affecting everyone or just git bash users?

Aw, Snap!

After a couple seconds when i am using this library chrome says: "Aw, Snap!".

I'm using pixijs. I'm just making some circles to move on the screen. The fps also lowers to under 18 fps.

why such a big dip every few seconds in fps?

Hello

I tried running your demo version of this loop on a 144hz monitor and occasionally am getting 100+ fps even if the slider is set to 60.

Am I misunderstanding what this will do for you or is this a bug?

This is roughly what I see happening:

image

`update` step implementation question

Currently the update function is called multiple times in a row:

numUpdateSteps = 0;
while (frameDelta >= simulationTimestep) {
    update(simulationTimestep);
    frameDelta -= simulationTimestep;

    if (++numUpdateSteps >= 240) {
        panic = true;
        break;
    }
}

Since it uses time delta anyway, why can't it be condensed into the single update call based on how much time has passed? It seems that this way would require only one update call for the loop to catch up instead of several at times.

if ((simulationTimestep / frameDelta) >= 240) {
    panic = true;
}

update(frameDelta);

draw(frameDelta / simulationTimestep);

Example that uses setBegin?

Hi @IceCreamYou ! I know it's been a couple of years since you've worked on this library, so no worries at all if you've moved on and don't have the time and/or interest to triage issues.

I love the example that ships with MainLoop. It really shows the value of using this library over many other solutions out there. I have a question for ya': do you happen to have another example handy that uses setBegin to process user input? It could be cool to demonstrate to folks best practices for how to use that method.

Thanks for reading!

Document or improve the fact that the `draw` callback gets called before the loop is run

Currently, when MainLoop.start() is called, draw() is called to render the initial state before any updates occur. This behavior could lead to users writing buggy code based on the reasonable expectation that begin() and end() are called before and after draw(), respectively.

I think it would be fine to call begin() and end() before and after draw() when MainLoop.start() is called, but this might be considered a backwards-incompatible change. The alternative would be to document this behavior so that people know to manually call begin() and end() if needed.

update() function always called with the same parametervalue

Hello,
Im new to this topic and read your mainLoop article at:
https://isaacsukin.com/news/2015/01/detailed-explanation-javascript-game-loops-and-timing#fps-control

I really found it helpful and want to try things out with this.
However I was wondering why the update(simulationTimestep) function always gets called with the same value.
Earlier in your article we passed delta as parameter, where it sent the elapsed time since the last update.
Later in the article you said it has to be called with the same delta since we would get wierd rounding errors, which leads to moving through objects.

So thinking about it is there any use for this "delta"? The calculated velocity/movement will always be the same amount anyways.

Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.