This document is about: QUANTUM 3
SWITCH TO

Frequently Asked Questions

Most of the FAQ content is derived from questions asked by developers in the #quantum-sdk-v2 and #beginner-questions Discord chat. Also use the search functionality there to find more help.

Quantum Unity Framework

Why is the Game scene not loading when starting the game via the demo menu?

When the AppId is rejected by the server there is no proper error message, yet. Make sure you have created your AppId correctly:

  1. Create a Quantum App Id on your Photon dashboard
  2. Click CREATE A NEW APP
  3. Set Photon Type to Photon Quantum
  4. Fill out the name field
  5. Scroll down and press CREATE

Why is there a 1 second delay before the Quantum game starts?

Check the Room Wait Time (seconds) in your Deterministic Config asset. There you can tweak it. The Room Wait Time is used to cope with fluctuating ping times.
It will always be used in full to help sync all client sessions at start. If you set it to 1 second, it will always wait the full 1 second.

N.B.: This should NOT be used to synchronize scene loading times! If you need to coordinate this, load the scene before starting the Quantum session and coordinate through Photon Realtime directly. In this scenario, the value can be set to 0 safely.

Why is the connection timing out while trying to connect to ns.exitgames.com?

To get more details increase the connection log level Network Logging in your PhotonServerSettings. Most often, the timeout is due to your UDP traffic being blocked.
For guide on how to analyze your situation, please refer to the Analyzing Disconnects page in the Photon Realtime documentation: Analyzing Disconnects

How can I debug a frozen game?

Adding the QUANTUM_STALL_WATCHER_ENABLED define to Project Settings > Player > Scripting Define Symbols will enable a watcher script to spin up a thread which will watch the Update loop. If a stall is detected (i.e. the Update takes more than X seconds to be called again), it will create a crash. This is useful when debugging freezes on the simulation as the generated crash should have the call stack from all threads running.

Is there an effective way to simulate network latency when running the game in Unity Editor?

The Quantum performance profiler has a latency simulation on board.

Download here: AddOns | Profiler

Or use an external network throttling tool for example like Clumsy (Windows) and filter out the game server ports:

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

Why is the game simulation running faster after pausing or debugging a breakpoint?

By default the time is measured internally and does not compensate for halting the simulation. When DeltaTimeType on the SimulationConfig is changed to EngineDeltaTime the gameplay will resume in regular speed after a pause. Caveat: Changing it will make every client use the setting which might be undesirable when only used for debugging. Although some games with very tight camera controls (e.g. flight simulation) will profit from setting it to EngineDeltaTime.

C#

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

Why are Navmesh islands being generated inside my geometry?

This will at some point be fixed by Unity (see forum post: forum.unity.com/threads/nav-generating-inside-non-walkable-objects)

The workaround with NavmeshModifierVolume will mitigate this (requires NavMeshComponents).

Triangle culling during the import step is another alternative which we could provide in the future.

Navmesh Island

Why does my game gets a timeout disconnection when the Scene loading takes too long?

When loading a Unity scene, even if it is done with LoadSceneAsync, the main thread can freeze for some time accordingly to the size and complexity of such scene. This can then result in a disconnect error due to timeout as the communication doesn't happen while the game is frozen.

To prevent this from happening, you can use some of the API provided in the ConnectionHandler class. Here is a step-by-step on how to setup and use it:

  • Check if there is any Game Object with the ConnectionHandler component. If there is none, please add one;

  • On the component, you'll be able to see a field named KeepAliveInBackground, which you can use to increase the time that the connection will be kept. The value is informed in milliseconds;

  • You should now inform what is the QuantumLoadBalancingClient, to which there is a static getter on UIMain, in case you use it (it comes by default with Quantum). Once you have done this, you can start the StartFallbackSendAckThread. Here is a sample snippet on how to achieve that:

C#

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

Developing A Simulation In Quantum

Why is the simulation starting with frame 60?

This is the number of rollback-able frames that are allocated at the start of the simulation. The number will match the value set up in DeterministicConfig->Rollback Window.

Why is Update() called multiple times with the same frame number?

When running Quantum online, Update() on systems are called multiple times for the same frame number in case of a rollback. This happens when the prediction of a remote players input was detected to be incorrect and the simulation has to re-run a frame with the correct input data to get back into a deterministic state.
It is possible though to check/log "frame.IsVerified" in order to check if a frame is verified. When running Quantum in offline mode, there are no duplicates due to rollbacks not happening in such situation.

What's the difference between FP.MaxValue and FP.UseableMax?

The fixed point math only uses 16+16 bits of its 64-bit value. This makes part of the math faster because we don't have to check for overflows. That said: FP.MinValue and FP.MaxValue are using all 64 bits and should never be used for calculations. Use FP.UseableMax and FP.UseableMin instead (to initialize a distance variable with the min FP value for example).

N.B.: FP can represent values from -32,768 to 32,768 (-2¹⁵ to 2¹⁵).

Why does the pointer to a new struct point to stale data?

Inside the loop the pointer to the struct gets the same stack pointer and will contain stale data if neither new or default was used.

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();
}

Why is my simulation desync-ing?

When DeterministicConfig.ChecksumInterval is > 0 a checksum of a verified frame is computed, sent to the server and compared with checksums that other clients have sent.

Most common causes are:

Writing to Quantum data assets

C#

var characterSpecAsset = frame.FindAsset<CharacterSpec>("WhiteFacedBarghast");
characterSpecAsset.RemainigLifetime = 21;

Never write anything to the Quantum assets. They contain read-only data.

Writing to Quantum from Unity thread

All scripts in Unity have read-only access to everything that is exposed through the Quantum Frame. Only influence the simulation by Input and/or Commands.

Caching data

The code snippet presented below will desync eventually when the simulation is rolled-back.

C#

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

Instead save non-transient data on the Frame or on Entity Components.

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;
        }
    }
}

Floating point math

Refrain from using floats inside the simulation and exclusively use FP math.

Handle FP.FromFloat_UNSAFE() with care. Using it "offline" for balancing asset generation on one machine is fine; however, be aware this can return different results on different platforms. If you need to import floats during run-time and cannot use integers or FPs (e.g. downloading balancing data), convert from String to FP.

Data Created During AssetObject.Loaded()

AssetObject.Loaded() is called once per asset during loading. It is totally fine to store, calculate and store new data inside the asset members at this time - N.B.: If you are running the simulation on the server, all games will share this one asset.

If your assets are loaded from Resources and you are either restarting the Unity Editor or resetting the Unity DB at runtime, be aware that Unity does not unload the Unity asset.

C#

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

  public override AssetObject AssetObject => Settings;

    public override void Loaded(IResourceManager resourceManager, Native.Allocator allocator)
    {
      base.Loaded(resourceManager, allocator);
    // 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);
  }
}

Can I reuse the Photon Room for new Quantum sessions with the same clients?

No, you should not reuse the room for another Quantum session.

But you can keep the players (or a portion of them) inside the room and soft-restart the simulation. This is how Stumble Guys progresses between with the three stages.

  • Keep the Quantum session running even if your game round has ended.
  • Add code to your gameplay systems (e.g. a game state machine) that handles the starting of a game rounds.
  • Deterministically disable Quantum systems and/or load a new Quantum map.
  • Reset or destroy all Quantum entities, reset your game view.
  • Players that lost a round can keep the connection to spectate the match but can't influence the game anymore.

Alternatively all players can switch the room. Because this involves server transitions and with distributed systems a lot of things can fail it is recommended to try to soft-restart the simulation and only use room transition as a last resort.

  • Share the id of the new room between the clients using (e.g. Photon room properties or a Quantum command). Or create the new room programmatically/deterministically (be careful that this cannot be guessed easily).
  • Every client stops the Quantum session and runs LeaveRoom() but does not disconnect.
  • All clients use JoinOrCreate() to connect to the new room.
  • Mitigate connection problems like clients restarting their app during the process, other connection errors, waiting for players to join, etc

Photon Realtime Matchmaking Guide

Can I use DSL generated union structs in Quantum assets?

Not directly. Unity does not support overlapping fields.

Instead <UnionName>_Prototype can be used as it is Unity-serializable and has a drawer.

To convert to a union-struct like this:

C#

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

과금

학생, 취미자 또는 인디 개발자를 위한 특별 제안이 있나요?

저희의 모든 제품에는 프리티어와 일회 지불하는 가격 정책이 있습니다.
또한 우리는 Unity 에셋 스토어에서 판매를 하고 있으며 때로는 바우처를 제공해드립니다.

Photon 애플리케이션 하나에 대해 100 CCU 플랜을 하나 이상 결합할 수 있습니까?

아니오.
100 CCU 플랜은 겹쳐서 사용할 수 없으며 AppId당 딱 한 번만 적용할 수 있습니다.
여러개의 PUN+ 에셋 시트를 구입한 경우 AppId 별로 100 CCU에 대해 리딤해주어야 합니다.
단일 앱에 대해 더 많은 CCU가 필요한 경우, 다음으로 높은 요금제는 500 CCU입니다.
월간 또는 연간 플랜에 가입한 경우, 월간/연간 플랜의 CCU에 추가로 12개월 동안 100 CCU가 유지됩니다.

Photon 플랜에 얼마나 많은 트래픽이 포함되어 있나요? 앱에서 포함된 한도를 넘는 트래픽을 생성하면 어떻게 되나요?

Photon Public Cloud 및 Premium Cloud 플랜에는 CCU당 3GB가 포함됩니다.

예를 들어, 1,000개의 CCU를 갖춘 월간 요금제에는 월 3TB의 트래픽이 포함됩니다.

앱이 더 많은 트래픽을 생성하면 자동으로 이메일을 통해 알림을 보냅니다. 매월 말에 Photon 계정 이메일 주소로 자동 생성된 초과 청구서를 받게 됩니다. 청구서 금액은 다음 계산을 기반으로 합니다.

총 트래픽 - 포함 트래픽 = 초과 트래픽(GB)

트래픽은 사용된 Photon Cloud 지역에 따라 GB당 90원/180원으로 계산되고 추가 청구됩니다.

Photon Cloud 플랜의 구독한 CCU를 피크 CCU가 초과하면 어떻게 되나요?

500 CCU / 1,000 CCU / 2,000 CCU 플랜에 가입한 경우, 애플리케이션에 대해 "CCU 버스트"가 자동으로 활성화됩니다. Photon Cloud는 사용자에게 최상의 경험을 제공하기 위해 예약한 것보다 더 많은 CCU를 허용합니다.

버스트가 시작되면 합의된 조건에 따라 48시간 이내에 필요한 구독 등급으로 업그레이드해야 합니다.

업그레이드하지 않으면 Photon 계정 이메일 주소로 "초과 청구서"를 보내고 구독한 플랜을 초과하는 각 CCU에 대해 CCU당 1350원/1400원/1800원(사용된 SDK 기준)의 수수료를 청구합니다.

Photon은 "피크 CCU"는 해당 달에 합산된 지역별 피크 CCU의 합계입니다. 피크에 도달한 후 사용량이 감소하더라도 초과 요금을 피하기 위해 업그레이드해야 합니다.

Back to top