아래의 스크립트는 네트워크 환경에서 플레이어가 공격을 할 때에만, 카메라의 정면 방향으로 캐릭터의 상체가 회전하게 만들기 위해 작성한 스크립트다.
using Photon.Pun;
using UnityEngine;
using UnityEngine.Animations.Rigging;
public class FireConstraint : MonoBehaviourPun,IPunObservable
{
[Header("Set References")]
[SerializeField] public Rig rigging;
[SerializeField] public Transform fireTarget;
private PlayerController player;
private Ray ray;
private RaycastHit rayHit;
private Vector3 rayDirection = new(0.5f, 0.5f, 0);
private Vector3 hitPoint;
private float rigWeight;
private float diff;
// 네트워크 세팅
private Vector3 networkPos;
private float deltaPos;
private float interpolatePos;
void Awake()
{
player = GetComponentInParent<PlayerController>();
}
void Update()
{
if (photonView.IsMine)
{
if (player.IsFiring) //공격중이면
{
RayCastToCamera(); //카메라 정중앙에 레이캐스트
ChangeWeight(1f); // weight변경해서 애니메이션 리깅 적용
}
else
{
ChangeWeight(0f); //weight 변경해서 애니메이션 리깅 해제
}
}
}
void FixedUpdate()
{
if (!photonView.IsMine) // 포톤뷰가 본인이 아닐 경우, 타겟 위치 보간 변경
{
deltaPos = Vector3.Distance(fireTarget.position, networkPos);
interpolatePos = deltaPos * Time.deltaTime * PhotonNetwork.SerializationRate;
fireTarget.position = Vector3.MoveTowards(fireTarget.position, networkPos, interpolatePos);
}
}
private void RayCastToCamera() // 카메라 정중앙에 레이캐스트하는 함수
{
if (Camera.main != null) ray = Camera.main.ViewportPointToRay(rayDirection);
fireTarget.position = Physics.Raycast(ray, out rayHit, 50f) ? rayHit.point : ray.GetPoint(50f);
}
private void ChangeWeight(float weight) // weight 변경함수
{
rigWeight = weight;
diff = Mathf.Abs(rigging.weight - rigWeight);
if (diff < 0.05)
{
rigging.weight = rigWeight;
return;
}
rigging.weight = Mathf.Lerp(rigging.weight, rigWeight, Time.deltaTime*10);
}
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
// 1 에임 타겟 위치 수신
stream.SendNext(fireTarget.position);
// 2 현재 Rig weight 수신
stream.SendNext(rigging.weight);
}
else if (stream.IsReading)
{
// 1 에임 타겟 위치 수신
networkPos = (Vector3)stream.ReceiveNext();
// 2 현재 Rig weight 수신
rigging.weight = (float)stream.ReceiveNext();
}
}
}
ParrelSync를 통해 클론 에디터를 켜서 네트워크 테스트를 진행해 본 결과, OnPhotonSerializeView()를 자동으로 수행하지 않는 것을 발견했다.
이상하게 하이어라키 창의 게임 오브젝트에 있는 PhotonView를 클릭해서 확인 하려고 하니까 그제서야 수행이 되기 시작했다. 슈뢰딩거의 고양이인가?

PhotonView 컴포넌트의 Synchronization 설정 문제 때문이다. 기본 값은 Unreliable On Changed 인데, 이는 값에 변화가 있을 때 부정확한 동기화를 통해 함수를 수행한다는 설정이다.
즉, 포톤뷰가 값의 변화가 없다고 판단한 것이다. 정확히는 포톤뷰가 어느정도의 변화값은 무시하는 경향이 있는데, 매 프레임마다 변화값이 너무 작을 경우 전부 무시된 것이다.
약간 네트워크의 성능에 부하가 걸리게 되지만, Unreliable을 쓰게 되면 매 프레임마다 값을 전송하게 된다.
아주 작은 값의 변화라도 있으면 전송하도록 커스텀으로 코드를 수정하면 다시 쓸 수 있다.
