Impact

» Edit Game Info

7 months ago by Joncom

This post is a work in progress.

I made this plugin:
https://github.com/Joncom/impactjs-fixed-tick

What does it do?

- adds new property ig.system.tickRate to the engine
- tickRate means "how many times per second to update the game"
- each update advances the game by a constant (no longer variable) time
- if tickRate is 10, then each update will advance the game exactly 100 ms
- update is called as needed to fulfill tickRate, instead of exactly once per draw

Here is a demo.

Why use it?

- if you need or want your game to be more deterministic for some reason, such as you want to create a replay system, or you have a multiplayer game that needs to run identical simulations on the client and the server

How to use it?

- simply run your game as usual
- set ig.system.tickRate to something else if the default 60 is not what you want
- TODO: explain panic() function

Further Reading

Work is based on this article:
http://www.isaacsukin.com/news/2015/01/detailed-explanation-javascript-game-loops-and-timing

Another excellent article on the subject:
http://gafferongames.com/game-physics/fix-your-timestep/

7 months ago by Bum

Hey this is real cool! Any chance of adding smooth interpolation to create a slow down/bullet-time effect in the engine with this?

7 months ago by Joncom

Quote from Bum
Hey this is real cool! Any chance of adding smooth interpolation to create a slow down/bullet-time effect in the engine with this?
Hey Bum. I think you can already achieve that without this patch?
ig.Timer.timeScale = 0.1;

7 months ago by Joncom

It occurred to me that directly patching the engine is not ideal because it violates the Open/Closed Principle:
software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
Therefore, I created a plugin version:

<plugin code removed, see https://github.com/Joncom/impactjs-fixed-tick instead>

The plugin does work, however there is an issue; It seems there's a 50/50 chance the debug module will not work each time the browser is refreshed.

Let me explain.

The debug module uses inject to hook into the ig.System.run function for the purpose of updating the on-screen performance graphs, framerate, etc.

The problem is that we need to execute the "fixed-tick" plugin before this happens, because we need to outright replace the run function first, so that the new version of the run function is the one the debug module hooks into.

And the following will not not guarantee any particular dependency load order.

ig.module( 
	'game.main' 
)
.requires(
	'plugins.fixed-tick',
	'impact.debug.debug',	
	'impact.game'
)
.defines(function(){
    MyGame = ig.Game.extend({
        // ...
    });
    // ...
});

I guess the problem stems from the fact that I'm still violating the "open/closed principle". I'm replacing ig.System.run rather than extending it. I'm not sure this can be helped though... Hmm...

Edit: Just thinking out loud. If the CommonJS module system were used, then dependency load order would be guaranteed, and this issue would not be.

7 months ago by Joncom

More thinking out loud. A possible work-around for the above issue could be to modify the module system via ImpactMixin such that load order is guaranteed.

5 months ago by Cakebit

Thanks for the plugin Joncom.

It should be super useful in my multiplayer endeavors.

I noticed when running the plugin in my project is that
‘this.delegate.update is not a function’

indicates that there is no update function in the ig.System class. Is that a method you left out - and should be injected by the plugin?

5 months ago by Joncom

Quote from Cakebit
indicates that there is no update function in the ig.System class. Is that a method you left out - and should be injected by the plugin?
Actually, delegate refers to the ig.game object. Therefore there really should be an update function there!

Since delegate seems to not reference your game, maybe you could drop a breakpoint on the line throwing that error and see what its value is?

5 months ago by Cakebit

Ah! You are correct Joncom.

The reason my delegate was returning undefined was due to the fact that I was using Dominic's (slightly hacky) Impact Splash Loader which set it's class as the delegate (instead of ig.game). (Splash Loader On Github)

Now to see if I can get both to play nicely together. Something as simple as
if(this.delegate){
	this.delegate.update();
}

acts as a work-around, but yields a weird transition when the delegate switches to ig.game.

It's probably something I need to look deeper into...

Thanks again!

5 months ago by Joncom

Glad that mystery is solved. :)

May I ask what is weird about the transition?

5 months ago by Cakebit

The loader runs just fine, but it doesn't to take over the delegate to fade into the game... (again, this could be something I'm overlooking - I haven't had the time to dive right into the system class yet)

Link to PasteBin Source

You can drop the loader directly into any Impact Project, and then add it to your ig.main arguments. (Also, you may want to change the run function name to update if testing with the Tick plugin).

:)

5 months ago by Joncom

Perhaps the transition looks weird because maybe you replaced ig.game.run (which normally did an update and a draw) with an ig.game.update but left out the ig.game.draw?

5 months ago by Cakebit

Quote from Joncom
Perhaps the transition looks weird because maybe you replaced ig.game.run (which normally did an update and a draw) with an ig.game.update but left out the ig.game.draw?

The draw function was left empty, simply to override the parent loader's draw function :)

It appears the problem was springing up when switching the delegate. The ig.system function setDelegate was expecting a run function in the new delegate (and throwing an error when not found). The fix was using your plugin to also overwrite the setDelegate method in ig.system to no longer require run, then use the update function, instead of the run function in the loader.

An example of the code used would look like
setDelegate: function(object) {
    if (typeof(object.update) == 'function' && typeof(object.draw) == 'function') {
        this.delegate = object;
        this.startRunLoop();
    } else {
        throw ('System.setDelegate: No update() or draw() function in object');
    }
}

instead of

setDelegate: function(object) {
    if (typeof(object.run) == 'function') {
        this.delegate = object;
        this.startRunLoop();
    } else {
        throw ('System.setDelegate: No run() function in object');
    }
},

Making this minor change fixes compatibility problems when switching the delegate. :)

Thanks for the help - the plugin is super.

Pastebin of Loader Code | Pastebin of Plugin Changes

5 months ago by Joncom

Quote from Cakebit
The ig.system function setDelegate was expecting a run function in the new delegate (and throwing an error when not found).
That is strange because your game still has a run function. Notice that the plugin simply overrides the run function in ig.Game. The splash loader plugin has one too. So, what supposedly has no run function?

5 months ago by Cakebit

Quote from Joncom
That is strange because your game still has a run function. Notice that the plugin simply overrides the run function in ig.Game. The splash loader plugin has one too. So, what supposedly has no run function?

Sorry for the confusion, I modified the SplashLoader to change the function name from run to update so that it would be called by this plugin before each frame.

You are correct, another option would be to keep an empty run function in the loader class, and have a separate update function.

5 months ago by Joncom

Quote from Cakebit
I modified the SplashLoader to change the function name from run to update so that it would be called by this plugin before each frame.
Ohhh, makes sense! Added your fix.

4 months ago by Joncom

Found a bug in the plugin.

It allowed ig.system.tick to be non-constant.

The issue was due to small floating point precision errors.

For example, at 60 FPS, ticks might look like this:
0.016666666666666666
0.016666666666666734
0.016666666666666579

However now with the fix, all such ticks are precisely 0.016666666666666666.
Page 1 of 1
« first « previous next › last »

Post Reply

Please login or register to reply.