Failing to sync a localEurlerAngles value

Options
chaozz
chaozz
edited August 2014 in Photon Unity Networking (PUN)
I am working on a multiplayer game and have a script called Bend.cs that handles rotating a spine bone of my player to look up and down.

I use an RPC to do the actual rotation, and for testing purposes I use this RPC to do the rotation on all photontargets including myself. But it only makes my local player bend his spine, not on any of my clients. What am I doing wrong here?
using UnityEngine;
using System.Collections;

public class Bend : MonoBehaviour {

	float sensitivityZ = 8F;
	float minimumZ = -60F;
	float maximumZ = 60F;
	float rotationZ = 0F;
	
	public GameObject Bone;
	PhotonView pv;
	
	void Start() {
	 pv = this.GetComponent<PhotonView>();
	}
	
	[RPC]
	void BendSpine_RPC (float rotz) {
		Bone.transform.localEulerAngles = new Vector3(0, Bone.transform.localEulerAngles.y,rotz);
	}	
	void LateUpdate ()
	{
		if (pv.isMine) {
			rotationZ += Input.GetAxis("Mouse Y") * sensitivityZ;
			rotationZ = Mathf.Clamp (rotationZ, minimumZ, maximumZ);
			pv.RPC ("BendSpine_RPC", PhotonTargets.All, rotationZ);	
		}
	}
}

I have been battling this problem for weeks, but I just don't see it.

Comments

  • vadim
    Options
    Can you check if other clients get RPC calls and rotz is properly set?
    How are objects instantiated? How is script attached to them?
  • chaozz
    Options
    it's a multiplayer third person game, and all other RPC calls like syncing and ARMED pose are working.

    rotz MUST be properly set, because it's responsible for the local animation as well, and that works.

    the bend script posted here is a script on the player object, just like the movement script, shooting script etc. all which have working RPC calls. For movement I use onserialize routines.
  • vadim
    Options
    If RPC called properly and rotz is ok then setting transform for your object does not work for some reason. May be it is reset somewhere else in you code. Can you log Bone.transform.localEulerAngles right after setting in RPC?
  • chaozz
    Options
    Can you log Bone.transform.localEulerAngles right after setting in RPC?

    As soon as I debug.log the rotation in the RPC call, unity hangs. So something is pretty wrong.

    Would anyone be willing to check out my project? Not exactly sure how, but this bone rotation thing is really putting a break on development.
  • chaozz
    Options
    I updated my script. I have the bend.cs on every player, but only enabled on the local player.

    The new script looks like this:
    using UnityEngine;
    using System.Collections;
    
    public class Bend : Photon.MonoBehaviour {
    
    	float sensitivityZ = 8F;
    	
    	float minimumZ = -60F;
    	float maximumZ = 60F;
    	
    	float rotationZ = 0F;
    	
    	public GameObject HeadMount;
    	GameObject Bone;
    	
    	[RPC]
    	void BendSpine_RPC (float rotZ) {
    		GameObject rotBone;
    		rotBone = GameObject.Find("CMan0010-M4-FBX/CMan0010-M3-CS_Hips/CMan0010-M3-CS_Spine/CMan0010-M3-CS_Spine1/CMan0010-M3-CS_Spine2");
    		rotBone.transform.localEulerAngles = new Vector3(0, rotBone.transform.localEulerAngles.y,rotZ);
    	}	
    	void Start () {
    		Bone = GameObject.Find("CMan0010-M4-FBX/CMan0010-M3-CS_Hips/CMan0010-M3-CS_Spine/CMan0010-M3-CS_Spine1/CMan0010-M3-CS_Spine2");
    	}
    	
    	void LateUpdate () 	{
    		//if (photonView.isMine) {
    			rotationZ += Input.GetAxis("Mouse Y") * sensitivityZ;
    			rotationZ = Mathf.Clamp (rotationZ, minimumZ, maximumZ);
    			Bone.transform.localEulerAngles = new Vector3(0, Bone.transform.localEulerAngles.y,rotationZ);
    			HeadMount.transform.localEulerAngles = new Vector3(rotationZ * -1, 0, 0); // rotate the camera along with the spine.
    		//}
    			photonView.RPC ("BendSpine_RPC", PhotonTargets.Others, rotationZ);	
    	}
    }
    

    When I add another network client that moves his mouse to rotates his spine, I get this warning in my console:
    Received unknown status code: QueueIncomingReliableWarning
    UnityEngine.Debug:LogError(Object)
    PhotonHandler:DebugReturn(DebugLevel, String) (at Assets/_IMPORTED/Photon Unity Networking/Plugins/PhotonNetwork/PhotonHandler.cs:170)
    NetworkingPeer:DebugReturn(DebugLevel, String) (at Assets/_IMPORTED/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs:802)
    NetworkingPeer:OnStatusChanged(StatusCode) (at Assets/_IMPORTED/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs:1330)
    ExitGames.Client.Photon.EnetPeer:QueueIncomingCommand(NCommand)
    ExitGames.Client.Photon.EnetPeer:ExecuteCommand(NCommand)
    ExitGames.Client.Photon.<>c__DisplayClass11:<ReceiveIncomingCommands>b__f()
    ExitGames.Client.Photon.EnetPeer:DispatchIncomingCommands()
    ExitGames.Client.Photon.PhotonPeer:DispatchIncomingCommands()
    PhotonHandler:Update() (at Assets/_IMPORTED/Photon Unity Networking/Plugins/PhotonNetwork/PhotonHandler.cs:76)

    I feel I'm getting closer. Anyone have any ideas?
  • vadim
    Options
    QueueIncomingReliableWarning tells that too much incoming messages, may be because of too frequent rpc calls. It' hardly related to your issue directly.
    For transforms, implementing OnPhotonSerializeView and interpolating is more appropriate solution than rpc call on every update.
    Btw, may be transform for the bone set somewhere else that resets angles that you calculate in rpc.
  • chaozz
    Options
    yes, i switched to OnPhotonSerializeView but without much luck
    using UnityEngine;
    using System.Collections;
    
    public class NetworkCharacter : Photon.MonoBehaviour {
    	
    	Vector3 realPosition = Vector3.zero;
    	Quaternion realRotation = Quaternion.identity;
    	
    	GameObject Bone;
    	Animator anim;
    	
    	bool gotFirstUpdate = false;
    	
    	// Use this for initialization
    	void Start () {
    		anim = GetComponent&lt;Animator&gt;();
    		Bone = GameObject.Find("CMan0010-M4-FBX/CMan0010-M3-CS_Hips/CMan0010-M3-CS_Spine/CMan0010-M3-CS_Spine1/CMan0010-M3-CS_Spine2");
    	}
    	
    	// Update is called once per frame
    	void Update () {
    		if(!photonView.isMine ) {
    			transform.position = Vector3.Lerp(transform.position, realPosition, 0.1f);
    			transform.rotation = Quaternion.Lerp(transform.rotation, realRotation, 0.1f);
    		}
    	}
    	public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) {
    		if(stream.isWriting) {
    			// This is OUR player. We need to send our actual position to the network.
    			stream.SendNext(transform.position);
    			stream.SendNext(transform.rotation);
    			stream.SendNext(Bone.transform.localEulerAngles);
    			stream.SendNext(anim.GetFloat("VerticalMove"));
    			stream.SendNext(anim.GetFloat("HorizontalMove"));
    		}
    		else {
    			// This is someone else's player. We need to receive their position (as of a few
    			// millisecond ago, and update our version of that player.
    			realPosition = (Vector3)stream.ReceiveNext();
    			realRotation = (Quaternion)stream.ReceiveNext();
    			Bone.transform.eulerAngles = (Vector3)stream.ReceiveNext();
    			anim.SetFloat("VerticalMove", (float)stream.ReceiveNext());
    			anim.SetFloat("HorizontalMove", (float)stream.ReceiveNext());
    			
    			if(gotFirstUpdate == false) {
    				transform.position = realPosition;
    				transform.rotation = realRotation;
    				gotFirstUpdate = true;
    			}
    			
    		}
    		
    	}
    }
    

    It all works, except for the bone rotation. I am not rotation the bone on other places.
  • vadim
    Options
    Why do you use Bone.transform.localEulerAngles for serialization? Can you send Bone.transform.rotation as you do for main object?
  • chaozz
    Options
    let me try! :)
  • chaozz
    Options
    Doesn't work. Neither .localRotation or .Rotation. But I am not setting the rotation, see here a snipplet of my bone bend routine, it follows the mouse Y movement and works great:
    rotationZ += Input.GetAxis("Mouse Y") * sensitivityZ;
    rotationZ = Mathf.Clamp (rotationZ, minimumZ, maximumZ);
    Bone.localEulerAngles = new Vector3(0, Bone.transform.localEulerAngles.y,rotationZ-defaultBend);
    

    it's that localEulerAngles that I need to sync over the network. but I have tried almost everything but CAN NOT get it to work :)
  • Tobias
    Options
    You are sending 1 RPC per frame. This won't work.
    You don't get 60 packages per second through our server but that's also not needed: You can send the current state and if that is different on the receiving client, that client will move the bone accordingly.

    Please do the Marco Polo Tutorial and see how we use OnPhotonSerializeView(). With the help of that method and observing a single script on the GameObject, you can send the needed values and interpolate the rest.
    http://doc.exitgames.com/en/pun/current ... marco-polo
  • chaozz
    Options
    Hi Tobias, I am well aware of the fact RPC is not the way to go, it was for testing.

    If you read two posts back you can see I already use OnPhotonSerializeView() and have added the bone rotation code. But it's not updating the bone rotation. I used the Quill18creates guides on Youtube and have been succesful so far, except for the bone :)

    Here it is again and slightly updated:
    using UnityEngine;
    using System.Collections;
    
    public class NetworkCharacter : Photon.MonoBehaviour {
    	
    	Vector3 realPosition = Vector3.zero;
    	Quaternion realRotation = Quaternion.identity;
    	Vector3 realSpinepos = Vector3.zero;
    	GameObject Bone;
    	Animator anim;
    	
    	bool gotFirstUpdate = false;
    	
    	// Use this for initialization
    	void Start () {
    		anim = GetComponent&lt;Animator&gt;();
    		Bone = transform.Find("CMan0010-M4-FBX/CMan0010-M3-CS_Hips/CMan0010-M3-CS_Spine/CMan0010-M3-CS_Spine1/CMan0010-M3-CS_Spine2").gameObject;
    	}
    	
    	// Update is called once per frame
    	void Update () {
    		if(!photonView.isMine ) {
    			transform.position = Vector3.Lerp(transform.position, realPosition, 0.1f);
    			transform.rotation = Quaternion.Lerp(transform.rotation, realRotation, 0.1f);
    			Bone.transform.localEulerAngles = Vector3.Lerp(Bone.transform.localEulerAngles, realSpinepos, 0.1f);
    		}
    	}
    	public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) {
    		if(stream.isWriting) {
    			// This is OUR player. We need to send our actual position to the network.
    			stream.SendNext(transform.position);
    			stream.SendNext(transform.rotation);
    			stream.SendNext(Bone.transform.localEulerAngles);
    			stream.SendNext(anim.GetFloat("VerticalMove"));
    			stream.SendNext(anim.GetFloat("HorizontalMove"));
    		}
    		else {
    			// This is someone else's player. We need to receive their position (as of a few
    			// millisecond ago, and update our version of that player.
    			realPosition = (Vector3)stream.ReceiveNext();
    			realRotation = (Quaternion)stream.ReceiveNext();
    			realSpinepos = (Vector3)stream.ReceiveNext();
    			anim.SetFloat("VerticalMove", (float)stream.ReceiveNext());
    			anim.SetFloat("HorizontalMove", (float)stream.ReceiveNext());
    			
    			if(gotFirstUpdate == false) {
    				transform.position = realPosition;
    				transform.rotation = realRotation;
    				Bone.transform.localEulerAngles = realSpinepos;
    				gotFirstUpdate = true;
    			}
    			
    		}
    		
    	}
    }
    
  • vadim
    Options
    Doesn't work. Neither .localRotation or .Rotation.
    Can you log bone transform before setting rotation and right after? This is only way to check if setting rotation works.
  • toreau
    Options
    Did you manage to solve this problem? I'm having the same problem; I'm unable to synchronize my player's spine bone localRotation over the network.
  • chaozz
    Options
    Yes, via serialization.
  • toreau
    Options
    Care to show your code? I'm also trying to do this inside OnPhotonSerializeView() and applying the rotation in the LastUpdate() because of the animation updates.

    Thanks in advance!
  • chaozz
    Options
    Yes, in short I ended up doing this: In mechanim, I made a float called Angle

    Then in the spinal bend code on the player I do:
    anim.SetFloat ("Angle", angle);
    

    Then in the serialization code, in isWriting:
    stream.SendNext(anim.GetFloat("Angle"));
    

    And in the receive part:
    Angle = (float)stream.ReceiveNext();
    

    And in the update of the serialization code:
    anim.SetFloat("Angle", Mathf.Lerp (anim.GetFloat ("Angle"), Angle, 0.1f));
    
  • toreau
    Options
    Ah, you misunderstand, or I wasn't clear enough.

    My problem is that I don't have a "bend back" animation, so I need to retrieve the spine bone and rotate it;
    void Start () {
    	skinnedMeshRenderer = GetComponentInChildren&lt;SkinnedMeshRenderer&gt;();
    	
    	foreach ( Transform boneTransform in skinnedMeshRenderer.bones ) {
    		if ( boneTransform.name == "Bip01 Spine1" ) { // TODO: Really?!
    			spineTransform = boneTransform;
    			break;
    		}
    	}
    	
    	if ( spineTransform == null ) {
    		Debug.LogError( "Failed to find the player's spine!" );
    	}
    }
    
    void Update () {
    	upDownRotation -= Input.GetAxis("Mouse Y") * mouseSensitivity;
    	upDownRotation  = Mathf.Clamp( upDownRotation, minUpDownRotation, maxUpDownRotation );
    }
    
    void LateUpdate () {
    	float spineUpDownRotation = -upDownRotation / 2;
    	
    	spineTransform.localRotation = Quaternion.Euler( 0, 0, spineUpDownRotation );
    }
    

    This code works just fine for the local player, but I'm unable to synchronize it over the network;
    void OnPhotonSerializeView ( PhotonStream stream, PhotonMessageInfo info ) {
    
    	// Our player?
    	if ( stream.isWriting ) {
    		stream.SendNext( transform.position );
    		stream.SendNext( transform.rotation );
    		stream.SendNext( playerAnimator.GetFloat("Speed") );
    		stream.SendNext( SpineController.spineTransform.localRotation );
    	}
    	else {
    		realPosition = (Vector3)stream.ReceiveNext();
    		realRotation = (Quaternion)stream.ReceiveNext();
    		playerAnimator.SetFloat( "Speed", (float)stream.ReceiveNext() );
    		SpineController.spineTransform.localRotation = (Quaternion)stream.ReceiveNext();
    	}
    
    }
    

    This simply doesn't work. No error messages or anything...
  • chaozz
    Options
    Read my previous post. Don't send the localrotation directly. Use a float in Mechanim.
  • toreau
    Options
    ...and where/how should I use this float?
  • chaozz
    Options
    The moment you set/change the angle on your local player. And how is in the first snippet of code I gave you.
  • toreau
    Options
    But - this doesn't have anything to do with the animations...?
  • chaozz
    Options
    Then I suggest you ignore my approach and figure out a better way. This worked for me. :)
  • toreau
    Options
    But did your animation(s) include looking up/down? :) I have to "manually" adjust the player model's spine bone.
  • chaozz
    Options
    No, I am manually adjusting the spine according to the Y-movement of the mouse.
  • toreau
    Options
    Can you show me the code where you actually adjust the rotation/angle of your model?