4 - 물리
개요
Fusion 104는 Fusion이 서버 권한이 있는 게임에서 PhysX와 어떻게 상호작용하는지 조사할 것입니다.
이 섹션의 끝에서 프로젝트를 통해 플레이어는 물리 제어된 볼을 생성하고 상호 작용할 수 있습니다.
설정
기본적으로 Fusion은 호스트에서 물리 시뮬레이션을 실행하고 클라이언트는 따라갑니다. 그러나 이렇게 하면 물리학 객체가 로컬 예측 객체와 다른 시간에 놓이게 되고, 이들 객체가 충돌할 경우 물리학 시뮬레이션에 문제가 발생합니다.
듀얼 타임은 다양한 방식으로 다룰 수 있는 복잡한 주제입니다 - 일부는 완전히 해킹이고, 다른 것은 일반적으로 적용하기에는 너무 비싸거나 게이머들이 기대하는 즉각적인 피드백을 제공하지 않습니다. 불행히도 모든 경우에 효과가 있는 간단한 해결책은 없습니다.
이 자습서에서는 Fusion의 물리 애드온을 사용하여 물리 객체를 로컬로 예측하고 플레이어가 제어하는 아바타와 동시에 물리 제어 객체를 배치합니다. 이는 프로덕션에도 사용할 수 있는 견고한 솔루션이지만 PhysX에서 예측 및 재시뮬레이션을 실행하려면 비용이 많이 듭니다(즉, 틱당 여러 번).
물리 추가 기능을 가져오려면 다운로드 페이지로 이동하고 유니티 패키지를 선택한 후 프로젝트로 가져옵니다.
물리 객체
네트워크화된 PhysX 제어 객체의 프리팹은 일반 유니티 PhysX 객체와 동일한 Rigidbody
를 사용하지만 NetworkRigidbody
라는 시각적 하위 객체를 동기화하기 위해 Fusion 컴포넌트가 다릅니다. 이는 네트워킹에 관한 한 NetworkTransform
을 대체합니다.
- 유니티 편집기에서 빈 게임 객체를 새로 만듭니다
GameObject
이름을PhysxBall
로 변경- 새로운
NetworkRigidbody3D
컴포넌트를 추가합니다. - Fusion에는
NetworkObject
컴포넌트가 누락되었다는 경고가 표시되므로Add Network Object
를 눌러 주십시오. Fusion은 일반 유니티 RigidBody도 PhysX 시뮬레이션에 필요하므로 자동으로 추가합니다. PhysxBall
의 자식에 구 추가- 모든 방향에서 크기를 0.2로 축소합니다.
- 자식 객체를 부모 객체의
NetworkRigidbody3D
컴포넌트의InterpolationTarget
으로 드래그합니다. - 구에서 콜라이더 제거
- 반지름이 0.1인 부모 개체에 새 구체 충돌기를 만들어 자식 개체를 완전히 덮습니다.
- 게임 객체에 새로운 스크립트를 추가하고
PhysxBall.cs
이라고 이름을 부여합니다. - 전체 객체를 프로젝트 폴더로 끌어다 놓고 프리팹을 만듭니다
- 씬을 저장하여 네트워크 객체를 베이크하고 씬에서 프리팹 인스턴스를 삭제합니다.
물리 객체의 경우 시각적인 것을 분리하여 보간 대상
으로 지정하는 것이 권장되지만 반드시 필요한 것은 아닙니다. 충돌체와 강체는 NetworkRigidbody와 함께 부모 객체에 있어야 합니다.
PhysxBall 스크립트
이 볼은 PhysX가 구동하고 NetworkRigidbody가 네트워크로 연결된 데이터를 처리하기 때문에 작동하는 데 필요한 코드가 운동학적 관계보다 적습니다. PhysxBall.cs
에 추가해야 할 것은 몇 초 후 볼이 사라지는 타이머(운동학적 볼의 경우와 정확히 같습니다)와 초기 정방향 속도를 설정하는 방법뿐입니다.
둘 다 다음과 같은 Init()
메소드로 다룹니다:
C#
using UnityEngine;
using Fusion;
public class PhysxBall : NetworkBehaviour
{
[Networked] private TickTimer life { get; set; }
public void Init(Vector3 forward)
{
life = TickTimer.CreateFromSeconds(Runner, 5.0f);
GetComponent<Rigidbody>().velocity = forward;
}
public override void FixedUpdateNetwork()
{
if(life.Expired(Runner))
Runner.Despawn(Object);
}
}
입력
공을 생성하려면 운동학적 공과 동일한 세 단계에 따라 코드를 확장해야 하지만 대신 두 번째 마우스 버튼을 사용하도록 변경해야 합니다:
1. NetworkInputData
NetworkInputData.cs
에서 간단히 새로운 버튼 플래그를 추가합니다:
C#
using Fusion;
using UnityEngine;
public struct NetworkInputData : INetworkInput
{
public const byte MOUSEBUTTON0 = 1;
public const byte MOUSEBUTTON1 = 2;
public NetworkButtons buttons;
public Vector3 direction;
}
2. BasicSpawner
BasicSpawner.cs
에서 첫 번째와 같은 방법으로 두 번째 마우스 버튼을 폴링하고 그에 따라 플래그를 설정합니다:
C#
private bool _mouseButton0;
private bool _mouseButton1;
private void Update()
{
_mouseButton0 = _mouseButton0 || Input.GetMouseButton(0);
_mouseButton1 = _mouseButton1 || Input.GetMouseButton(1);
}
public void OnInput(NetworkRunner runner, NetworkInput input)
{
var data = new NetworkInputData();
...
data.buttons.Set(NetworkInputData.MOUSEBUTTON0, _mouseButton0);
_mouseButton0 = false;
data.buttons.Set(NetworkInputData.MOUSEBUTTON1, _mouseButton1);
_mouseButton1 = false;
input.Set(data);
}
3. Player
Player.cs
는 실제 볼 프리팹을 생성하는 코드를 보유하고 있어 이와 같은 프리팹 참조가 필요할 뿐만 아니라,
C#
[SerializeField] private PhysxBall _prefabPhysxBall;
Player
는 앞에서 작성한 Init()
메소드를 사용하여 스폰을 호출하고 속도(마지막 순방향에 곱한 상수)를 설정해야 합니다.
C#
public override void FixedUpdateNetwork()
{
if (GetInput(out NetworkInputData data))
{
data.direction.Normalize();
_cc.Move(5*data.direction*Runner.DeltaTime);
if (data.direction.sqrMagnitude > 0)
_forward = data.direction;
if (HasStateAuthority && delay.ExpiredOrNotRunning(Runner))
{
if (data.buttons.IsSet(NetworkInputData.MOUSEBUTTON0))
{
delay = TickTimer.CreateFromSeconds(Runner, 0.5f);
Runner.Spawn(_prefabBall,
transform.position+_forward,
Quaternion.LookRotation(_forward),
Object.InputAuthority,
(runner, o) =>
{
// Initialize the Ball before synchronizing it
o.GetComponent<Ball>().Init();
});
}
else if (data.buttons.IsSet(NetworkInputData.MOUSEBUTTON1))
{
delay = TickTimer.CreateFromSeconds(Runner, 0.5f);
Runner.Spawn(_prefabPhysxBall,
transform.position+_forward,
Quaternion.LookRotation(_forward),
Object.InputAuthority,
(runner, o) =>
{
o.GetComponent<PhysxBall>().Init( 10*_forward );
});
}
}
}
}
네트워크 물리가 러너 객체에서 RunnerSimulatePhysics3D
컴포넌트를 작동시키려면 필요합니다. 따라서 BasicSpawner.cs
을 열고 StartGame
함수에서 러너 컴포넌트를 추가한 줄 뒤에 다음 줄을 추가합니다
C#
gameObject.AddComponent<RunnerSimulatePhysics3D>();
마지막으로 새로운 공을 테스트하기 위해 생성된 프리팹을 Player
프리팹의 Prefab Physx Ball
필드에 할당하고 빌드 하여 실행합니다.