So, after a month of struggling, I finally understood how to create a ghost entity in the baking process. I did not find any information about this online, and so I am sharing an up-to-date code on how to do this, for people who, like I am, are new to Netcode and you just want to make stuff work.
Please note that this is a very early version and may not work if there are multiple instances of a prespawn ghost in the subscene, or some other edge case happens. I will be updating the post though when I’ll fix the issues.
Right, so here’s what you need to do:
1)You need a non-ghost entity that you want to be base your new ghost upon on.
2)Create a system:
[BurstCompile]
[UpdateInGroup(typeof(PostBakingSystemGroup))]
[UpdateBefore(typeof(PreSpawnedGhostsBakingSystem))]
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
public partial struct YourPrespawnGhostMakingSystem: ISystem
The [UpdateBefore(typeof(PreSpawnedGhostsBakingSystem))] here is very important because it adds prespawn-components to the ghosts. And we cannot add them after this system because it creates a deterministic hash that is used to look up prespawned ghosts on the client-side.
3)In the update of the system, you`ll need to go over all entities that need to be converted, add:
ecb.AddComponent<GhostPrefabRuntimeStrip>(entities[i]);
4)convert the entity to ghost prefab using GhostPrefabCreation.ConvertToGhostPrefab.
Add GhostAuthoringComponentBakingData with necessary data either after ConvertToGhostPrefab or create your own ConvertToGhostPrefab function and simply add at the end:
entityManager.AddComponentData(prefab, new GhostAuthoringComponentBakingData
{
GhostName = config.Name,
GhostNameHash = TypeHash.FNV1A64(config.Name),
GhostType = ghostType,
Target = target,
IsPrefab = true,
IsActive = true
});
5)Now you need to spawn the entities. Either create another system that runs after this one or playback the buffer and continue in the current system.
And so, you instantiate the entities and remove unnecessary components; set the GhostAuthoringComponentBakingData:
Entity newGhost = ecb.Instantiate(entities[i]);
ecb.RemoveComponent<GhostPrefabMetaData>(newGhost);
ecb.RemoveComponent<PredictedGhostSpawnRequest>(newGhost);
ecb.RemoveComponent<GhostPrefabRuntimeStrip>(newGhost);
GhostAuthoringComponentBakingData ghostAuthoringComponentBakingData = state.EntityManager.GetComponentData<GhostAuthoringComponentBakingData>(newGhost);
ecb.SetComponent(newGhostJoint, new GhostAuthoringComponentBakingData
{
GhostName = ghostAuthoringComponentBakingData.GhostName,
GhostNameHash = ghostAuthoringComponentBakingData.GhostNameHash,
GhostType = ghostAuthoringComponentBakingData.GhostType,
Target = ghostAuthoringComponentBakingData.Target,
IsPrefab = !ghostAuthoringComponentBakingData.IsPrefab,
IsActive = ghostAuthoringComponentBakingData.IsActive
});
And thats it.
P.S. You`ll need to reimport the subscene every time you edit the subscene. I’ll think of a fix for that later.
P.P.S. Originally this was a post about Ghost/networked joint in ECS.
I have made them work, with clientside prediction on clients, but they still need to be polished. I will probably post code on gitgub and a link to ir sometime later when I fully fix them.