I've built a Server / Client environment for my Unity game which is pretty simple. It is written in C# with asynchronous sockets. To keep it simple, the player sends his input data and the server broadcasts it to every connected player.
My message packets have to the following strucutre:
Username|Command|Scene ID|X Position|Y Position|Horizontal Axis Input|Vertical Axis Input
An example packet for player movement / position data would look like this:
PlayerA|P|0|0.12341123|1.3423424|-1|0
My first approach was to sent the data every frame. As you can imagine that was too often and the game was laggy with only two players moving simultaneously. Then I've read about interpolation. So I reduced sending the data. I now send only position data if a certain distance was walked or after a certain amount of milliseconds. The rest is calculated via interpolation. Unfortunately I got my interpolation code from a tutorial using the Unity multiplayer functionality.
Now the result I get is that the player kind of jumps. I have a guess why. The player sends his position when he reached it. I think that is too late. But I have no idea how I could fix that problem.
I have two objects. A player object, which is using the script PlayerMovement and a prefab which is using the CharacterMovement for in game movement. I think these two scripts are the important once to help me, so here they are:
PlayerMovement.cs
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PlayerMovement : MonoBehaviour
{
Rigidbody2D Body;
Animator Animator;
private bool sendStopWalking = false;
public float Threshold = 0.5f;
DateTime lastSentDateTime = DateTime.Now;
private Vector3 lastSentPosition;
private void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
void Start()
{
Body = GetComponent<Rigidbody2D>();
Animator = GetComponent<Animator>();
}
void Update()
{
// GetAxisRaw Returns 0 / 1 / -1
Vector2 movementVector = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
if(movementVector != Vector2.zero)
{
Animator.SetBool("IsWalking", true);
Animator.SetFloat("InputX", movementVector.x);
Animator.SetFloat("InputY", movementVector.y);
if (NetworkManagement.IsConnected)
{
Vector3 currentPosition = Body.transform.position;
if(isOverThreshold(lastSentPosition, currentPosition, Threshold) || isTimeToSend(500))
{
lastSentDateTime = DateTime.Now;
NetworkManagement.ClientSocket.Send(string.Format("P|{0}|{1}|{2}|{3}|{4}", SceneManager.GetActiveScene().buildIndex, currentPosition.x, currentPosition.y, Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")));
lastSentPosition = Body.transform.position;
sendStopWalking = true;
}
}
}
else
{
Animator.SetBool("IsWalking", false);
if(sendStopWalking && NetworkManagement.IsConnected)
{
NetworkManagement.ClientSocket.Send(string.Format("P|{0}|{1}|{2}|{3}|{4}", SceneManager.GetActiveScene().buildIndex, Body.transform.position.x, Body.transform.position.y, Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")));
sendStopWalking = false;
}
}
Body.MovePosition(Body.position + movementVector * Time.deltaTime);
}
private bool isOverThreshold(Vector3 oldPosition, Vector3 currentPosition, float threshold)
{
return Vector3.Distance(oldPosition, currentPosition) > threshold;
}
private bool isTimeToSend(double milliseconds)
{
return ((DateTime.Now - lastSentDateTime).TotalMilliseconds > milliseconds);
}
}
CharacterMovement.cs
using UnityEngine;
public class CharacterMovement : MonoBehaviour
{
Rigidbody2D Body;
Animator Animator;
public float AxisRawHorizontal;
public float AxisRawVertical;
public Vector3 ServerPosition;
public float LerpValue = 4;
void Start()
{
Body = GetComponent<Rigidbody2D>();
Animator = GetComponent<Animator>();
}
void Update()
{
Vector2 movementVector = new Vector2(AxisRawHorizontal, AxisRawVertical);
if (movementVector != Vector2.zero)
{
Animator.SetBool("IsWalking", true);
Animator.SetFloat("InputX", movementVector.x);
Animator.SetFloat("InputY", movementVector.y);
}
else
{
Animator.SetBool("IsWalking", false);
}
interpolate();
//Body.MovePosition(Body.position + movementVector * Time.deltaTime);
}
private void interpolate()
{
Vector3 position = Vector3.Lerp(this.transform.position, ServerPosition, Time.deltaTime * LerpValue);
this.transform.position = position;
}
}
I seriously have no more idea and unfortunately I never have been good in maths and physics, so I hope someone can help me here. If there is any more information needed, please tell me. I tried to reduce the question to the most important things.