This document is about: QUANTUM 2
SWITCH TO

자주 문의하는 질문

대부분의 FAQ 내용은 #quantum-sdk-v2#beginner-questions 디스코드 채팅에서 개발자가 문의하셨던 질문들을 모아놓은 것입니다. 또한 더 많은 도움이 필요하시면 검색 기능을 사용하십시오.

Quantum 유니티 프레임워크

유니티 프로젝트 이름은 어떻게 변경하나요?

  • 단계 1: quantum_unity 폴더 이름을 원하는 이름으로 변경합니다.
  • 단계 2: quantum.code.csproj 에서 사전 빌드 이벤트용 경로를 단계 1에서 선택한 이름으로 편집합니다. 가장 쉬운 방법은 텍스트 편집기로 .csproj 파일을 열고 quantum_unity를 새 이름으로 변경하는 찾기 및 바꾸기를 실행하는 것입니다.

데모 메뉴에서 게임을 시작할 때 게임 씬이 로드되지 않는 이유는 무엇입니까?

서버에서 AppId를 거부하면 아직은 적절한 오류 메시지가 표시되지 않습니다. AppId를 올바르게 생성했는지 확인해 주십시오:

  1. Photon 관리 화면에서 Quantum App Id를 생성합니다.
  2. 새로운 앱 생성을 클릭하세요.
  3. Photon 유형을 Photon Quantum으로 설정합니다.
  4. 이름 필드를 입력합니다.
  5. 아래로 스크롤 하여 CREATE를 누릅니다.

Quantum 게임이 시작되기 전에 왜 1초가 지연되나요?

Deterministic Config에서 Room Wait Time (seconds)를 확인하세요. 이곳에서 수정할 수 있습니다. Room Wait Time은 변동하는 핑 시간을 처리하는 데 사용됩니다. 시작 시 모든 클라이언트 세션을 동기화하는 데 항상 완전히 사용됩니다. 1초로 설정하면 항상 1초 동안 기다립니다.

주의: 씬 로딩 시간을 동기화하는 데 사용하면 안 됩니다! 이를 조정하려면 Quantum 세션을 시작하기 전에 씬을 로드하고 Photon Realtime을 통해 직접 조정합니다. 이 시나리오에서는 값을 0으로 안전하게 설정할 수 있습니다.

ns.exitgames.com에 연결하는 동안 연결 시간이 초과되는 이유는 무엇입니까?

연결 로그의 상세 내용을 보기 위해서 PhotonServerSettings에서 로그 레벨을 Network Logging으로 높일 수 있습니다. UDP 트래픽이 차단되어 종종 타임아웃이 발생됩니다. 상황분석 방법은 Photon Realtime 문서: Analyzing Disconnects 연결 해제 분석하기 페이지를 참고하세요.

NetworkPeer 클래스에서 처리되지 않은 이벤트 경고를 받는 이유는 무엇인가요?

이벤트 102 그리고 103.

이 이벤트들은 Session.Join() 호출전에 수신된 클럭 동기화 및 입력 메시지입니다. 시뮬레이션에 참여한 후 클라이언트가 관련 정보를 다시 받게 됩니다.

기본적으로 이미 룸에 있으므로 실행 중인 플러그인 세션은 로컬 세션을 시작하기 전부터 메시지를 전송합니다.

Quantum 솔루션을 유니티 프로젝트에 통합할 수 있습니까?

Quantum 2.1 이후 버전, 유니티 2019.4에서 가능합니다.

단계별 가이드가 포함된 READMEtools\codeintegration_unity 폴더의 SDK에 포함되어 있습니다.

중요: 이것은 단방향 변환입니다!

멈춘 게임을 어떻게 디버깅을 할 수 있나요?

Adding the QUANTUM_STALL_WATCHER_ENABLED 정의를 Project Settings > Player > Scripting Define Symbols에 추가하면 감시자 스크립트가 업데이트 루프를 감시할 스레드를 스핀업할 수 있습니다. 중지가 감지되면(즉, 업데이트를 다시 호출하는 데 X초 이상 소요됨) 충돌이 발생합니다. 생성된 충돌에서 모든 스레드의 호출 스택이 실행되어야 하므로 디버깅이 시뮬레이션에서 중지될 때 유용합니다.

편집 모드에서 Quantum AssetRefs를 사용할 수 없는 이유는 무엇인가요?

AssetRef following전에 UnityDB.Init()을 호출하세요. 여러 번 안전하게 호출할 수 있으며 게임이 실행되지 않을 때도 호출할 수 있습니다. 링크에서 인스턴스 getter를 사용하면 링크에 저장된 GUID를 사용하여 Quantum DB에서 Quantum 에셋을 검색하려고 합니다.

유니티 편집기에서 게임을 실행할 때 네트워크 지연 시간을 시뮬레이션하는 효과적인 방법이 있습니까?

Quantum 성능 프로파일러에는 지연 시뮬레이션이 적재되어 있습니다.

여기에서 다운로드: 애드온 | 프로파일러

또는 Clumsy (Windows)와 같은 외부 네트워크 조절 도구를 사용하여 게임 서버 포트를 필터링합니다:

  • UDP 5056
  • TCP 4531

Unknown

Clumsy Filter: (udp.DstPort == 5056 or udp.SrcPort == 5056) or (tcp.DstPort == 4531 or tcp.SrcPort == 4531)

중단점을 일시 중지하거나 디버깅한 후 게임 시뮬레이션이 더 빠르게 실행되는 이유는 무엇입니까?

기본적으로 시간은 내부적으로 측정되며 시뮬레이션 중지를 보상하지 않습니다. SimulationConfig의 DeltaTimeTypeEngineDeltaTime으로 변경될 때 게임 플레이는 일시 중지된 후 정상 속도로 재개됩니다. Caveat은 다음과 같은 이 설정을 변경하면 모든 클라이언트가 이 설정을 사용하게 되며 디버깅에만 사용할 경우 바람직하지 않을 수 있습니다. 카메라 컨트롤이 매우 민감한 일부 게임(예: 비행 시뮬레이션)은 EngineDeltaTime으로 설정하면 이익을 얻을 수 있습니다.

C#

public enum SimulationUpdateTime {
    Default = 0,                        // internal clock
    EngineDeltaTime = 1,                // Time.deltaTime (Unity)
    EngineUnscaledDeltaTime = 2         // Time.unscaledDeltaTime
}

유니티에서 수정해야할 부분입니다 (포럼 게시물 참고: forum.unity.com/threads/nav-generating-inside-non-walkable-objects)

NavmeshModifierVolume을 사용한 해결 방법은 이 문제를 완화합니다 (필요 NavMeshComponents).

불러오기 단계 중 트라이앵글 컬링은 우리가 미래에 제공할 수 있는 또 다른 대안입니다.

navmesh island
### 씬 로드가 너무 오래 걸리는 경우 게임이 시간 초과 연결 해제되는 이유는 무엇입니까?

유니티 씬을 로드할 때 LoadSceneAsync로 로드하더라도 씬의 크기와 복잡성에 따라 메인 스레드가 한동안 멈출 수 있습니다. 그러면 게임이 중지된 동안 통신이 이루어지지 않기 때문에 시간 초과로 인한 연결 끊김 오류가 발생할 수 있습니다.

이를 방지하기 위해 ConnectionHandler 클래스에 제공된 API 중 일부를 사용할 수 있습니다. 다음은 설정 및 사용 방법에 대한 단계별 설명입니다:

  • ConnectionHandler 컴포넌트가 있는 Game Object가 있는지 확인합니다. 없는 경우 하나를 추가하세요.

  • 콤퍼넌트에서 연결 유지 시간을 늘리는 데 사용할 수 있는 KeepAliveInBackground 필드를 볼 수 있습니다. 값은 밀리초 단위입니다.

  • 이제 UIMain에 정적 getter가 있는 QuantumLoadBalancingClient가 무엇인지 알려주어야 합니다(기본적으로 Quantum과 함께 제공). 이렇게 하면 StartFallbackSendAckThread를 시작할 수 있습니다. 다음은 이를 수행하는 방법에 대한 샘플 코드입니다:

C#

    // Before starting loading the scene
    if (_connectionHandler != null)
    {
      _connectionHandler.Client = UIMain.Client;
      _connectionHandler.StartFallbackSendAckThread();
    }

Quantum에서 시뮬레이션 개발

PhotonQuantum-Documentation.chm 을 읽을 수 없는 이유는 무엇인가요?

윈도우즈 에서 SDK 압축 파일을 받으신 후 파일 차단 해제를 해 줘야합니다: 우측 클릭 > 속성 > 차단 해제 확인 > OK

시뮬레이션은 왜 프레임 60부터 시작하나요?

시뮬레이션 시작 시 할당된 롤백 가능한 프레임의 수입니다. 이 숫자는 DeterministicConfig->Rollback 윈도우에 설정된 값과 일치합니다.

동일한 프레임 번호에서 Update()가 왜 여러 번 호출되나요?

롤백 시 동일한 프레임 번호에 대해 시스템에서 Update()를 여러 번 호출합니다. 이 사항은 원격 플레이어 입력의 예측이 잘못된 것으로 감지되고 시뮬레이션이 결정론적 상태로 돌아가기 위해 올바른 입력 데이터가 있는 프레임을 다시 실행해야 하는 경우에 발생합니다.

Visual Studio에서 포인터를 디버깅하려면 어떻게 해야 합니까? 주소만 보입니다.

  • 유니티 2018 그리고 Visual Studio 2017가 필요합니다.
  • 유니티 프로젝트의 Scripting Runtime Version.NET 4.x Equivalent으로 설정합니다.
  • Quantum dlls 을 Debug dlls로 빌드 했는지 확인해 주십시오.
  • VS 메뉴 Debug->Attach Unity Debugger 메뉴를 통해 Visual Studio를 Unity에 연결하고 올바른 Unity Editor 인스턴스를 선택합니다.
  • VS 및 Unity Editor의 모든 인스턴스를 닫고 VS를 시작하고 Quantum dll을 다시 구성한 다음 Unity를 시작하고 다시 로드한 다음 Debugger를 연결합니다.

편집기를 유니티(VS 2019)에 어떻게 연결합니까?

최신 유니티 편집기는 PDB와 직접 작동하지만 이전에 생성된 MDB는 삭제하지 않습니다. 강제로 변경하고 문제를 해결하려면 다음 단계를 수행합니다:

  1. 출력 디버그 정보를 포터블로 변경합니다. (마우스 오른쪽 버튼으로 Quantum.Code project->Open Properties->Build->Advanced를 클릭합니다.)
  2. 유니티에서 이전 quantum dlls mdb 및 pdb 파일을 삭제합니다. (Assets/Photon/Quantum/Assemblies)
  3. Quantum 프로젝트를 구축합니다.
  4. 유니티는 이제 포터블 pdb에서 mdb 파일을 재구성해야 하며 작동해야 합니다.

FP.MaxValue 와 FP.UseableMax 의 차이점은 무엇입니까?

고정 소수점 산술은 64비트 값의 16+16비트만 사용합니다. 이렇게 하면 오버플로를 확인할 필요가 없기 때문에 연산의 일부가 더 빨리 만들어집니다. 다시 말하면: FP.MinValueFP.MaxValue는 64비트를 모두 사용하고 있으므로 절대로 계산에 사용하면 안 됩니다. 대신 FP.UseableMaxFP.UseableMin를 사용합니다(예를 들어 최소 FP 값으로 거리 변수를 초기화합니다).

주의: FP는 -32,768 에서 32,768 까지의 값을 나타낼 수 있습니다 (-2¹⁵ 부터 2¹⁵ 까지).

새 구조체를 가리키는 포인터가 오래된 데이터를 가리키는 이유는 무엇입니까?

루프 내부에서 구조물에 대한 포인터는 동일한 스택 포인터를 가지며 new 또는 default를 사용하지 않는 경우 오래된 데이터가 포함됩니다.

C#

struct Bar {
    public bool Foo;
}

static unsafe void Main(string[] args) {
    for (int i = 0; i < 2; i++) {

        Bar bar;
        //Bar bar = default(Bar); // <---- Fixes the stale data
       
        Bar* barPt = &bar;
        if (barPt->Foo)
            Console.WriteLine("Stuff and Things");
  
        barPt->Foo = true;
    }

    Console.ReadKey();
}

시뮬레이션이 동기화되지 않는 이유는 무엇입니까?

DeterministicConfig.ChecksumInterval 이 > 0 일 때 확인된 프레임의 체크섬이 계산되어 서버로 전송되고 다른 클라이언트가 전송한 체크 섬과 비교됩니다.

대부분의 원인은 다음과 같습니다:

Quantum 데이터 에셋에 쓰는 것

C#

c->CharacterSpec = DB.FindAsset<CharacterSpec>("WhiteFacedBarghast");
c->CharacterSpec.RemainigLifetime = 21;

절대로 Quantum 에셋에 아무것도 쓰지 마세요. 여기에는 읽기 전용 데이터가 포함되어 있습니다.

유니티 쓰레드에서 Quantum에 쓰기

Unity의 모든 스크립트는 Quantum 프레임을 통해 노출되는 모든 항목에 읽기 전용 접근 권한을 가집니다. 입력 및/또는 명령을 통해서만 시뮬레이션에 영향을 미칩니다.

데이터 캐싱

다음 코드는 시뮬레이션이 롤백 되었을 때 점차적으로 비동기화하는 것을 보여줍니다.

C#

public class CleaningSystem : SystemBase {
    public Boolean HasShoweredToday;    // <----- Error
    public override void Update(Frame f) {
        if (!HasShoweredToday && f.Global->ElapsedTime > 100) {
            Shower();
            HasShoweredToday = true;
        }
    }
}

대신 프레임 또는 엔티티 컴포넌트에 비투명 데이터를 저장합니다.

C#

// Frame
unsafe partial class Frame {
    public Boolean HasShoweredToday;
    partial void CopyFromUser(Frame frame) {
        // Implement copy of the custom parameters.
    }
}

public class CleaningSystem : SystemBase {
    public override void Update(Frame f) {
        if (!f.HasShoweredToday && f.Global->ElapsedTime > 100) {
            Shower();
            f.HasShoweredToday = true;
        }
    }
}

Frame.User.cs 사용 방법 예제를 읽어보세요.

부동 소수점 대수

시뮬레이션 내부의 부동액 사용을 자제하고 FP 수학만 사용합니다.

또한 FP.FromFloat_UNSAFE()를 주의해서 다루십시오. 한 시스템에서 자산 생성의 밸런스를 조정하는 데 사용되는 "오프라인"은 괜찮습니다. 하지만 이렇게 하면 서로 다른 플랫폼에서 서로 다른 결과를 얻을 수 있습니다. 런타임 중에 부동 소수점을 가져와야 하며 정수 또는 FP를 사용할 수 없는 경우입니다.(예 밸런싱 데이터 다운로드): String 에서 FP로 변환하십시오.

Asset.Loaded() 동안 생성된 데이터

로딩하는 동안 Assets.Loaded()은 에셋 별로 한 번 호출됩니다. 이때 에셋 멤버 내부에 새 데이터를 저장, 계산 및 저장하는 것은 전혀 문제가 없습니다. - 중요: 서버에서 시뮬레이션을 수행하고 있는 경우, 모든 게임은 이 하나의 에셋을 공유합니다.

리소스에서 에셋을 로드한 상태에서 유니티 편집기를 다시 시작하거나 런타임에 유니티 DB를 재설정하는 경우 유니티에서 유니티 에셋을 언로드하지 않습니다.

C#

public partial class FooAsset : AssetBase {
  public Foo Settings;
  public int RawData;
  
  [NonSerialized]
  public List<int> Bar = new List<int>();

  public override AssetObject AssetObject => Settings;

  public override void Loaded() {
    // This will break on the second run (see above) because Bar needs to be reset by either Bar.Clear() or Bar = new List<int>()
    Bar.Add(RawData);
  }
}

동일한 클라이언트에서 새로운 Quantum 세션을 위해 Photon 룸을 재사용할 수 있나요?

아니오. 권장되고 깔끔한 해결 방법은 룸을 바꾸는 것입니다.

하지만 플레이어(또는 플레이어의 일부)를 룸에 두고 시뮬레이션을 소프트 코딩할 수 있습니다. 이것이 Stumble Guys가 세 스테이지 사이에서 진행하는 방법입니다.

  • 게임 라운드가 종료된 경우에도 Quantum 세션을 계속 실행합니다.
  • 게임 라운드 시작을 처리하는 게임 플레이 시스템(예: 게임 상태 기계)에 코드를 추가합니다.
  • 결정적으로 Quantum 시스템을 비활성화하거나 새 Quantum 맵을 로드합니다.
  • 모든 Quantum 엔티티를 재설정 또는 파괴하고 게임 뷰를 재설정합니다.
  • 라운드에서 진 플레이어들은 경기를 관전하기 위해 연결을 유지할 수 있지만 더 이상 경기에 영향을 미칠 수 없습니다.

선택적으로 모든 플레이어는 룸을 바꿀 수 있습니다. 여기에는 서버 전환이 수반되고 분산 시스템에서는 많은 작업이 실패할 수 있으므로 시뮬레이션을 소프트 재시작하고 룸 전환만 최후의 수단으로 사용하는 것이 좋습니다.

  • (예: Photon 룸 또는 Quantum 명령)을 사용하여 클라이언트 간에 새 룸의 ID를 공유합니다. 또는 프로그래밍적으로/결정적으로 새 룸을 작성합니다(이를 쉽게 추측할 수 없도록 주의하십시오).
  • 모든 클라이언트가 Quantum 세션을 중지하고 LeaveRoom()을 실행하지만 연결이 끊어지지 않습니다.
  • 모든 클라이언트는 JoinOrCreate()를 사용하여 새 룸에 연결합니다.
  • 클라이언트가 프로세스 중에 앱을 다시 시작하는 경우, 기타 연결 오류, 참가자 대기 등과 같은 연결 문제를 완화합니다

Photon Realtime 매치메이킹 지침서

Quantum 에셋에서 DSL 생성 유니온 구조체를 사용할 수 있습니까?

직접적으로 사용할 수 없습니다. 유니티는 중첩 필드를 지원하지 않습니다.

대신 <UnionName>_Prototype은 유니티-직렬화가 가능하고 drawer가 있어 사용할 수 있습니다.

union-구조체로 변환은 다음과 같습니다:

C#

UnionName result = default;
prototype.Materialize(frame, ref result, default);
Back to top