I am building a 2D top-down MMO that's very lightweight.
I have a master client that determines everything's position and actions, and sends this information to all other clients.
Now I have made a great deal of different solutions to the movement of my NPCs across the network, and some solutions are better than others, but none have been perfect. So I would love some feedback on this.
Master client sends information to server:
void Start () {
InvokeRepeating("CheckForMove", 1.0f, 1f);
}
void CheckForMove()
{
var distance = Vector3.Distance(lastPos, transform.position);
if (distance > 0.02) // Dont send information if movement is very small i.e bumping into a wall repeatedly etc
{
socket.Emit("enemyMove", Network.MobIdAndLocationToJson(gameObject.name, transform.position));
moveEmit = true;
lastPos = transform.position;
spawnScript.spawnedEnemies[gameObject.name] = transform.position;
}
}
Server reveives the information:
socket.on('enemyMove', function(data){
// moves the monster that matches the given ID
console.log('enemy is moving', data);
socket.broadcast.emit('enemyMove', data);
});
Player Clients receive the move information:
private void OnEnemyMove(SocketIOEvent e)
{
var position = new Vector3(e.data["x"].n, e.data["y"].n, -0.5f);
var enemy = enemySpawner.FindEnemy(e.data["id"].str);
var enemyNavigator = enemy.GetComponent<EnemyNavigator>();
enemyNavigator.MoveThisEnemy(position);
}
Now the question becomes: what is the best way to go from here to smooth this movement as best I can?
The solutions I have tried are as follows:
------------------ solution 1 ----------------------
public void MoveThisEnemy(Vector3 position)
{
anim.SetBool("walk", true);
startTime = Time.time;
DistanceToDest = Vector3.Distance(transform.position, position);
destPos = position;
isMoving = true;
}
and in update:
if (isMoving)
{
if (currentTarget != null)
{
float currentDuration = Time.time - startTime;
float journeyFraction = currentDuration / totalDistanceToDestination;
transform.position = Vector3.Lerp(transform.position, destPos, journeyFraction);
}
}
Although this works fine, the NPCs' movement is wonky: it finishes its movement faster than the next update can come no matter what, so despite lerping, it seems to move faster than I can provide it with updates. Imagine a cartoon slug that moves fast, stops, moves again, stops. So the NPC is always there and not just teleporting but still not the effect I am looking for.
------------------ solution 2 ----------------------
Here I simply try to just reproduce on the player client what I am doing on the master client but I make sure to correct the position of the NPC ever so often to guarantee its in the correct position.
public void MoveThisEnemy(Vector3 position)
{
anim.SetBool("walk", true);
transform.position = position;
isMoving = true;
}
and in update:
if (isMoving)
{
if (currentTarget != null)
{
var targetDistance = Vector3.Distance(transform.position, currentTarget.transform.position);
if (targetDistance > minDist)
{
Vector3 direction = (currentTarget.transform.position - transform.position).normalized;
rigid.MovePosition(transform.position + direction * speed * Time.deltaTime);
}
}
}
This is actually a very good solution as I can get the position correct with minimal amounts of data sent over the network. But since I have obstacles in my game, it gets very weird as moving the NPC's transform doesn't include collisions and it can perform weird movements when colliding. Furthermore, it seems to teleport to its position because of the transform.position
updates when playing in full screen. But that's barely visible in smaller resolution for some reason.
I have also tried using rigidbody.MovePosition
in the MoveThisEnemy
function, but this just means it doesn't get updated correctly - it seems to be less precise than transform.position
.
Any feedback would be greatly appreciated.