Will "Rubber banding" resolve multiplayer interpolation stutter? - java

I wrote multiplayer Pong using UDP. I am using interpolation and extrapolation in order to create a smooth looking effect on the client.
It works. However, there is a bit of constant stuttering in the ball. It jumps a tiny bit forward every time a new packet is received. It looks a little laggy, but it is playable.
There must be a way to make the game look smoother. I've read about Rubber Banding. What would be the best way to move from here?
I hope someone who is able to answer my question well will find it.
Update
As requested by Ivan, here is a graph of the ping times. However, I do believe that the problem exists inside the client smoothing code.

Filling in with context from your previous question, I understand that you are sending paddle & ball positions from each client to the other. However, as long as the clients agree on where the paddles are at each moment in time, the ball's movement is completely determined (barring rounding errors), and you should experiment zero ball-stutter.
Each client should keep its own internal state with the positions and speeds of paddles and the ball. Pseudocode would be similar to the following:
// input thread
if input changed,
alter paddle speed and/or direction
send timestamped message to inform my opponent of paddle change
// incoming network thread
if paddle packet received
alter opponent's paddle speed and/or direction at time it was sent
fix any errors in previously extrapolated paddle position <--- Easy
if ball-packet received
fix any errors in ball position and speed <--- Tricky
// update entities thread
for each entity (my paddle, opponent paddle, the ball)
compute updated entity position, adjusted by time-since-last-update
if ball reached my end, send ball-packet to other side
draw updated entity
This assumes that two package types are being exchanged:
paddle packets are timestamped positions + speeds of paddles, and are sent whenever a client alters the speed of its own paddle.
ball packets are timestamped positions + speeds of the ball, and are sent whenever a ball reaches a client's (local) side, whether it bounces off the paddle or not.
The pseudocode is performing extrapolation ("assume things keep on moving as usual") for all unknowns in the update-entities thread. The only point where problems arise is marked with <--- arrows.
You can easily correct for paddle positions by warping them to their new position, possibly interpolating the movement over a short period to make it less jarring.
Correcting for ball positions is easy if both clients more-or-less agree (and then you can do the interpolation trick again, to smoothen it up further). However, one client may see a near miss and another a near hit. In this case, since you are using a peer-to-peer model, we are letting the local client make the call, and explain what happened to the opponent (in another design, you would have a central server making these kinds of decisions; this is good to avoid cheating). You cannot avoid an ugly jump there if both clients disagree - but hopefully, this should be relatively rare and short, unless it coincides with a ping spike.

One of the ideas which allows to get rid of this effect is Using smoothing when applying error prediction corrections on client.
How that works
At some point in your code you identify that ball position and client are different.
Instead of applying that as a correction to client code immediately (which is one reason you can see those jumps), you perform that over some time, cl_smoothtime e.g. 500ms.
At first your program should store time when the error detection event occured m_flPredictionErrorTime.
public void onErrorDetected() {
this.m_flPredictionErrorTime = System.currentTimeMillis();
}
Somewhere close to the display code you calculate how much of error you're going to display. Here's some pseudocode for that.
public void draw() {
Point preditctionError = this.clientPredictedBallCoordinates - this.serverCoordinates;
Point deltaToDisplay = calculateErrorVector(preditctionError);
Point positionToDisplay = clientPredictedBallCoordinates + deltaToDisplay;
// actually draw the ball here
}
public Point calculateErrorVector(Point coordinatesDelta) {
double errorAmount = ( System.currentTimeMillis() - this.m_flPredictionErrorTime ) / this.cl_smoothtime.
if (errorAmount > 1.0) {
// whole difference applied in full, so returning zero delta
return new Point(0,0);
}
if (errorAmount < 0) {
// no errors detected yet so return zero delta
return new Point(0,0);
}
Point delta = new Point(coordinates.x*errorAmount, coordinates.y*errorAmount);
return delta;
}
I've picked this idea from Source Multiplayer Networking wiki. Actual code example in Cpp is available in their SDK around GetPredictionErrorSmoothingVector function.

Related

Gravitational attraction and collision

I made a simple program that simulates how a round object would behave with a roudn surface in an environment with gravity and collision.
My problem is related with the application of gravity: whenever an object get's very close to the attractor it starts reegaining height, what I think is causing the issue is that, let's say that the ball is touching the ground with very low speed, my program applies gravity and, sicne it will make contact, reverses the force and sends it into the air again.
I tried stopping the ball once it reached low enough speed, but the effect is overall unpleasing (it never is slow enough to make it seemless)
This is the code, what do you thin kis the mistake? As I listed only part of the code and it's pretty complciated I don't expect a specific response, but where do you think the issue could generally lie?
My guess is that I didn't respect the kinetic/potential energy relation, but I wouldn't know how to make it right either :/
void update(ArrayList<Attracter>a) {
pos.add(acceleration);
println(acceleration.mag());
for (Attracter ar : a)
if (PVector.dist(pos, ar.pos)<ar.size/2+size/2) {
//send the compenetrated body back
float difference=((ar.size/2+size/2)-PVector.dist(pos, ar.pos)+1);
pos.sub(acceleration.copy().normalize().mult(difference));
//calculate the new acceleration
PVector perpendicular= PVector.sub(pos,ar.pos).normalize(); //perpendicolare
float angle=perpendicular.rotate(-PI/2).heading();//angolo dellatangente
perpendicular.rotate(-angle); //normalizzo l'angolo
acceleration.rotate(-angle); //normalizzo l'accellerazione
PVector newAcceleration= PVector.fromAngle(perpendicular.heading()-acceleration.heading());
acceleration=newAcceleration.setMag(acceleration.mag());
acceleration.rotate(angle); //denormalizzo l'accellerazione
//push the body forward
pos.add(acceleration.copy().normalize().mult(difference));
acceleration.mult(0.9);
}
}
Remove the +1 in the difference calculation, i.e. try to replace
float difference=((ar.size/2+size/2)-PVector.dist(pos, ar.pos)+1);
with
float difference=((ar.size/2+size/2)-PVector.dist(pos, ar.pos));

Quick Time Event in Swing [pseudocode request]

I am in the extremely early stages of designing a game (so early that I haven't even committed to it yet). The idea is an RPG with a Quick Time Event for attacking and defending that relies on precise mouse movements or timed button presses to reduce damage taken or increase the damage of an attack (similar to Mario and Luigi or Paper Mario). I'm wondering the best way to go about programming a class that can handle this. Ultimately, I'd like to be able to write code such as this when I call the attack function, where all of the functionality of the QTE occurs in a single function call;
//Example code.
int attackPower = 23;
double qteSucessPercent = -1;
//more code
if(e.getSource == player.sword()) //when the player chooses to execute a sword attack.
{
qteSucessPercent = QTE.Jump();
if(qteSucessPercent< 0)
throw BadQTEException;
else if(qteSucessPercent>= .9)
player.attack(enemy[0], attackPower*2);
else if(qteSucessPercent>= .75)
player.attack(enemy[0], attackPower*1.5);
else if(qteSucessRate>= .5)
player.attack(enemy[0], attackPower);
else
System.out.println("Attack Failed");
}
This class needs to do several things.
It needs to be able to draw itself.
The QTE class needs to be able to handle key pressing or mouse motion events. I can handle the code for each type of QTE I wish to implement, but they need to be able to detect, on their own, where the mouse is and what buttons are being pressed.
Whatever it is drawing needs to delete itself once the QTE is over.
There will be multiple types of Quick Time Events. Each one will be drawn differently, have different victory conditions (one may require you to hit a sequence of buttons in the correct order, another may make you press a single button in rapid succession as many times as you can, ect), and modify the attack in various ways. The basic design of all QTEs is that they return a double value between 0 and 1, depending on how well the player did (generally, the higher the number, the better; if the attack returns 0, then the QTE received no input at all). In some situations, the QTE may be pass/fail, but the rest of the program is just watching for a non-0 number to be passed from the function call. For the sword, the attack's power is being modified by how well you do.
I know Swing isn't thread-safe (which, to my knowledge, means components won't update properly if you try to activate parts of their code from a thread other than the one you called them with, meaning I can't have two blocks of code executing at the same time if I want both sets of code to display animations), which is why I've written the code the way I have. The main thread gets to line 6, and executes QTE.Sword(). Until QTE.Sword() returns (which could be as long as five seconds), the rest of the program will freeze and not animate. I plan to use this to my advantage to achieve a bullet time effect, where the only thing animating is the QTE and the players and enemies remain stationary - though if I wanted an animation, like for a Mario and Luigi-like jump QTE, I'd set the player's sprite to be an animated gif that only plays once and only for as long as the QTE goes off.
Note that I am NOT ASKING FOR CODE. I'm asking for either pseudo-code or just a paragraph explanation of how I could program this. I think I can find a dead, open-sourced project on Github or Google Code and figure out the basics of the rest of the engine from there, but this is really the sticking point for me. Also, I am not asking for help handling mouse movements or keyboard presses. I have enough knowledge to code these on my own - I'm just not sure how to implement this in the context of an RPG. I know this isn't really stackoverflow's thing, and this question will probably get deleted, but I figure this is the best place to ask this sort of thing.

Box2D - Character sticking to sides of objects

I am using Box2D with libgdx. I am having an issue with the default collision action. When I jump or hit the top of an object, everything works fine. My object doesn't stick. If it hits the top, it stands on it. If it hits the bottom, it falls back down. But if it hits either of the sides, my object sticks, as long as I'm moving in that direction. In other words, the gravity has no effect on it while it collides with the side of the block/wall. I did some research, but all solutions said to use the b2Settings, which I can't use with libgdx. Is there any way I can fix this? The code I use to move my character(moving left) is as follows:
level.character.body.setLinearVelocity(
-level.character.terminalVelocity.x,
level.character.body.getLinearVelocity().y);
Here's an illustration. As you can see, it sticks to the brick instead of falling. (My character is currently a coin :p)
Instead of using SetLinearVelocity, try using ApplyForce or ApplyImpulse to move things around. The problem is that SetLinearVelocity allows you to create unrealistic situations, for example in this case when the ball hits the wall it should stop and the horizontal velocity really should be zero, but you are overriding the natural result and saying that the ball did not stop at all, and it is still moving.
Note that you may still get this problem even when using ApplyForce or ApplyImpulse, if the force is strong enough and there is enough friction between the fixtures (just like in the real world, if you push something against a wall hard enough and the surfaces are not too slippery, you can stop it from falling).

Moving objects smoothly, bad perfomance

I just finished developing my mini-game on Android and I got some questions about perfomance and generally "how to": If I have 25+ objects on the creeen (just a polygon with 7-8 angles) its starts to lag hard and CPU usage is very high (15-20% without colision detection, up to 50% with collision detection on samsung note 10.1 tablet).
So I have ~25 polygons (asteroids) and they are always "flying". So to move them I make a Timer, right? On TimerTask I got something like this
public void move() {
translate[1] += speed[1];
translate[0] += speed[0];
updateTranslateMatrix(); // updating translate matrix and then send it into vertex shader
updateAABBCoords(); // update coordinates of Axis-aligned bounding box
updateCoordsByTranslate(); // update coordinates of verticles (to be able to define exact collision (with bullets and starship))
}
Is there something unnecessary in this method?
Also I want to ask if it is OK to run this timer every 17 ms? Or 17 ms is too often? I feel smoothness of movement only at 17 ms, may be I am doing it wrong? And same Timer interval I got on starship/bullets movement and on Collision detection.
Help me, please. I feel like missing something huge and fundamental, because 50% CPU on note 10.1 in that simple Asteroids game is not normal.
One key issue, I believe, is that you are assigning an individual Timer to every object instead of employing a general purpose game loop. A vast majority of games use a game loop which runs continuously and can be split, typically, into two components: Update and Render.
An article on basic game loop design for Java/Android
An answer to a relevant question on game loop design on gamedev.stackexchange.com
When using a game loop you can not guarantee being able to update exactly every 17ms or some other arbitrary duration. Instead, the Update and Render methods receive a DeltaTime parameter which is the time since the last frame.
This is then used when, for example, getting new object positions by multiplying it with the object's velocity. Doing so allows for smooth interpolation of position and ensures that object speed is not affected by CPU speed.

Dead Reckoning - Client/Server game - How to handle keystrokes

I've read a few articles about dead reckoning, but it's still a bit confusing to me.
I'm pretty sure I understand the concept of point-and-click movement, but how would I update keystroke movement between the client and server, when the end-point of the movement is unknown?
How would you estimate where the player is going (server-side) if there is no end-point?
Let's start with dead reckoning. Try to think about it on a straight line where there is no left or right movement.
You are at point A and want to get to point B. You know the distance to point B so you can compute how much acceleration and velocity it will take to get there. Dead Reckoning works the other way. You are at point A and will move at a set speed to get near point B.
In the video game world they use this method all the time. So don't think about it as you're moving to point B you're just moving towards point B in increments because in like a FPS your point B will constantly moving. When in fact its really only moving in increments in the direction of point B.
Once you get moving forward then you can start worrying about left/right and up/down which will follow the same principle just in different directions.
As for implementing this you have 2 options.
One way, you could make this calculation on the client side then send the new position to the server. Then update what everyone else sees on screen.
The other way which I think is better you can make all these calculations on the server side and just receive an update where you ended up. X-Box live makes one of the consoles the host so that machine is running all the software and the external users are just firing events. This is why you'll hear people complain about having an unfair host advantage.
Now let's look at some code. This script comes from the Unity Sdk standard install package.
/// This script moves the character controller forward
/// and sideways based on the arrow keys.
/// It also jumps when pressing space.
/// Make sure to attach a character controller to the same game object.
/// It is recommended that you make only one call to Move or SimpleMove per frame.
var speed : float = 6.0;
var jumpSpeed : float = 8.0;
var gravity : float = 20.0;
private var moveDirection : Vector3 = Vector3.zero;
function Update() {
var controller : CharacterController = GetComponent(CharacterController);
if (controller.isGrounded) {
// We are grounded, so recalculate
// move direction directly from axes
moveDirection = Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButton ("Jump")) {
moveDirection.y = jumpSpeed;
}
}
// Apply gravity
moveDirection.y -= gravity * Time.deltaTime;
// Move the controller
controller.Move(moveDirection * Time.deltaTime);
}
So in this example we have speed which is set to a constant. So when moving in a direction we will travel in that Vector at a constant rate with no care about where point B is located.
Jump speed is how fast will we move in the y axis in the positive direction and gravity in the negative direction stopping at point 0.
So, How can you use this? You could build a server script that executes a move action and have a client side controller that passes the direction information to the server.
So lets say on the client side action is key press down, you send a call to the server to move you in the direction selection and the server will keep you moving you in that direction until action key press up or a change in direction from another input.
I hope this helped.
Let's see... I understand you know how to code, but not what, am I right?
You can feed the keystrokes directly to the server and let it do all the work. The server has no need to know where you are heading, it has your coordinates and direction and that's all it will need, unless you have server-handled AI. In the later case, you can do something similar to raycasting in Unity, start checking what is straight ahead and see if it is anything of interest, if so you know the potential destination.
It is safe to constantly send back to the client all his data, so it is always up to date. If you believe this will strain your connection you can do that every 50ms or as often as you believe is safe, and for smooth function estimate everything on the client side too. Make sure the server does all the collision detection and all the mechanics not related to the UI, or else the game will be prone to client-side malfunction or malicious tampering.
If you do have to look at where the player might be going, you can use a few approaches, you could either have a number of virtual cubes in the world which keep track of what is inside them, which will simplify finding what's ahead in terms of resources, or you could check everything there is, which is rather heavy on the machine, but this adds some other complexities too. In the first case do not forget to stop looking once you hit an obstacle.
You can also calculate the angle between the player's direction and other items of importance to check what else might be on his mind.
The question about UDP has already been answered, plus I'm sure Wikipedia has a helpful book on UDP and TCP, I have nothing to add.
The explanation of dead reckoning was also quite satisfactory, I hope I added other angles to the concept.
I hope I helped, if you need any clarifications or any other help feel free to contact me, if I was less than unhelpful then feel free to downvote, I study computer engineering and am creating my second game for PC so maybe I have ran into some problem already and can share the knowledge.

Categories