diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj index b49e8b7..d59c742 100644 --- a/Assembly-CSharp.csproj +++ b/Assembly-CSharp.csproj @@ -79,6 +79,7 @@ + diff --git a/Assets/NanoBrain/Cluster.cs b/Assets/NanoBrain/Cluster.cs index 5b349e1..2216d32 100644 --- a/Assets/NanoBrain/Cluster.cs +++ b/Assets/NanoBrain/Cluster.cs @@ -3,28 +3,30 @@ using UnityEngine; [CreateAssetMenu(menuName = "Passer/Cluster")] public class Cluster : ScriptableObject, INucleus { - - // private string _name; - // public new string name { - // get { return _name; } - // set { _name = value; } - // } - + public Cluster cluster => this; - public List nuclei = new(); + [SerializeReference] + public List nuclei = new(); - public Nucleus output => this.nuclei[0]; + public INucleus output => this.nuclei[0]; - [SerializeField] - private readonly List _synapses = new(); // = inputs, compare receptors in NanoBrain + //private readonly List _inputs = new(); + public List inputs { // = compare receptors in NanoBrain + // for now all nuclei are inputs + get { return this.nuclei; } + } + + // The synapses of all inputs + private readonly List _synapses = new(); public List synapses => _synapses; + // Call this function to ensure that there is at least one nucleus // This is an invariant and should be ensured before the nucleus is used // because output requires it. public void EnsureInitialization() { - nuclei ??= new List(); + nuclei ??= new List(); if (nuclei.Count == 0) new Neuroid(this, "Output"); // Every cluster should have at least 1 neuroid } @@ -36,12 +38,12 @@ public class Cluster : ScriptableObject, INucleus { public void RemoveReceiver(INucleus receiver) { output.RemoveReceiver(receiver); } - public List receivers { + public List receivers { get => output.receivers; } - public void AddSynapse(INucleus sender) { - Synapse synapse = new (sender, 1.0f); + public void AddSynapse(IReceptor sender) { + Synapse synapse = new(sender, 1.0f); synapses.Add(synapse); } @@ -63,17 +65,18 @@ public class Cluster : ScriptableObject, INucleus { foreach (Synapse synapse in nucleus.synapses) { if (synapse != null && synapse.nucleus != null) { visitedSynapses.Add(synapse); - MarkNuclei(visitedNuclei, synapse.nucleus); + if (synapse.nucleus is INucleus synapse_nucleus) + MarkNuclei(visitedNuclei, synapse_nucleus); } } nucleus.synapses.RemoveAll(synapse => visitedSynapses.Contains(synapse) == false); } if (nucleus.receivers != null) { - HashSet visitedReceivers = new(); - foreach (Receiver receiver in nucleus.receivers) { - if (receiver != null && receiver.nucleus != null) { + HashSet visitedReceivers = new(); + foreach (INucleus receiver in nucleus.receivers) { + if (receiver != null && receiver != null) { visitedReceivers.Add(receiver); - visitedNuclei.Add(receiver.nucleus); + visitedNuclei.Add(receiver); } } nucleus.receivers.RemoveAll(receiver => visitedReceivers.Contains(receiver) == false); @@ -91,5 +94,17 @@ public class Cluster : ScriptableObject, INucleus { // it is not, I should take inputs from the synapses and route them to the right internal nuclei } + public void UpdateNuclei() { + foreach (INucleus nucleus in nuclei) + nucleus.IncreaseAge(); + + } + + public void IncreaseAge() { + foreach (INucleus nucleus in nuclei) + nucleus.IncreaseAge(); + } + // ha ha ha + #endregion Dynamics } \ No newline at end of file diff --git a/Assets/NanoBrain/INucleus.cs b/Assets/NanoBrain/INucleus.cs index 46d424a..45c0acd 100644 --- a/Assets/NanoBrain/INucleus.cs +++ b/Assets/NanoBrain/INucleus.cs @@ -1,33 +1,45 @@ using System.Collections.Generic; using UnityEngine; -public interface INucleus { +public interface INucleus : IReceptor { #region static struct - public string name { get; set; } - // Cluster public Cluster cluster { get; } - // Receivers - public List receivers { get; } - public void AddReceiver(INucleus receiver); - public void RemoveReceiver(INucleus receiverNucleus); - // Senders public List synapses { get; } - public void AddSynapse(INucleus sender); + public void AddSynapse(IReceptor sender); #endregion static struct #region dynamic state - public bool isSleeping { get; } + public void UpdateState(); + + public void IncreaseAge(); + + #endregion dynamic state +} + +public interface IReceptor { + #region static + + public string name { get; set; } + + // Receivers + public List receivers { get; } + public void AddReceiver(INucleus receiver); + public void RemoveReceiver(INucleus receiverNucleus); + + #endregion static + + #region dynamic public Vector3 outputValue { get; } - public void UpdateState(); + public bool isSleeping { get; } - #endregion dynamic state + #endregion dynamic } \ No newline at end of file diff --git a/Assets/NanoBrain/Neuroid.cs b/Assets/NanoBrain/Neuroid.cs index cd534e0..9e56de1 100644 --- a/Assets/NanoBrain/Neuroid.cs +++ b/Assets/NanoBrain/Neuroid.cs @@ -2,49 +2,8 @@ using UnityEngine; [System.Serializable] public class Neuroid : Nucleus { - public enum CurvePresets { - Linear, - Power, - Sqrt, - Reciprocal, - Custom - } - [SerializeField] - private CurvePresets _curvePreset; - public CurvePresets curvePreset { - get { return _curvePreset; } - set { - _curvePreset = value; - this.curve = GenerateCurve(); - } - } - public AnimationCurve curve; - public float curveMax = 1.0f; - - public AnimationCurve GenerateCurve() { - switch (this.curvePreset) { - case CurvePresets.Linear: - this.curveMax = 1; - return Synapse.Presets.Linear(1); - case CurvePresets.Power: - this.curveMax = 1; - return Synapse.Presets.Power(2.0f, 1); - case CurvePresets.Sqrt: - this.curveMax = 1; - return Synapse.Presets.Power(0.5f, 1); - case CurvePresets.Reciprocal: - this.curveMax = 1 / 0.01f * 1; - return Synapse.Presets.Reciprocal(1); - default: - this.curveMax = 1; - return this.curve; - } - } public bool average = false; - public bool inverse = false; - public float exponent = 1.0f; - public Neuroid(Cluster brain, string name) : base(name) { this.cluster = brain; diff --git a/Assets/NanoBrain/Nucleus.cs b/Assets/NanoBrain/Nucleus.cs index 601c462..41ac085 100644 --- a/Assets/NanoBrain/Nucleus.cs +++ b/Assets/NanoBrain/Nucleus.cs @@ -19,26 +19,65 @@ public class Nucleus : INucleus { private List _synapses = new(); public List synapses => _synapses; - [SerializeField] - private List _receivers = new(); - public List receivers => _receivers; + [SerializeReference] + private List _receivers = new(); + public List receivers => _receivers; #region Serialization [SerializeField] protected string nucleusType; + public enum CurvePresets { + Linear, + Power, + Sqrt, + Reciprocal, + Custom + } + [SerializeField] + private CurvePresets _curvePreset; + public CurvePresets curvePreset { + get { return _curvePreset; } + set { + _curvePreset = value; + this.curve = GenerateCurve(); + } + } + public AnimationCurve curve; + public float curveMax = 1.0f; + + public AnimationCurve GenerateCurve() { + switch (this.curvePreset) { + case CurvePresets.Linear: + this.curveMax = 1; + return Synapse.Presets.Linear(1); + case CurvePresets.Power: + this.curveMax = 1; + return Synapse.Presets.Power(2.0f, 1); + case CurvePresets.Sqrt: + this.curveMax = 1; + return Synapse.Presets.Power(0.5f, 1); + case CurvePresets.Reciprocal: + this.curveMax = 1 / 0.01f * 1; + return Synapse.Presets.Reciprocal(1); + default: + this.curveMax = 1; + return this.curve; + } + } + public virtual void Rebuild(NanoBrain brain) { if (this.synapses != null) { foreach (Synapse synapse in synapses) synapse.Rebuild(brain); } - foreach (Receiver receiver in receivers.ToArray()) { - if (receiver.Rebuild(brain) == false) { - Debug.Log("Rebuilding failed, removing receiver."); - receivers.Remove(receiver); - } - } + // foreach (INucleus receiver in receivers.ToArray()) { + // if (receiver.Rebuild(brain) == false) { + // Debug.Log("Rebuilding failed, removing receiver."); + // receivers.Remove(receiver); + // } + // } } public static Nucleus RebuildType(NanoBrain brain, Nucleus nucleus) { @@ -63,8 +102,7 @@ public class Nucleus : INucleus { public Cluster cluster { get; set; } private Vector3 _outputValue; - public Vector3 outputValue - { + public Vector3 outputValue { get { return _outputValue; } set { this.stale = 0; @@ -96,30 +134,33 @@ public class Nucleus : INucleus { } public virtual void AddReceiver(INucleus receivingNucleus) { - this.receivers.Add(new Receiver(receivingNucleus)); + // this.receivers.Add(new Receiver(receivingNucleus)); + this.receivers.Add(receivingNucleus); //receivingNucleus.SetWeight(this, 1.0f); receivingNucleus.AddSynapse(this); } public void RemoveReceiver(INucleus receiverNucleus) { - this.receivers.RemoveAll(receiver => receiver.nucleus == receiverNucleus); + this.receivers.RemoveAll(receiver => receiver == receiverNucleus); receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this); } public static void Delete(INucleus nucleus) { foreach (Synapse synapse in nucleus.synapses) { - if (synapse.nucleus.receivers.Count > 1) { - // there is another nucleus feeding into this input nucleus - synapse.nucleus.receivers.RemoveAll(r => r.nucleus == nucleus); - } - else { - // No other links, delete it. - Nucleus.Delete(synapse.nucleus); + if (synapse.nucleus is Nucleus synapse_nucleus) { + if (synapse_nucleus.receivers.Count > 1) { + // there is another nucleus feeding into this input nucleus + synapse_nucleus.receivers.RemoveAll(r => r == nucleus); + } + else { + // No other links, delete it. + Nucleus.Delete(synapse_nucleus); + } } } - foreach (Receiver receiver in nucleus.receivers) { - if (receiver.nucleus != null && receiver.nucleus.synapses != null) - receiver.nucleus.synapses.RemoveAll(s => s.nucleus == nucleus); + foreach (INucleus receiver in nucleus.receivers) { + if (receiver != null && receiver.synapses != null) + receiver.synapses.RemoveAll(s => s.nucleus == nucleus); } if (nucleus.cluster != null) { @@ -141,7 +182,7 @@ public class Nucleus : INucleus { return false; } - public void AddSynapse(INucleus sendingNucleus) { + public void AddSynapse(IReceptor sendingNucleus) { Synapse synapse = new(sendingNucleus, 1.0f); this.synapses.Add(synapse); } @@ -166,146 +207,36 @@ public class Nucleus : INucleus { // } this.outputValue = result; - foreach (Receiver receiver in this.receivers) - receiver.nucleus.UpdateState(); + foreach (INucleus receiver in this.receivers) + receiver.UpdateState(); } } -[Serializable] -public class Synapse { - [NonSerialized] - public INucleus nucleus; - public Cluster cluster; - public int nucleusId; - public float weight; +// [Serializable] +// public class Receiver { +// [NonSerialized] +// public INucleus nucleus; +// //public int nucleusId; - public enum CurvePresets { - Linear, - Power, - Sqrt, - Reciprocal, - Custom - } - // public CurvePresets curvePreset; - // public AnimationCurve curve; - public float curveMax = 1.0f; +// public Receiver(INucleus nucleus) { +// this.nucleus = nucleus; +// //this.nucleusId = nucleus.id; +// } - public Synapse(INucleus nucleus, float weight) { - this.nucleus = nucleus; - //this.nucleusId = nucleus.id; - this.weight = weight; - } +// public bool Rebuild(NanoBrain brain) { +// if (brain == null) { +// return false; +// } - public void Rebuild(NanoBrain brain) { - if (brain == null) { - return; - } - - foreach (Nucleus nucleus in brain.nuclei) { - if (nucleus.id == this.nucleusId) { - this.nucleus = nucleus; - return; - } - } - foreach (Perceptoid perceptoid in brain.perceptei) { - if (perceptoid.id == this.nucleusId) { - this.nucleus = perceptoid; - return; - } - } - Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}"); - } - - // public AnimationCurve GenerateCurve() { - // switch (this.curvePreset) { - // case CurvePresets.Linear: - // this.curveMax = this.weight; - // return Presets.Linear(this.weight); - // case CurvePresets.Power: - // this.curveMax = this.weight; - // return Presets.Power(2.0f, this.weight); - // case CurvePresets.Sqrt: - // this.curveMax = this.weight; - // return Presets.Power(0.5f, this.weight); - // case CurvePresets.Reciprocal: - // this.curveMax = 1 / 0.01f * this.weight; - // return Presets.Reciprocal(this.weight); - // default: - // this.curveMax = weight; - // return AnimationCurve.Constant(0, 1, weight); - // } - // } - - public static class Presets { - private const int samples = 32; - public static AnimationCurve Linear(float weight) { - return AnimationCurve.Linear(0f, 0f, 1000f, weight * 1000); - } - public static AnimationCurve Power(float exponent, float weight) { - // build keyframes - Keyframe[] keys = new Keyframe[samples]; - for (int i = 0; i < samples; i++) { - float t = i / (float)(samples - 1); - float v = Mathf.Pow(t, exponent) * weight; - keys[i] = new Keyframe(t, v); - } - - AnimationCurve curve = new(keys); - - // set tangent modes for each key to Auto (smooth). Use Linear if you prefer straight segments. - for (int i = 0; i < curve.length; i++) { - AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Auto); - AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Auto); - } - - return curve; - } - public static AnimationCurve Reciprocal(float weight) { - int samples = 128; - float xMin = 0.001f; - float xMax = 1; - var keys = new Keyframe[samples]; - for (int i = 0; i < samples; i++) { - float t = i / (float)(samples - 1); - float x = Mathf.Lerp(xMin, xMax, t); - float y = 1f / x * weight; - keys[i] = new Keyframe(x, y); - } - var curve = new AnimationCurve(keys); - for (int i = 0; i < curve.length; i++) { - AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Linear); - AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Linear); - } - return curve; - } - } -} - -[Serializable] -public class Receiver { - [NonSerialized] - public INucleus nucleus; - //public int nucleusId; - - public Receiver(INucleus nucleus) { - this.nucleus = nucleus; - //this.nucleusId = nucleus.id; - } - - public bool Rebuild(NanoBrain brain) { - if (brain == null) { - return false; - } - - // Use SerializedReference instead? - // foreach (Nucleus nucleus in brain.nuclei) { - // if (nucleus.id == this.nucleusId) { - // this.nucleus = nucleus; - // return true; - // } - // } - //Debug.LogWarning($"Receiver deserialization error: could not find nucleus with id {this.nucleusId}"); - return false; - } -} \ No newline at end of file +// // Use SerializedReference instead? +// // foreach (Nucleus nucleus in brain.nuclei) { +// // if (nucleus.id == this.nucleusId) { +// // this.nucleus = nucleus; +// // return true; +// // } +// // } +// //Debug.LogWarning($"Receiver deserialization error: could not find nucleus with id {this.nucleusId}"); +// return false; +// } +// } \ No newline at end of file diff --git a/Assets/NanoBrain/Perceptoid.cs b/Assets/NanoBrain/Perceptoid.cs index 85f0bc1..b30847e 100644 --- a/Assets/NanoBrain/Perceptoid.cs +++ b/Assets/NanoBrain/Perceptoid.cs @@ -36,19 +36,19 @@ public class Perceptoid : Neuroid { this.receptor.thingType = perceptoid.thingType; // Point all receivers to this perceptoid instead of the default nucleus - foreach (Receiver receiver in nucleus.receivers) { - foreach (Synapse synapse in receiver.nucleus.synapses) { + foreach (INucleus receiver in nucleus.receivers) { + foreach (Synapse synapse in receiver.synapses) { if (synapse.nucleus == nucleus) synapse.nucleus = this; } } // Point all synapses to this perceptoid instead of the default nucleus - foreach (Synapse synapse in nucleus.synapses) { - foreach (Receiver receiver in synapse.nucleus.receivers) { - if (receiver.nucleus == nucleus) - receiver.nucleus = this; - } - } + // foreach (Synapse synapse in nucleus.synapses) { + // foreach (INucleus r in synapse.nucleus.receivers) { + // if (r == nucleus) + // this.receiver = this; + // } + // } // Copying disabled for now // // Copy all the synapses // this.synapses = nucleus.synapses; diff --git a/Assets/NanoBrain/Receptor.cs b/Assets/NanoBrain/Receptor.cs index ed8ba96..dcd3c28 100644 --- a/Assets/NanoBrain/Receptor.cs +++ b/Assets/NanoBrain/Receptor.cs @@ -2,7 +2,36 @@ using System.Collections.Generic; using UnityEngine; using LinearAlgebra; -public class Receptor { +public class Receptor : IReceptor { + [SerializeField] + protected string _name; + public virtual string name { + get => _name; + set => _name = value; + } + + public Cluster cluster; + //public INucleus nucleus; + + //[SerializeField] + [SerializeReference] + private List _receivers = new(); + public List receivers => _receivers; + + public virtual void AddReceiver(INucleus receivingNucleus) { + //this.receivers.Add(new Receiver(receivingNucleus)); + this.receivers.Add(receivingNucleus); + //receivingNucleus.SetWeight(this, 1.0f); + receivingNucleus.AddSynapse(this); + } + + public void RemoveReceiver(INucleus receiverNucleus) { + this.receivers.RemoveAll(receiver => receiver == receiverNucleus); + receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this); + } + + public bool isSleeping => false; + /// /// The list of perceptoid which can process stimuli from this receptor /// @@ -22,12 +51,26 @@ public class Receptor { public float distanceResolution = 0.1f; public float directionResolution = 5; + public Vector3 outputValue { + get { return localPosition; } + set { + localPosition = value; + } + } + + public Receptor(NanoBrain brain, int thingType) { this.thingType = thingType; //this.perceptei.Add(perceptoid); brain.receptors.Add(this); } + public Receptor(Cluster cluster, INucleus nucleus) { + this.cluster = cluster; + //nucleus.AddSynapse(this); + this.AddReceiver(nucleus); + } + public static Receptor GetReceptor(NanoBrain brain, int thingType) { foreach (Receptor receptor in brain.receptors) { if (thingType == 0 || receptor.thingType == thingType) @@ -37,46 +80,68 @@ public class Receptor { return newReceptor; } + public static Receptor CreateReceptor(Cluster cluster, string nucleusName) { + if (cluster == null) + return null; + + foreach (INucleus nucleus in cluster.inputs) { + if (nucleus != null && nucleus.name == nucleusName) { + Receptor receptor = new(cluster, nucleus); + return receptor; + } + } + return null; + } + public virtual void ProcessStimulus(int thingId, Vector3 newLocalPositionVector, string thingName = null) { this.localPosition = newLocalPositionVector; - Perceptoid selectedPerceptoid = null; - foreach (Perceptoid perceptoid in this.perceptei) { - if (perceptoid.thingId == thingId) { - // We found an existing perceptoid for this thing - selectedPerceptoid = perceptoid; - // Do not look any further - - break; - } - else if (perceptoid.isSleeping) { - // A sleeping perceptoid is not active and can therefore always be reused - selectedPerceptoid = perceptoid; - // Look further because we could find a existing perceptoid for this thing - } - - else if (selectedPerceptoid == null) { - // If we haven't found a perceptoid yet, just start by taking the first - selectedPerceptoid = perceptoid; - } - - else if (selectedPerceptoid.isSleeping == false) { - // If no existing or sleeping perceptoid is found, we look for the perceptoid - // we the furthest (least interesting) stimulus - if (perceptoid.receptor.localPosition.magnitude < selectedPerceptoid.receptor.localPosition.magnitude) { - Debug.Log($"{selectedPerceptoid.name} {selectedPerceptoid.receptor.localPosition.magnitude} {perceptoid.receptor.localPosition.magnitude} "); - selectedPerceptoid = perceptoid; - } - } + INucleus selectedReceiver = null; + foreach (INucleus receiver in this.receivers) { + selectedReceiver = receiver; } - if (selectedPerceptoid == null) { - Debug.Log("No perceptoid selected, stimulus is ignored"); - return; - } - // Debug.Log($"Stimulus {thingType} {thingId} {selectedPerceptoid.name}"); - selectedPerceptoid.thingId = thingId; - if (thingName != null) - selectedPerceptoid.name = selectedPerceptoid.baseName + " " + thingName; - selectedPerceptoid.UpdateState(); + // selectedReceiver.thingId = thingId; + // if (thingName != null) + // selectedReceiver.nucleus.name = selectedReceiver.nucleus.baseName + " " + thingName; + selectedReceiver.UpdateState(); + + // Perceptoid selectedPerceptoid = null; + // foreach (Perceptoid perceptoid in this.perceptei) { + // if (perceptoid.thingId == thingId) { + // // We found an existing perceptoid for this thing + // selectedPerceptoid = perceptoid; + // // Do not look any further + + // break; + // } + // else if (perceptoid.isSleeping) { + // // A sleeping perceptoid is not active and can therefore always be reused + // selectedPerceptoid = perceptoid; + // // Look further because we could find a existing perceptoid for this thing + // } + + // else if (selectedPerceptoid == null) { + // // If we haven't found a perceptoid yet, just start by taking the first + // selectedPerceptoid = perceptoid; + // } + + // else if (selectedPerceptoid.isSleeping == false) { + // // If no existing or sleeping perceptoid is found, we look for the perceptoid + // // we the furthest (least interesting) stimulus + // if (perceptoid.receptor.localPosition.magnitude < selectedPerceptoid.receptor.localPosition.magnitude) { + // Debug.Log($"{selectedPerceptoid.name} {selectedPerceptoid.receptor.localPosition.magnitude} {perceptoid.receptor.localPosition.magnitude} "); + // selectedPerceptoid = perceptoid; + // } + // } + // } + // if (selectedPerceptoid == null) { + // Debug.Log("No perceptoid selected, stimulus is ignored"); + // return; + // } + // // Debug.Log($"Stimulus {thingType} {thingId} {selectedPerceptoid.name}"); + // selectedPerceptoid.thingId = thingId; + // if (thingName != null) + // selectedPerceptoid.name = selectedPerceptoid.baseName + " " + thingName; + // selectedPerceptoid.UpdateState(); } } \ No newline at end of file diff --git a/Assets/NanoBrain/Synapse.cs b/Assets/NanoBrain/Synapse.cs new file mode 100644 index 0000000..7bcdfde --- /dev/null +++ b/Assets/NanoBrain/Synapse.cs @@ -0,0 +1,115 @@ +using System; +using UnityEngine; +using UnityEditor; + +[Serializable] +public class Synapse { + //[NonSerialized] + [SerializeReference] + public IReceptor nucleus; + [SerializeReference] + public Cluster cluster; + //public int nucleusId; + public float weight; + + public enum CurvePresets { + Linear, + Power, + Sqrt, + Reciprocal, + Custom + } + // public CurvePresets curvePreset; + // public AnimationCurve curve; + public float curveMax = 1.0f; + + public Synapse(IReceptor nucleus, float weight) { + this.nucleus = nucleus; + //this.nucleusId = nucleus.id; + this.weight = weight; + } + + public void Rebuild(NanoBrain brain) { + // if (brain == null) { + // return; + // } + + // foreach (Nucleus nucleus in brain.nuclei) { + // if (nucleus.id == this.nucleusId) { + // this.nucleus = nucleus; + // return; + // } + // } + // foreach (Perceptoid perceptoid in brain.perceptei) { + // if (perceptoid.id == this.nucleusId) { + // this.nucleus = perceptoid; + // return; + // } + // } + // Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}"); + } + + // public AnimationCurve GenerateCurve() { + // switch (this.curvePreset) { + // case CurvePresets.Linear: + // this.curveMax = this.weight; + // return Presets.Linear(this.weight); + // case CurvePresets.Power: + // this.curveMax = this.weight; + // return Presets.Power(2.0f, this.weight); + // case CurvePresets.Sqrt: + // this.curveMax = this.weight; + // return Presets.Power(0.5f, this.weight); + // case CurvePresets.Reciprocal: + // this.curveMax = 1 / 0.01f * this.weight; + // return Presets.Reciprocal(this.weight); + // default: + // this.curveMax = weight; + // return AnimationCurve.Constant(0, 1, weight); + // } + // } + + public static class Presets { + private const int samples = 32; + public static AnimationCurve Linear(float weight) { + return AnimationCurve.Linear(0f, 0f, 1000f, weight * 1000); + } + public static AnimationCurve Power(float exponent, float weight) { + // build keyframes + Keyframe[] keys = new Keyframe[samples]; + for (int i = 0; i < samples; i++) { + float t = i / (float)(samples - 1); + float v = Mathf.Pow(t, exponent) * weight; + keys[i] = new Keyframe(t, v); + } + + AnimationCurve curve = new(keys); + + // set tangent modes for each key to Auto (smooth). Use Linear if you prefer straight segments. + for (int i = 0; i < curve.length; i++) { + AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Auto); + AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Auto); + } + + return curve; + } + public static AnimationCurve Reciprocal(float weight) { + int samples = 128; + float xMin = 0.001f; + float xMax = 1; + var keys = new Keyframe[samples]; + for (int i = 0; i < samples; i++) { + float t = i / (float)(samples - 1); + float x = Mathf.Lerp(xMin, xMax, t); + float y = 1f / x * weight; + keys[i] = new Keyframe(x, y); + } + var curve = new AnimationCurve(keys); + for (int i = 0; i < curve.length; i++) { + AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Linear); + AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Linear); + } + return curve; + } + } +} \ No newline at end of file diff --git a/Assets/NanoBrain/Synapse.cs.meta b/Assets/NanoBrain/Synapse.cs.meta new file mode 100644 index 0000000..e62612c --- /dev/null +++ b/Assets/NanoBrain/Synapse.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 334a58eafccd60cbdb32f719e9e861c6 \ No newline at end of file diff --git a/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs b/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs index bd681fa..b3a443b 100644 --- a/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs +++ b/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs @@ -75,7 +75,7 @@ public class ClusterInspector : Editor { INucleus currentNucleus; GameObject gameObject; private List layers = new(); - private readonly Dictionary neuroidPositions = new(); + private readonly Dictionary neuroidPositions = new(); Vector2 pan = Vector2.zero; //float zoom = 1f; @@ -101,7 +101,7 @@ public class ClusterInspector : Editor { RegisterCallback(OnMouseUp); } - public void SetGraph(GameObject gameObject, Cluster brain, Nucleus nucleus, VisualElement inspectorContainer) { + public void SetGraph(GameObject gameObject, Cluster brain, INucleus nucleus, VisualElement inspectorContainer) { this.gameObject = gameObject; this.cluster = brain; if (Application.isPlaying == false) @@ -135,8 +135,8 @@ public class ClusterInspector : Editor { NeuroidLayer currentLayer = new() { ix = layerIx }; if (selectedNucleus.receivers != null) { - foreach (Receiver receiver in selectedNucleus.receivers) { - INucleus outputNeuroid = receiver.nucleus; + foreach (INucleus receiver in selectedNucleus.receivers) { + INucleus outputNeuroid = receiver; if (outputNeuroid != null) { AddToLayer(currentLayer, outputNeuroid); // Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}"); @@ -158,7 +158,7 @@ public class ClusterInspector : Editor { if (selectedNucleus.synapses != null) { foreach (Synapse synapse in selectedNucleus.synapses) { - INucleus input = synapse.nucleus; + IReceptor input = synapse.nucleus; AddToLayer(currentLayer, input); // Debug.Log($"layer {layerIx} nucleus {input.name}"); } @@ -168,7 +168,7 @@ public class ClusterInspector : Editor { } } - private void AddToLayer(NeuroidLayer layer, INucleus nucleus) { + private void AddToLayer(NeuroidLayer layer, IReceptor nucleus) { if (nucleus == null) return; layer.neuroids.Add(nucleus); @@ -225,8 +225,8 @@ public class ClusterInspector : Editor { // Determine the maximum value in this layer // This is used to 'scale' the output value colors of the nuclei float maxValue = 0; - foreach (Receiver receiver in nucleus.receivers) { - if (receiver.nucleus is Neuroid neuroid) { + foreach (INucleus receiver in nucleus.receivers) { + if (receiver is Neuroid neuroid) { float value = neuroid.outputValue.magnitude; if (value > maxValue) maxValue = value; @@ -238,8 +238,8 @@ public class ClusterInspector : Editor { float margin = 10 + spacing / 2; int row = 0; - foreach (Receiver receiver in nucleus.receivers) { - INucleus receiverNucleus = receiver.nucleus; + foreach (INucleus receiver in nucleus.receivers) { + INucleus receiverNucleus = receiver; if (receiverNucleus == null) continue; @@ -286,13 +286,14 @@ public class ClusterInspector : Editor { // } // else { - DrawNucleus(synapse.nucleus, pos, maxValue, size); + if (synapse.nucleus != null) + DrawNucleus(synapse.nucleus, pos, maxValue, size); row++; // } } } - private void DrawNucleus(INucleus nucleus, Vector3 position, float maxValue, float size) { + private void DrawNucleus(IReceptor nucleus, Vector3 position, float maxValue, float size) { if (nucleus.isSleeping) Handles.color = Color.darkRed; else { @@ -360,7 +361,7 @@ public class ClusterInspector : Editor { // To do: add HandleClick (see above) to expand the array } - private void HandleMouseHover(INucleus nucleus, Rect rect) { + private void HandleMouseHover(IReceptor nucleus, Rect rect) { GUIContent tooltip; if (nucleus is Perceptoid perceptoid) { if (perceptoid.receptor != null) { @@ -377,10 +378,14 @@ public class ClusterInspector : Editor { $"\nValue: {nucleus.outputValue}"); } } - else { + else if (nucleus is INucleus n) { + tooltip = new( + $"{nucleus.name}" + + $"\nsynapse count {n.synapses.Count}" + + $"\nValue: {nucleus.outputValue}"); + } else { tooltip = new( $"{nucleus.name}" + - $"\nsynapse count {nucleus.synapses.Count}" + $"\nValue: {nucleus.outputValue}"); } @@ -393,9 +398,11 @@ public class ClusterInspector : Editor { GUI.Box(tooltipRect, tooltip); } - private void HandleClicked(INucleus nucleus) { - this.currentNucleus = nucleus; + private void HandleClicked(IReceptor nucleus) { + if (nucleus is INucleus n) { + this.currentNucleus = n; BuildLayers(); + } } void DrawInspector(VisualElement inspectorContainer) { @@ -430,7 +437,7 @@ public class ClusterInspector : Editor { perceptoid.array.RemovePerceptoid(); EditorGUILayout.EndHorizontal(); } - else if (this.currentNucleus is Neuroid neuroid) { + else if (this.currentNucleus is Nucleus neuroid) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150)); if (neuroid.curveMax > 0) @@ -517,9 +524,9 @@ public class ClusterInspector : Editor { return; if (nucleus.cluster != null) this.currentNucleus = nucleus.cluster.output; - foreach (Receiver receiver in nucleus.receivers) { - if (receiver.nucleus != null) { - this.currentNucleus = receiver.nucleus; + foreach (INucleus receiver in nucleus.receivers) { + if (receiver != null) { + this.currentNucleus = receiver; break; } } @@ -548,7 +555,7 @@ public class ClusterInspector : Editor { if (cluster == null) return; - IEnumerable synapseNuclei = this.currentNucleus.synapses.Select(synapse => synapse.nucleus.name); + IEnumerable synapseNuclei = this.currentNucleus.synapses.Select(synapse => synapse.nucleus != null ? synapse.nucleus.name: ""); //IEnumerable perceptei = this.currentNucleus.brain.perceptei.Select(i => i.name).Except(synapseNuclei); IEnumerable nuclei = cluster.nuclei.Select(i => i.name).Except(synapseNuclei); //string[] names = perceptei.Concat(nuclei).ToArray(); @@ -564,7 +571,7 @@ public class ClusterInspector : Editor { // Nucleus n = this.currentNucleus.brain.nuclei[selectedIndex - perceptei.Count()]; // n.AddReceiver(this.currentNucleus); // } - Nucleus n = cluster.nuclei[selectedIndex]; + INucleus n = cluster.nuclei[selectedIndex]; n.AddReceiver(this.currentNucleus); } } @@ -606,7 +613,7 @@ public class ClusterInspector : Editor { public class NeuroidLayer { public int ix = 0; - public List neuroids = new(); + public List neuroids = new(); } public class ClusterWrapper : ScriptableObject { diff --git a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainComponent_Editor.cs b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainComponent_Editor.cs index 749b3d5..6da496b 100644 --- a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainComponent_Editor.cs +++ b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainComponent_Editor.cs @@ -20,7 +20,7 @@ public class NanoBrainComponent_Editor : Editor { public override VisualElement CreateInspectorGUI() { //NanoBrainComponent component = target as NanoBrainComponent; - NanoBrain brain = Application.isPlaying ? component.brain : component.defaultBrain; + Cluster brain = Application.isPlaying ? component.brain : component.defaultBrain; if (Application.isPlaying == false) serializedObject.Update(); @@ -52,8 +52,8 @@ public class NanoBrainComponent_Editor : Editor { minHeight = 500, } }; - NanoBrainInspector.GraphView board; - board = new NanoBrainInspector.GraphView(); + ClusterInspector.GraphView board; + board = new ClusterInspector.GraphView(); board.style.flexGrow = 1; inspectorContainer = new VisualElement { diff --git a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs index 7290c03..c8cc316 100644 --- a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs +++ b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs @@ -73,7 +73,7 @@ public class NanoBrainInspector : Editor { INucleus currentNucleus; GameObject gameObject; private List layers = new(); - private readonly Dictionary neuroidPositions = new(); + private readonly Dictionary neuroidPositions = new(); Vector2 pan = Vector2.zero; //float zoom = 1f; @@ -133,8 +133,8 @@ public class NanoBrainInspector : Editor { NeuroidLayer currentLayer = new() { ix = layerIx }; if (selectedNucleus.receivers != null) { - foreach (Receiver receiver in selectedNucleus.receivers) { - INucleus outputNeuroid = receiver.nucleus; + foreach (INucleus receiver in selectedNucleus.receivers) { + INucleus outputNeuroid = receiver; if (outputNeuroid != null) { AddToLayer(currentLayer, outputNeuroid); // Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}"); @@ -156,7 +156,7 @@ public class NanoBrainInspector : Editor { if (selectedNucleus.synapses != null) { foreach (Synapse synapse in selectedNucleus.synapses) { - INucleus input = synapse.nucleus; + IReceptor input = synapse.nucleus; AddToLayer(currentLayer, input); // Debug.Log($"layer {layerIx} nucleus {input.name}"); } @@ -166,7 +166,7 @@ public class NanoBrainInspector : Editor { } } - private void AddToLayer(NeuroidLayer layer, INucleus nucleus) { + private void AddToLayer(NeuroidLayer layer, IReceptor nucleus) { if (nucleus == null) return; layer.neuroids.Add(nucleus); @@ -223,8 +223,8 @@ public class NanoBrainInspector : Editor { // Determine the maximum value in this layer // This is used to 'scale' the output value colors of the nuclei float maxValue = 0; - foreach (Receiver receiver in nucleus.receivers) { - if (receiver.nucleus is Neuroid neuroid) { + foreach (INucleus receiver in nucleus.receivers) { + if (receiver is Neuroid neuroid) { float value = neuroid.outputValue.magnitude; if (value > maxValue) maxValue = value; @@ -236,8 +236,8 @@ public class NanoBrainInspector : Editor { float margin = 10 + spacing / 2; int row = 0; - foreach (Receiver receiver in nucleus.receivers) { - INucleus receiverNucleus = receiver.nucleus; + foreach (INucleus receiver in nucleus.receivers) { + INucleus receiverNucleus = receiver; if (receiverNucleus == null) continue; @@ -290,7 +290,7 @@ public class NanoBrainInspector : Editor { } } - private void DrawNucleus(INucleus nucleus, Vector3 position, float maxValue, float size) { + private void DrawNucleus(IReceptor nucleus, Vector3 position, float maxValue, float size) { if (nucleus.isSleeping) Handles.color = Color.darkRed; else { @@ -358,7 +358,7 @@ public class NanoBrainInspector : Editor { // To do: add HandleClick (see above) to expand the array } - private void HandleMouseHover(INucleus nucleus, Rect rect) { + private void HandleMouseHover(IReceptor nucleus, Rect rect) { GUIContent tooltip; if (nucleus is Perceptoid perceptoid) { if (perceptoid.receptor != null) { @@ -375,10 +375,15 @@ public class NanoBrainInspector : Editor { $"\nValue: {nucleus.outputValue}"); } } + else if (nucleus is INucleus n) { + tooltip = new( + $"{nucleus.name}" + + $"\nsynapse count {n.synapses.Count}" + + $"\nValue: {nucleus.outputValue}"); + } else { tooltip = new( $"{nucleus.name}" + - $"\nsynapse count {nucleus.synapses.Count}" + $"\nValue: {nucleus.outputValue}"); } @@ -391,9 +396,11 @@ public class NanoBrainInspector : Editor { GUI.Box(tooltipRect, tooltip); } - private void HandleClicked(INucleus nucleus) { - this.currentNucleus = nucleus; - BuildLayers(); + private void HandleClicked(IReceptor nucleus) { + if (nucleus is INucleus n) { + this.currentNucleus = n; + BuildLayers(); + } } void DrawInspector(VisualElement inspectorContainer) { @@ -515,9 +522,9 @@ public class NanoBrainInspector : Editor { return; if (nucleus.cluster != null) this.currentNucleus = nucleus.cluster.output; - foreach (Receiver receiver in nucleus.receivers) { - if (receiver.nucleus != null) { - this.currentNucleus = receiver.nucleus; + foreach (INucleus receiver in nucleus.receivers) { + if (receiver != null) { + this.currentNucleus = receiver; break; } } @@ -561,8 +568,8 @@ public class NanoBrainInspector : Editor { // Nucleus n = this.currentNucleus.brain.nuclei[selectedIndex - perceptei.Count()]; // n.AddReceiver(this.currentNucleus); // } - Nucleus n = this.currentNucleus.cluster.nuclei[selectedIndex]; - n.AddReceiver(this.currentNucleus); + INucleus n = this.currentNucleus.cluster.nuclei[selectedIndex]; + n.AddReceiver(this.currentNucleus); } } diff --git a/Assets/NanoBrain/VisualEditor/NanoBrain.cs b/Assets/NanoBrain/VisualEditor/NanoBrain.cs index 8ac0896..e9b5a8b 100644 --- a/Assets/NanoBrain/VisualEditor/NanoBrain.cs +++ b/Assets/NanoBrain/VisualEditor/NanoBrain.cs @@ -17,7 +17,7 @@ public class NanoBrain : ScriptableObject, ISerializationCallbackReceiver { public NanoBrain() { // this.cluster = new(); // this.output = new Neuroid(this.cluster, "Root"); - } + } public Cluster cluster; @@ -38,7 +38,9 @@ public class NanoBrain : ScriptableObject, ISerializationCallbackReceiver { } public void OnBeforeSerialize() { - this.rootId = output.id; + if (output != null) { + this.rootId = output.id; + } } public void OnAfterDeserialize() { try { diff --git a/Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs b/Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs index 5be46f1..475e1a0 100644 --- a/Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs +++ b/Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs @@ -1,11 +1,11 @@ using UnityEngine; public class NanoBrainComponent : MonoBehaviour { - public NanoBrain defaultBrain; - private NanoBrain brainInstance; + public Cluster defaultBrain; + private Cluster brainInstance; - public Nucleus root => brainInstance.output; - public NanoBrain brain { + public INucleus root => brainInstance.output; + public Cluster brain { get { if (brainInstance == null && defaultBrain != null) { brainInstance = Instantiate(defaultBrain); @@ -21,8 +21,8 @@ public class NanoBrainComponent : MonoBehaviour { } } - public static void UpdateWeight(NanoBrain brain, string name, float weight) { - Nucleus root = brain.output; + public static void UpdateWeight(Cluster brain, string name, float weight) { + INucleus root = brain.output; foreach (Synapse synapse in root.synapses) { if (synapse.nucleus.name == name) { synapse.weight = weight; diff --git a/Assets/Scenes/Boids/New Cluster.asset b/Assets/Scenes/Boids/New Cluster.asset deleted file mode 100644 index 4f690b1..0000000 --- a/Assets/Scenes/Boids/New Cluster.asset +++ /dev/null @@ -1,20 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3} - m_Name: New Cluster - m_EditorClassIdentifier: Assembly-CSharp::Cluster - nuclei: - - id: 764290112 - _name: Output - _synapses: [] - _receivers: [] - nucleusType: diff --git a/Assets/Scenes/Boids/NewSwarm.asset b/Assets/Scenes/Boids/NewSwarm.asset new file mode 100644 index 0000000..d64c329 --- /dev/null +++ b/Assets/Scenes/Boids/NewSwarm.asset @@ -0,0 +1,100 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3} + m_Name: NewSwarm + m_EditorClassIdentifier: Assembly-CSharp::Cluster + nuclei: + - rid: 2243601034565648442 + - rid: 2243601034565648443 + references: + version: 2 + RefIds: + - rid: 2243601034565648442 + type: {class: Neuroid, ns: , asm: Assembly-CSharp} + data: + id: 322343360 + _name: Output + _synapses: + - nucleus: + rid: 2243601034565648443 + cluster: {fileID: 0} + weight: 1 + curveMax: 1 + _receivers: [] + nucleusType: + _curvePreset: 0 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1000 + value: 1000 + inSlope: 1 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + curveMax: 1 + average: 0 + inverse: 0 + exponent: 1 + - rid: 2243601034565648443 + type: {class: Neuroid, ns: , asm: Assembly-CSharp} + data: + id: -1924138416 + _name: Avoidance + _synapses: [] + _receivers: + - rid: 2243601034565648442 + nucleusType: + _curvePreset: 0 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1000 + value: 1000 + inSlope: 1 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + curveMax: 1 + average: 0 + inverse: 0 + exponent: 1 diff --git a/Assets/Scenes/Boids/New Cluster.asset.meta b/Assets/Scenes/Boids/NewSwarm.asset.meta similarity index 100% rename from Assets/Scenes/Boids/New Cluster.asset.meta rename to Assets/Scenes/Boids/NewSwarm.asset.meta diff --git a/Assets/Scenes/Boids/Prefabs/Boid.prefab b/Assets/Scenes/Boids/Prefabs/Boid.prefab index 37277d0..f4f2d9b 100644 --- a/Assets/Scenes/Boids/Prefabs/Boid.prefab +++ b/Assets/Scenes/Boids/Prefabs/Boid.prefab @@ -179,4 +179,4 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 92f34a5e4027a1dc39efd8ce63cf6aba, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::NanoBrainComponent - defaultBrain: {fileID: 11400000, guid: fc1a4800a8c531eb4855b436bc9084ae, type: 2} + defaultBrain: {fileID: 11400000, guid: eddc759ede59e66cd936ad6ae2c55c46, type: 2} diff --git a/Assets/Scenes/Boids/Scripts/Boid.cs b/Assets/Scenes/Boids/Scripts/Boid.cs index 7b16c88..a3f81d3 100644 --- a/Assets/Scenes/Boids/Scripts/Boid.cs +++ b/Assets/Scenes/Boids/Scripts/Boid.cs @@ -1,104 +1,109 @@ -using UnityEngine; - -//[RequireComponent(typeof(NanoBrain))] -[RequireComponent(typeof(NanoBrainComponent))] -public class Boid : MonoBehaviour { - public static int BoundaryType = 1; - public static int BoidType = 2; - public static int BoidVelocityType =3; - - public SwarmControl sc; - public Vector3 velocity = Vector3.zero; - public Vector3 acceleration = Vector3.zero; - - private Bounds innerBounds; - - public NanoBrainComponent nanoBrain; - public Receptor boundaryReceptor; - public Receptor boidReceptor; - public Receptor boidVelocityReceptor; - - public int id; - - public Material red; - public Material gray; - - void Awake() { - this.id = this.GetInstanceID(); - - nanoBrain = GetComponent(); - boundaryReceptor = Receptor.GetReceptor(nanoBrain.brain, BoundaryType); - boidReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidType); - boidVelocityReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidVelocityType); - - sc = FindFirstObjectByType(); - - innerBounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth); - } - - void Update() { - Collider[] results = Physics.OverlapSphere(this.transform.position, sc.perceptionDistance); - foreach (Collider c in results) { - if (c as CapsuleCollider != null) { - Boid neighbour = c.GetComponentInParent(); - if (neighbour == null || neighbour == this) - continue; - - int thingId = neighbour.GetInstanceID(); - - Vector3 localPosition = this.transform.InverseTransformPoint(neighbour.transform.position); - float d = localPosition.magnitude; - if (d <= sc.separationDistance) - localPosition = localPosition.normalized * 0.01f; - else - localPosition = localPosition.normalized * (localPosition.magnitude - sc.separationDistance); - if (localPosition.sqrMagnitude > 0) - boidReceptor?.ProcessStimulus(thingId, localPosition, neighbour.name); - - Vector3 localVelocity = this.transform.InverseTransformVector(neighbour.velocity); - if (localVelocity.sqrMagnitude > 0) - boidVelocityReceptor?.ProcessStimulus(thingId, localVelocity); - } - } - - if (!innerBounds.Contains(this.transform.position)) { - Vector3 point = this.transform.position; - Vector3 pointOnBounds = innerBounds.ClosestPoint(point); - Vector3 desiredWorldSpace = (pointOnBounds - point).normalized * sc.speed; - Vector3 desiredLocalSpace = -this.transform.InverseTransformPoint(desiredWorldSpace); - boundaryReceptor.ProcessStimulus(777, desiredLocalSpace); - } - - Vector3 worldForce = this.transform.TransformDirection(nanoBrain.root.outputValue); - - this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity; - if (this.velocity.magnitude > 0) - this.velocity = this.velocity.normalized * sc.speed; - else - this.velocity = this.transform.forward * sc.speed; - //Debug.DrawRay(this.transform.position, this.velocity, Color.blue); - - this.transform.position += this.velocity * Time.deltaTime; - - if (this.velocity != Vector3.zero) { - Quaternion targetRotation = Quaternion.LookRotation(this.velocity); - transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f); // Adjust the speed of rotation - } - - nanoBrain.brain.UpdateNuclei(); - - // Renderer renderer = GetComponentInChildren(); - // results = Physics.OverlapSphere(this.transform.position, 0.1f); - // if (results.Length > 1) { - // // string s= this.name; - // // foreach (Collider c in results) - // // s += " " + c.transform.parent.gameObject.name; - // // Debug.Log(s); - // renderer.sharedMaterial = red; - // } - // else { - // renderer.sharedMaterial = gray; - // } - } - -} +using UnityEngine; + +//[RequireComponent(typeof(NanoBrain))] +[RequireComponent(typeof(NanoBrainComponent))] +public class Boid : MonoBehaviour { + public static int BoundaryType = 1; + public static int BoidType = 2; + public static int BoidVelocityType =3; + + public SwarmControl sc; + public Vector3 velocity = Vector3.zero; + public Vector3 acceleration = Vector3.zero; + + private Bounds innerBounds; + + public NanoBrainComponent nanoBrain; + public Receptor boundaryReceptor; + public Receptor boidReceptor; + public Receptor boidVelocityReceptor; + + public int id; + + public Material red; + public Material gray; + + void Awake() { + this.id = this.GetInstanceID(); + + nanoBrain = GetComponent(); + // boundaryReceptor = Receptor.GetReceptor(nanoBrain.brain, BoundaryType); + // boidReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidType); + // boidVelocityReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidVelocityType); + boundaryReceptor = Receptor.CreateReceptor(nanoBrain.brain, "Avoidance"); + boundaryReceptor.name = "Boundary"; + boundaryReceptor.receivers[0].synapses[0].weight = -1; + boidReceptor = Receptor.CreateReceptor(nanoBrain.brain, "Boid"); + boidVelocityReceptor = Receptor.CreateReceptor(nanoBrain.brain, "Alignment"); + + sc = FindFirstObjectByType(); + + innerBounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth); + } + + void Update() { + Collider[] results = Physics.OverlapSphere(this.transform.position, sc.perceptionDistance); + foreach (Collider c in results) { + if (c as CapsuleCollider != null) { + Boid neighbour = c.GetComponentInParent(); + if (neighbour == null || neighbour == this) + continue; + + int thingId = neighbour.GetInstanceID(); + + Vector3 localPosition = this.transform.InverseTransformPoint(neighbour.transform.position); + float d = localPosition.magnitude; + if (d <= sc.separationDistance) + localPosition = localPosition.normalized * 0.01f; + else + localPosition = localPosition.normalized * (localPosition.magnitude - sc.separationDistance); + if (localPosition.sqrMagnitude > 0) + boidReceptor?.ProcessStimulus(thingId, localPosition, neighbour.name); + + Vector3 localVelocity = this.transform.InverseTransformVector(neighbour.velocity); + if (localVelocity.sqrMagnitude > 0) + boidVelocityReceptor?.ProcessStimulus(thingId, localVelocity); + } + } + + if (!innerBounds.Contains(this.transform.position)) { + Vector3 point = this.transform.position; + Vector3 pointOnBounds = innerBounds.ClosestPoint(point); + Vector3 desiredWorldSpace = (pointOnBounds - point).normalized * sc.speed; + Vector3 desiredLocalSpace = -this.transform.InverseTransformPoint(desiredWorldSpace); + boundaryReceptor.ProcessStimulus(777, desiredLocalSpace); + } + + Vector3 worldForce = this.transform.TransformDirection(nanoBrain.root.outputValue); + + this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity; + if (this.velocity.magnitude > 0) + this.velocity = this.velocity.normalized * sc.speed; + else + this.velocity = this.transform.forward * sc.speed; + //Debug.DrawRay(this.transform.position, this.velocity, Color.blue); + + this.transform.position += this.velocity * Time.deltaTime; + + if (this.velocity != Vector3.zero) { + Quaternion targetRotation = Quaternion.LookRotation(this.velocity); + transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f); // Adjust the speed of rotation + } + + nanoBrain.brain.UpdateNuclei(); + + // Renderer renderer = GetComponentInChildren(); + // results = Physics.OverlapSphere(this.transform.position, 0.1f); + // if (results.Length > 1) { + // // string s= this.name; + // // foreach (Collider c in results) + // // s += " " + c.transform.parent.gameObject.name; + // // Debug.Log(s); + // renderer.sharedMaterial = red; + // } + // else { + // renderer.sharedMaterial = gray; + // } + } + +} diff --git a/Assets/Scenes/Boids/Scripts/Editor/SwarmControl_Editor.cs b/Assets/Scenes/Boids/Scripts/Editor/SwarmControl_Editor.cs index 5adbcb7..d8dbd3b 100644 --- a/Assets/Scenes/Boids/Scripts/Editor/SwarmControl_Editor.cs +++ b/Assets/Scenes/Boids/Scripts/Editor/SwarmControl_Editor.cs @@ -1,33 +1,33 @@ -using UnityEditor; -using UnityEngine; - -[CustomEditor(typeof(SwarmControl))] -public class SwarmControl_Editor : Editor { - public override void OnInspectorGUI() { - EditorGUI.BeginChangeCheck(); - - DrawDefaultInspector(); - - if (EditorGUI.EndChangeCheck()) { - SwarmControl swarmControl = (SwarmControl)target; - NanoBrain[] nanoBrains = FindObjectsByType(FindObjectsSortMode.None); - - foreach (NanoBrain brain in nanoBrains) { - NanoBrainComponent.UpdateWeight(brain, "Avoidance", swarmControl.avoidanceForce); - NanoBrainComponent.UpdateWeight(brain, "Cohesion", swarmControl.cohesionForce); - NanoBrainComponent.UpdateWeight(brain, "Separation", swarmControl.separationForce); - NanoBrainComponent.UpdateWeight(brain, "Alignment", swarmControl.alignmentForce); - } - Debug.Log("Updated weights"); - } - } - - // protected void UpdateWeight(NanoBrain brain, string name, float weight) { - // Nucleus root = brain.root; - // foreach (Synapse synapse in root.synapses) { - // if (synapse.nucleus.name == name) { - // synapse.weight = weight; - // } - // } - // } +using UnityEditor; +using UnityEngine; + +[CustomEditor(typeof(SwarmControl))] +public class SwarmControl_Editor : Editor { + public override void OnInspectorGUI() { + EditorGUI.BeginChangeCheck(); + + DrawDefaultInspector(); + + if (EditorGUI.EndChangeCheck()) { + SwarmControl swarmControl = (SwarmControl)target; + Cluster[] nanoBrains = FindObjectsByType(FindObjectsSortMode.None); + + foreach (Cluster brain in nanoBrains) { + NanoBrainComponent.UpdateWeight(brain, "Avoidance", swarmControl.avoidanceForce); + NanoBrainComponent.UpdateWeight(brain, "Cohesion", swarmControl.cohesionForce); + NanoBrainComponent.UpdateWeight(brain, "Separation", swarmControl.separationForce); + NanoBrainComponent.UpdateWeight(brain, "Alignment", swarmControl.alignmentForce); + } + Debug.Log("Updated weights"); + } + } + + // protected void UpdateWeight(NanoBrain brain, string name, float weight) { + // Nucleus root = brain.root; + // foreach (Synapse synapse in root.synapses) { + // if (synapse.nucleus.name == name) { + // synapse.weight = weight; + // } + // } + // } } \ No newline at end of file diff --git a/Assets/Scenes/Boids/SwarmingBrain.asset b/Assets/Scenes/Boids/SwarmingBrain.asset index ffe8dd0..a0943f9 100644 --- a/Assets/Scenes/Boids/SwarmingBrain.asset +++ b/Assets/Scenes/Boids/SwarmingBrain.asset @@ -7478,8 +7478,8 @@ MonoBehaviour: array: rid: -2 thingType: 3 - cluster: {fileID: 0} rootId: -1707533328 + cluster: {fileID: 0} references: version: 2 RefIds: