Prediction

Network Transform의 Interpolation Data Source를 Predicted로 하고 Interpolation Space를 World로 한다.

포톤 퓨전 Prediction 이해를 위한 영상

- Interpolation Target을 분리해 놓은 이유 : Render는 유니티의 Update에서 불리고 Interpolation으로 계속 그려줘야한다. 그러나 이동 입력은 FixedUpdateNetwork에서 불리기 때문에 둘의 단위가 달라서 구분해 준 것이다.

TickTimer를 설정하기 위해 NetworkedAttribute의 기본 생성자인 [Networked] 를 속성으로 부여했다.

FixedUpdateNetwork에서 TIckTimer.Expired(NetworkRunner Runner)가 다 되면 NetworkRunner.Despawn(Network Object)으로 NetworkObject를 파괴하고 안 되면 앞 쪽으로 이동하게 한다.

Init() 함수에서 TickTimer.CreateFromSeconds(NetworkRunner runner, float delayInSeconds)로 Senconds에 대해 타겟의 Tick을 생성하고 반환한다.

public class Ball : NetworkBehaviour
{
    [Networked] TickTimer Life { get; set; }

    public override void FixedUpdateNetwork()
    {
        if (Life.Expired(Runner))
        {
            Runner.Despawn(Object);
        }
        else
        {
            transform.position += 5 * transform.forward * Runner.DeltaTime;
        }
    }

    public void Init()
    {
        Life = TickTimer.CreateFromSeconds(Runner, 5.0f);
        print(Life.TargetTick);
    }
}

PhotonInstantiate 스크립트

NetworkInputData.MOUSEBUTTON1로부터 mouseButton0을 Set해준다.

public void OnInput(NetworkRunner runner, NetworkInput input)
{
    var data = new NetworkInputData();

    if (Input.GetKey(KeyCode.W))
        data.direction += Vector3.forward;
    if (Input.GetKey(KeyCode.S))
        data.direction += Vector3.back;
    if (Input.GetKey(KeyCode.A))
        data.direction += Vector3.left;
    if (Input.GetKey(KeyCode.D))
        data.direction += Vector3.right;

    if (_mouseButton0)
    {
        data.buttons |= NetworkInputData.MOUSEBUTTON1;
    }

    _mouseButton0 = false;
    
    input.Set(data);
}

마우스 좌클릭으로 Ball을 생성하는데 시간차를 두기 위해 Networked속성을 적용한 TickTimer를 생성하여 적용하였다.

public class Player : NetworkBehaviour
{
    [SerializeField] Ball _prefabBall;
    [Networked] TickTimer delay { get; set; }
    
    NetworkCharacterControllerPrototype _cc;
    Vector3 _forward;

    void Awake()
    {
        _cc = GetComponent<NetworkCharacterControllerPrototype>();
        _forward = transform.forward;
    }

    public override void FixedUpdateNetwork()
    {
        if (GetInput(out NetworkInputData data))
        {
            data.direction.Normalize();
            _cc.Move(5 * data.direction * Runner.DeltaTime);
            
            // OnInput에서 Set된 NetworkInputData.direction 값이 0보다 클 때
            if (data.direction.sqrMagnitude > 0)
            {
                _forward = data.direction;
            }

            // NetworkRunner의 TickTimer가 도달했거나 세팅되지 않았을 때 -> 생성 빈도의 제한을 거는 것 
            if (delay.ExpiredOrNotRunning(Runner))
            {
                // NetworkInputData.MOUSEBUTTON1이 0x01이니 byte인 data.button이 1이고 &연산(둘 다 1일 때)으로 1이 될 때
                if ((data.buttons & NetworkInputData.MOUSEBUTTON1) != 0)
                {
                    delay = TickTimer.CreateFromSeconds(Runner, 0.5f);
                    // NetworkRunner의 바라보는 forward만큼 앞으로, 회전은 입력의 방향으로(data.direction의 방향) 
                    // Object.InputAuthority는 누구의 입력에서 나왔는지에 대한 PlayerRef이다(PlayerID를 포함한 정보).
                    // Spawn하기 전에 파괴될 틱을 설정해주어야 하기 때문에(Life.CreateFromSeconds(NetworkRunner, int delayInSeconds))
                    // 마지막의 람다식 delegate void OnBeforeSpawned(NetworkRunner runner, NetworkObject obj)에서 Ball의 Init을 호출한다.
                    Runner.Spawn(_prefabBall, transform.position + _forward, Quaternion.LookRotation(_forward),
                        Object.InputAuthority,
                        (runner, o) =>
                        {
                            print("Lambda init");
                            o.GetComponent<Ball>().Init();
                        });
                }
            }
        }
    }
}

 

+ Recent posts