Jump to content
  • Advertisement
Sign in to follow this  
DanB91

[XNA] Host and non-host machine have unwanted offset

This topic is 2948 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am currently making a Pong game using XNA which will have Live. I am right now working on the movement/position of the ball using networking.

The host machine will send the non-host machine the coordinates of the ball, so the host will have the "true" position of the ball.
I am using a smoothing and prediction algorithm for the ball and the host sends packets every 6 frames. The problem is, is that the host sends the packets too late and there is a noticeable offset in the ball (the non-host machine is down and to the right of the true position).

When I send packets every frame, the problem goes away. I don't understand how I can do this without sending a packet every frame.

Help is much appreciated. Thank you very much.

Share this post


Link to post
Share on other sites
Advertisement
Prediction often requires that you extrapolate (like interpolation, but past a time of 1.0) your values in order to guess where things would end up frames in advance.

Make sure you're able to determine how much lag there is on the messages, so that you can forward the positions accordingly.

Share this post


Link to post
Share on other sites
Also, when the received position differs from the predicted one, instead of jumping immediately (noticeable even for smaller difference), do it gradually over several frames.

Share this post


Link to post
Share on other sites
You should determine a fixed rate to send ball updates, not do updates based on frame rate.

Pong is only 2-4 players so maybe try 20-30 updates per second and it shouldn't be much bandwidth. Perhaps also do an immediate ball update when a player hits the ball with a paddle to ensure the other players immediately get the correct physics of the hit.

Interpolation works well in most cases - except when objects instantly get an large unpredictable change in velocity. Thus a fixed update rate with immediate update on ball hits should fix your issues.

Share this post


Link to post
Share on other sites
Thanks for the help guys. The game does use LERP for smoothing and has the same prediction system as the one provided by Microsoft. The problem isnt that its not smooth but rather consistently has a different position from the host. The packets are sent out every update, not necessarily frame (should have been more clear). Perhaps I should have provided code first so here it is (a lot of it). If this can help identify the problem please do. Would it be best for me to record the problem using FRAPS or something?

If the code is partially unreadable, its much more readable here:
http://forums.xna.com/forums/t/58505.aspx

partial class NetworkBall : Ball
{
public override void KillAndReset()
{

active = false;

if (simState.position.X > 1280)
{
simState.position.X = 1191;
simState.position.Y = 64;
simState.motion.X = -defaultMotion.X;
simState.motion.Y = defaultMotion.Y;
player.score++;
active = true;
}
else
{
simState.position.X = 89;
simState.position.Y = 64;
simState.motion = defaultMotion;
secondPlayer.score++;
active = true;
}


}

public override void Update(GameTime gt)
{
//If hits either wall go in opposite direction in Y plane
if (simState.position.Y > 660 || simState.position.Y < 20)
{
simState.motion.Y = -simState.motion.Y;
bloop.Play(1f, 0, 0, false);
}

//If hits either player, go in opposite direction in X plane
if ((secondPlayer.GetRectangle().Intersects(GetRectangle()) ||
player.GetRectangle().Intersects(GetRectangle())) && !alreadyHit)
{
simState.motion.X = -simState.motion.X;
blip.Play(1f, 0, 0, false);
alreadyHit = true;
}


//If it reaches the goal, kill and reset the ball, else update it
if (simState.position.X > 1280 || simState.position.X < 0)
KillAndReset();
else
UpdateState(ref simState);

//this is just so it doesn't glitch if it hits a player in weird spot
if (alreadyHit)
{
elapsed += (float)gt.ElapsedGameTime.TotalSeconds;
if (elapsed > .5)
{
alreadyHit = false;
elapsed = 0;
}
}

}

public void UpdateLocal(GameTime gt)
{
Update(gt);

displayState = simState;
}

public void UpdateRemote(int framesBetweenPackets)
{
float smoothingDecay = 1.0f / framesBetweenPackets;
currentSmoothing -= smoothingDecay;

if (currentSmoothing < 0)
currentSmoothing = 0;

UpdateState(ref simState);
if (currentSmoothing > 0)
{
UpdateState(ref prevState);
ApplySmoothing();
}
else
{
displayState = simState;
}




}

void ApplySmoothing()
{
//Not needed as the motion is constant
//displayState.motion = Vector2.Lerp(simState.motion, prevState.motion, currentSmoothing);
displayState.position = Vector2.Lerp(simState.position, prevState.position, currentSmoothing);
}

public void WritePackets(PacketWriter writer, GameTime gt)
{
writer.Write((float)gt.TotalGameTime.TotalSeconds);
writer.Write(motion);
writer.Write(simState.position);


}

public void ReadPackets(PacketReader reader, GameTime gt, TimeSpan latency)
{
prevState = displayState;
currentSmoothing = 1;

float packetSendTime = reader.ReadSingle();
simState.motion = reader.ReadVector2();
simState.position = reader.ReadVector2();


ApplyPrediction(gt, latency, packetSendTime);
}

void ApplyPrediction(GameTime gt, TimeSpan latency, float packetSendTime)
{
float localTime = (float)gt.TotalGameTime.TotalSeconds;

float timeDelta = localTime - packetSendTime;
clockDelta.AddValue(timeDelta);

float timeDeviation = timeDelta - clockDelta.AverageValue;

latency += TimeSpan.FromSeconds(timeDeviation);

TimeSpan oneFrame = TimeSpan.FromSeconds(1.0 / 60.0);

while (latency >= oneFrame)
{
UpdateState(ref simState);

latency -= oneFrame;
}
}

void UpdateState(ref BallState state)
{

state.position += state.motion;
}
}


partial class NetworkClassicPong
{
public override void Update(GameTime gt)
{





if (!gameEnded)
{
frameCount++;

if (frameCount == framesBetweenPackets)
{
sendPacketThisFrame = true;
frameCount = 0;
}

UpdateLocalPlayer(gt, ns.LocalGamers[0]);
ReceivePackets(gt, ns.LocalGamers[0]);
UpdateRemotePlayers(gt);

}





}

public void UpdateRemotePlayers(GameTime gt)
{
foreach (NetworkGamer gamer in ns.AllGamers)
{
if (gamer.IsLocal)
continue;


NetworkPlayer player = gamer.Tag as NetworkPlayer;

player.UpdateRemote(framesBetweenPackets);

}

}

public void UpdateLocalPlayer(GameTime gt, LocalNetworkGamer lg)
{

NetworkPlayer localPlayer = lg.Tag as NetworkPlayer;

localPlayer.UpdateLocal();

if (lg.IsHost)
ball.UpdateLocal(gt);
else
ball.UpdateRemote(framesBetweenPackets);


if (sendPacketThisFrame)
{
localPlayer.WritePackets(writer, gt);

if (lg.IsHost)
{
writer.Write((byte)ball.player.score);
writer.Write((byte)ball.secondPlayer.score);

ball.WritePackets(writer, gt);

}

lg.SendData(writer, SendDataOptions.InOrder);
sendPacketThisFrame = false;
}




}

public void ReceivePackets(GameTime gt, LocalNetworkGamer lg)
{

while (lg.IsDataAvailable)
{
NetworkGamer sender;


lg.ReceiveData(reader, out sender);

if (sender.IsLocal)
continue;

NetworkPlayer remotePlayer = sender.Tag as NetworkPlayer;

TimeSpan latency = ns.SimulatedLatency +
TimeSpan.FromTicks(sender.RoundtripTime.Ticks / 2);

remotePlayer.ReadPackets(reader, gt, latency);

if (!lg.IsHost)
{
ball.player.score = reader.ReadByte();
ball.secondPlayer.score = reader.ReadByte();

ball.ReadPackets(reader, gt, latency);
}


}





}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!