using System; using System.Collections.Generic; using Passer.LinearAlgebra; namespace Passer.RoboidControl { /// <summary> /// A thing is the primitive building block /// </summary> [Serializable] public class Thing { #region Types #endregion Types #region Properties public delegate void ChangeHandler(); public delegate void SphericalHandler(Spherical v); public delegate void ThingHandler(Thing t); /// <summary> /// The participant to which this thing belongs /// </summary> public RemoteParticipant participant; /// <summary> /// The network ID of this thing. /// </summary> public byte networkId; /// <summary> /// The ID of this thing /// </summary> public byte id; /// <summary> /// Predefined thing types /// </summary> public enum Type { Undetermined, // Sensor Switch, DistanceSensor, DirectionalSensor, TemperatureSensor, // Motor ControlledMotor, UncontrolledMotor, Servo, // Other Roboid, Humanoid, ExternalSensor }; /// <summary> /// The type of this thing. This can be either a Thing::Type (needs casting) /// or a byte value for custom types. /// </summary> public byte type; /// <summary> /// Event which is triggered when the parent changes /// </summary> public event ChangeHandler OnParentChanged = delegate { }; private Thing _parent; /// <summary> /// The parent of this thing /// </summary> public Thing parent { get => _parent; set { if (_parent == value) return; if (value == null) { _parent?.RemoveChild(this); _parent = null; } else { value.AddChild(this); OnParentChanged?.Invoke(); } } } /// <summary> /// Attach a thing as a child of this thing /// </summary> /// <param name="child">The thing to attach as a child</param> public void AddChild(Thing child) { if (children.Find(thing => thing == child) != null) return; child._parent = this; children.Add(child); } /// <summary> /// Remove the given thing as a child of this thing /// </summary> /// <param name="child">The child to remove</param> public void RemoveChild(Thing child) { children.Remove(child); } /// <summary> /// The list of children of this thing /// </summary> [NonSerialized] public List<Thing> children = new List<Thing>(); /// <summary> /// Event which is triggered when the name changes /// </summary> public event ChangeHandler OnNameChanged = delegate { }; private string _name = ""; /// <summary> /// The name of the thing /// </summary> public virtual string name { get => _name; set { if (_name != value) { _name = value; OnNameChanged?.Invoke(); } } } /// <summary> /// An URL pointing to the location where a model of the thing can be found /// </summary> public string modelUrl = ""; /// <summary> /// Event triggered when the position has changed /// </summary> public event ChangeHandler OnPositionChanged = delegate { }; private Spherical _position = Spherical.zero; /// <summary> /// The position of the thing in local space, in meters. /// </summary> public Spherical position { get { return _position; } set { if (_position != value) { _position = value; OnPositionChanged?.Invoke(); } } } /// <summary> /// Event triggered when the orientation has changed /// </summary> public event ChangeHandler OnOrientationChanged = delegate { }; private SwingTwist _orientation = SwingTwist.zero; /// <summary> /// The orientation of the thing in local space /// </summary> public SwingTwist orientation { get { return _orientation; } set { if (_orientation != value) { _orientation = value; OnOrientationChanged?.Invoke(); } } } /// <summary> /// Event triggered when the linear velocity has changed /// </summary> public event SphericalHandler OnLinearVelocityChanged = delegate { }; private Spherical _linearVelocity = Spherical.zero; /// <summary> /// The linear velocity of the thing in local space in meters per second /// </summary> public Spherical linearVelocity { get => _linearVelocity; set { if (_linearVelocity != value) { _linearVelocity = value; OnLinearVelocityChanged?.Invoke(_linearVelocity); } } } /// <summary> /// The angular velocity of the thing in local space /// </summary> public Spherical angularVelocity = Spherical.zero; #if UNITY_5_3_OR_NEWER /// <summary> /// A reference to the representation of the thing in Unity /// </summary> [NonSerialized] public Unity.Thing component = null; #endif #endregion Properties #region Init /// <summary> /// Create a new thing for the given participant /// </summary> /// <param name="participant">The participant for which this thing is created</param> /// <param name="invokeEvent">True when a new thing event should be triggered</param> public Thing(RemoteParticipant participant, bool invokeEvent = false) { this.participant = participant; if (invokeEvent) InvokeNewThing(this); } /// <summary> /// Create a new thing for the given participant /// </summary> /// <param name="participant">The participant for which this thing is created</param> /// <param name="networkId">The network ID of the thing</param> /// <param name="thingId">The ID of the thing</param> /// <param name="thingType">The type of thing</param> public Thing(RemoteParticipant participant, byte networkId, byte thingId, byte thingType = 0) { this.participant = participant; this.id = thingId; this.type = thingType; this.networkId = networkId; } /// <summary> /// Function which can be used to create components in external engines. /// </summary> /// Currently this is used to create GameObjects in Unity public virtual void CreateComponent() { #if UNITY_5_3_OR_NEWER this.component = Unity.Thing.Create(this); this.component.core = this; #endif } #endregion Init #region Update #if UNITY_5_3_OR_NEWER /// <summary> /// Convience function for use in Unity which removes the need for a currentTime argument /// </summary> public void Update() { Update((ulong)UnityEngine.Time.time * 1000); } #endif /// <summary> /// Update this thing /// </summary> /// <param name="currentTime">The current time in milliseconds</param> public virtual void Update(ulong currentTime) { // should recurse over children... } /// <summary> /// Function used to generate binary data for this thing /// </summary> /// <returns>a byte array with the binary data</returns> /// @sa Passer::RoboidControl::BinaryMsg public virtual byte[] GenerateBinary() { return new byte[0]; } /// <summary> /// Function used to process binary data received for this thing /// </summary> /// <param name="bytes">The binary data</param> public virtual void ProcessBinary(byte[] bytes) { } #endregion Update /// <summary> /// Event triggered when a new thing has been created /// </summary> public static event ThingHandler OnNewThing = delegate { }; /// <summary> /// Trigger the creation for the given thing /// </summary> /// <param name="thing">The created thing</param> public static void InvokeNewThing(Thing thing) { OnNewThing?.Invoke(thing); } /// <summary> /// Check if the thing has the given properaties /// </summary> /// <param name="thing">The thing to check</param> /// <param name="networkId">The network ID to compare to</param> /// <param name="thingId">The thing ID to compare to</param> /// <returns>True when the thing has the given properties</returns> public static bool IsThing(Thing thing, byte networkId, byte thingId) { if (thing == null) return false; return (thing.networkId == networkId) && (thing.id == thingId); } } }