I’m exploring the possibilities of instantiating two predicted ghosts that can interact with each other, while ensuring their correct behavior during the resimulation phase in DOTS Netcode. Specifically, I’m looking at an example involving two interacting ghosts: a projectile and an Instant AOE Selector.
Scenario:
In our Gameplay Ability System, we have a Thunderbolt Ability with the following interactions:
The projectile is instantiated and launched in a specified direction, targeting enemies upon impact. Once launched, it is a predicted ghost and gets validated by the server.
Upon impact, an Instant AOE Selector is created, which is responsible for selecting nearby enemies for the next target. The Instant AOE is a temporary entity and exists only for a single frame on both the client and server. It has no visual representation and its only purpose is to identify potential targets based on certain conditions (e.g., proximity, enemy tags).
Once the AOE Selector identifies new targets, the projectile follows a newly selected actor, repeating the process until the specified hit count is reached.
Problem:
The main challenge arises when the ping is high (e.g., >170ms). The Instant AOE Selector, being temporary, does not get consistently resimulated, while the projectile ghost is included in the resimulation phase. This causes the projectile configuration to rely on incomplete or outdated data from the AOE Selector, resulting in incorrect behavior. The projectile may end up being misconfigured and wrongly destroyed due to missing data.
In my current setup:
The projectile is a predicted ghost, instantiated and validated by the server.
The Instant AOE Selector is also a predicted ghost, but it only lives for a single frame and isn’t sent to interpolated clients.
This inconsistency causes issues during resimulation, as the projectile ghost relies on the Instant AOE Selector ghost, which might not be fully synchronized.
Question:
Is it possible to instantiate two predicted ghosts that can interact with each other in a way that guarantees proper resimulation in DOTS Netcode?
How can I ensure that both ghosts (the projectile and the Instant AOE Selector) interact correctly during resimulation without running into issues when the AOE Selector is missing or hasn’t yet been updated?
Are there best practices for handling interactions between predicted ghosts, particularly when one of them has a very short lifespan (e.g., the AOE Selector living for only a single frame)?
Should I consider changing the AOE Selector into a different type of entity or modify the way resimulation and prediction works for these kinds of temporary interactions?
Any insights, suggestions, or experiences with similar scenarios would be greatly appreciated!
Hi! I’ve asked this question a while back Ghost affecting other ghosts prediction stability . What we did since then was to store prediction history for each tick to be able to resimulate all predicted entities from any tick when any receive partial snapshot.
PredictedSpawnedGhostRollbackToSpawnTick property to the GhostAuthoringComponent to allow predicted ghost spawned by client to rollback and re-simulate their state starting from their spawn tick, until the authoritative spawn has been received from the server. The rollback only occurs if the client receives new snapshots from server that contains at least one predicted ghost."
For your problem I’m not sure I understand the need of the Instant AOE Selector, why can’t it be resolved on the same tick by same entity or intermediate data structure? It seems a bit unecessary to sync for just one tick of lifetime. If I remember correctly, destroying ghosts instantly may remove it from ever being sent as well. Thus we try to keep things networked for at least some extra seconds by marking it “destroyed” in logic. If you still need it one tick later & predicted, have you considered moving it to a “singleton ghost” with a dynamic circle buffer?
I hope Unity will eventually provide a solid solution for resimulating predicted spawned ghosts, as relying solely on the authoring default values doesn’t work for every scenario and it could even brake it. In our case, we need a specific configuration for the ghost entity, which we defer to the instantiation.
My apologies for not providing enough context earlier. (I’m restricted in how much detail I can share about the framework we’re using.) The system relies on reusing existing logic to maximize performance and maintain flexibility.
We’re using a Gameplay Ability System framework that allows designers to compose abilities using a GraphView, connecting nodes of events and actions. These actions include things like “Create Projectile” or “Select Unit” using the Instant AOE Selector. The AOE Selector exists only for a single frame and selects specific entities based on filters, triggering related events to enable additional actions (such as reconfiguring the projectile, or applying gameplay effects like healing, speed boosts, damage, stuns, etc.). Thus, we can’t integrate the selection logic directly into the projectile behavior without losing the flexibility that our framework provides.
We ended up using a similar approach. Instead of using ghosts to simulate or resimulate the Instant AOE Selectors (since they don’t represent any persistent state), we now use ghost entities (Ability Instances) with buffers to store Instant AOE selection requests. This makes them fully compatible with resimulation.
BTW, I just want to mention—it feels good to see that the DOTS community still has some active members. Lately, I’ve posted a couple of questions that went completely ignored, which is quite different from how active the community used to be. I remember a time when even Joachim Ante was responding to everyone. I wasn’t active for nearly a year, and coming back, it almost feels like a ghost town. I really hope this doesn’t mean the DOTS dream is fading away.