Initial Doxygen documentation

This commit is contained in:
Pascal Serrarens 2026-04-09 15:46:29 +02:00
parent 7ced91909a
commit f2884327cf
12 changed files with 378 additions and 317 deletions

8
Documentation.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c2c74f5b3b38f630c9211ae171b6c481
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

14
Documentation/Ant2.md Normal file
View File

@ -0,0 +1,14 @@
Lorum ipsum
Heading level 1
===============
Lorum ipsum
Heading Level 2
---------------
Lorum ipsum
here is a an image:
![Passer Logo](images/PasserLifeLogoRight1_300.png)

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e216f4e0f4902417980c167612b0e0c1
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,2 @@
/// \page GettingStarted Getttting Started
Getting starttted

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 02f2904aebc8f9475b2ab67bc25205f8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
/// \page Installation Installation
You can import the NanoBrain Ant package in Unity directly with the Package Manager git package importer.
See Unity: [Installing from a Git URL](https://docs.unity3d.com/Manual/upm-ui-giturl.html)
Use the link from 'Clone with HTTP' (for example: https://git.passer.life/CreatureControl/Ant.git) In this way you can always retrieve the latest version by pressing the Update button in the Package Manager.
Optionally, you can use a tag to retrieve a specific version. For example: https://git.passer.life/CreatureControl/Ant.git#0.1.0. This will give you a stable version which does not change. Updating can be done by retrieving the package with a link to a new release.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c97243ffb6244df0d9470dd96c86f8fc
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,2 +1,6 @@
# Ant
# %NanoBrain Ant
Table of Contents
-----------------
- [Installation](\ref Installation)
- [Getting started](\ref GettingStarted)

View File

@ -1,13 +1,28 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NanoBrain;
namespace Passer.CreatureControl {
/// <summary>
/// Simulated ant using a NanoBrain
/// </summary>
[RequireComponent(typeof(Brain))]
public class Ant : Insect {
/// <summary>
/// inertia controls how quickly the ant can change it direction
/// </summary>
private readonly float inertia = 0.2f;
/// <summary>
/// The maximum distance at which the ant can smell things
/// </summary>
/// The strength of the smell decreases with the inverse square law
/// to zero at this distance
private readonly float smellRadius = 0.2f;
/// <summary>
/// The angle to the left and right within the ant can smell things
/// </summary>
private readonly float smellAngle = 80.0f;
public GameObject homePheromonePrefab;
@ -18,16 +33,25 @@ namespace Passer.CreatureControl {
public Brain nanoBrain;
// brain output
public Neuron targetDirection;
public Neuron hasFood;
// brain input
/// <summary>
/// The (heart) beat for the brain
/// </summary>
/// It is used by the brain to do things periodically
/// like placing pheromones
public Nucleus beat;
public Nucleus pheromoneSteering;
public Nucleus hitLeft;
public Nucleus hitRight;
public Nucleus beat;
public Receptor foodReceptor;
public Receptor homeReceptor;
public Vector3 linearVelocity;
public Vector3 linearVelocity = Vector3.forward;
public Vector3 angularVelocity;
#region Init
@ -49,30 +73,37 @@ namespace Passer.CreatureControl {
Cluster brain = this.nanoBrain.brain;
if (brain != null) {
// brain outputs
this.pheromoneSteering = brain.GetNucleus("Pheromone Steering");
//--- brain inputs
if (brain.GetNucleus("Home Pheromones") is Neuron homePheromones)
homePheromones.WhenFiring += PlaceHomePheromone;
if (brain.GetNucleus("Food Pheromones") is Neuron foodPheromones)
foodPheromones.WhenFiring += PlaceFoodPheromone;
this.hasFood = brain.GetNucleus("Having Food") as Neuron;
// brain inputs
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.pheromoneSteering = brain.GetNucleus("Pheromone Steering");
//--- brain outputs
this.targetDirection = brain.defaultOutput;
// Try to find the Home Pheromones Neuron
if (brain.GetNucleus("Home Pheromones") is Neuron homePheromones)
// and call PlaceHomePheromone when it is firing
homePheromones.WhenFiring += PlaceHomePheromone;
// Try to find the Food Pheromones Neuron
if (brain.GetNucleus("Food Pheromones") is Neuron foodPheromones)
// and call PlaceFoodPheromone when it is firing
foodPheromones.WhenFiring += PlaceFoodPheromone;
this.hasFood = brain.GetNucleus("Having Food") as Neuron;
}
this.linearVelocity = Vector3.forward;
// Initialize the callbacks for the antenna colliders
if (touchLeft != null)
touchLeft.touched += OnAntennaTouchLeft;
if (touchRight != null)
touchRight.touched += OnAntennaTouchRight;
StartCoroutine(Beat());
}
#endregion Start
@ -88,86 +119,56 @@ namespace Passer.CreatureControl {
pheromoneObj.transform.position = this.model.position;
}
// Update is called once per frame
public override void Update() {
base.Update();
UpdateBeat();
UpdateSmell();
if (this.nanoBrain == null || this.nanoBrain.brain == null || this.animator == null)
return;
Vector3 localForce = nanoBrain.brain.defaultOutput.outputValue;
//Vector3 localForce = this.transform.InverseTransformDirection(worldForce);
this.linearVelocity = (1 - inertia) * (Time.deltaTime * localForce.normalized) + inertia * this.linearVelocity;
this.linearVelocity = this.linearVelocity.normalized; // * this.forwardSpeed;
//this.animator.SetFloat("Forward", this.forwardSpeed); //this.linearVelocity.z * this.forwardSpeed);
float forwardParam = Mathf.Clamp01(this.linearVelocity.z); // / this.forwardSpeed);
float angleDeg = 0;
if (this.linearVelocity.magnitude > 1e-5f)
angleDeg = Mathf.Atan2(this.linearVelocity.x, this.linearVelocity.z) * Mathf.Rad2Deg;
// base turn in -1..1
float baseTurn = Mathf.Clamp(angleDeg / 45, -1f, 1f);
float turnParam = baseTurn; // * Mathf.Max(0.6f, 1 - forwardParam);
// Rotate towards the movement direction
// if (this.linearVelocity != Vector3.zero) {
// Quaternion targetRotation = Quaternion.LookRotation(this.linearVelocity);
// Quaternion worldRotation = transform.rotation * targetRotation;
// Quaternion deltaRotation = worldRotation * Quaternion.Inverse(transform.rotation);
// Vector3 eulerAngleChange = deltaRotation.eulerAngles;
// // Normalize the Euler angles to avoid unexpected jumps due to 360-degree rotations
// eulerAngleChange = new Vector3(
// LinearAlgebra.Angles.Normalize(eulerAngleChange.x),
// LinearAlgebra.Angles.Normalize(eulerAngleChange.y),
// LinearAlgebra.Angles.Normalize(eulerAngleChange.z)
// );
// float rotSpeed = (eulerAngleChange.y / 45) * this.rotationSpeed;
// this.animator.SetFloat("Rotate", rotSpeed);
// Debug.Log($"fw {this.forwardSpeed} ang {rotSpeed}");
// }
// Smooth against current animator values
// float curF = animator.GetFloat("Forward");
// float curT = animator.GetFloat("Turn");
// forwardParam = Mathf.Lerp(curF, forwardParam, 1f - Mathf.Exp(-10 * Time.deltaTime));
// turnParam = Mathf.Lerp(curT, turnParam, 1f - Mathf.Exp(-10 * Time.deltaTime));
this.animator.SetFloat("Forward", forwardParam);
this.animator.SetFloat("Rotate", turnParam);
UpdateMovement();
}
public virtual void FixedUpdate() {
CheckGrounded();
}
public float beatInterval = 3;
float lastBeatTime = 0;
void UpdateBeat() {
if (lastBeatTime == 0) {
ulong delay = (ulong)(Random.value * beatInterval);
lastBeatTime = Time.time - delay;
}
if (Time.time - lastBeatTime >= beatInterval) {
lastBeatTime = Time.time;
beat?.SetBias(Vector3.one); //, 0);
protected void UpdateMovement() {
if (this.targetDirection == null || this.animator == null)
return;
Vector3 movementDir = this.targetDirection.outputValue;
this.linearVelocity =
(1 - this.inertia) * (Time.deltaTime * movementDir.normalized) +
this.inertia * this.linearVelocity;
this.linearVelocity = this.linearVelocity.normalized;
float forwardParam = this.linearVelocity.z;
float angleRad = this.linearVelocity.sqrMagnitude > 1e-10f ?
Mathf.Atan2(this.linearVelocity.x, this.linearVelocity.z) :
0;
// map -20..20 degrees to -1..1
float rotateParam = Mathf.Clamp(angleRad * 3, -1f, 1f);
this.animator.SetFloat("Forward", forwardParam);
this.animator.SetFloat("Rotate", rotateParam);
}
private static readonly WaitForSeconds _waitForSeconds3 = new(3);
IEnumerator Beat() {
while (Application.isPlaying) {
// Beat signal to the brain
beat?.SetBias(Vector3.one);
// Set random direction to simulate noisy smells perception
// which will result in a bit of random walking when no clear
// smells are received
float randomAngle = Random.Range(-smellAngle, smellAngle);
Vector3 randomDirection = Quaternion.AngleAxis(randomAngle, Vector3.up) * Vector3.forward * 1.01f;
pheromoneSteering?.SetBias(randomDirection); //, 0, "random");
Vector3 randomDirection = Quaternion.AngleAxis(randomAngle, Vector3.up) * Vector3.forward;
pheromoneSteering?.SetBias(randomDirection);
yield return _waitForSeconds3;
}
}
void UpdateSmell() {
// To generate random basic movement, we add a small with a random direction with low intensity
Collider[] colliders = Physics.OverlapSphere(this.transform.position, smellRadius);
foreach (Collider collider in colliders) {
SmellPheromones(collider);
@ -199,7 +200,6 @@ namespace Passer.CreatureControl {
}
//Debug.DrawLine(this.transform.position, pheromone.transform.position, Color.magenta);
}
}
void SmellFood(Collider thing) {

View File

@ -6,7 +6,10 @@ namespace Passer.CreatureControl {
public float strength = 1;
public float StrengthAt(float distance) {
float intensity = this.strength * (1 / distance);
if (distance <= 0)
return this.strength;
float intensity = this.strength / (4.0f * Mathf.PI * distance * distance);
return intensity;
}
}

View File

@ -28,7 +28,7 @@ BlendTree:
m_Motion: {fileID: 7400000, guid: ab82ff68e62ea3b1c8e6523f8d46c142, type: 2}
m_Threshold: 0.19200002
m_Position: {x: -1, y: 0}
m_TimeScale: 2
m_TimeScale: 5
m_CycleOffset: 0
m_DirectBlendParameter: Forward
m_Mirror: 0
@ -36,7 +36,7 @@ BlendTree:
m_Motion: {fileID: 7400000, guid: 91229db5e929c379bbfd5bf417848488, type: 2}
m_Threshold: 0.28800002
m_Position: {x: 1, y: 0}
m_TimeScale: 2
m_TimeScale: 1
m_CycleOffset: 0
m_DirectBlendParameter: Forward
m_Mirror: 0

File diff suppressed because it is too large Load Diff