Search Unity

Question Predicted Spawning in Netcode v0.50

Discussion in 'NetCode for ECS' started by RichardWepner, Apr 16, 2022.

  1. RichardWepner

    RichardWepner

    Joined:
    May 29, 2013
    Posts:
    33
    Hello fellow developers,

    Netcode v0.50 release already a while ago, and after some other difficulties I faced with the previous version (and Unity 2021 - physic queries didn't work in a build, but in editor), I made the change to v0.50 (and Unity 2020).
    Players in my game will be able to shoot projectiles (that are not just ray based hit detections), which for a better player experience should utilize predicted spawning. The documentation contains an entire section dedicated to predicted spawning of player spawned entites. Sadly, though, I have difficulties understanding what I'm supposed to do - in client _and_ server code - in order to get predicted spawning to work.

    The first sentence mentions "the client prediction system" - does this refer to the
    ClientSimulationSystemGroup
    , the
    GhostPredictionSystemGroup
    , a different system group, or something else? If it's the
    ClientSimulationSystemGroup
    , how does the server handle the spawning then? If it's the
    GhostPredictionSystemGroup
    , how are multiple predicted spawnings (resimulation of the same frame) handled?

    In the next section, entries from 2 buffers should be compared, and things should be done if they are equal. How could this equality be checked? The
    PredictedGhostSpawn
    buffer elements contain the entity, so one could in theory check everything stored for that entity, whereas the
    GhostSpawnBuffer
    buffer element doesn't contain an entity. It contains a
    GhostId
    , but this would require one client to only predict a single entity of the same type in one frame, which is unsuitble for lot's of cases. It also contains a
    DataOffset
    and a
    DynamicDataSize
    . If then some memory location would be known, maybe the data received from the server could be parsed manually, but it doesn't seem like a good approach to take.

    The last sentence of the 2nd paragraph also says, that the
    GhostSpawnBuffer
    entry should be adjusted, and then removed. Shouldn't it be the
    PredictedGhostSpawn
    that gets removed? how else can Netcode make the connection between the prespawned entity, and the received snapshot entity? And how should netcode know, that the
    PredictedGhostSpawn
    entry is not required anymore?

    Maybe someone here will be able to help me with this one. =/
    Richard
     
    hippocoder, Krooq and ddfrathb like this.
  2. timjohansson

    timjohansson

    Unity Technologies

    Joined:
    Jul 13, 2016
    Posts:
    473
    The system doing predicted spawning should be in the GhostPredictionSystemGroup. You need to check and only spawn if the tick being predicted is predicted for the first time and is not a partial tick. it is also possible to base the spawning logic on a cooldown or timer which is no being rolled back - but it is not as robust. The cooldown is what we did in the asteroids sample for 0.50, https://github.com/Unity-Technologi...les/Asteroids/Mixed/Systems/SteeringSystem.cs . We are working on making the first time prediction full tick easier and aim to update the sample once we have an easier solution for it.

    The sample we have for matching checks ghost type and spawn tick, you could also read the position, rotation or some other data if you want a better match, but you have to extract the snapshot data which is usually quantized and compare that. It is currently pretty complex to extract data from the snapshot the right way. See https://github.com/Unity-Technologi...es/Asteroids/Client/BulletGhostSpawnSystem.cs for our asteroids implementation.

    Yes, it should - the GhostSpawnBuffer gets updated to use the predicted spawned entity and the item in the PredictedGhostSpawn gets removed. I'll file a bug to make it more clear when we do a pass on the docs next time.
     
    Krooq and Occuros like this.
  3. Krooq

    Krooq

    Joined:
    Jan 30, 2013
    Posts:
    194
    @timjohansson In the asteroids sample you do the following
    Code (CSharp):
    1.  
    2.           if (ghost.GhostType == spawnList[j].ghostType && !SequenceHelpers.IsNewer(spawnList[j].spawnTick, ghost.ServerSpawnTick + 5) && SequenceHelpers.IsNewer(spawnList[j].spawnTick + 5, ghost.ServerSpawnTick))
    3.                         {
    4.                             ghost.PredictedSpawnEntity = spawnList[j].entity;
    5.                             spawnList[j] = spawnList[spawnList.Length-1];
    6.                             spawnList.RemoveAt(spawnList.Length - 1);
    7.                             break;
    8.                         }
    9.  
    The part I'm not sure on is the seemingly arbitrary 5 tick... umm "window" I think?, i.e. the part with the sequence helpers.
    As @RichardWepner mentioned, there could be more than one entity spawned per tick.
    In the asteroids sample is that reduced even more, down to a 5 tick window?

    Seems kinda crazy to be guessing which spawned entity is which using position or rotation even if one can read the snapshot.
    I'm guessing that predicted spawning is currently very very alpha and it's probably better to wait?
     
    adammpolak likes this.
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    I tested this sample once (in 0.6) and I could never actually get this code to trigger. A bit of debugging and the setup timing just always seemed wrong. It was a while ago but it was something like by the time the server tick was valid the ghost had already been removed from list.

    Haven't tested latest sample though if something has changed.
     
    Occuros and Krooq like this.
  5. adammpolak

    adammpolak

    Joined:
    Sep 9, 2018
    Posts:
    450
    I am thinking that what would be best to handle the case of multiple ghosts in a single tick is adding a Ghosted value, something like:
    int ClientPredictedSpawnGhostId


    So when inspecting the snapshot you can check if it is the same id.