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

Main loop and staging #7

Open
joskuijpers opened this issue Apr 16, 2014 · 10 comments
Open

Main loop and staging #7

joskuijpers opened this issue Apr 16, 2014 · 10 comments
Assignees
Labels

Comments

@joskuijpers
Copy link
Contributor

There is a very important part of the API I have not been able to wrap my head around: where the actual game code goes.

We talked about a stage system for different game stages. I think that would be part of the problem stated.

Well, I have no clue. 😄

@Radnen
Copy link
Member

Radnen commented Apr 16, 2014

I'm guessing that there is a global stage handler and it's active so long as it has stages in it.

Example:

var stage = new Sphere.Stage()
.enter = function() { }
.leave = function() { }
.load/init = function() { };

Sphere.stages.add(stage);

I've been using angularjs so dependency injection could also be nice.

new Sphere.Stage("mystage");
.enter = function() { }
.leave = function() { }
.load/init = function() { };

Sphere.stages.add("mystage");

@joskuijpers
Copy link
Contributor Author

Alright, but where would that be called? Where would the first stage be created?
And does a stage have some .loop() ?

@joskuijpers
Copy link
Contributor Author

@Radnen Could you look into such stage system? Designing it for Pegasus 😄

@Radnen
Copy link
Member

Radnen commented May 4, 2014

I added a basic stage and stage manager in commit 0fa8a42. Its only bare bones and does not have dependency injection.

Currently the API has it so there is a single loop method in the stage. From my experience creating games across multiple libraries this is not so good. In Sphere you could get by with that but in reality most games separate their update and render loops. Game logic should happen in the update loop while graphics are drawn in the render loop.

This is essential when we start asking questions like what kind of timestep system will we use? Will the games be drawn at a fixed fps? Use a clock - aka variable timestep (my favorite)? And it's useful to separate update and render so that things render as they should at 60fps despite what timestep method you use while the logic can update as fast as it can. This is seen particularly useful when games dip below 60 fps but still have accurate physics etc.

If update and render are separated, then when we pause a stage we should allow the ability to pause one, the other or both. Sometimes it's useful when displaying menus to pause the logic of all previous stages or scenes while the topmost stage is active. Then in the case of a HUD it may be that nothing on that scene is paused. However, there is generally no reason to pause just the render portion, but just in case I think it's okay to still expose a method of doing that.

Any thoughts on the api before I add this functionality?

@joskuijpers
Copy link
Contributor Author

First of all, thanks!
Second, a style-thing: I recently put all prototype methods that should not be overridden inside the class function: is cleaner and makes them readonly.

Also, do you think we could merge stage.js and stagemanager.js? If stage.js only exports Stage as a class so that var stage = require("stage"); new stage.Stage(), then we could aswell put the Stage class in the stagemanager file? (And rename that file to stage/stages).

What about making the this.loaded in Stage readonly (specs only, in actual implementation one could use .defineProperty with the writable:no key, or some other way). Then we have nicely stage.loaded instead of stage.isLoaded().

Then, third, about your splitting of update and render loop: Excellent idea!
On the mac, I happen to have my render loop limited to the display refresh rate to prevent flickering or over-use of the processor. And I get calls like 'update to animation time xxx': so that if there is lag, the game knows what needs to be updated to.
I think this is what you refer to? Having a stable render loop and some time-based updateLoop(time)?

@Radnen
Copy link
Member

Radnen commented May 5, 2014

Second, a style-thing: I recently put all prototype methods that should not be overridden inside
the class function: is cleaner and makes them readonly.

Putting methods into the constructor can cause weird memory issues. I ran into this when testing my Link.js library. Therefore I personally adopted the method of always using prototype. I'm not entirely convinced V8 will take care of .defineProperty calls in the constructor and cache/optimize them. Even though you may get more freedom out of it, games I've come to learn need every ounce of speed we can squeeze out of the engine. I learned this the hard way too trying to use defineProperty with Color objects in Jurassic/SSFML.

What about making the this.loaded in Stage readonly (specs only, in actual implementation one
could use .defineProperty with the writable:no key, or some other way). Then we have nicely
stage.loaded instead of stage.isLoaded().

Hmm, the StageManager has to be able to set that variable. Or there must be a method that somehow sets it and since there's no easy way of forcing people to properly polymorph/inherit functions, it becomes tricky to implement something like that. But you are right the user shouldn't be able to set .loaded at will.

Then, third, about your splitting of update and render loop: Excellent idea!
On the mac, I happen to have my render loop limited to the display refresh rate to prevent
flickering or over-use of the processor. And I get calls like 'update to animation time xxx': so that > if there is lag, the game knows what needs to be updated to. I think this is what you refer to?
Having a stable render loop and some time-based > updateLoop(time)?

Precisely. But I think the update loop can take either a fixed timestep or variable timestep depending on what works best for your game. We either design this into Pegasus or define ways of letting the game developer handle this on their own.

Oh, and I could have put StageManager into stage.js, but I had separated them because of separation of concerns. Personal stage modules may never touch StageManager directly so why expose that to them? When I'm installing a new kitchen sink I don't want to include the stove, microwave and refrigerator too. The stage manager would only be used for higher level debugging or handling of scenes, such as when you first set up the game.

@joskuijpers
Copy link
Contributor Author

On putting methods within the function braces: remember this is only the API and that the implementation does not matter. What is important is clean code, and that the API is implementable.

When I want functions to be 'protected': only be used by the same module, I hack by adding an underscore to the function so that it shouldn't be used by others.
E.g.: var myPrivateVar; this._setMyPrivateVar = function(v) {};

I do understand the separation of concerns, but I think modules are at a higher level. (we are talking SRP here, right?):
The stage class represents a stage. StageManager class manages the current stage and the stage stack/queue/whatever. These together make the stage system, which is then the stage module. That is how I think of it. But you might disagree of course.

@joskuijpers
Copy link
Contributor Author

@Radnen Can you give me a test case or definitive article telling me that defineProperty is a huge performance hit? Because I really like using properties instead of two setX and getX methods. (I used it in the sound module too.).
But if you convince me that it sucks ballz in performance, please do tell. I do think however that we either use properties everywhere, or use get+set everywhere to make the API more intuitive.

Btw, you can set normal properties ot be readonly, without using a custom getter.
Also, I don't think using defineProperty and {value:10,readonly:true} is any more costly than using this.x = 10, because it becomes the same thing in the engine (AFAIK).

@joskuijpers
Copy link
Contributor Author

@Radnen Could you write an example using the stage manager? I have a hard time grasping it. And I want to implement it to test the dev experience and to workings, Thanks!

@Radnen
Copy link
Member

Radnen commented Jun 16, 2014

To your earlier question, defineProperty has no real performance hit: http://jsperf.com/defineproperty-vs-regular-property

To your later question, here is a gist example of a stage example: https://gist.github.com/Radnen/2c1afc0528090ea63d71

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants