Added insect body parts

This commit is contained in:
Pascal Serrarens 2026-04-23 17:39:51 +02:00
parent 4cb6286f20
commit 9eda1cdf06
19 changed files with 1789 additions and 1220 deletions

View File

@ -17,23 +17,13 @@ namespace CreatureControl {
EditorGUILayout.BeginHorizontal();
string legName = ConvertCamelCase(legProp.name);
showfield = EditorGUILayout.Foldout(showfield, legName, true, foldoutStyle);
SerializedProperty femurProp = legProp.FindPropertyRelative(nameof(Leg.femur));
SerializedProperty tibiaProp = legProp.FindPropertyRelative(nameof(Leg.tibia));
SerializedProperty tarsusProp = legProp.FindPropertyRelative(nameof(Leg.tarsus));
SerializedProperty femurProp = legProp.FindPropertyRelative("_femur");//nameof(Leg._femur));
SerializedProperty tibiaProp = legProp.FindPropertyRelative("_tibia"); //nameof(Leg._tibia));
SerializedProperty tarsusProp = legProp.FindPropertyRelative("_tarsus"); //nameof(Leg._tarsus));
Transform newFemur = (Transform)EditorGUILayout.ObjectField(femurProp.objectReferenceValue, typeof(Transform), true);
if (newFemur != femurProp.objectReferenceValue) {
// Try to determine further bones when the femur has been updated
femurProp.objectReferenceValue = newFemur;
if (newFemur != null) {
if (tibiaProp.objectReferenceValue == null && newFemur.childCount == 1)
tibiaProp.objectReferenceValue = newFemur.GetChild(0);
Transform tibia = (Transform)tibiaProp.objectReferenceValue;
if (tibia != null) {
if (tarsusProp.objectReferenceValue == null && tibia.childCount == 1)
tarsusProp.objectReferenceValue = tibia.GetChild(0);
}
}
leg.DetectBones(newFemur);
anythingChanged = true;
}
EditorGUILayout.EndHorizontal();

View File

@ -0,0 +1,9 @@
namespace CreatureControl {
public enum Side {
Unknown,
Left,
Right
};
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0c445c1f36ba4b819cc479fe343eaa5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
using UnityEngine;
namespace CreatureControl {
[System.Serializable]
public class Antenna {
private Transform _scape;
public Transform scape {
get => this._scape;
set {
this._scape = value;
this._scapeLength = 0;
}
}
private Transform _funiculus;
public Transform funiculus {
get => this._funiculus;
set {
this._funiculus = value;
this._scapeLength = 0;
this._funiculusLength = 0;
}
}
private Transform _end;
private float _scapeLength = 0;
public float scapeLength {
get {
if (_scapeLength <= 0 && this.scape != null && this._funiculus != null)
_scapeLength = Vector3.Distance(this._scape.position, this._funiculus.position);
return _scapeLength;
}
}
private float _funiculusLength = 0;
public float funuculusLength {
get {
if (_funiculusLength <= 0 && this._funiculus != null && this._end != null)
_funiculusLength = Vector3.Distance(this._funiculus.position, this._end.position);
return _funiculusLength;
}
}
public Side side;
public bool isLeft => side == Side.Left;
public bool isRight => side == Side.Right;
// Movement it seems:
// head-scape joint: mainly 2 degrees of freedom: pitch and yaw. Limited roll is possible
// scape-funiculus joint: mainly 1 degree of freedom: pitch. Limited yaw is possible
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0a74d6a0259bc3239ce87703b015c8a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,3 +1,5 @@
using UnityEngine;
namespace CreatureControl {
/// <summary>
@ -10,6 +12,10 @@ namespace CreatureControl {
public float forwardSpeed = 1;
public float rotationSpeed = 1;
public InsectHead head;
public Transform thorax;
public Transform abdomen;
public Leg leftFrontLeg;
public Leg leftMiddleLeg;
public Leg leftHindLeg;

View File

@ -0,0 +1,9 @@
namespace CreatureControl {
public class InsectHead {
public Antenna leftAntenna;
public Antenna rightAntenna;
public Mandible leftMandible;
public Mandible rightMandible;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c81d225e1c5078b5aba909ac5c4876e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using UnityEngine;
namespace CreatureControl {
public class Mandible {
public Transform bone;
public Side side;
public bool isLeft => side == Side.Left;
public bool isRight => side == Side.Right;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed9a3aa6498a84ac9bfd3a036847635f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using UnityEngine;
namespace CreatureControl {
@ -7,21 +8,94 @@ namespace CreatureControl {
/// </summary>
[System.Serializable]
public class Leg {
[SerializeField]
private Transform _coxa;
/// <summary>
/// The hip bone
/// </summary>
public Transform coxa {
get => this._coxa;
set {
this._coxa = value;
this._femurLength = 0;
}
}
[SerializeField]
private Transform _femur;
/// <summary>
/// The upper leg or thigh bone
/// </summary>
public Transform femur;
public Transform femur {
get => this._femur;
set {
this._femur = value;
this._femurLength = 0;
}
}
[SerializeField]
private Transform _tibia;
/// <summary>
/// The lower leg or shank bone
/// </summary>
public Transform tibia;
public Transform tibia {
get => this._tibia;
set {
this._tibia = value;
this._femurLength = 0;
this._tibiaLength = 0;
}
}
[SerializeField]
private Transform _tarsus;
/// <summary>
/// The foot bone
/// </summary>
public Transform tarsus;
public Transform tarsus {
get => this._tarsus;
set {
this._tarsus = value;
this._tibiaLength = 0;
this._tarsusLength = 0;
}
}
[SerializeField]
private Transform _phalanges;
/// <summary>
/// The toes
/// </summary>
public Transform phalanges {
get => this._phalanges;
set {
this._phalanges = value;
this._tarsusLength = 0;
this._phalangesLength = 0;
}
}
[SerializeField]
private Transform _end;
/// <summary>
/// The end of the leg
/// </summary>
public Transform end {
get => this._end;
set {
this._end = value;
this._phalangesLength = 0;
}
}
#region Bones
[SerializeField]
private float _femurLength;
/// <summary>
/// The length of the femur bone
/// </summary>
public float femurLength {
get {
if (_femurLength <= 0 && this.femur != null && this.tibia != null)
@ -29,9 +103,12 @@ namespace CreatureControl {
return _femurLength;
}
}
// A bit inefficient is this is used a lot...
//public float tibiaLength => Vector3.Distance(this.tibia.position, this.tarsus.position);
[SerializeField]
private float _tibiaLength;
/// <summary>
/// The length of the tibia bone
/// </summary>
public float tibiaLength {
get {
if (_tibiaLength <= 0 && this.tibia != null && this.tarsus != null)
@ -39,18 +116,137 @@ namespace CreatureControl {
return _tibiaLength;
}
}
[SerializeField]
private float _tarsusLength;
/// <summary>
/// The length of the tarsus bone
/// </summary>
public float tarsusLength {
get {
if (_tarsusLength <= 0 && this.tarsus != null && this.phalanges != null)
_tarsusLength = Vector3.Distance(this.tarsus.position, this.phalanges.position);
return _tarsusLength;
}
}
[SerializeField]
private float _phalangesLength;
/// <summary>
/// The length of the phalanges
/// </summary>
public float phalangesLength {
get {
if (_phalangesLength <= 0 && this.phalanges != null && this.end != null)
_phalangesLength = Vector3.Distance(this.phalanges.position, this.end.position);
return _phalangesLength;
}
}
/// <summary>
/// The length of the leg from hip to foot
/// </summary>
/// This consists of the femur and tibia
public float length => femurLength + tibiaLength;
/// <summary>
/// The size of the foot
/// </summary>
/// This consists of the tarsus and phalanges
public float footLength => tarsusLength + phalangesLength;
public void ResetLengths() {
_femurLength = 0;
_tibiaLength = 0;
_tibiaLength = 0;
_tarsusLength = 0;
_phalangesLength = 0;
}
/// <summary>
/// Try to determine the leg bones
/// </summary>
/// <param name="firstLegBone">the first bone of the leg (could be coxa or femur depending on chain length)</param>
public void DetectBones(Transform root) {
if (root == null)
return;
coxa = femur = tibia = tarsus = phalanges = end = null;
// gather a straight chain following single-child links
List<Transform> chain = new();
Transform current = root;
chain.Add(current);
while (current.childCount == 1) {
current = current.GetChild(0);
chain.Add(current);
}
// the detected end bone is the last element in the collected chain
end = chain[^1];
// map chain length to bone roles according to rules:
// 1 => femur
// 2 => femur, tibia
// 3 => femur, tibia, tarsus
// 4 => femur, tibia, tarsus, phalanges
// 5+ => coxa, femur, tibia, tarsus, phalanges (first 5)
int count = chain.Count;
if (count == 1)
femur = chain[0];
else if (count == 2) {
femur = chain[0];
tibia = chain[1];
}
else if (count == 3) {
femur = chain[0];
tibia = chain[1];
tarsus = chain[2];
}
else if (count == 4) {
femur = chain[0];
tibia = chain[1];
tarsus = chain[2];
phalanges = chain[3];
}
else // count >= 5
{
coxa = chain[0];
femur = chain[1];
tibia = chain[2];
tarsus = chain[3];
phalanges = chain[4];
}
}
#endregion Bones
/// <summary>
/// Check if all bones of the legs have been configured
/// </summary>
/// <returns>True when all bones are configured</returns>
public bool isConfigured => femur != null && tibia != null && tarsus != null;
public bool isConfigured =>
this.femur != null &&
this.tibia != null &&
this.tarsus != null &&
this.phalanges != null &&
this.end != null;
public Side side;
public bool isLeft => side == Side.Left;
public bool isRight => side == Side.Right;
private Vector3 _kneeAxis = Vector3.zero;
public Vector3 kneeAxis {
get {
if (this._kneeAxis.sqrMagnitude == 0) {
Vector3 femurWorldDirection = (this.tibia.position - this.femur.position).normalized;
Vector3 tibiaWorldDirection = (this.tarsus.position - this.tibia.position).normalized;
Vector3 kneeWorldAxis = Vector3.Cross(femurWorldDirection, tibiaWorldDirection);
Vector3 kneeLocalAxis = this.femur.InverseTransformDirection(kneeWorldAxis);
this._kneeAxis = kneeLocalAxis;
}
return this._kneeAxis;
}
}
}
}

View File

@ -84,8 +84,8 @@ namespace NanoBrain {
void OnAddClusterOutput() {
Nucleus newOutput = new Neuron(this.prefab, "New Output");
this.prefab.RefreshOutputs();
outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
outputsPopup.value = newOutput.name;
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
// outputsPopup.value = newOutput.name;
this.currentNucleus = newOutput;
}
@ -185,7 +185,7 @@ namespace NanoBrain {
if (newName != this.currentNucleus.name) {
this.currentNucleus.name = newName;
this.prefab.RefreshOutputs();
outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
anythingChanged = true;
}
}
@ -537,11 +537,11 @@ namespace NanoBrain {
}
this.prefab.nuclei.Remove(nucleus);
if (outputsPopup.value == nucleus.name) {
this.prefab.RefreshOutputs();
outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
outputsPopup.index = 0;
}
// if (outputsPopup.value == nucleus.name) {
// this.prefab.RefreshOutputs();
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
// outputsPopup.index = 0;
// }
Neuron.Delete(nucleus);

View File

@ -25,7 +25,7 @@ namespace NanoBrain {
protected VisualElement topMenuContainer;
protected ScrollView scrollView;
protected IMGUIContainer graphContainer;
protected readonly PopupField<string> outputsPopup;
//protected readonly PopupField<string> outputsPopup;
public enum Mode {
Focus,

View File

@ -1,2 +1,11 @@
fileFormatVersion: 2
guid: 4fe58945c76d153edacc220597474ad2
guid: 4fe58945c76d153edacc220597474ad2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -48,8 +48,8 @@ namespace CreatureControl {
public Nucleus pheromoneSteering;
public Nucleus hitLeft;
public Nucleus hitRight;
public Receptor foodReceptor;
public Receptor homeReceptor;
public Neuron foodReceptor;
public Neuron homeReceptor;
public Vector3 linearVelocity = Vector3.forward;
public Vector3 angularVelocity;
@ -76,8 +76,8 @@ namespace CreatureControl {
this.beat = brain.GetNucleus("Beat");
this.hitLeft = brain.GetNucleus("Hit Left");
this.hitRight = brain.GetNucleus("Hit Right");
this.foodReceptor = brain.GetNucleus("Food Receptor") as Receptor;
this.homeReceptor = brain.GetNucleus("Home Receptor") as Receptor;
this.foodReceptor = brain.GetNucleus("Food Receptor") as Neuron;
this.homeReceptor = brain.GetNucleus("Home Receptor") as Neuron;
this.pheromoneSteering = brain.GetNucleus("Pheromone Steering");
//--- brain outputs
@ -190,10 +190,10 @@ namespace CreatureControl {
Vector3 smell = smellDirection.normalized * intensity;
switch (pheromone.type) {
case Pheromone.Type.Food:
foodReceptor?.ProcessStimulus(smellDirection.normalized * intensity, pheromone.GetInstanceID(), "food pheromone");
foodReceptor?.ProcessStimulus(smellDirection.normalized * intensity); //, pheromone.GetInstanceID(), "food pheromone");
break;
case Pheromone.Type.Home:
homeReceptor?.ProcessStimulus(smellDirection.normalized * intensity, pheromone.GetInstanceID(), "home pheromone");
homeReceptor?.ProcessStimulus(smellDirection.normalized * intensity); //, pheromone.GetInstanceID(), "home pheromone");
break;
}
//Debug.DrawLine(this.transform.position, pheromone.transform.position, Color.magenta);
@ -214,7 +214,7 @@ namespace CreatureControl {
float angle = Vector3.Angle(Vector3.forward, smellDirection);
if (angle < smellAngle && distance > 0.01) {
float intensity = food.StrengthAt(distance);
foodReceptor?.ProcessStimulus(smellDirection.normalized * intensity, food.GetInstanceID(), "food");
foodReceptor?.ProcessStimulus(smellDirection.normalized * intensity); //, food.GetInstanceID(), "food");
Debug.DrawLine(this.transform.position, food.transform.position, Color.red);
}
}
@ -234,7 +234,7 @@ namespace CreatureControl {
if (angle < smellAngle && distance > 0.01) {
float intensity = nest.StrengthAt(distance);
Vector3 value = smellDirection.normalized * intensity;
homeReceptor?.ProcessStimulus(value, nest.GetInstanceID(), "nest");
homeReceptor?.ProcessStimulus(value); //, nest.GetInstanceID(), "nest");
Debug.DrawLine(this.transform.position, nest.transform.position, Color.red);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 0f7345a3a2017e01383b1d8392b40304
guid: 81292defec7ff5278a48a5d154659d00
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0f7345a3a2017e01383b1d8392b40304
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant: