Client Side Prediction Keeping Movement in Sync with Server? - javascript

I am working on a multiplayer game at the moment. I am experiencing an issue with my client side prediction. After moving around for a while, the server position and client position are offset. Here is the game currently. The server position is shown in orange. Your local position is shown in white. What I am doing, is simply simulating the players movement locally and on the server. Here is the movement code:
var xMov = 0;
var yMov = 0;
if (player.keys.up) {
yMov -= 1;
}
if (player.keys.down) {
yMov += 1;
}
if (player.keys.right) {
xMov += 1;
}
if (player.keys.left) {
xMov -= 1;
}
xMov /= REFRESH_INTERVAL;
yMov /= REFRESH_INTERVAL;
var length = Math.sqrt(xMov * xMov + yMov * yMov);
if (length != 0) {
playerMoved = true;
xMov /= length;
yMov /= length;
xMov *= player.speed;
yMov *= player.speed;
player.deltaX += xMov;
player.deltaY += yMov;
} else {
playerMoved = false;
}
var oldX = player.x;
var oldY = player.y;
player.deltaX *= c.speedDecel * delta;
if (player.deltaX <= 0.1 && player.deltaX >= -0.1)
player.deltaX = 0;
player.deltaY *= c.speedDecel * delta;
if (player.deltaY <= 0.1 && player.deltaY >= -0.1)
player.deltaY = 0;
player.x += player.deltaX * delta;
player.y += player.deltaY * delta;
player.x = Math.round(player.x);
player.y = Math.round(player.y);
The code on the client and server side is identical. When a user presses a key, the key information is sent to the server:
sendDataArray[0] = key;
sendDataArray[1] = val;
socket.emit('3', sendDataArray);
Then on the server, the key value is set. The problem I think I am having is a timing issue on the client and server side. The movement method shown above is called from a setInterval on both the client and server side:
setInterval(move, 31);
So when I press the key, on the client side, it could be 5ms until the next move() is called. Whereas on the server, when he receives the key info, it may be another 30ms. This might be causing an inconsistency.
How could this be resolved?

Using error correction info
From the client you should also be sending the position,and TIME with every call. That way the server can decide if X time has passed since last message and that time seems correct, then look at the position, if the position relative to the last known position and known time seem acceptable then adjust the server position to match.

Related

How to stop the wheel of fortune at a particular segment smoothly

I have created this wheel of fortune game in reactJS. It is completely random and worked correctly till now. I was able to catch the segment where it stopped but now I am receiving the winning segment also from backend side so I have to stop this wheel exactly at that segment.
I thought that may be if I compare the actual winner value with the current segment running in downtime (that is when it is getting slowed down) and then suddenly stop it if the actual winner value is equal to the current segment by making angle equal to zero but it is not stopping and still kept moving. if I add this snippet, it will work but it will just stop suddenly and user may think that it is fixed.
if(this.state.winner === this.winningSegment){
progress = 1;
}else{
progress = duration / this.downTime;
}
code editor link
onTimerTick = () => {
this.frames++;
this.draw();
console.log(frames);
var duration = new Date().getTime() - this.spinStart;
var progress = 0;
var finished = false;
if (duration < this.upTime) {
progress = duration / this.upTime;
this.angleDelta = this.maxSpeed * Math.sin((progress * Math.PI) / 2);
} else {
progress = duration / this.downTime;
this.angleDelta =
this.maxSpeed * Math.sin((progress * Math.PI) / 2 + Math.PI / 2);
if (this.winningSegment === this.state.winner) {
this.angleDelta = 0;
this.angleCurrent = 0;
}
if (progress >= 1) finished = true;
}
this.angleCurrent += this.angleDelta;
while (this.angleCurrent >= Math.PI * 2)
// Keep the angle in a reasonable range
this.angleCurrent -= Math.PI * 2;
if (finished) {
console.log(this.state);
this.setState({
isFinished: true
});
clearInterval(this.timerHandle);
this.timerHandle = 0;
this.angleDelta = 0;
}
};

Parts of if statement not being exectued

I am using the Zdog library to create 3d objects in javascript. In the function that controls my animations I am trying to have a particle move around randomly and then after an interval return back to its original point. Here is my code:
if (radioOn == true){
radio.zoom = size;
path = [Math.floor((Math.random() * 3 - 1)),Math.floor((Math.random() * 2)),Math.floor((Math.random() * 2 - 1))];
origin[0] += path[0];
origin[1] += path[1];
origin[2] += path[2];
lightning.translate.x += path[0];
lightning.translate.y -= path[1];
lightning.translate.z += path[2];
timer++;
}
if (timer == 20){
timer = 0;
lightning.translate.x -= origin[0];
lightning.translate.y += origin[1];
lightning.translate.z -= origin[2];
origin = [0,0,0];
}
I have put alerts in the second if statement and timer is getting reset but origin is not. Also the particle initially does what I need but after it gets reset it will no longer move.

Increase speed on start and slow down at end

I have simple function that moves a circle in specific direction:
var rad = (a) => Math.PI / 180 * a;
this.x += Math.cos(rad) * this.throttle();
this.y += Math.sin(rad) * this.throttle();
I am also calculating distance to a target:
var distance = (p1, p2) => Math.sqrt( (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) );
this.destination_distance = parseInt(distance( { x: this.x, y: this.y }, { x: x, y: y } ));
I started to work on this.throttle function but i cannot get my head around it.
I wanted to achieve simple thing, when circle starts to move i want to increase speed from min to max by some step and when it is close to destination i want it to start slow down until it reaches min.
This is my current approach:
this.min_speed = 0.1;
this.max_speed = 1.5;
this.current_speed = 0.1;
this.throttle = function() {
if(this.destination_distance > 300) {
this.current_speed += 0.002;
} else {
this.current_speed -= 0.002;
}
if(this.current_speed < this.min_speed) {
this.current_speed = this.min_speed;
}
if(this.current_speed > this.max_speed) {
this.current_speed = this.max_speed;
}
return this.current_speed;
};
This doesnt work, because if the distance is smaller then 300 it doesnt speed up at all its always on min speed, so i suppose it should be somehow related to the distance variable. Maybe someone could help me solve this problem.
You need to calculate the difference between target speed and current speed, then add some specific fraction of that.
this.throttle = function() {
var target_speed = this.destination_distance > 300 ? this.max_speed : this.min_speed;
var diff = target_speed - this.current_speed;
this.current_speed += diff * laziness;
return this.current_speed;
};
laziness is something between 0.0001 and 1; the greater the value the faster the change of velocity.
Your starting speed is your minimum speed, so if the starting distance is <300 the circle will never speed up or slow down. Why not make your starting speed depend on the initial distance? Here's a crude implementation
if (this.destination_distance<300) {
this.current_speed = 1.5; //start fast when in the deceleration zone
} else {
this.current_speed = 0.1; //start slow when in the acceleration zone
}
Why don't you use something like percent of total distance passed? That way you will avoid having hardcoded threshold of 300 and it should work with distances of arbitrary length (with some tweaking of the speeds). Your changed function would look something like this:
this.throttle = function() {
if(this.destination_distance > 0.5 * total_distance) {
this.current_speed += 0.002;
} else {
this.current_speed -= 0.002;
}
if(this.current_speed < this.min_speed) {
this.current_speed = this.min_speed;
}
if(this.current_speed > this.max_speed) {
this.current_speed = this.max_speed;
}
return this.current_speed;
};
Here I consider total_distance to be the distance from the start to the final destination. This way the circle will travel approximately half of the way with increasing speed and the other half with decreasing speed. If you replace 0.5 by let 0.8 then the speed will increase for the first 80% and decrease for the last 20% of the path.

Returning precise vector components in js canvas

I have been wrestling with rendering an animation that fires a projectile accurately from an "enemy" node to a "player" node in a 2D 11:11 grid (0:0 = top-left) in JS/Canvas. After a lot of reading up I've managed to get the shots close, but not quite bang on. I think my velocity function is a little out but I really don't know why. This is the trigonometric function:
this.getVelocityComponents = function(speed){
// loc (location of enemy actor) = array(2) [X_coord, Y_coord]
// des (destination (ie. player in this instance)) = array(2) [X_coord, Y_coord]
var i, sum, hyp, output = [], dis = [];
var higher = false;
for (i in loc) {
sum = 0;
if (loc[i] > des[i])
sum = loc[i] - des[i];
if (loc[i] < des[i])
sum = des[i] - loc[i];
dis.push(sum);
}
hyp = Math.sqrt(Math.pow(dis[X], 2) + Math.pow(dis[Y], 2));
if (dis[X] > dis[Y]) {
output[X] = (speed * Math.cos(dis[X]/hyp))
output[Y] = (speed * Math.sin(dis[Y]/hyp))
} else if (dis[X] < dis[Y]) {
output[X] = (speed * Math.cos(dis[Y]/hyp))
output[Y] = (speed * Math.sin(dis[X]/hyp))
}
return output;
}
and this is the instruction that tells the X and the Y of the projectile frame to advance:
var distance = [];
for (i in loc) {
var sum = 0;
if (loc[i] > des[i])
sum = loc[i] - des[i];
if (loc[i] < des[i])
sum = des[i] - loc[i];
distance.push(sum);
}
if (distance[X] > distance[Y]) {
frm[X] += (loc[X] < des[X]) ? v[X] : -v[X];
frm[Y] += (loc[Y] < des[Y]) ? v[Y] : -v[Y];
} else {
frm[Y] += (loc[Y] < des[Y]) ? v[X] : -v[X];
frm[X] += (loc[X] < des[X]) ? v[Y] : -v[Y];
}
Below is a screenshot. Blue is player, pink enemy and the yellow circles are projectiles
as you can see, it's almost on the mark.
Have I done something wrong? what do I need to do?
To calculate the direction from enemy to player you can simplify the calculations a little.
Find direction angle
var diffX = Player.x - Enemy.x, // difference in position
diffY = Player.y - Enemy.y,
angle = Math.atan2(diffY, diffX); // atan2 will give the angle in radians
Notice also difference for Y comes first for atan2 as canvas is oriented 0° pointing right.
Velocity vector
Then calculate the velocity vector using angle and speed:
// calculate velocity vector
var speed = 8,
vx = Math.cos(angle) * speed, // angle x speed
vy = Math.sin(angle) * speed;
You might want to consider using time as a factor if that is important. You can see my answer from a while back here for an example on this.
Demo
Using these calculations you will be able to always "hit" the player with the projectile (reload demo to change enemy position to random y):
var ctx = document.querySelector("canvas").getContext("2d"),
Player = {
x: 470,
y: 75
},
Enemy = {
x: 100,
y: Math.random() * 150 // reload demo to change y-position
};
// calculate angle
var diffX = Player.x - Enemy.x,
diffY = Player.y - Enemy.y,
angle = Math.atan2(diffY, diffX);
// calculate velocity vector
var speed = 8,
vx = Math.cos(angle) * speed, // angle x speed
vy = Math.sin(angle) * speed,
x = Enemy.x, // projectil start
y = Enemy.y + 50;
// render
(function loop() {
ctx.clearRect(0, 0, 500, 300);
ctx.fillRect(Player.x, Player.y, 30, 100);
ctx.fillRect(Enemy.x, Enemy.y, 30, 100);
ctx.fillRect(x - 3, y -3, 6, 6);
x += vx;
y += vy;
if (x < 500) requestAnimationFrame(loop);
})();
<canvas width=500 height=300></canvas>
The solution is much simpler than that.
What should you do ?
1) compute the vector that leads from you enemy to the player. That will be the shooting direction.
2) normalize the vector : meaning you build a vector that has a length of 1, with the same direction.
3) multiply that vector by your speed : now you have a correct speed vector, with the right norm, aimed at the player.
Below some code to help you understand :
function spawnBullet(enemy, player) {
var shootVector = [];
shootVector[0] = player[0] - enemy[0];
shootVector[1] = player[1] - enemy[1];
var shootVectorLength = Math.sqrt(Math.pow(shootVector[0], 2) + Math.pow(shootVector[1],2));
shootVector[0]/=shootVectorLength;
shootVector[1]/=shootVectorLength;
shootVector[0]*=bulletSpeed;
shootVector[1]*=bulletSpeed;
// ... here return an object that has the enemy's coordinate
// and shootVector as speed
}
Then, since you don't use time in your computations (!! wrooong !! ;-) ) you will make the bullet move with the straightforward :
bullet[0] += bullet.speed[0];
bullet[1] += bullet.speed[1];
Now the issue with fixed-step is that your game will run, say, twice slower on a 30fps device than on a 60fps device. The solution is to compute how much time elapsed since the last refresh, let's call this time 'dt'. Using that time will lead you to an update like :
bullet[0] += dt * bullet.speed[0];
bullet[1] += dt * bullet.speed[1];
and now you'll be framerate-agnostic, your game will feel the same on any device.

(Game Programming) How to add delay between attacks?

I'm making a game where you control your character with the arrow keys and attack with 'A'. My problem is that the attack has no delay so when I hold 'A' the enemy's hp depletes rapidly. How do I add delay? I tried adding delay, here is my code:
var DELAY = 2;
var cooldown = 0;
function update(time) {
// UP
if (38 in keysDown) {
player.y -= player.speed * time;
}
// DOWN
if (40 in keysDown) {
player.y += player.speed * time;
}
// LEFT
if (37 in keysDown) {
player.x -= player.speed * time;
}
// RIGHT
if (39 in keysDown) {
player.x += player.speed * time;
}
// 'A'
if(65 in keysDown) {
player.attacking = true;
cooldown -= time;
}
else
player.attacking = false;
// Collision
if( (player.x + pImage.width-5) >= monster.x &&
(player.x + pImage.width-5) < monster.x + enImage.width &&
player.y >= monster.y && player.y < (monster.y + enImage.height) &&
player.attacking)
{
if(cooldown <= 0) {
monster.hp -= player.dmg;
cooldown = DELAY;
}
if(monster.hp <= 0) {
relocEn();
}
}
}
The problem is that the cooldown counts only when I'm holding 'A' and resets only when the player is touching the monster. I want something like when I press 'A' the cooldown timer sets off. Also, I want the sprite(in attacking state) to go along with the delay and goes back to "standing" state. Thanks in advance
Here's what I would do:
I would create a variable for the time of the last attack and move all your code relevant to attacking to another function. I'm assuming time is measured in milliseconds so you're probably going to want your delay to be in the hundreds.
var DELAY = 400; //Change this to a higher value if it's not long enough.
var timeSinceLastAttack = -400; // The opposite of the DELAY if you want to attack at the start.
function update(time) {
...
// 'A'
if(65 in keysDown
&& time > (timeSinceLastAttack + DELAY) ) {
player.attack();
timeSinceLastAttack = time;
}
Then in your attack() function you can do your collision detection.
Okay,
1- Try assigning a larger value for the DELAY
2- Print the "time" value to see how much you add each loop to the DELAY if it was a big value - larger than 0.00x - divide it
Another thing:
Place the "cooldown -= time" outside the "A pressed" parentheses
It's not good, to force the user to hold the A button for a certain amount of time each time he want to shoot

Categories