How to make fps run at the same pace across different clients - javascript

Both of the server and client have the code
setInterval(function(){
player.x++;
player.y++;
}, 20);
I have a node.js / socket.io server where the player's x and y are being updated every frame and sent to the client. The client is also having a different x and y being updated using the same logic. The client seems to be moving faster than the server though so it's making the game not in sync.. What should I be doing differently?

that's a huge and kinda complex topic, I'm not in the mood to google these topics for you; but a few hints/directions.
This ain't meant to be a full answer, but it's more explanation, a comment can take.
1st: don't increment values, neither in the back- nor in the frontend.
Define them as an animation: { startTime, startValue, endTime, endValue }. Now you can interpolate the current position at any time, independant of the interval.
2nd: you have to account for the time-offset between front- and backend.
3rd: check in the frontend for manipulations of the timer: this is a common way to cheat. Don't take the correctness of the clock for granted.
4th: you also have to account for the ping, when recieving actions from the frontend for example, you should subtract the ping to know when the action has really been made. And don't forget to check the ping over and over again: it might change over time.
And again, you can only trust your own clock, and you should check for manipulations (eg. artificially slowing down the connection). google ping-cheater, ...
finally: You're not the first one to create a real-time game. Search for these topics, they have been solved, and they have been asked by other people in the past.
Search especially in the flash-community. Flash still is a popular gaming-platform, and AS3-code should be pretty easy to understand and to port to JS.

It was actually really really easy, anyone with the same problem just read this article. https://www.viget.com/articles/time-based-animation
The simplest way to put it "move based on how much time has passed since you've last moved" So just find a deltaTime variable by subtracting time_now from time_then.
This is what my updatePosition function looks like now
self.updatePosition = function(){
var now_time = (new Date).getTime();
var delta_time = now_time - self.then_time;
self.x += delta_time * self.velocityX;
self.y += delta_time * self.velocityY;
self.then_time = now_time;
}
Just set the then_time in your initialization or whenever your character can first start moving.

Related

Animation alternatives Javascript

So I want to make a small JavaScript game.
And in order to do that I need to animate a few things.
I went researching and found about setInterval and requestAnimationFrame.
I can use either of those 2 for making my game work, however I understood that requestAnimationFrame is the better alternative there.
The problem I see with this is that while the function has its benefits , you are unable to set a framerate , or an update rate for it easily.
I found another thread that explained a way of making this work however it seemed somewhat complicated.
Controlling fps with requestAnimationFrame?
Is there an easier way of animating with a set framerate ?
Is there an easier way of animating with a set framerate ?
Simply put: no. Since rendering is one of the most computation heavy process for a browser, which can be triggered in various ways, it is not possible to foretell how long an update will take, since it can range from drawing one circle on a canvas up to a complete replace of all the visible content of the page.
To overcome this, browser offer a way to call a function a often as possible and the developer is responsible to make his animation sensitive to different time deltas/time steps.
One way to tackle that is to use the concept of velocity. velocity = distance / time. If you want an asset to have a constant velocity you can do the following, since distance = velocity * time follows:
var
asset_velocity = 1; //pixel per millisecond
last = new Date().getTime()
;
(function loop () {
var
now = new Date().getTime(),
delta = now - last,
distance = asset_velocity * delta
;
//update the asset
last = now;
window.requestAnimationFrame(loop)
})();

Inconsistent Timestamp in JavaScript?

I'm running into some strange error with the Date.now() function in JS.
Background Info: I'm developing a webApp that sends and receives packets to/from a server. All my incoming packets are marked with a timestamp, generated by Date.now(). Among these packets is data, that is printed into a line chart. So I get a value on the y axis and the timestamp on the x axis. So far so good.
Letting the app run has shown me some strange behaviour -> My line chart sometimes draws data points back in the past, right before already present data points! While checking the timestamps of these points i saw, that my chart does everything right, but the timestamps are wrong.
So I wrote a little test script, to collect some more evidence:
var i = 0;
var ts = [Date.now()];
setInterval(function () {
ts.push(Date.now());
console.info("checking next timestamp ...");
if (ts[i] > ts[i+1]) {
console.error("old timestamp (" + ts[i] + ") is BIGGER than current timestamp (" + ts[i+1] + ")!");
}
i++;
}, 100);
Running this over a few minutes prints me one error:
Way later i get another one, and so on.
For this test i chose 100ms interval time to check many timestamps. But my App has an update interval of maybe 5s and still there arrives this error sometimes. Not as often as with 100ms, but it's there :(
Has anyone else seen this behaviour before? Am I doing something wrong here?
BTW: The packets in my App come in one by one, so it's impossible that the order of the packets is mixed up or sth.
Well, as I have taken a look onto the Performance API yesterday, I think this is the way to go. I implemented it in my application as follows:
Everytime when I receive an input packet from my server I give them a timestamp. Until now I had realised this by Date.now() but this seems to be inconsistent as the OS or the VM (or both) recalculate their current time from time to time.
So now I calculate my own timestamp. For this I have to read the timestamp from when my application started. The performance API gives me this value by
var startTS = window.performance.timing.navigationStart; //returns a timestamp in ms
So this is a value available everywhere in my app since it exists in the window namespace and does not need a global variable made by myself. Very nice.
Now, to get the current timestamp, I have to add a time value that indicates how long my application is running since startTS. To get this we just use:
var timeSinceStartup = window.performance.now(); //returns sth. like 1337.9836718 (ms with fractional part)
If needed, one can round this value as the fractional part might not be needed:
var rounded = (timeSinceStartup + 0.5) | 0; //same as but faster than Math.round(timeSinceStartup);
Well, the rest is easy. We just have to add those values (as both are milliseconds) and voila:
var currentTS = startTS + timeSinceStartup;
I have updated the little code snippet from my question to test this:
var i = 0;
var start = window.performance.timing.navigationStart;
var ts = [start + window.performance.now()];
setInterval(function () {
ts.push(start + window.performance.now());
console.info("checking next timestamp ...");
if (ts[i] > ts[i+1]) {
console.error("old timestamp (" + ts[i] + ") is BIGGER than current timestamp (" + ts[i+1] + ")!");
}
i++;
}, 100);
Chrome is running this test at the moment (in VM) and it looks like everything is just fine (17500 checks without an error). I would have wondered if sth. else would happen, as we just add a constant with a monoton rising value ;)
Only drawback of this solution seems to be the browser support. As of now every modern browser but Safari support this. As my app is developed to run only on modern browsers this is ok for me, but the lack of Safari is bad.
http://caniuse.com/#feat=high-resolution-time
I will have to take a look into crossbrowser solutions/polyfills/Frameworks or sth. Thanks everybody for your help. Anyway I'm still curious if there might be a solution for Date.now() in Chrome, as FF and IE had no problems with using it.
https://code.google.com/p/chromium/issues/detail?id=408077
EDIT
This seems to be a good polyfill for a missing Performance API:
https://gist.github.com/paulirish/5438650
Ofcourse it falls back to Date.now(), so in browsers that don't support the API I might run into the same problem as before. For this case I just keep checking my timestamps in the app and if a timing error occurs I'm going to handle it silently (e.g. don't draw it to the chart, instead wait for the next value).
So long...

best way to sync data with html5 video

I am building an application that takes data from an android app and replays it in a browser. The android app basically allows the user to record a video and while it is recording it logs data every 100ms such as gps position, speed and accelerometer readings to a database. So i want the user to be able to play the video back in their browser and have charts, google map etc show a realtime representation of the data synced to the video. I have already achieved this functionality but it's far from perfect and I can't help thinking there must be a better way. What I am doing at the moment is getting all of the data from the database ordered by datetime ascending and outputting it as a json encoded array. Here is the process I am doing in pseudo code:
Use video event listener to find the current datetime of video
do a while loop from the current item in the data array
For each iteration check whether the datetime for that row is less than the current datetime from the video
If it is then update the dials from the data
Increment array key
Here is my code:
var points = <?php echo json_encode($gps); ?>;
var start_time = <?php echo $gps[0]->milli; ?>;
var current_time = start_time;
$(document).ready(function()
{
top_speed = 240;
min_angle = -210;
max_angle = 30;
total_angle = 0 - ((0-max_angle)+min_angle);
multiplier = top_speed / total_angle;
speed_i=0;
video.addEventListener('timeupdate', function() {
current_time = start_time + parseInt((video.currentTime * 1000).toFixed(0));
while(typeof points[speed_i] !== 'undefined' && current_time > points[speed_i].milli)
{
newpos = new google.maps.LatLng(points[speed_i].latitude, points[speed_i].longitude);
marker.setPosition(newpos);
map.setCenter(newpos);
angle = min_angle + (points[speed_i].speed * multiplier);
$("#needle").rotate({
animateTo : angle,
center: [13,11],
duration: 100
});
speed_i++;
}
}
});
Here are the issues I seem to have encountered:
- Have to load thousands of rows into json array which can't be very good for permorance
- Have to do while loop on every video call back - again can't be very good for performance
- Playback is always a bit behind
Can anyone think of any ways this can be improved or a better way completely to do it?
There are a few reasons why this may be running slowly. First, the timeupdate event only runs about every 250ms. So, if you're going to refresh at that rate, dandavis is right and you don't need that much data. But if you want animation that's that smooth, I suggest using requestAnimationFrame to update every 16ms or so.
As it is, if you update every 250ms, you're cycling through 2 or 3 data points and updating the map and needle three times in a row, which is unnecessary.
I recommend looking into Popcorn.js, which is built exactly for this kind of thing and will take care of this for you. It will also handle seeking or playing backwards. You'll want to pre-process the data so each point has a start time and an end time in the video.
There are also some things you can do to make the data transfer more efficient. Take out any extra properties that you don't need on every point. You can store each data point as an array, so the property names don't have to be included in your JSON blob, and then you can clean that up with a few lines of JS code on the client side.
Finally, separate your data file from your script. Save it as a static JSON file (maybe even gzipped if your server configuration can handle it) and fetch it with XMLHttpRequest. That way, you can at least display the page sooner while waiting for the code to download. Better yet, look into using a JSON streaming tool like Oboe.js to start displaying data points even before the whole file is loaded.

What is the correct way to send position updates via WebSockets?

I need to broadcast position updates in a game-world running with node.js. My current idea is to use BinaryJS. I'm not sure what is the correct way to use it, though. I have thought in 3 options:
How I'm doing currently, using socket.io: I programmatically store a buffer of position-updates that happened in a given amount of time and then send it all at once:
setTimeout(function(){
//called a fixed amount of times per second (40)
//this variable will hold the accumulated position changes on the last 25ms
//var accumulated_position_changes = [id0,x0,y0,z0, id1,x1,y1,z1...];
client.send(accumulated_position_changes);
},25);
But if binary.js does the chunking by itself (does it?), then should I just indiscriminately send position changes every time they happen?
MyClass.prototype.set_position = function(x,y,z){
// this is called thousands times a second
this.x = x, this.y = y, this.z = z;
client.send([this.id, x, y, z]);
};
Or should I somehow create an object deriving node.js's stream and use it? Maybe with stream.write? Or some way else? What is the right way to deal with this problem?
Binary.JS seems to write a packet every time you write to the pipe, in my use cases.
Why not write the position changes to a buffer, and then use Underscore.js' throttling. You could call update() or whatever, which writes the whole buffer of changes all at once. You can call that function as often as you want, but use the throttled version so that it only actually runs at a maximum of every 50ms or so.
That delay is incredibly fast for web applications. Be sure you are considering the rate at which your clients can receive and ACK that info. Honestly, I wouldn't update the clients until the data has successfully been sent and ACKed. Otherwise, you will have way too high of an update rate for some.

Getting certain frequency with setInterval method

In a javascript code I develop, some function should be called every 1 second. But to be sure that this operation takes place every 1 second, the following code is utilized:
setInterval(doIt, 500);
function doIt() {
var now = (new Date()).getTime();
if(lastUpdate + 1000 >= now) {
/// code...
lastUpdate = now;
}
}
As far as I know setInterval(doIt, 1000) doesn't always mean that it's called every one second.
Is the above solution is a valid one? If not, what do you recommend?
You could use setTimeout instead of setInterval, and make dynamic adjustments each time your function is called. The idea is to set the timeout for a number of milliseconds sufficient to carry you to the next second boundary.
function timeoutFunc() {
// do interesting things
var time = new Date().getTime();
setTimeout(timeoutFunc, 1000 - time % 1000);
}
You'd start it off with:
setTimeout(timeoutFunc, 1000 - new Date().getTime() % 1000);
Synchronizing with the server seems like a bad idea, because you have no way of knowing whether the client clock is synchronized to anything (like the NTP server network). If it's not, then you server synchronizations are going to make things look wrong at the client, because the client clock will always be what seems right.
well setInterval IS defined in milliseconds. so it means its called every X millisdconds.
however the system can freeze or something like that!
but theres no practical better solution, you approach is fine.
if you really have an extensive javascript client application the results could stretch a little bit.
a possible solution for that is to get the system time and have a counter in your function. then ever X executions you align with the system clock, calculate how many function calls you should have until now and speed up the interval or slow it down.
this is as far as you can get to perfection. but it will be only a matter of milliseconds and probably not worth the effort.
may i ask what you are developing?

Categories

Resources