Let's now learn how to get and set data on a component using a system.
Get and Set the Color The component data is a ^byte[]^
, and it's up to the developer to decide what data to store and how. Create two methods to convert ^Color^
to ^byte[]^
and vice versa;
// Convert Color32 to byte array
private byte[] ColorToByteArray ( Color32 color )
{
byte[] colorBytes = new byte[ 4 ];
colorBytes[ 0 ] = color.r;
colorBytes[ 1 ] = color.g;
colorBytes[ 2 ] = color.b;
colorBytes[ 3 ] = color.a;
return colorBytes;
}
// Convert byte array to Color32
public Color32 ByteArrayToColor ( byte[] bytes )
{
if (bytes.Length < 4 )
{
Debug.LogError( "Byte array must have at least 4 elements (R, G, B, A)." );
return Color.clear;
}
byte r = bytes[ 0 ];
byte g = bytes[ 1 ];
byte b = bytes[ 2 ];
byte a = bytes[ 3 ];
Color32 color = new Color32(r, g, b, a);
return color;
}
Now create a ^SetColor^
method that will update the component with the new color locally and broadcast the update to all the other participants. We'll need to first verify that an entity with the given id exists. If the entity exists but doesn't yet have the ^color^
component, add a new one.
public bool SetColor ( uint entityId, Color color )
{
// Check if the entity with the given id exists
var entity = _session.GetEntity(entityId);
if (entity == null ) return false ;
// Store the data locally
_entityColorDataMap[entityId] = color;
// If the entity doesn't already have Color component add one
var component = _session.GetEntityComponent(entityId, COLOR_COMPONENT_NAME);
if (component == null )
{
_session.AddComponent(
COLOR_COMPONENT_NAME,
entityId,
ColorToByteArray(color),
() => {},
error => Debug.LogError(error)
);
return true ;
}
else
{
return _session.UpdateComponent(
COLOR_COMPONENT_NAME,
entityId,
ColorToByteArray(color)
);
}
}
Create a ^GetColor^
method that will return the locally stored value or a default one if the component or the entity doesn't exist.
public Color GetColor ( uint entityId )
{
if (_session.GetEntity(entityId) == null || !_entityColorDataMap.ContainsKey(entityId))
return Color.clear;
return _entityColorDataMap[entityId];
}
Override the Delete method that will be called by ConjureKit when other participants remove a component from an entity and a ^DeleteColor
^ method to delete the color locally.
public override void Delete ( IReadOnlyList<(EntityComponent component, bool localChange)> deleted )
{
foreach ( var (entityComponent, localChange) in deleted)
{
var entity = _session.GetEntity(entityComponent.EntityId);
if (entity == null ) continue ;
_entityColorDataMap.Remove(entity.Id);
}
}
In this tutorial, we are not going to use the delete functionality, but it is necessary to override the ^Delete^
method. You can later implement a public ^DeleteColor^
method that can be called to remove a color component from an entity.
public void DeleteColor ( uint entityId )
{
_session.DeleteComponent(COLOR_COMPONENT_NAME, entityId, () =>
{
_entityColorDataMap.Remove(entityId);
});
}
Initialize the system and update the color for everyone In the ^ConjureKitManager^
class, declare a ^ColorSystem^
and a Dictionary to store the cubes.
private ColorSystem _colorSystem;
private Dictionary<uint, Renderer> _cubes = new Dictionary<uint, Renderer>();
In ^Conjurekit.Onjoined^ callback, register the color system to the joining participant
_conjureKit.OnJoined += session =>
{
sessionID.text = session.Id.ToString();
!!
_colorSystem = new ColorSystem(session);
session.RegisterSystem(_colorSystem, () => Debug.Log( "System registered in session" ));
_colorSystem.OnColorComponentUpdated += OnColorComponentUpdated;
!!
};
Create a method that will change the cube's color when its component is updated.
private void OnColorComponentUpdated ( uint entityId, Color color )
{
_cubes[entityId].material.color = color;
}
Simplify the ^TouchableByHand^
class to stop updating the color locally and instead trigger an event when the cube is touched.
public class TouchableByHand : MonoBehaviour
{
public event Action OnTouched;
private void OnTriggerEnter ( Collider other )
{
if (other.CompareTag( "hand" ))
{
OnTouched?.Invoke();
}
}
}
Update the ^CreateCubeEntity^
and the ^CreateCube^
methods to initialize a cube with white color and update with random color when touched.
public void CreateCubeEntity ( )
{
if (_conjureKit.GetState() != State.Calibrated)
return ;
Vector3 position = arCamera.transform.position + arCamera.transform.forward * 0. 5f;
Quaternion rotation = Quaternion.Euler( 0 , arCamera.transform.eulerAngles.y, 0 );
Pose entityPos = new Pose(position, rotation);
_conjureKit.GetSession().AddEntity(
entityPos,
onComplete : entity =>
{
!!
// Initialize with white color
_colorSystem.SetColor(entity.Id, Color.white);
!!
CreateCube(entity);
},
onError : error => Debug.Log(error));
}
private void CreateCube ( Entity entity )
{
if (entity.Flag == EntityFlag.EntityFlagParticipantEntity) return ;
var pose = _conjureKit.GetSession().GetEntityPose(entity);
var touchableCube = Instantiate(cube, pose.position, pose.rotation).GetComponent<TouchableByHand>();
!!
_cubes[entity.Id] = touchableCube.GetComponent<Renderer>();
_cubes[entity.Id].material.color = _colorSystem.GetColor(entity.Id);
touchableCube.OnTouched += () =>
{
_colorSystem.SetColor(entity.Id, Random.ColorHSV());
_cubes[entity.Id].material.color = _colorSystem.GetColor(entity.Id);
};
!!
}
Build and run on two or more devices, and you should see cubes with shared colors.