In the latest release (0.50.0) it is now possible to switch command targets.
But how can multiple target entities be used from the same client?
For example, if there are two local players (two game-controllers sending input), or in VR we have often 3 inputs (headset, 2 controllers), I would want to have a command stream for each of these inputs linked to an entity, but the connection needs to define the CommandTargetComponent (which can refer only to one entity)
The updated command stream supports multiple command targets and even multiple ICommandData on each command target. The simplest way to use it is to check “Support Auto Command Target” on the Ghost Authoring Component.
Really? That’s awesome!
The docs are a little confusing here because the legacy way of doing things is still supported and only has the single command target option via CommandTargetComponent on the connection entity.
If we really can have multiple command targets (hooray!) can I recommend it be mentioned explicitly in the docs.
I agree we need some more work on the docs for this, we are planning to focus more on docs and samples but I can’t say exactly when that will be available.
I can’t really point you to any code to look at since the point of auto command target is that it doesn’t require any code, but if you look at the Cube prefab in the NetCube sample you can see that it enables “Support Auto Command Target” and “Has Owner”, sets “Default Ghost Mode” = “Owner Predicted” and adds the input at authoring by adding the “Ghost Input Authoring” component - which is all you need to do.
Asteroids is a bit more complex as it is using the old CommandTargetComponent for thin clients but the new auto command target for real clients.
Here’s an example I made today.
The idea is that you can have some entity be “operable”, e.g. emplaced weapon, vehicle etc.
In your code you just set operable.NewOperator and the OperationSystem will handle switching the command targets.
I know this isn’t exactly your situation but hopefully the usage sheds light on how it works.
Also I am no expert, but this seems to work for me.
[Serializable]
[GhostComponent]
public struct Operable : IComponentData
{
[GhostField] public Entity NewOperator;
[GhostField] public Entity CurrentOperator;
}
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
public partial class OperationSystem : SystemBase
{
protected override void OnUpdate()
{
Entities
.WithAll<GhostOwnerComponent>()
.WithAll<AutoCommandTarget>()
.ForEach((Entity operableEntity, ref Operable operable) =>
{
// Shortcut return if the operator hasn't changed.
if (operable.NewOperator == operable.CurrentOperator) return;
// Operator arrives.
if (operable.NewOperator != Entity.Null && operable.CurrentOperator == Entity.Null)
{
// Enable the operable commands.
var newOperatorGhostOwner = GetComponent<GhostOwnerComponent>(operable.NewOperator);
SetComponent(operableEntity, newOperatorGhostOwner);
SetComponent(operableEntity, new AutoCommandTarget { Enabled = true });
// Disable the operator commands.
if (HasComponent<AutoCommandTarget>(operable.NewOperator))
SetComponent(operable.NewOperator, new AutoCommandTarget { Enabled = false });
}
// Operator leaves.
if (operable.NewOperator == Entity.Null && operable.CurrentOperator != Entity.Null)
{
// Disable the operable commands.
SetComponent(operableEntity, new GhostOwnerComponent { NetworkId = 0 });
SetComponent(operableEntity, new AutoCommandTarget { Enabled = false });
// Enable the operator commands.
if (HasComponent<AutoCommandTarget>(operable.CurrentOperator))
SetComponent(operable.CurrentOperator, new AutoCommandTarget { Enabled = true });
}
operable.CurrentOperator = operable.NewOperator;
this.Log(operable, "New Operator");
}).WithoutBurst().Run();
}