Thursday, June 7, 2012

Hello Friend.

My player got to meet someone last night. Actually, he met me, testing from my laptop, but was still quite amazing (personally anyway). As you can see there are differing results based on small tweaks I made along the way.  It still isn't "perfect" by any means, and making the movement smoother is actually trickier than you may think.

Check out the video. Feel free to skip the rant afterwords. Things are going to get deep, not so much technical, but should give you an idea what I'm up against. It will also help me think this through.



For the sake of trying not to lose you, in this next section, let me define some things. The player is the person on screen representing you, the player, as you move about the world.  The friend (usually your friend) is the other player on the screen, controlled by someone else, as they move about (I actually named the class that holds all this information "Friend". How cute.)

In the initial part of the video, the friend is moving (woohoo!) but irratically, as if we turned a slow strobe light on. I'm not going to talk about the messages the player is sending, only those being received by the player from the friend, because it affects what we, the players, see. I'm also not going to talk about latency/lag (at least not yet) because, since I'm testing on my local area network, I receive messages within 2-4 milliseconds. Therefore, the choppiness is not due to lag.

What's happening is that the friend is sending messages to the player every 1/5 of a second, actually, the friend to the server to the player, but that's all about lag. To simplify, the message is coming directly from the friend every 1/5 of a second.  The message contains the friend's position, velocity, and head angle (and soon facing direction). Every 1/5 of a second, the friend is updated to the new position. Velocity isn't coded right yet, so the friend doesn't actually move, he just "teleports" to the message's given locations.  Your brain tries to link the positions together, and would have an easier time if we increased the number of messages per second.  Also, the friend is updating the head angle, but is trumped by code telling the head to watch the cursor (same thing is happening to the facing direction, but it isn't receiving messages for that yet)

In the second portion of video, I added animation to the friend and made the head angle independent of the cursor. That's when I realized the friend was teleporting. Damn it Google, how do you not know that teleporting is a word. Teleport, teleporting, teleportation. All words. Welcome to the twenty-first century Google.


The third section of video works like this. Player receives message from the friend that says the friend is located at A, moving X speed. Until the next message, keep moving the friend at X speed (plus gravity and collisions) to help connect the teleporting positions. This is closer to smooth movement, however, any change in behavior that the friend makes (starting, stopping, jumping) causes a little stutter effect, as the player is incorrectly guessing where the friend's next location will be. Understand? No? Try this analogy:
Your friend is driving down the interstate. They call you to check in once an hour. They first call and tell you the mile marker they are at and their speed (they always drive the speed limit) of 65 mph. In one hour, they call you again. They are still going 65 mph, and they are 65 miles further than where they were the first time. You could, in theory, know exactly where the friend is along their route at any time, without having to talk to your friend.
However, the next time your friend calls, an hour after the last call, they are still going 65 mph, but they only drove 45 miles, due to bad traffic along the way. The traffic is now clear, so they are cruising at 65 mph, but you have to adjust your friends position on the highway because it was 20 miles off. If you were graphing your friends progress every minute, you would have a big jump in your friends distance traveled to correct when you got their call. 
Even more severe, say your friend calls, and they drove 50 miles since the last call, and are currently driving 55 mph.  Are they continuing to go 55mph? Are they speeding up? Slowing down?
This is what is happening in my game. The game assumes the car is going the same speed, but is updated and told it was off, so it corrects (causing a stutter-like effect), then keeps assuming based on the new speed.  You can see above how this can be an issue.

In this third section of the video, to be more accurate, I cranked up the messages to 20 messages a second instead of 5.  The movement is smoother, but still stutters when the player changes direction. This is treating the symptoms, not fixing the problem. It's also adding the amount of information being sent per second. If enough information per second piles on (from player info, monster info, item info) it can bottleneck and really cause issues.

I am proposing this to smooth out the friend's movement: add a buffer which waits for an amount of time after a message is received before actually updating the friend.  Then, with the messages received, connect the positions, using the the below methods to avoid jarring jumps that cause stutter effects. If a message isn't received in time for the next update, continue moving the friend in the direction and speed last sent, as we are doing currently. There might be an abrupt movement when the next message arrives, but these glitches are unavoidable when latency is poor.

I've created these graph to help demonstrate.  The green line is the actual position of the friend in some direction in time.

Currently the game acts like the orange line. It receives a message from your friend with a position and velocity, and continues in that direction until you get another message, then corrects. Look how choppy that becomes! I should also mention, this is extremely exaggerated, if you hadn't guessed. My messages arrive 5 or 20 to a second, and my player cant change directions that quickly.  To fix this, I can use a buffer.

The game adds a delay (in this case, its as long as the period between messages).  When a message tells the game where the friend is, it determines the velocity needed to move the friend to that new point in the amount of time the delay is for. The red line demonstrates how close the viewed motion of the friend is to the original, however, due to the delay, you actually witness the friend at the dotted red line. When it comes to a fraction of a second, this may not matter. Because this is exaggerated, the actually game might look pretty good like this. But if not, there's plan B.

Plan B (which I might do anyway): Instead of setting the velocity that will get the friend's location to the next message position within the delay time, you calculate the acceleration needed to get to that position and smoothly roll into it. Then the new velocity takes over until you get another message, and change the acceleration to turn you to that point.

HOLY CRAP! Long post, but it helped me figure out what to do next. Thanks guys! Now that the night is shot, I'll code tomorrow. Good night!


1 comment:

  1. Man, and I was thinking quadratics. Cubic Splines are the way to go!

    http://www.gamedev.net/page/resources/_/technical/multiplayer-and-network-programming/defeating-lag-with-cubic-splines-r914

    ReplyDelete