Cluster no longer clones ClusterPrefab

This commit is contained in:
Pascal Serrarens 2026-01-29 17:41:55 +01:00
parent b60fc19d96
commit 5cb9e788a4
9 changed files with 262 additions and 116 deletions

View File

@ -7,38 +7,31 @@ using static Unity.Mathematics.math;
[System.Serializable]
public class Cluster : INucleus {
// The ScriptableObject asset from which the runtime object has been created
[SerializeField]
public ClusterPrefab prefab;
public ClusterPrefab storedPrefab;
//public ClusterPrefab prefab;
public ClusterPrefab cluster { get; set; }
[SerializeField]
protected string _name;
public virtual string name {
get => _name;
set => _name = value;
public Cluster() {
}
public Cluster(Cluster parent) {
this.parent = parent;
}
public Cluster(ClusterPrefab prefab) {
this.prefab = UnityEngine.Object.Instantiate(prefab);
this.storedPrefab = prefab;
//this.prefab = prefab.Clone();
this.name = prefab.name;
this.cluster = null;
if (this.cluster != null)
this.cluster.nuclei.Add(this);
this.cluster.nuclei.Add(this);
}
// Hmm, a cluster instance can never be part of a scriptable object...(Cluster)
public Cluster(ClusterPrefab parent, ClusterPrefab prefab) {
this.prefab = prefab.Clone(); //UnityEngine.Object.Instantiate(prefab);
this.name = prefab.name;
public Cluster(ClusterPrefab parent, ClusterPrefab realPrefab) {
this.storedPrefab = realPrefab;
//this.prefab = realPrefab.Clone();
this.name = realPrefab.name;
this.cluster = parent;
if (this.cluster != null)
this.cluster.nuclei.Add(this);
// foreach (IReceptor nucleus in this.prefab.nuclei) {
// IReceptor clone = nucleus.CloneTo(null);
// this.dynamicNuclei.Add(clone);
// }
}
public virtual IReceptor Clone() {
@ -56,7 +49,112 @@ public class Cluster : INucleus {
return clone;
}
public INucleus output => prefab.output;
// public IReceptor CloneTo(ClusterPrefab parent) {
// Cluster clone = new(parent, this.prefab);
// return clone;
// }
public IReceptor ShallowCloneTo(Cluster parent) {
Cluster clone = new(parent);
return clone;
}
// public IReceptor ShallowCloneTo(ClusterPrefab parent) {
// Cluster clone = new(parent, this.prefab);
// return clone;
// }
// Deep clone a nucleus with its connections
public virtual Cluster InstantiatePrefab(ClusterPrefab prefab) {
Cluster clone = new Cluster {
nuclei = new()
};
IReceptor[] nucleiArray = this.nuclei.ToArray();
// first clone the nuclei without their connections
foreach (IReceptor nucleus in this.nuclei)
nucleus.ShallowCloneTo(clone);
IReceptor[] clonedNuclei = clone.nuclei.ToArray();
// Now clone the connections
for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) {
IReceptor receptor = nucleiArray[nucleusIx];
IReceptor clonedReceptor = clonedNuclei[nucleusIx];
if (clonedReceptor == null)
continue;
// Copy the synapses
if (receptor is INucleus nucleus) {
foreach (Synapse synapse in nucleus.synapses) {
if (clonedReceptor is not INucleus clonedNucleus)
continue;
int ix = GetNucleusIndex(nucleiArray, synapse.nucleus);
if (ix < 0)
continue;
IReceptor clonedSynapseNucleus = clonedNuclei[ix];
if (clonedSynapseNucleus == null)
continue;
clonedNucleus.AddSynapse(clonedSynapseNucleus, synapse.weight);
}
}
// Copy the receivers
foreach (INucleus receiver in nucleiArray[nucleusIx].receivers) {
int ix = GetNucleusIndex(nucleiArray, receiver);
if (ix < 0)
continue;
if (clonedNuclei[ix] is not INucleus clonedReceiver)
continue;
clonedReceptor.AddReceiver(clonedReceiver);
}
}
return clone;
}
private int GetNucleusIndex(IReceptor[] nucleiArray, IReceptor nucleus) {
for (int i = 0; i < nucleiArray.Length; i++) {
if (nucleus == nucleiArray[i])
return i;
}
return -1;
}
public ClusterPrefab cluster { get; set; }
public Cluster parent { get; set; }
[SerializeReference]
public List<IReceptor> nuclei = new();
[SerializeField]
protected string _name;
public virtual string name {
get => _name;
set => _name = value;
}
public List<INucleus> _inputs = null;
public virtual List<INucleus> inputs {
get {
if (this._inputs == null) {
this._inputs = new();
foreach (IReceptor receptor in this.nuclei) {
if (receptor is INucleus nucleus) {
// inputs have no incoming synapses yet.
if (nucleus.synapses.Count == 0)
this._inputs.Add(nucleus);
}
}
}
return this._inputs;
}
}
//public INucleus output => prefab.output;
public virtual INucleus output => this.nuclei[0] as INucleus;
// Not sure if this belongs here...
[SerializeReference]
@ -68,27 +166,14 @@ public class Cluster : INucleus {
#region Synapses
// class ClusterSynapse : Synapse {
// public IReceptor receptor;
// public ClusterSynapse(IReceptor nucleus, INucleus receptor, float weight = 1.0f) : base(nucleus, weight) {
// this.receptor = receptor;
// }
// }
[SerializeField]
private List<Synapse> _synapses = new();
public List<Synapse> synapses => _synapses;
public Synapse AddSynapse(IReceptor sendingNucleus) {
Synapse synapse = new(sendingNucleus); //, this.prefab.inputs[0]);
public Synapse AddSynapse(IReceptor sendingNucleus, float weight = 1.0f) {
Synapse synapse = new(sendingNucleus, weight); //, this.prefab.inputs[0]);
this._synapses.Add(synapse);
return synapse;
// else {
// INucleus receptor = (INucleus)this.prefab.nuclei.Find(nucleus => nucleus is INucleus n && nucleus.name == nucleusName);
// ClusterSynapse synapse = new(sendingNucleus, receptor);
// receptor.AddSynapse(sendingNucleus);
// }
// // Add synapse to which neuron?
// return null;
}
// Does this even exist already?
@ -117,48 +202,6 @@ public class Cluster : INucleus {
receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
}
// public void AddReceiver(INucleus receivingNucleus) {
// int newLength = this._receivers.Count + 1;
// INucleus[] newReceivers = new INucleus[newLength];
// // Copy the existing receivers
// for (int ix = 0; ix < this._receivers.Count; ix++)
// newReceivers[ix] = this._receivers[ix];
// // Add the new receivers
// newReceivers[this._receivers.Count] = receivingNucleus;
// // Replace the receivers with the new receivers
// this._receivers = new(newReceivers);
// receivingNucleus.AddSynapse(this);
// }
// public void RemoveReceiver(INucleus receivingNucleus) {
// Debug.Log("Clusterinstance. remote receiver");
// int newLength = this._receivers.Count - 1;
// if (newLength < 0)
// // Array was empty, so we cannot remove anything
// return;
// INucleus[] newReceivers = new INucleus[newLength];
// int newIx = 0;
// // Copy all receivers except receivingNucleus
// for (int ix = 0; ix < this._receivers.Count; ix++) {
// if (this._receivers[ix] == receivingNucleus)
// // skip the receiver we want to remote
// continue;
// if (newIx >= newLength)
// // We want to copy more elements than expected
// // the receivingNucleus is not found
// // and the original array is returned
// return;
// newReceivers[newIx] = this._receivers[ix];
// newIx++;
// }
// this._receivers = new(newReceivers);
// }
#endregion Receivers
#region Runtime
@ -190,10 +233,10 @@ public class Cluster : INucleus {
foreach (Synapse synapse in this.synapses) {
sum += synapse.weight * synapse.nucleus.outputValue;
}
// This does not work because the prefab nucleus does not have a state
this.prefab.inputs[0].UpdateState(sum);
//this._outputValue = this.output.outputValue;
//this.prefab.inputs[0].UpdateState(sum);
this.inputs[0].UpdateState(sum);
UpdateResult(this.output.outputValue);
}
@ -208,13 +251,14 @@ public class Cluster : INucleus {
foreach (INucleus receiver in this.receivers)
receiver.UpdateState();
}
public void UpdateNuclei() {
this.stale++;
if (this.stale > 2)
if (this.stale > 2)
_outputValue = Vector3.zero;
foreach (IReceptor nucleus in this.prefab.nuclei)
//foreach (IReceptor nucleus in this.prefab.nuclei)
foreach (IReceptor nucleus in this.nuclei)
nucleus.UpdateNuclei();
}

View File

@ -36,12 +36,67 @@ public class ClusterPrefab : ScriptableObject {
}
}
/*
// Deep clone a nucleus with its connections
public virtual ClusterPrefab Clone() {
ClusterPrefab clone = Instantiate(this);
clone.nuclei = new();
// foreach (IReceptor nucleus in this.nuclei)
// nucleus.CloneTo(clone);
IReceptor[] nucleiArray = this.nuclei.ToArray();
// first clone the nuclei without their connections
foreach (IReceptor nucleus in this.nuclei)
nucleus.ShallowCloneTo(clone);
IReceptor[] clonedNuclei = clone.nuclei.ToArray();
// Now clone the connections
for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) {
IReceptor receptor = nucleiArray[nucleusIx];
IReceptor clonedReceptor = clonedNuclei[nucleusIx];
if (clonedReceptor == null)
continue;
// Copy the synapses
if (receptor is INucleus nucleus) {
foreach (Synapse synapse in nucleus.synapses) {
if (clonedReceptor is not INucleus clonedNucleus)
continue;
int ix = GetNucleusIndex(nucleiArray, synapse.nucleus);
if (ix < 0)
continue;
IReceptor clonedSynapseNucleus = clonedNuclei[ix];
if (clonedSynapseNucleus == null)
continue;
clonedNucleus.AddSynapse(clonedSynapseNucleus, synapse.weight);
}
}
// Copy the receivers
foreach (INucleus receiver in nucleiArray[nucleusIx].receivers) {
int ix = GetNucleusIndex(nucleiArray, receiver);
if (ix < 0)
continue;
if (clonedNuclei[ix] is not INucleus clonedReceiver)
continue;
clonedReceptor.AddReceiver(clonedReceiver);
}
}
return clone;
}
private int GetNucleusIndex(IReceptor[] nucleiArray, IReceptor nucleus) {
for (int i = 0; i < nucleiArray.Length; i++) {
if (nucleus == nucleiArray[i])
return i;
}
return -1;
}
*/
// 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.

View File

@ -10,7 +10,7 @@ public interface INucleus : IReceptor {
// Senders
public List<Synapse> synapses { get; }
public Synapse AddSynapse(IReceptor sender);
public Synapse AddSynapse(IReceptor sender, float weight = 1.0f);
public NucleusArray array { get; set; }
@ -48,6 +48,8 @@ public interface IReceptor {
#endregion dynamic
//public IReceptor ShallowCloneTo(ClusterPrefab parent);
public IReceptor ShallowCloneTo(Cluster parent);
//public IReceptor CloneTo(ClusterPrefab parent);
public IReceptor Clone();
}

View File

@ -88,6 +88,7 @@ public class Neuron : INucleus {
#region Runtime state (not serialized)
public ClusterPrefab cluster { get; set; }
public Cluster parent { get; set; }
#region Activation
@ -159,12 +160,19 @@ public class Neuron : INucleus {
this.stale++;
// this._isSleeping = this.stale > 2;
// if (isSleeping)
if (this.stale > 2)
if (this.stale > 2)
_outputValue = Vector3.zero;
}
#endregion Runtime state
public Neuron(Cluster parent, string name) {
this.parent = parent;
this.name = name;
if (this.cluster != null) {
this.cluster.nuclei.Add(this);
}
}
public Neuron(ClusterPrefab parent, string name) {
this.cluster = parent;
this.name = name;
@ -179,6 +187,28 @@ public class Neuron : INucleus {
// this._name = name;
// }
// this clone the nucleus without the synapses and receivers
public virtual IReceptor ShallowCloneTo(Cluster newParent) {
Neuron clone = new(newParent, this.name) {
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
average = this.average
};
return clone;
}
public virtual IReceptor ShallowCloneTo(ClusterPrefab newParent) {
Neuron clone = new(newParent, this.name) {
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
average = this.average
};
return clone;
}
public virtual IReceptor CloneTo(ClusterPrefab parent) {
Neuron clone = new(parent, this.name) {
array = this.array,
@ -187,8 +217,6 @@ public class Neuron : INucleus {
curveMax = this.curveMax,
average = this.average
};
// if (clone.cluster != null)
// clone.cluster.nuclei.Add(clone);
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
@ -254,8 +282,8 @@ public class Neuron : INucleus {
}
}
public Synapse AddSynapse(IReceptor sendingNucleus) {
Synapse synapse = new(sendingNucleus);
public Synapse AddSynapse(IReceptor sendingNucleus, float weight = 1.0f) {
Synapse synapse = new(sendingNucleus, weight);
this.synapses.Add(synapse);
return synapse;
}

View File

@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[Serializable]
public class Receptor : IReceptor {
private Cluster cluster;
private ClusterPrefab cluster;
private Cluster parent;
[SerializeField]
protected string _name;
@ -14,16 +17,21 @@ public class Receptor : IReceptor {
set => _name = value;
}
public Receptor(Cluster cluster) {
public Receptor(Cluster parent) {
this.parent = parent;
if (parent != null)
parent.nuclei.Add(this);
}
public Receptor(ClusterPrefab cluster) {
this.cluster = cluster;
if (cluster != null)
cluster.prefab.nuclei.Add(this);
cluster.nuclei.Add(this);
}
public Receptor(Cluster cluster, INucleus nucleus) {
public Receptor(ClusterPrefab cluster, INucleus nucleus) {
this.cluster = cluster;
if (cluster != null)
cluster.prefab.nuclei.Add(this);
cluster.nuclei.Add(this);
this.AddReceiver(nucleus);
}
@ -31,8 +39,10 @@ public class Receptor : IReceptor {
if (cluster == null)
return null;
//Receptor receptor = new(cluster.prefab);
Receptor receptor = new(cluster);
foreach (INucleus nucleus in cluster.prefab.inputs) {
//foreach (INucleus nucleus in cluster.prefab.inputs) {
foreach (INucleus nucleus in cluster.inputs) {
if (nucleus != null && nucleus.name == nucleusName) {
// Receptor receptor = new(cluster, nucleus);
// return receptor;
@ -45,7 +55,16 @@ public class Receptor : IReceptor {
return receptor;
}
public virtual IReceptor CloneTo(Cluster parent) {
public virtual IReceptor ShallowCloneTo(Cluster parent) {
Receptor clone = new(parent);
return clone;
}
public virtual IReceptor ShallowCloneTo(ClusterPrefab parent) {
Receptor clone = new(parent);
return clone;
}
public virtual IReceptor CloneTo(ClusterPrefab parent) {
Receptor clone = new(parent);
foreach (INucleus receiver in this.receivers) {

View File

@ -13,18 +13,18 @@ MonoBehaviour:
m_Name: Velocity
m_EditorClassIdentifier: Assembly-CSharp::ClusterPrefab
nuclei:
- rid: 2243601420268863545
- rid: 2243601425629446539
references:
version: 2
RefIds:
- rid: 2243601420268863545
- rid: 2243601425629446539
type: {class: Neuron, ns: , asm: Assembly-CSharp}
data:
_name: Output
_synapses: []
_receivers: []
_array:
rid: 2243601420268863546
rid: 2243601425629446540
_curvePreset: 0
curve:
serializedVersion: 2
@ -52,9 +52,9 @@ MonoBehaviour:
m_RotationOrder: 4
curveMax: 1
average: 0
- rid: 2243601420268863546
- rid: 2243601425629446540
type: {class: NucleusArray, ns: , asm: Assembly-CSharp}
data:
_nuclei:
- rid: 2243601420268863545
- rid: 2243601425629446539
name: Output

View File

@ -620,19 +620,16 @@ public class ClusterInspector : Editor {
ClusterPickerWindow.ShowPicker(brain => OnClusterPicked(nucleus, brain), "Select Cluster");
}
private void OnClusterPicked(INucleus nucleus, ClusterPrefab subCluster) {
Cluster subclusterInstance = new(this.cluster, subCluster);
//this.cluster.AddSubCluster(subclusterInstance);
//this.cluster.nuclei.Add(subclusterInstance);
private void OnClusterPicked(INucleus nucleus, ClusterPrefab prefab) {
Cluster subclusterInstance = new(this.cluster, prefab);
subclusterInstance.AddReceiver(nucleus);
}
private void EditCluster(Cluster subCluster) {
//var currentActiveObject = Selection.activeObject;
Selection.activeObject = subCluster.prefab;
EditorGUIUtility.PingObject(subCluster.prefab);
var editor = Editor.CreateEditor(subCluster.prefab);
//Selection.activeObject = currentActiveObject;
// May be used with storedPrefab...
Selection.activeObject = subCluster.storedPrefab;
EditorGUIUtility.PingObject(subCluster.storedPrefab);
var editor = Editor.CreateEditor(subCluster.storedPrefab);
}
// Connect to another nucleus in the same cluster

View File

@ -22,8 +22,8 @@ public class NanoBrainComponent_Editor : Editor {
}
public override VisualElement CreateInspectorGUI() {
//NanoBrainComponent component = target as NanoBrainComponent;
ClusterPrefab brain = Application.isPlaying ? component.brain.prefab : component.defaultBrain;
//ClusterPrefab brain = Application.isPlaying ? component.brain.prefab : component.defaultBrain;
Cluster brain = component.brain;
if (Application.isPlaying == false)
serializedObject.Update();
@ -79,7 +79,7 @@ public class NanoBrainComponent_Editor : Editor {
});
if (brain != null && board != null)
board.SetGraph(component.gameObject, brain, brain.output, inspectorContainer);
board.SetGraph(component.gameObject, brain.storedPrefab, brain.output, inspectorContainer);
// else
// Debug.LogWarning(" No brain!");

View File

@ -1,6 +1,7 @@
using UnityEngine;
public class NanoBrainComponent : MonoBehaviour {
[SerializeField]
public ClusterPrefab defaultBrain;
private Cluster brainInstance;