This is my first project using Photon or any sort of networking. I’m trying to set a float within Mecanim each frame, using an RPC function. I call this when I check for my key inputs:
Movement controller
void FixedUpdate() {
float moveX = Input.GetAxisRaw ("Horizontal");
float moveZ = Input.GetAxisRaw ("Vertical");
photonView.RPC ("AnimationMovement", PhotonTargets.All, new Vector2 (moveX, moveZ));
// More movement stuff below
}
Animator script
[PunRPC]
public void AnimationMovement(Vector2 movement) {
Debug.Log ("Setting movement animations");
model.GetComponent<Animator> ().SetFloat ("RunDirection", movement.y);
model.GetComponent<Animator> ().SetFloat ("StrafeDirection", movement.x);
}
I get a huge framerate drop (3fps) when I do this. Is there a better way? or am I approaching this wrong?
Sorry I meant RPC. So I moved it to the regular Update method and the same thing is happening. How does every other game manage to get 100 different animations synced but I cant even send 2 accurately?
Sending a RPC each frame will break your client really quickly.
Your approach is to send the bool (let’s say) 60 times a second. Do that for a few values and to 4 players, you easily break the message limit with that one value already. You can break mobile clients with this alone.
The trick is to send updates when necessary only.
How to do that exactly, is beyond a quick explanation here. We do have a PhotonAnimatorView component in PUN, that might help and do what you plan to do.
Otherwise, I suggest you take a look at how we do position updates in the Marco Polo Tutorial (same principle), have a look at our page about synchronizing states and then take a look at the PhotonAnimatorView code. If it doesn’t do what you need to, it can be a basis for your own code, too.
Thanks tobiass, I figured out a way to do it properly after some digging. I am syncing the animation values with OnPhotonSerializeView(). This seems to work without a big performance drop.
@Saladon : Happy to read that
I would like to encourage you to take a few minutes and post your solution in a bit more detail. When you ask for help, its always nice to also show solutions you found, if time permits.
Apologies, that was selfish. Below is the solution I got.
** SOLUTION **
After much deliberation and breaking things, figured out better approach to this and it works without a big performance drop.
Instead of calling an RPC method from each characters controller, as fast as possible in the Update() method, I decided to sync the animation states over the network and set them once they are received. Code below:
NetworkPlayer Script (Attached to each player)
void Start ()
{
if (photonView.isMine) {
// Activate components that are needed for the controlling player
} else {
// Start updating player over the network, if not the controlling player
StartCoroutine("UpdateData");
}
}
// Set animation state every iteration
IEnumerator UpdateData()
{
while (true) {
transform.position = Vector3.Lerp(transform.position, position, networkMovementSmoothing*Time.deltaTime);
transform.rotation = Quaternion.Lerp(transform.rotation, rotation, networkMovementSmoothing*Time.deltaTime);
animator.SetFloat("RunDirection", animRunDir);
animator.SetFloat("StrafeDirection", animStrafeDir);
animator.SetBool("isMoving", animIsRunning);
yield return null;
}
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting) {
stream.SendNext (transform.position);
stream.SendNext (transform.rotation);
// Write animation state to the network
stream.SendNext (animator.GetFloat("RunDirection"));
stream.SendNext (animator.GetFloat("StrafeDirection"));
stream.SendNext (animator.GetBool("isMoving"));
} else {
position = (Vector3)stream.ReceiveNext();
rotation = (Quaternion)stream.ReceiveNext();
// Recieve animation state from network
animRunDir = (float)stream.ReceiveNext();
animStrafeDir = (float)stream.ReceiveNext();
animIsRunning = (bool)stream.ReceiveNext();
}
}
So now when the controlling player changes the animation state in the player controller, it will sync across the network and update that player on all the other clients.
If I’m right, the coroutine is running every single frame. If so, that is going to be a lot of data.
Keep in mind: We do not limit you in what you send but we also can’t guarantee that things will work nicely, no matter what you send! It might end up as too much information for some devices, breaking in real world usage on a customer’s device with lesser connections.
I can only encourage you to rethink your approach. Usually, it’s not a good thing to send each frame’s values.
Your game should be able to looks good with only 10…20 updates per second.
The best way to do this is don’t send animation info directly at all. Drive that from what you know about movement and in response to other commands like ‘jump’, ‘shoot’, etc…
I normally do this by having my character controller drive movement animations. What you want to send over the network is position of the character and heading. Maybe sync some state variables (as integers) to say if the character is running or walking. Then your animations are driven locally. If you get a remote position and it’s not where your local character is, you know you need to move the character, and thus know you need a movement animation. Then you can check if the character state is set to walk or run to see which animation to use.
Another issue with sending animations is those will be out of sync with actual movement. You are lerping, that means your movement is lagging behind your animations. And that will often be fairly visible when transitioning from stopped to moving and visa versa.
I actually agree with that. By giving it a limit then performance can be controlled. I’ve modified my coroutine to delay the update so that it sends at a rate of about 20 per second. There is a bit more of a delay but it seems to work well.
In my case, I have to send floats for my animation states because I use blending. In my reply to Tobiass above, I mentioned that I limited the update rate by adding a 5 millisecond delay each iteration. I understand that the players won’t be exactly accurate, but that is just the nature of networking and latency. I will handle my shooting logic in a special way so that I can fairly see if a player was shot or not (Average positioning between both clients, etc… Still working on it).