Procedural 데모
소개
이 데모에서는 절차적인 세계 생성 처리와 변경사항이 Photon Cloud에 어떻게 적용 되는지에 대한 방법을 설명할 것 입니다. 따라서 PUN 2 패키지에는 큐브/블록으로 구성된 세계를 만들 수 있는 데모가 포함되어 있습니다. 생성 프로세스 자체를 위해 다른 세계를 생성하기 위해 선택할 수 있는 여러 방법이 있습니다.
이 문서 페이지에서는 데모 및 세계의 생성 방식과 수정 사항들이 클라이언트로 어떻게 동기화하는지에 하는 방법을 설명합니다. 또한 Photon Cloud를 사용하여 절차적으로 생성된 세계를 생성할 때 발생하는 가장 일반적으로 많이 하는 실수들 몇 가지를 보여 줍니다.
데모의 동작 방식
룸에 연결될 때 MasterClient는 컨트롤 패널을 사용하여 결정론적 월드 제너레이터를 제어할 수 있습니다. 컨트롤 패널 및 관련 옵션은 다음 장에 설명되어 있습니다. 월드 제너레이터가 실행 중일 때 각각 세계를 형성하는 여러 블록이 포함된 여러 클러스터를 생성합니다. 월드를 서로 다른 클러스터로 분할하면 나중에 수정 사항이 적용될 때 월드를 동기화하는 데 도움이 됩니다. 동기화의 동작 방식도 이 페이지에서 설명됩니다. 월드에 수정 사항을 적용하기위해, 모든 클라이언트가 특정 블록을 마우스 왼쪽 버튼으로 클릭하여 높이를 낮추거나 마우스 오른쪽 버튼을 클릭하여 높이를 높일 수 있습니다.
지금은 여러분이 블록의 높이를 낮추거나 올리는 것에 대해 궁금해 할 수 있습니다. Y축 상의 블록 스케일을 사용하여 생성된 세계의 높이 수준을 설명하기로 했습니다. 이것은 몇 가지 장점이 있습니다. 첫 번째는 GameObjects가 씬에 많이 없어 성능 면에서 좋다는 것입니다. 유니티는 분명히 거의 무한개의 객체를 다룰 수 없습니다. 이 데모에서는 구현에 문제가 없습니다. 또 다른 측면은 적용된 수정 사항을 모든 클라이언트가 사용할 수 있도록 어떻게든 저장해야 한다는 것입니다. Photon Cloud에서 사용 가능한 커스터 서버측 로직이 없기 때문에( 기업용 Cloud를 사용하지 않는 한), 적용된 수정 사항을 저장하는 데 사용자 지정 룸 속성을 사용하기로 결정했습니다. 마지막으로, 구현된 솔루션은 더 복잡하고 ‘생산 전용’ 솔루션보다 이해하기 쉬울 수 있습니다. 하지만 이 데모에서는 절차적으로 생성된 게임을 개발할 때 Photon Cloud에서 어떤 것을 사용할 수 있는지에 대한 것을 보여 줍니다.
세계 생성
데모를 시작할 때 게임 창의 왼쪽 상단 모서리에 있는 제어판을 볼 수 있습니다. 이 패널은 World Generator를 제어하기 때문에 MasterClient에서만 사용할 수 있습니다. 이 데모에서는 세계 생성기에 영향을 미치는 네 가지 옵션을 사용할 수 있습니다.
이러한 옵션 중 하나는 이 데모에서 숫자로 표시되는 시드입니다. 시드에는 최소 한 자릿수가 필요하며 최대 10자리까지 포함될 수 있으므로 0에서 999,999,999 사이의 값 입니다.
또 다른 옵션은 세계의 전체 크기를 설명합니다. 이 데모에서는 16 x 16 블록에서 128 x 128 블록까지의 크기를 가진 월드를 만들 수 있습니다. 이 데모에서는 생성된 세계는 한정적 입니다.
세 번째 옵션은 클러스터 크기입니다. 클러스터에는 최대 64개의 블록이 포함될 수 있습니다. 생성된 클러스터 수는 이 값과 앞서 언급한 세계 크기에 따라 달라집니다. 블록 수가 많은 클러스터를 구축하면 (당사의 구현으로 인해) 세계 생성 속도가 빨라집니다.
다른 옵션은 생성된 수준의 모양에 영향을 미치는 세계의 유형입니다. 데모에는 세 가지 옵션이 포함되어 있으며, 주로 생성 프로세스 중에 블록의 최대 높이에 영향을 미칩니다.
MasterClient가 제어판에서 확인 버튼을 클릭할 때마다 선택한 옵션이 사용자 지정 룸 속성에 저장되어 동일한 룸의 모든 클라이언트가 사용할 수 있게 됩니다. 클라이언트가 이러한 업데이트를 수신할 때마다 새로운 세계를 생성하기 위해 월드 제너레이터를 로컬로 다시 실행합니다(재실행). 이러한 옵션은 동기화되며 임의 기능을 사용할 수 없으므로 각 클라이언트는 동일한 월드를 생성합니다.
세계를 생성하기 위해, 우리는 Simplex Noise 알고리즘을 사용합니다. 이를 위해 각 인스턴스 블록의 x- 및 z- 좌표를 CalcPixel2D 함수로 전달합니다. Benjamin Ward의 Simplex Noise 구현은 2D 기능 외에도 1D 및 3D 기능도 제공합니다. 각각의 기능은 두 가지 중요한 측면을 공통으로 가지고 있습니다. 첫째, 이러한 기능은 항상 -1.0f와 1.0f 사이의 간격으로 값을 반환합니다. 각 블록의 높이(y-scale)를 계산하는 데 이 값을 사용합니다. 높이는 주로 이 값과 사용된 세계 유형에 따라 달라집니다. 두 번째 측면은 입력이 동일하게 유지되는 한 이 기능의 출력은 항상 동일하다는 것입니다. 즉, 입력 파라미터를 변경할 때 다른 결과가 나타납니다(예: 시드). 이것이 주로 우리가 사용한 시드와 월드 제너레이터의 다른 옵션을 동기화해야 하는 이유입니다.
변경사항 동기화
앞서 언급한 바와 같이, 세계에 적용되는 수정사항은 사용자 정의 룸 속성에 저장됩니다. 이는 한 가지 주요 이점이 있습니다. 각 클라이언트는 모든 수정 사항을 포함하여 최신 사용자 지정 룸 속성을 자동으로 수신합니다. 클라이언트측에서만 처리하면 됩니다. 이것은 또한 나중에 입장한 클라이언트의 상황을 더 쉽게 만들 수 있습니다. 왜냐하면 우리는 그들과 세계의 최신 상태를 공유할 수 있는 방법을 생각할 필요가 없기 때문입니다. 이것은 단순하며 ‘자동적으로’ 일어납니다.
수정된 세계 데이터를 사용자 지정 룸 속성에 저장하기 위해 각 클러스터는 고유한 식별자와 딕셔너리를 추가합니다. 딕셔너리에는 특정 블록의 고유 식별자와 관련 높이(y-scale)가 포함되어 있습니다. 여기서 중요한 점은 수정된 블록만 사용자 정의 룸 속성에 저장된다는 것입니다. 수정되지 않은 블록은 앞서 설명한 월드 제너레이터 설정에 의해 계속 설명되므로 여기에 저장할 필요가 없습니다.
이 데모에서는 완벽하게 작동합니다. 예를 들어 ‘무한’ 월드로 더 큰 규모의 게임을 만들려면 Enterprise Cloud 또는 자체적으로 Photon Server를 호스팅해야 합니다. 이러한 두 가지 옵션을 사용하면 플러그인을 사용하거나 서버 어플리케이션을 단독으로 구현하여 사용자 지정 서버 측 로직을 실행할 수 있습니다. 게임 입장시 최대 세계 크기나 로딩 시간 등의 제한 사항을 우회할 수 있기 때문에 게임에 필수적일 수 있습니다.
가장 많이 하는 실수들
이 챕터에서는 자연적으로 생성되고 네트워크로 연결된 게임을 개발하면서 저지를 수 있는 가장 일반적인 실수를 다룹니다.
여러분이 할 수 있는 첫 번째 실수는 모든 것을 ‘네트워크 인스턴스화’를 하려는 것입니다. 여러분이 몇 개의 벽으로 이루어진 미로를 만들고 싶다고 상상해보세요. PUN의 경우, 이 벽에 대한 프리팹를 작성하고, 여기에 PhotonView 컴포넌트를 추가하여 Unity Editor와 작업하는 동안 직접 씬에 배치하거나 PhotonNetwork를 사용하여 런타임에 인스턴스화하는 것이 쉬운 방법입니다. Instantiate 또는 PhotonNetwork를 사용합니다. InstantiateSceneObject입니다. 이 작업은 실제로 특정 개체의 양에 대해 작동할 수 있지만 권장되지 않는 한 가지 이유는 클라이언트당 ViewID가 제한이 있기 때문입니다. 이 제한은 씬 객체뿐만 아니라 사용자에게도 적용됩니다. 이러한 제한으로 인해 생성된 미로는 크기나 복잡성에 의해 제한될 수도 있습니다.
또 다른 일반적인 오류는 각 클라이언트에서 Unity 또는 System 네임스페이스의 랜덤 클래스를 개별적으로 사용하는 것입니다. 랜덤 클래스의 문제는 동일한 시드를 사용하지 않는 한 서로 다른 시스템에서 다른 결과를 얻을 수 있다는 것입니다. 그 결과, 여러 클라이언트가 서로 다른 세계를 생성하게 되는데, 이는 멀티플레이어 게임 측면에서 정말 좋지 않습니다. 지금도 동기화된 시드와 함께 랜덤 클래스를 사용하는 것에 대해 생각한다면, 시각적으로 만족하는 결과를 얻지 못할 가능성이 높다는 또 다른 큰 단점이 있습니다. 설명한 대로 노이즈 알고리즘은 노이즈 생성 설정에 따라 서로 다른 높이 레벨 간에 전환되는 일종의 높이 맵을 생성합니다. 랜덤 클래스를 사용할 때 다른 높이 레벨 간에 좋은 전환이 없을 가능성이 높습니다. 대신 시각적으로 실망스러운 결과로 끝나는 패치워크가 많을 것입니다.
사용자지정 룸 속성을 사용하여 일부 데이터를 저장하는 접근 방식을 보았으므로 생성된 세계를 완전히 저장하는 데 이러한 데이터를 사용하는 것이 좋습니다. 세계가 너무 커지면 어느 정도 효과가 있을지도 모릅니다. 그러나 문제는 많은 데이터가 클라이언트로 전송되어야 하기 때문에 룸에 가입하는 데 시간이 오래 걸린다는 것입니다. 이 경우에 대한 해결책은 서버측 로직을 추가하여 서버가 클라이언트로 전송해야 하는 데이터를 결정할 수 있도록 하는 것입니다. 즉, 서버는 전 세계 상태를 한 번에 전송하는 대신, 현재 클라이언트가 필요로 하는 것만 전송하고 나중에 필요할 때 업데이트할 것입니다.
Back to top