Browse Source

naja

master
subversion23 2 years ago
parent
commit
eafb8a2355
  1. 2
      PlantWorld.cs
  2. 1
      icon.svg
  3. 11
      main.tscn
  4. 8
      src/BaseLifeComponent.cs
  5. 67
      src/Genetics.cs
  6. 474
      src/Plant.cs
  7. 87
      src/PlantLife.cs
  8. 91
      src/PlantParmeters.cs
  9. 94
      src/SegmentNodes.cs

2
PlantWorld.cs

@ -60,7 +60,7 @@ public partial class PlantWorld : Node2D
if (eventMouseButton.ButtonIndex == MouseButton.Right && eventMouseButton.Pressed) if (eventMouseButton.ButtonIndex == MouseButton.Right && eventMouseButton.Pressed)
{ {
var pos = eventMouseButton.Position * GetViewportTransform(); var pos = eventMouseButton.Position * GetViewportTransform();
CreatePlant(pos, 128); CreatePlant(pos, 12);
} }
} }
} }

1
icon.svg

@ -0,0 +1 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>

After

Width:  |  Height:  |  Size: 950 B

11
main.tscn

@ -1,10 +1,13 @@
[gd_scene load_steps=3 format=3 uid="uid://d2rgfedikyr2w"] [gd_scene load_steps=4 format=3 uid="uid://d2rgfedikyr2w"]
[ext_resource type="PackedScene" uid="uid://ixy2rotcnjk2" path="res://plant.tscn" id="1_mv10f"] [ext_resource type="Script" path="res://Main.cs" id="1_jrcl7"]
[ext_resource type="PackedScene" path="res://camera_2d.tscn" id="2_pvpm2"] [ext_resource type="PackedScene" path="res://camera_2d.tscn" id="2_pvpm2"]
[ext_resource type="PackedScene" uid="uid://cvjqldltcr7nl" path="res://plantWorld.tscn" id="2_t7d21"]
[node name="Main" type="Node2D"] [node name="Main" type="Node2D"]
script = ExtResource("1_jrcl7")
[node name="Plant" parent="." instance=ExtResource("1_mv10f")]
[node name="Camera2D" parent="." instance=ExtResource("2_pvpm2")] [node name="Camera2D" parent="." instance=ExtResource("2_pvpm2")]
[node name="PlantWorld" parent="." node_paths=PackedStringArray("Camera") instance=ExtResource("2_t7d21")]
Camera = NodePath("../Camera2D")

8
src/BaseLifeComponent.cs

@ -1,4 +1,4 @@
namespace DrawingDemo; namespace LifeComponent;
using Godot; using Godot;
@ -6,7 +6,7 @@ public delegate void DiedEventHandler();
public class BaseLifeComponent public class BaseLifeComponent
{ {
public float Energy = 1000; public float Energy = 100;
public float Age = 0; public float Age = 0;
public int MaxAge = 150; public int MaxAge = 150;
public float Health = 100; public float Health = 100;
@ -15,10 +15,10 @@ public class BaseLifeComponent
public event DiedEventHandler OnDied; public event DiedEventHandler OnDied;
public void Tick(float timeDelta) public void Tick(float timeDelta, float size)
{ {
Age += timeDelta; Age += timeDelta;
ConsumeEnergy(0.1f); //Baseconsumption ConsumeEnergy(0.1f * size); //Baseconsumption
if (Age > MaxAge || Health <= 0) if (Age > MaxAge || Health <= 0)
{ {

67
src/Genetics.cs

@ -1,9 +1,10 @@
using System;
using Godot; using Godot;
using Color = Godot.Color; using Color = Godot.Color;
namespace DrawingDemo; namespace LifeComponent;
public static class ColorHelper public static class GeneHelper
{ {
public static Color ColorFrom6Bit(byte colorcode) public static Color ColorFrom6Bit(byte colorcode)
{ {
@ -19,10 +20,22 @@ public static class ColorHelper
return new Color(value1 / colorperBit, value2 / colorperBit, value3 / colorperBit, value4 / colorperBit); return new Color(value1 / colorperBit, value2 / colorperBit, value3 / colorperBit, value4 / colorperBit);
} }
// public static int MapCodonToInt(byte codon, int min, int max)
// {
// int result = 0;
// if (codon > max)
// result = codon / max;
// if (codon < min)
// result = codon;
//
// return result;
// }
} }
public class Genetics public class Genetics
{ {
public GenStorage DNA = new GenStorage(); public GenStorage DNA;
public Color MainColor public Color MainColor
{ {
@ -30,10 +43,17 @@ public class Genetics
{ {
var codon = DNA.GetCodon(GenMapper.MainColor); var codon = DNA.GetCodon(GenMapper.MainColor);
return ColorHelper.ColorFrom6Bit(codon); return GeneHelper.ColorFrom6Bit(codon);
} }
} }
public Genetics()
{
DNA = new GenStorage(12);
DNA.Randomize();
}
} }
public enum GenMapper public enum GenMapper
@ -52,9 +72,10 @@ public struct GenStorage
{ {
public byte[] Codons; public byte[] Codons;
public GenStorage() public GenStorage(int codonCount)
{ {
Codons = new byte[4] {42,0,0,0}; Codons = new byte[codonCount];// {42,0,0,0};
} }
@ -89,4 +110,38 @@ public struct GenStorage
return Codons[codon]; return Codons[codon];
} }
public byte[] GetBasePairs(int codonNr)
{
var codon = GetCodon(codonNr);
byte value1 = (byte)((codon & 0b11000000) >> 6);
byte value2 = (byte)((codon & 0b00110000) >> 4);
byte value3 = (byte)((codon & 0b00001100) >> 2);
byte value4 = (byte)(codon & 0b00000011);
return new[] { value1, value2, value3, value4 };
}
public float CodonAsFloat(int codonNr, float min = 0, float max = 1, int pairs = 4)
{
float result = 0;
var codon = GetCodon(codonNr);
if (pairs == 4)
{
result = Mathf.Lerp(min, max, (float) 255 / codon);
}
else
{
var values = GetBasePairs(codonNr);
// ...
}
return result;
}
}
public class PhenotypeAttribute : Attribute
{
// Codon Marker index and base-pair lenght
// z.b. für angle between 0 - 6.2 = 1 Codon = 8 Byte -> lerp(0, 6.2, codon)
} }

474
src/Plant.cs

@ -4,112 +4,302 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
public partial class Plant : MeshInstance2D public partial class Plant : MeshInstance2D
{ {
private PlantLife _plantLife;
public Dictionary<int, PlantSegmentNode> Segments; public Dictionary<int, PlantSegmentNode> Segments;
public Vector2 Origin;
private int _segmentIdCounter = 0; private int _segmentIdCounter = 0;
public PlantSegmentNode RootNode; public PlantSegmentNode RootNode;
public Rect2 ClientRectangle = new Rect2(Vector2.Zero, new Vector2(-80, 80)); public Rect2 ClientRectangle = new Rect2(Vector2.Zero, new Vector2(-10, 10));
public bool CanGrow = true;
public override void _Ready() public override void _Ready()
{ {
Start(); //Start();
} }
public void Start() public void Init(int startNodes)
{ {
_plantLife = new PlantLife(this);
Segments = new Dictionary<int, PlantSegmentNode>(); Segments = new Dictionary<int, PlantSegmentNode>();
PlantSegmentNode root = new PlantSegmentNode(null, maxChildren:1); _plantLife.Init();
root.Direction = Vector2.Up;
root.NodeType = SegmentNodeTypes.Trunk; PlantSegmentNode root = new PlantSegmentNode(null, maxChildren:_plantLife._plantParams.RootChildren);
root.Direction = _plantLife._plantParams.GrowDirection;
root.NodeType = PlantNodeTypes.Trunk;
RootNode = root; RootNode = root;
Segments.Add(_segmentIdCounter, root); Segments.Add(_segmentIdCounter, root);
_segmentIdCounter++; _segmentIdCounter++;
GrowDemo(4096); GrowDemo(startNodes);
this.Mesh = CreateMesh(); this.Mesh = CreateMesh();
CalulateClientRectangle();
} }
public Mesh CreateMesh() public void CreateRootNode()
{ {
ArrayMesh arrMesh = new ArrayMesh();
var arrays = new Godot.Collections.Array();
arrays.Resize((int)Mesh.ArrayType.Max);
//Vector2[] vertices = CalculateVertices();
Vector2[] vertices = CalculateLineVertices();
Color[] colors = Helper.MakeColorArray(vertices.Length, 3);
arrays[(int)Mesh.ArrayType.Vertex] = vertices;
arrays[(int)Mesh.ArrayType.Color] = colors;
//arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Lines, arrays);
return arrMesh;
} }
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) public override void _Process(double delta)
{ {
if (_plantLife != null)
_plantLife.Tick((float)delta);
}
#region Segments
public void GrowDemo(int nodes)
{
for (int i = 0; i < nodes -1; i++)
{
GrowNewNode( PlantParams.Default());
} }
public void GrowDemo(int nodes) }
public void AddNewSegment(PlantSegmentNode seg, PlantSegmentNode parent, int index)
{ {
for (int i = 0; i < nodes -1; i++) if (parent.AddChild(seg, index))
{
Segments.Add(_segmentIdCounter, seg);
_segmentIdCounter++;
this.UpdateAllGeometry();
}
else
{ {
GrowNode(); GD.PrintErr("Error bei Add");
} }
} }
public void GrowNewNode(PlantParams pp)
{
int maxChildren = pp.MaxChildren;
PlantNodeTypes type = PlantNodeTypes.Trunk;
type = pp.GetRatioNodeType();
PlantNodeParams p = pp.NodeParams;
int maxDescentants = 4096;
var maxADiv = p.MaxToParentAngleRange / 2;
var a1 = p.MinToParentAngleRange;
var a2 = p.MaxToParentAngleRange;
var randAngleZeroDiv = (float)GD.RandRange(a1, a2);
float randF = (0.5f - GD.Randf());
int randI = GD.RandRange(0, 3);
public void GrowNewNode(PlantNodeParams p) var parent = GetFreeSlotSegment(false);
if (parent == null)
{ {
//var randAngle = (float)GD.RandRange(-p.AllowedParentDiv/2, p.AllowedParentDiv/2); GD.PrintErr("Outof Nodes");
var randAngleZeroDiv = (float)GD.RandRange(20, 45); CanGrow = false;
return;
float randF = (0.5f - GD.Randf()); }
int randI = GD.RandRange(0, 10);
PlantSegmentNode seg;
int maxChildren = p.MaxChildren; Vector2 dir = parent.Direction;
int maxDescentants = 4096; Vector2 size = parent.Size;
var parent = GetFreeSlotSegment(false);
PlantSegmentNode seg; // get sibling dirs, if
Vector2 dir = parent.Direction; // var sibRots = parent.GetChildrenToParentRotations();
// if (sibRots.Length == 0) // first child -> dir: center
// {
var freeindex = parent.FreeNodeIndex(); // dir = dir.Rotated(randF/8);
if (freeindex == -1) // }
throw new System.Exception("Now free index, trotz free slot"); // else
switch (freeindex) // {
{ // calc free spot:
case 0: // Center // min distAngle = ~ maxparentdiv / max_children
dir = dir.Rotated(randF); // float optimalSpace = p.MaxToParentAngleRange / p.MaxChildren;
maxChildren = 2; // if (p.MinAngleBetweenSibs > optimalSpace)
maxDescentants = 4096; // {
break; // // to tight -> dont grow.
case 1: // left // }
dir = dir.Rotated(-randAngleZeroDiv); //
break; // var r = FindSpot(p, sibRots);
case 2: //}
dir = dir.Rotated(+randAngleZeroDiv);
break; var freeindex = parent.FreeNodeIndex();
// 3D... if (freeindex == -1)
throw new System.Exception("Now free index, trotz free slot");
switch (freeindex)
{
case 0: // Center
dir = dir.Rotated(randF/8);
maxChildren = 2;
size *= 0.95f;
maxDescentants = 4096;
break;
case 1: // left
dir = dir.Rotated(-randAngleZeroDiv);
size *= 0.85f;
maxDescentants = 128;
maxChildren = maxChildren;
break;
case 2:
dir = dir.Rotated(+randAngleZeroDiv);
size *= 0.85f;
maxDescentants = 128;
maxChildren = maxChildren;
break;
// 3D...
}
if (RootNode.Direction.Dot(dir) < 0) // ..<0 = 180 grad maxRootDeviationAngle)
maxChildren = 0; // wenn überhang -> branach ende
if (maxChildren == 0) // wird nix mehr -> kann ein blatt werden
{
//type = PlantNodeTypes.Leafes;
}
seg = new PlantSegmentNode(parent, maxChildren, maxDescentants);
seg.Direction = dir;
seg.Size = size;
seg.NodeType = type;
// if (CheckCollision(seg))
// {
// // Add stump dummy - so slot is ignored
// seg.NodeType = PlantNodeTypes.Stump;
// seg.MaxChildren = seg.ChildrenCount;
// seg.Size = Vector2.One;
// }
AddNewSegment(seg, parent, freeindex);
return;
if (parent.AddChild(seg, freeindex))
{
Segments.Add(_segmentIdCounter, seg);
_segmentIdCounter++;
}
else
{
GD.PrintErr("Error bei Add");
}
}
public float FindSpot(PlantNodeParams p, float[] usedAngles)
{
// calc free spot:
// min distAngle = ~ maxparentdiv / max_children
if (usedAngles.Length == 0)
return p.PreferedRotationToParent;
float optimalSpace = p.MaxToParentAngleRange / p.MaxChildren;
if (p.MinAngleBetweenSibs > optimalSpace)
{
// to tight -> dont grow.
GD.PrintErr("FindSpot: to tight! = params shit");
return 0;
}
List<float> spots = new List<float>();
for (int i = 0; i < usedAngles.Length; i++)
{
if (i + 1 >= usedAngles.Length)
break;
} if (usedAngles[i] - usedAngles[i + 1] >= p.MinAngleBetweenSibs)
seg = new PlantSegmentNode(parent, p.MaxChildren);
if (parent.AddChild(seg, freeindex))
{ {
Segments.Add(_segmentIdCounter, seg); spots.Add(usedAngles[i]);
_segmentIdCounter++;
}
else
{
GD.PrintErr("Error bei Add");
} }
} }
public void GrowNode()
if (spots.Count > 0)
{
return spots[GD.RandRange(0, spots.Count - 1)];
}
else
{
return 0;
}
}
public void GrowNewNode2(PlantNodeParams p)
{
int maxChildren = p.MaxChildren;
int maxDescentants = 4096;
//var randAngle = (float)GD.RandRange(-p.AllowedParentDiv/2, p.AllowedParentDiv/2);
var randAngleZeroDiv = (float)GD.RandRange(12, 23);
randAngleZeroDiv = Mathf.DegToRad(randAngleZeroDiv);
float randF = (0.5f - GD.Randf());
int randI = GD.RandRange(0, 3);
var parent = GetFreeSlotSegment(false);
PlantSegmentNode seg;
Vector2 dir = parent.Direction;
Vector2 size = parent.Size;
// get sibling dirs, if
var sibRots = parent.GetChildrenToParentRotations();
var freeindex = parent.FreeNodeIndex();
if (freeindex == -1)
throw new System.Exception("Now free index, trotz free slot");
switch (freeindex)
{
case 0: // Center
dir = dir.Rotated(randF/8);
maxChildren = 2;
size *= 0.95f;
maxDescentants = 4096;
break;
case 1: // left
dir = dir.Rotated(-randAngleZeroDiv);
size *= 0.85f;
maxDescentants = 128;
maxChildren = maxChildren - randI;
break;
case 2:
dir = dir.Rotated(+randAngleZeroDiv);
size *= 0.85f;
maxDescentants = 128;
maxChildren = maxChildren - randI;
break;
// 3D...
}
if (RootNode.Direction.Dot(dir) < 0) // ..<0 = 180 grad maxRootDeviationAngle)
maxChildren = 0; // wenn überhang -> branach ende
seg = new PlantSegmentNode(parent, maxChildren, maxDescentants);
seg.Direction = dir;
seg.Size = size;
// if (CheckCollision(seg))
// {
// // Add stump dummy - so slot is ignored
// seg.NodeType = PlantNodeTypes.Stump;
// seg.MaxChildren = seg.ChildrenCount;
// seg.Size = Vector2.One;
// }
if (parent.AddChild(seg, freeindex))
{
Segments.Add(_segmentIdCounter, seg);
_segmentIdCounter++;
}
else
{
GD.PrintErr("Error bei Add");
}
}
public void GrowNode_old()
{ {
// Config // Config
float minAngleDiv =Mathf.DegToRad(23); float minAngleDiv =Mathf.DegToRad(23);
@ -173,7 +363,7 @@ public partial class Plant : MeshInstance2D
// { // {
// //GD.Print("Collision ", seg); // //GD.Print("Collision ", seg);
// // Add stump dummy - so slot is ignored // // Add stump dummy - so slot is ignored
// seg.NodeType = SegmentNodeTypes.Stump; // seg.NodeType = PlantNodeTypes.Stump;
// seg.MaxChildren = seg.ChildrenCount; // seg.MaxChildren = seg.ChildrenCount;
// seg.Size = Vector2.Zero;//seg.Size * 0.4f; // seg.Size = Vector2.Zero;//seg.Size * 0.4f;
// } // }
@ -189,6 +379,86 @@ public partial class Plant : MeshInstance2D
} }
} }
public bool CheckCollision(PlantSegmentNode node)
{
foreach (var kv in Segments)
{
var seg = kv.Value;
if (seg == node || seg.NodeType == PlantNodeTypes.Stump || seg == node.Parent)
continue;
Vector2 from1 = node.PositionRelativeParent;
Vector2 to1 = from1 + node.Line;
Vector2 from2 = seg.PositionRelativeParent;
Vector2 to2 = seg.PositionRelativeParent + seg.Line;
var intersection = Geometry2D.SegmentIntersectsSegment(from1, to1, from2, to2);
//Geometry2D.inter
if (intersection.VariantType != Variant.Type.Nil)
{
return true;
}
}
return false;
}
private PlantSegmentNode GetFreeSlotSegment(bool descending = false)
{
Dictionary<int, PlantSegmentNode> elements = Segments;
//List<PlantSegmentNode> list = elements.OrderBy(x => x.Key).ToList<PlantSegmentNode>();
// if (descending)
// elements = elements.OrderByDescending(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
// else
// {
// elements = elements.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
//
// }
foreach (var item in elements)
{
if (item.Value.HasFreeChildSlot)
return item.Value;
}
return null;
}
#endregion
#region Geometry
public void UpdateAllGeometry()
{
this.Mesh = CreateMesh();
CalulateClientRectangle();
}
public Mesh CreateMesh()
{
ArrayMesh arrMesh = new ArrayMesh();
var arrays = new Godot.Collections.Array();
arrays.Resize((int)Mesh.ArrayType.Max);
//Vector2[] vertices = CalculateVertices();
Vector2[] vertices = CalculateLineVertices();
Color[] colors = Helper.MakeColorArray(vertices.Length, 3);
arrays[(int)Mesh.ArrayType.Vertex] = vertices;
arrays[(int)Mesh.ArrayType.Color] = colors;
//arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Lines, arrays);
return arrMesh;
}
public Vector2[] CalculateLineVertices() public Vector2[] CalculateLineVertices()
{ {
int vertexCount = 2 * Segments.Count; int vertexCount = 2 * Segments.Count;
@ -246,55 +516,29 @@ public partial class Plant : MeshInstance2D
} }
return vertices; return vertices;
} }
public bool CheckCollision(PlantSegmentNode node) public void CalulateClientRectangle()
{ {
return false; float margin = 2;
float maxX = Segments.Max(x => (x.Value.VecToRoot + x.Value.Line).X);
} float maxY = Segments.Max(x => (x.Value.VecToRoot + x.Value.Line).Y);
private PlantSegmentNode GetFreeSlotSegment(bool descending = false) float minX = Segments.Min(x => x.Value.VecToRoot.X);
{ float minY = Segments.Min(x => x.Value.VecToRoot.Y);
Dictionary<int, PlantSegmentNode> elements = Segments; Vector2 min = new Vector2(minX - margin, minY - margin);
//List<PlantSegmentNode> list = elements.OrderBy(x => x.Key).ToList<PlantSegmentNode>(); Vector2 max = new Vector2(maxX + margin, maxY + margin);
Vector2 pos = min;
if (descending) Vector2 size = max - min;
elements = elements.OrderByDescending(x => x.Key).ToDictionary(x => x.Key, x => x.Value); pos += Position;
else ClientRectangle = new Rect2(pos, size);
{ // je nachdemn ob + oder - ....
elements = elements.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); //ClientRectangle = new Rect2(min, max);
}
foreach (var item in elements)
{
if (item.Value.HasFreeChildSlot)
return item.Value;
}
return null;
} }
} #endregion
public struct PlantNodeParams
{
// Angle to grow - calculated from 0°(front) localspace
public float AllowedParentDiv = Mathf.DegToRad(80);
public float AllowedRootDiv = Mathf.DegToRad(180);
//public float MinAngleParentDiv = 0;
//public float MaxAngleParentDiv = 1f;
//public float MaxRootDeviationAngle = 3.14f;
public int MaxChildren = 3;
public Vector2 Size = Vector2.One;
public Vector2 Direction = Vector2.Up;
public string ConfigName = "default";
public SegmentNodeTypes NodeType = SegmentNodeTypes.Trunk;
public PlantNodeParams()
{
}
} }

87
src/PlantLife.cs

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using Godot;
using LifeComponent;
public class PlantLife
{
private Genetics _genes;
private BaseLifeComponent _baseLife;
private Plant _plant_RefUp;
public PlantParams _plantParams;
public float _size = 1f;
//public PlantNodeParams RootParams;
public PlantLife(Plant plantRef)
{
_plant_RefUp = plantRef;
_genes = new Genetics();
_baseLife = new BaseLifeComponent();
}
public void Init()
{
_plantParams = new PlantParams();
_plantParams.NodeParams.maxToRootAngleRange = Mathf.DegToRad(270);
GenesToPhenes();
//_plantParams.GrowDirection = Vector2.Right;
_plantParams.NodeParams.MaxToParentAngleRange = Mathf.DegToRad(90);
_baseLife.Born();
}
public void GenesToPhenes()
{
// var c3 = _genes.DNA.Codons[3];
var bytes = _genes.DNA.GetBasePairs(3);
var a = bytes[0];
_plantParams.AverageChildren = a;
var b = bytes[1];
//var c = bytes[2];
//_plantParams.MaxChildren = c + 1;
var d = bytes[3];
if (b == 0)
_plantParams.GrowDirection = Vector2.Up;
if (b == 1)
_plantParams.GrowDirection = Vector2.Left;
if (b == 2)
_plantParams.GrowDirection = Vector2.Right;
if (b == 3)
_plantParams.GrowDirection = Vector2.Down;
//var v = Mathf.LerpAngle(0.5f, 5.8f, (float)d/4); // 0 - 6.2
var v = Mathf.Lerp(0.5f, 5.8f, (float)d/4);
_plantParams.NodeParams.MaxToParentAngleRange = v;
}
public void Tick(float deltaTime)
{
_baseLife.Tick(deltaTime, _size);
_baseLife.AddEnergy(1 * _size * 0.7f);
Grow();
}
public void Grow()
{
if (_baseLife.Energy > 50)
{
if (_plant_RefUp.CanGrow)
_plant_RefUp.GrowNewNode(_plantParams);
//GD.Print("Life grew! :) ");
_baseLife.Energy -= 30;
_size = _plant_RefUp.Segments.Count;
//_plant_RefUp.AddNewSegment()
}
}
}

91
src/PlantParmeters.cs

@ -0,0 +1,91 @@
using System;
using Godot;
using LifeComponent;
public struct PlantParams
{
public PlantNodeParams NodeParams;
public float maxToRootAngleRange = Mathf.DegToRad(180);
public Vector2 GrowDirection = Vector2.Up;
public int RootChildren = 1;
public int AverageChildren = 2;
[Phenotype]
public int MaxChildren = 3; // Hardlimit - should overrule Nodes
public float SizeDecayOrderFactor = 0.99f;
public float LeaveRatio = 0.1f;
public float TrunkRatio = 0.5f;
public PlantParams()
{
//RootParams = rootParams;
NodeParams = PlantNodeParams.Default();
NodeParams.MaxChildren = MaxChildren;
}
public static PlantParams Default()
{
return new PlantParams();
}
public PlantNodeTypes GetRatioNodeType()
{
var r1 = (float)GD.RandRange(0, 10) / 10;
//if (r1 < LeaveRatio)
// return PlantNodeTypes.Leafes;
if (r1 > LeaveRatio && r1 < TrunkRatio)
return PlantNodeTypes.Trunk;
return PlantNodeTypes.Branch;
}
}
public struct PlantNodeParams
{
// Angle to grow - calculated from 0°(front) localspace. +/- angle/2
[Phenotype]
public float MinToParentAngleRange = Mathf.DegToRad(12);
[Phenotype]
public float MaxToParentAngleRange = Mathf.DegToRad(80);
public float maxToRootAngleRange = Mathf.DegToRad(180);
public float MinAngleBetweenSibs = Mathf.DegToRad(12);
//public float MinAngleParentDiv = 0;
//public float MaxAngleParentDiv = 1f;
//public float MaxRootDeviationAngle = 3.14f;
public int MaxChildren = 3;
public Vector2 Size = Vector2.One;
public float PreferedRotationToParent = 0f;
public string ConfigName = "default";
public PlantNodeTypes NodeType = PlantNodeTypes.Trunk;
public PlantNodeParams()
{
}
public static PlantNodeParams Default()
{
return new PlantNodeParams();
}
public static PlantNodeParams Trunk()
{
var p = new PlantNodeParams();
p.MaxChildren = 2;
p.NodeType = PlantNodeTypes.Trunk;
p.maxToRootAngleRange = 0.1f;
return p;
}
}

94
src/SegmentNodes.cs

@ -4,11 +4,11 @@ using System.Linq;
using Godot; using Godot;
public enum SegmentNodeTypes public enum PlantNodeTypes
{ {
Trunk, Trunk,
Branch, Branch,
Leave, Leafes,
Fruit, Fruit,
Stump, // basically blocks growth Stump, // basically blocks growth
} }
@ -21,33 +21,41 @@ public class PlantSegmentNode : LifeFormSegmentNode
public Vector2 VecToRoot; public Vector2 VecToRoot;
public Vector2 Direction; public Vector2 Direction;
private SegmentNodeTypes _nodeType = SegmentNodeTypes.Branch; private PlantNodeTypes _nodeType = PlantNodeTypes.Branch;
public SegmentNodeTypes NodeType public PlantNodeTypes NodeType
{ {
get { return _nodeType; } get { return _nodeType; }
set { set {
if (value == SegmentNodeTypes.Stump) _nodeType = value;
if (value == PlantNodeTypes.Stump)
{ {
Size = Vector2.Zero; Size = Vector2.Zero;
MaxChildren = ChildrenCount; MaxChildren = ChildrenCount;
Direction = ((PlantSegmentNode)Parent).Direction.Normalized(); Direction = ((PlantSegmentNode)Parent).Direction.Normalized();
_nodeType = value; base.RedimChildren();
} }
else if (value == PlantNodeTypes.Branch)
{
//MaxChildren = base.MaxChildren + 2;
Size = Size * 0.95f;
}
else if (value == PlantNodeTypes.Leafes)
{
MaxChildren = ChildrenCount;
base.RedimChildren();
}
} }
} }
public float Rotation
{
get { return Mathf.RadToDeg(Direction.Angle()); }
}
public PlantSegmentNode(PlantSegmentNode parent, int maxChildren = 2, int maxRecursiveChildren = 4096) : base(parent, maxChildren, maxRecursiveChildren) public PlantSegmentNode(PlantSegmentNode parent, int maxChildren = 2, int maxRecursiveChildren = 4096) : base(parent, maxChildren, maxRecursiveChildren)
{ {
if (parent != null) // is child if (parent != null) // is child
{ {
Direction = parent.Direction.Normalized(); Direction = parent.Direction.Normalized();
Size = parent.Size / parent.MaxChildren; Size = parent.Size; // parent.MaxChildren;
PositionRelativeParent = parent.PositionRelativeParent + parent.Line; PositionRelativeParent = parent.PositionRelativeParent + parent.Line;
VecToRoot = parent.VecToRoot + parent.Line; VecToRoot = parent.VecToRoot + parent.Line;
} }
@ -62,6 +70,20 @@ public class PlantSegmentNode : LifeFormSegmentNode
} }
} }
public float[] GetChildrenToParentRotations()
{
float[] r = new float[ChildrenCount];
for (int i = 0; i < ChildrenCount - 1; i++)
{
var child = Get_Child(i);// Children[i];
if (child != null)
r[i] = ((PlantSegmentNode)Children[i]).Direction.Angle();
}
return r;
}
public void UpdateSelfToParent() public void UpdateSelfToParent()
{ {
if (IsRoot) if (IsRoot)
@ -178,14 +200,14 @@ public abstract class LifeFormSegmentNode
} }
else else
{ {
value = 0; _maxChildren = 0;
} }
} }
} }
public LifeFormSegmentNode(LifeFormSegmentNode parent, int maxChildren = 3, int maxRecursiveChildren = 4096) public LifeFormSegmentNode(LifeFormSegmentNode parent, int maxChildren = 3, int maxRecursiveChildren = 4096)
{ {
_maxChildren = maxChildren; MaxChildren = maxChildren;
if (parent == null) if (parent == null)
{ {
@ -204,13 +226,13 @@ public abstract class LifeFormSegmentNode
if (_maxRecursiveChildren <= 0) if (_maxRecursiveChildren <= 0)
{ {
_maxRecursiveChildren = 0; _maxRecursiveChildren = 0;
_maxChildren = 0; MaxChildren = 0;
} }
else if (_maxRecursiveChildren < _maxChildren) else if (_maxRecursiveChildren < _maxChildren)
_maxChildren = maxRecursiveChildren; MaxChildren = maxRecursiveChildren;
} }
Children = new LifeFormSegmentNode[_maxChildren]; Children = new LifeFormSegmentNode[MaxChildren];
// Iterate ID // Iterate ID
Id = LifeFormSegmentNode.NodeCounter; Id = LifeFormSegmentNode.NodeCounter;
@ -263,15 +285,34 @@ public abstract class LifeFormSegmentNode
} }
public int ChildrenCount public int ChildrenCount
{ {
//get { return Children.Count(x => x != null); }
get { return _childCount; } get { return _childCount; }
} }
public bool HasFreeChildSlot public bool HasFreeChildSlot
{ {
get { return ChildrenCount < _maxChildren; } get { return ChildrenCount < MaxChildren; }
} }
public void RedimChildren()
{
if (Children.Length > MaxChildren)
{
if (ChildrenCount <= MaxChildren)
Children = new LifeFormSegmentNode[MaxChildren];
else
{
// hmmm.... sollte eigentlich nicht sein. wenn ich nicht lösche, mal schauen.
GD.PrintErr("Hui RedimChildren: nodes löschen?");
}
//LifeFormSegmentNode[] cc = new LifeFormSegmentNode[MaxChildren];
//var copy = Children.Clone();
}
}
public int FreeNodeIndex() public int FreeNodeIndex()
{ {
// nur first ist immer rechtslastig -> rand? // nur first ist immer rechtslastig -> rand?
@ -282,18 +323,17 @@ public abstract class LifeFormSegmentNode
if (Children[0] == null) // center zuerst if (Children[0] == null) // center zuerst
return 0; return 0;
if (_maxChildren > 1) // 1 rechts oder 2 links if (MaxChildren > 1) // 1 rechts oder 2 links
{ {
List<int> nodes = new List<int>(); List<int> freeidx = new List<int>();
for (int j = 0; j < _maxChildren; j++) for (int j = 0; j < MaxChildren; j++)
{ {
if (Children[j] == null) if (Children[j] == null)
nodes.Add(j); freeidx.Add(j);
} }
int li = 0; int li = GD.RandRange(0, freeidx.Count-1);
li = GD.RandRange(0, nodes.Count-1); return freeidx[li];
return nodes[li];
} }
return -1; return -1;
} }

Loading…
Cancel
Save