commit
808aee880a
10 changed files with 1020 additions and 0 deletions
@ -0,0 +1,50 @@ |
|||||||
|
extends Camera2D |
||||||
|
|
||||||
|
var velocity = Vector2(0, 0) |
||||||
|
var mouse_down_pos = Vector2.ZERO |
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time. |
||||||
|
func _ready(): |
||||||
|
pass # Replace with function body. |
||||||
|
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the elapsed time since the previous frame. |
||||||
|
func _process(delta): |
||||||
|
var dir = Vector2(0,0) |
||||||
|
|
||||||
|
if Input.is_key_pressed(KEY_W): |
||||||
|
dir.y -= 1 |
||||||
|
if Input.is_key_pressed(KEY_S): |
||||||
|
dir.y += 1 |
||||||
|
if Input.is_key_pressed(KEY_A): |
||||||
|
dir.x -= 1 |
||||||
|
if Input.is_key_pressed(KEY_D): |
||||||
|
dir.x += 1 |
||||||
|
|
||||||
|
position += dir * delta * 100 |
||||||
|
|
||||||
|
|
||||||
|
func _input(event): |
||||||
|
if event is InputEventMouseButton: |
||||||
|
if event.button_index == 1: |
||||||
|
if event.pressed: |
||||||
|
mouse_down_pos = event.position |
||||||
|
else: |
||||||
|
var move = mouse_down_pos - event.position |
||||||
|
position += move |
||||||
|
|
||||||
|
var zoomf = 0 |
||||||
|
var nzoom = 0 |
||||||
|
if event.button_index == 4: |
||||||
|
zoomf += 0.1 |
||||||
|
|
||||||
|
if event.button_index == 5: |
||||||
|
zoomf -= 0.1 |
||||||
|
|
||||||
|
nzoom = zoom + Vector2(zoomf, zoomf) |
||||||
|
if nzoom.length() != 0 and zoomf != 0: |
||||||
|
zoom = nzoom |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# print( event.button_index) |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bnsf7lnm8elui"] |
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://camera2D.gd" id="1_exvdf"] |
||||||
|
|
||||||
|
[node name="Camera2D" type="Camera2D"] |
||||||
|
position_smoothing_enabled = true |
||||||
|
script = ExtResource("1_exvdf") |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://d2rgfedikyr2w"] |
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ixy2rotcnjk2" path="res://plant.tscn" id="1_mv10f"] |
||||||
|
[ext_resource type="PackedScene" path="res://camera_2d.tscn" id="2_pvpm2"] |
||||||
|
|
||||||
|
[node name="Main" type="Node2D"] |
||||||
|
|
||||||
|
[node name="Plant" parent="." instance=ExtResource("1_mv10f")] |
||||||
|
|
||||||
|
[node name="Camera2D" parent="." instance=ExtResource("2_pvpm2")] |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://ixy2rotcnjk2"] |
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://src/Plant.cs" id="1_3shty"] |
||||||
|
|
||||||
|
[node name="Plant" type="MeshInstance2D"] |
||||||
|
script = ExtResource("1_3shty") |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
; Engine configuration file. |
||||||
|
; It's best edited using the editor UI and not directly, |
||||||
|
; since the parameters that go here are not all obvious. |
||||||
|
; |
||||||
|
; Format: |
||||||
|
; [section] ; section goes between [] |
||||||
|
; param=value ; assign values to parameters |
||||||
|
|
||||||
|
config_version=5 |
||||||
|
|
||||||
|
[application] |
||||||
|
|
||||||
|
config/name="PlantLife2D" |
||||||
|
run/main_scene="res://main.tscn" |
||||||
|
config/features=PackedStringArray("4.2", "C#", "Forward Plus") |
||||||
|
config/icon="res://icon.svg" |
||||||
|
|
||||||
|
[dotnet] |
||||||
|
|
||||||
|
project/assembly_name="PlantLife2D" |
||||||
@ -0,0 +1,93 @@ |
|||||||
|
namespace DrawingDemo; |
||||||
|
|
||||||
|
using Godot; |
||||||
|
|
||||||
|
public delegate void DiedEventHandler(); |
||||||
|
|
||||||
|
public class BaseLifeComponent |
||||||
|
{ |
||||||
|
public float Energy = 1000; |
||||||
|
public float Age = 0; |
||||||
|
public int MaxAge = 150; |
||||||
|
public float Health = 100; |
||||||
|
private float MaxHealth = 100; |
||||||
|
public bool Alive = true; |
||||||
|
public event DiedEventHandler OnDied; |
||||||
|
|
||||||
|
|
||||||
|
public void Tick(float timeDelta) |
||||||
|
{ |
||||||
|
Age += timeDelta; |
||||||
|
ConsumeEnergy(0.1f); //Baseconsumption |
||||||
|
|
||||||
|
if (Age > MaxAge || Health <= 0) |
||||||
|
{ |
||||||
|
Die(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void Born() |
||||||
|
{ |
||||||
|
Alive = true; |
||||||
|
} |
||||||
|
public void AddEnergy(float amount) |
||||||
|
{ |
||||||
|
if (Alive) |
||||||
|
Energy += amount; |
||||||
|
} |
||||||
|
public void Heal(float amount) |
||||||
|
{ |
||||||
|
if (Health + amount >= MaxHealth) |
||||||
|
Health = MaxHealth; |
||||||
|
else |
||||||
|
Health += amount; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public float ConsumeEnergy(float amount, float costFactor = 1) |
||||||
|
{ |
||||||
|
if (!Alive) |
||||||
|
return 0; |
||||||
|
float cost = amount * costFactor; |
||||||
|
float consume = amount; |
||||||
|
|
||||||
|
if (Energy > 5) |
||||||
|
{ |
||||||
|
consume = Mathf.Clamp(consume, 0, Energy - 5); |
||||||
|
Energy -= consume; |
||||||
|
float energy = consume; |
||||||
|
return energy; |
||||||
|
} |
||||||
|
else |
||||||
|
return 0; |
||||||
|
} |
||||||
|
private void Die() |
||||||
|
{ |
||||||
|
Alive = false; |
||||||
|
Health = 0; |
||||||
|
Energy = 0; |
||||||
|
if (OnDied != null) OnDied(); |
||||||
|
} |
||||||
|
|
||||||
|
public string Stats() |
||||||
|
{ |
||||||
|
string stats = $"Health: {Health}\nEnergy:{Energy}\nAge: {Age}"; |
||||||
|
return stats; |
||||||
|
} |
||||||
|
|
||||||
|
public bool CanReproduce => (Age > 5 && Energy > 100 && Health > 50); |
||||||
|
|
||||||
|
public void Reproduce() |
||||||
|
{ |
||||||
|
if (CanReproduce) |
||||||
|
{ |
||||||
|
Energy -= 50; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,92 @@ |
|||||||
|
using Godot; |
||||||
|
using Color = Godot.Color; |
||||||
|
|
||||||
|
namespace DrawingDemo; |
||||||
|
|
||||||
|
public static class ColorHelper |
||||||
|
{ |
||||||
|
public static Color ColorFrom6Bit(byte colorcode) |
||||||
|
{ |
||||||
|
// 64 Colors - 2 bit for farbe A... mal schaun |
||||||
|
// oder 256 farben / 4 = 64 Values |
||||||
|
float bitsPerColor = 2; |
||||||
|
float colorperBit = 4; |
||||||
|
|
||||||
|
byte value1 = (byte)((colorcode & 0b11000000) >> 6); |
||||||
|
byte value2 = (byte)((colorcode & 0b00110000) >> 4); |
||||||
|
byte value3 = (byte)((colorcode & 0b00001100) >> 2); |
||||||
|
byte value4 = (byte)(colorcode & 0b00000011); |
||||||
|
|
||||||
|
return new Color(value1 / colorperBit, value2 / colorperBit, value3 / colorperBit, value4 / colorperBit); |
||||||
|
} |
||||||
|
} |
||||||
|
public class Genetics |
||||||
|
{ |
||||||
|
public GenStorage DNA = new GenStorage(); |
||||||
|
|
||||||
|
public Color MainColor |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
var codon = DNA.GetCodon(GenMapper.MainColor); |
||||||
|
|
||||||
|
return ColorHelper.ColorFrom6Bit(codon); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public enum GenMapper |
||||||
|
{ |
||||||
|
// 1. byte - 1 codon - 1. phenotyp |
||||||
|
|
||||||
|
MainColor, |
||||||
|
Color2, |
||||||
|
Color3, |
||||||
|
GrowSpeed, |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public struct GenStorage |
||||||
|
{ |
||||||
|
public byte[] Codons; |
||||||
|
|
||||||
|
public GenStorage() |
||||||
|
{ |
||||||
|
Codons = new byte[4] {42,0,0,0}; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void Randomize() |
||||||
|
{ |
||||||
|
for (int i = 0; i <= Codons.Length-1; i++) |
||||||
|
{ |
||||||
|
Codons[i] = (byte)(GD.Randi() % 255); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public byte[] GetMutatedGenome(float mutationRate) |
||||||
|
{ |
||||||
|
var ri = GD.Randi() % 32; |
||||||
|
byte[] mut = Codons; |
||||||
|
|
||||||
|
for (int i = 0; i < Codons.Length-1; i++) |
||||||
|
{ |
||||||
|
//mut[i] += (byte)ri; |
||||||
|
} |
||||||
|
|
||||||
|
return mut; |
||||||
|
} |
||||||
|
|
||||||
|
public byte GetCodon(GenMapper codon) |
||||||
|
{ |
||||||
|
return Codons[(int)codon]; |
||||||
|
} |
||||||
|
public byte GetCodon(int codon) |
||||||
|
{ |
||||||
|
return Codons[codon]; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
|
||||||
|
using Godot; |
||||||
|
|
||||||
|
public static class Helper |
||||||
|
{ |
||||||
|
public static Color[] myColors = new[] |
||||||
|
{ |
||||||
|
Colors.Red, Colors.Blue, Colors.Green, Colors.Yellow, Colors.Purple, Colors.Orange, Colors.Indigo, Colors.White |
||||||
|
}; |
||||||
|
|
||||||
|
public static Color[] MakeColorArray(int vertexCount, int verticePerColor) |
||||||
|
{ |
||||||
|
Color[] vertices = new Color[vertexCount]; |
||||||
|
int ci = -1; |
||||||
|
for (int i = 0; i < vertexCount; i++) |
||||||
|
{ |
||||||
|
if (i % verticePerColor == 0) |
||||||
|
{ |
||||||
|
ci++; |
||||||
|
} |
||||||
|
|
||||||
|
vertices[i] = myColors[ci % myColors.Length]; |
||||||
|
} |
||||||
|
return vertices; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,300 @@ |
|||||||
|
using System; |
||||||
|
using Godot; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
public partial class Plant : MeshInstance2D |
||||||
|
{ |
||||||
|
public Dictionary<int, PlantSegmentNode> Segments; |
||||||
|
public Vector2 Origin; |
||||||
|
private int _segmentIdCounter = 0; |
||||||
|
public PlantSegmentNode RootNode; |
||||||
|
public Rect2 ClientRectangle = new Rect2(Vector2.Zero, new Vector2(-80, 80)); |
||||||
|
|
||||||
|
public override void _Ready() |
||||||
|
{ |
||||||
|
Start(); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void Start() |
||||||
|
{ |
||||||
|
Segments = new Dictionary<int, PlantSegmentNode>(); |
||||||
|
PlantSegmentNode root = new PlantSegmentNode(null, maxChildren:1); |
||||||
|
root.Direction = Vector2.Up; |
||||||
|
root.NodeType = SegmentNodeTypes.Trunk; |
||||||
|
RootNode = root; |
||||||
|
Segments.Add(_segmentIdCounter, root); |
||||||
|
_segmentIdCounter++; |
||||||
|
GrowDemo(4096); |
||||||
|
|
||||||
|
this.Mesh = CreateMesh(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
// Called every frame. 'delta' is the elapsed time since the previous frame. |
||||||
|
public override void _Process(double delta) |
||||||
|
{ |
||||||
|
} |
||||||
|
public void GrowDemo(int nodes) |
||||||
|
{ |
||||||
|
for (int i = 0; i < nodes -1; i++) |
||||||
|
{ |
||||||
|
GrowNode(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void GrowNewNode(PlantNodeParams p) |
||||||
|
{ |
||||||
|
//var randAngle = (float)GD.RandRange(-p.AllowedParentDiv/2, p.AllowedParentDiv/2); |
||||||
|
var randAngleZeroDiv = (float)GD.RandRange(20, 45); |
||||||
|
|
||||||
|
float randF = (0.5f - GD.Randf()); |
||||||
|
int randI = GD.RandRange(0, 10); |
||||||
|
|
||||||
|
int maxChildren = p.MaxChildren; |
||||||
|
int maxDescentants = 4096; |
||||||
|
var parent = GetFreeSlotSegment(false); |
||||||
|
PlantSegmentNode seg; |
||||||
|
Vector2 dir = parent.Direction; |
||||||
|
|
||||||
|
|
||||||
|
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); |
||||||
|
maxChildren = 2; |
||||||
|
maxDescentants = 4096; |
||||||
|
break; |
||||||
|
case 1: // left |
||||||
|
dir = dir.Rotated(-randAngleZeroDiv); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
dir = dir.Rotated(+randAngleZeroDiv); |
||||||
|
break; |
||||||
|
// 3D... |
||||||
|
|
||||||
|
} |
||||||
|
seg = new PlantSegmentNode(parent, p.MaxChildren); |
||||||
|
if (parent.AddChild(seg, freeindex)) |
||||||
|
{ |
||||||
|
Segments.Add(_segmentIdCounter, seg); |
||||||
|
_segmentIdCounter++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
GD.PrintErr("Error bei Add"); |
||||||
|
} |
||||||
|
} |
||||||
|
public void GrowNode() |
||||||
|
{ |
||||||
|
// Config |
||||||
|
float minAngleDiv =Mathf.DegToRad(23); |
||||||
|
float maxAngleDiv = Mathf.DegToRad(48); |
||||||
|
int maxChildren = 2; |
||||||
|
int maxDescentants = 128; |
||||||
|
float maxRootDeviationAngle = Mathf.DegToRad(180); |
||||||
|
|
||||||
|
// Params |
||||||
|
var randAngle = (float)GD.RandRange(minAngleDiv, maxAngleDiv); |
||||||
|
float randF = (0.5f - GD.Randf()); |
||||||
|
int randI = GD.RandRange(0, 10); |
||||||
|
// Get lowest free node |
||||||
|
var parent = GetFreeSlotSegment(false); |
||||||
|
PlantSegmentNode seg; |
||||||
|
Vector2 dir = parent.Direction; |
||||||
|
|
||||||
|
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); |
||||||
|
maxChildren = 2; |
||||||
|
maxDescentants = 4096; |
||||||
|
break; |
||||||
|
case 1: // left |
||||||
|
dir = dir.Rotated(-randAngle); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
dir = dir.Rotated(+randAngle); |
||||||
|
break; |
||||||
|
// 3D... |
||||||
|
|
||||||
|
} |
||||||
|
if (randI < 6) |
||||||
|
maxChildren = 1; |
||||||
|
|
||||||
|
if (RootNode.Direction.Dot(dir) < 0) // ..<0 = 180 grad maxRootDeviationAngle) |
||||||
|
maxChildren = 0; // wenn überhang -> branach ende |
||||||
|
|
||||||
|
seg = new PlantSegmentNode(parent, maxChildren); |
||||||
|
seg.Direction = dir; |
||||||
|
|
||||||
|
|
||||||
|
if (freeindex == 0) // center node |
||||||
|
{ |
||||||
|
seg.Size = parent.Size * 0.95f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
seg.Size = parent.Size * (0.88f + randF/10); |
||||||
|
if (randI < 6) |
||||||
|
maxChildren = 2; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// if (CheckShootCollided(seg)) |
||||||
|
// { |
||||||
|
// //GD.Print("Collision ", seg); |
||||||
|
// // Add stump dummy - so slot is ignored |
||||||
|
// seg.NodeType = SegmentNodeTypes.Stump; |
||||||
|
// seg.MaxChildren = seg.ChildrenCount; |
||||||
|
// seg.Size = Vector2.Zero;//seg.Size * 0.4f; |
||||||
|
// } |
||||||
|
|
||||||
|
if (parent.AddChild(seg, freeindex)) |
||||||
|
{ |
||||||
|
Segments.Add(_segmentIdCounter, seg); |
||||||
|
_segmentIdCounter++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
GD.PrintErr("Error bei Add"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Vector2[] CalculateLineVertices() |
||||||
|
{ |
||||||
|
int vertexCount = 2 * Segments.Count; |
||||||
|
Vector2[] vertices = new Vector2[vertexCount]; |
||||||
|
|
||||||
|
for (int i = 0; i < _segmentIdCounter; i++) |
||||||
|
{ |
||||||
|
PlantSegmentNode segment = Segments[i]; |
||||||
|
|
||||||
|
Vector2 pos = segment.VecToRoot; |
||||||
|
Vector2 par = segment.PositionRelativeParent; |
||||||
|
Vector2 dir = segment.Direction; |
||||||
|
Vector2 line = segment.Line; |
||||||
|
Vector2 size = segment.Size; |
||||||
|
|
||||||
|
Vector2 start = pos; |
||||||
|
Vector2 end = pos + line; |
||||||
|
int j = i * 2; |
||||||
|
vertices[j] = start; |
||||||
|
vertices[j+1] = end; |
||||||
|
|
||||||
|
} |
||||||
|
return vertices; |
||||||
|
} |
||||||
|
|
||||||
|
public Vector2[] CalculateVertices() |
||||||
|
{ |
||||||
|
Vector2[] vertices = new Vector2[6 * _segmentIdCounter]; |
||||||
|
|
||||||
|
for (int i = 0; i < Segments.Count; i++) |
||||||
|
{ |
||||||
|
PlantSegmentNode segment = Segments[i]; |
||||||
|
|
||||||
|
Vector2 pos = segment.VecToRoot; |
||||||
|
Vector2 par = segment.PositionRelativeParent; |
||||||
|
Vector2 dir = segment.Direction; |
||||||
|
Vector2 line = segment.Line; |
||||||
|
Vector2 size = segment.Size; |
||||||
|
float width = size.X; |
||||||
|
|
||||||
|
Vector2 a = pos; |
||||||
|
//Vector2 b = new Vector2(a.X, a.Y + size.Y); |
||||||
|
//Vector2 c = new Vector2(b.X + width, b.Y); |
||||||
|
Vector2 b = a + line; |
||||||
|
Vector2 c = new Vector2(b.X + width, b.Y); |
||||||
|
Vector2 d = new Vector2(a.X + width, a.Y); |
||||||
|
|
||||||
|
int j = i * 6; |
||||||
|
vertices[j] = a; |
||||||
|
vertices[j+1] = b; |
||||||
|
vertices[j+2] = c; |
||||||
|
vertices[j+3] = a; |
||||||
|
vertices[j+4] = c; |
||||||
|
vertices[j+5] = d; |
||||||
|
} |
||||||
|
return vertices; |
||||||
|
} |
||||||
|
|
||||||
|
public bool CheckCollision(PlantSegmentNode node) |
||||||
|
{ |
||||||
|
|
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
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() |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,414 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using Godot; |
||||||
|
|
||||||
|
|
||||||
|
public enum SegmentNodeTypes |
||||||
|
{ |
||||||
|
Trunk, |
||||||
|
Branch, |
||||||
|
Leave, |
||||||
|
Fruit, |
||||||
|
Stump, // basically blocks growth |
||||||
|
} |
||||||
|
|
||||||
|
public class PlantSegmentNode : LifeFormSegmentNode |
||||||
|
{ |
||||||
|
//public LifeformNodeConfiguration Settings; |
||||||
|
public Vector2 Size; |
||||||
|
public Vector2 PositionRelativeParent; |
||||||
|
public Vector2 VecToRoot; |
||||||
|
public Vector2 Direction; |
||||||
|
|
||||||
|
private SegmentNodeTypes _nodeType = SegmentNodeTypes.Branch; |
||||||
|
|
||||||
|
public SegmentNodeTypes NodeType |
||||||
|
{ |
||||||
|
get { return _nodeType; } |
||||||
|
set { |
||||||
|
if (value == SegmentNodeTypes.Stump) |
||||||
|
{ |
||||||
|
Size = Vector2.Zero; |
||||||
|
MaxChildren = ChildrenCount; |
||||||
|
Direction = ((PlantSegmentNode)Parent).Direction.Normalized(); |
||||||
|
_nodeType = value; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
public float Rotation |
||||||
|
{ |
||||||
|
get { return Mathf.RadToDeg(Direction.Angle()); } |
||||||
|
} |
||||||
|
|
||||||
|
public PlantSegmentNode(PlantSegmentNode parent, int maxChildren = 2, int maxRecursiveChildren = 4096) : base(parent, maxChildren, maxRecursiveChildren) |
||||||
|
{ |
||||||
|
if (parent != null) // is child |
||||||
|
{ |
||||||
|
Direction = parent.Direction.Normalized(); |
||||||
|
Size = parent.Size / parent.MaxChildren; |
||||||
|
PositionRelativeParent = parent.PositionRelativeParent + parent.Line; |
||||||
|
VecToRoot = parent.VecToRoot + parent.Line; |
||||||
|
} |
||||||
|
else // is root |
||||||
|
{ |
||||||
|
// must be root |
||||||
|
Direction = Vector2.Up; |
||||||
|
VecToRoot = Vector2.Zero; |
||||||
|
Size = new Vector2(5, 20); |
||||||
|
PositionRelativeParent = Vector2.Zero; //new Vector2() + Line; |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void UpdateSelfToParent() |
||||||
|
{ |
||||||
|
if (IsRoot) |
||||||
|
return; |
||||||
|
|
||||||
|
var parent = (PlantSegmentNode)Parent; |
||||||
|
PositionRelativeParent = parent.PositionRelativeParent + parent.Line; |
||||||
|
} |
||||||
|
|
||||||
|
public Vector2 Line |
||||||
|
{ |
||||||
|
get { return Direction.Normalized() * Size.Length(); } |
||||||
|
} |
||||||
|
|
||||||
|
// public Vector2[] GetPoints() |
||||||
|
// { |
||||||
|
// //Polygon2D polygon = new Polygon2D(); |
||||||
|
// float width = Size.X; |
||||||
|
// float height = Size.Y; |
||||||
|
// |
||||||
|
// Vector2 leftbottom = new Vector2(0 - width/2, 0); |
||||||
|
// Vector2 rightbottom = new Vector2(width / 2, 0); |
||||||
|
// |
||||||
|
// Vector2 lefttop = leftbottom + new Vector2(0, height); |
||||||
|
// Vector2 righttop = rightbottom + new Vector2(0, height); |
||||||
|
// |
||||||
|
// var points = new Vector2[] |
||||||
|
// { |
||||||
|
// leftbottom, lefttop, righttop, rightbottom, leftbottom |
||||||
|
// }; |
||||||
|
// return points; |
||||||
|
// //return polygon; |
||||||
|
// } |
||||||
|
public PlantSegmentNode Get_Child(int index) |
||||||
|
{ |
||||||
|
if (Children[index] is PlantSegmentNode) |
||||||
|
{ |
||||||
|
return (PlantSegmentNode)Children[index]; |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Calls recursive through all Children |
||||||
|
/// </summary> |
||||||
|
public override void UpdateChildren() |
||||||
|
{ |
||||||
|
UpdateSelfToParent(); |
||||||
|
if (HasChildren) |
||||||
|
{ |
||||||
|
foreach (var child in Children) |
||||||
|
{ |
||||||
|
child?.UpdateChildren(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
public List<PlantSegmentNode> GetChildren() |
||||||
|
{ |
||||||
|
List<PlantSegmentNode> result = new List<PlantSegmentNode>(); |
||||||
|
|
||||||
|
for (int i = 0; i < Children.Length; i++) |
||||||
|
{ |
||||||
|
if (Children[i] != null) |
||||||
|
result.Add((PlantSegmentNode)Children[i]); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public bool AddChild(PlantSegmentNode child, SegmentDirections dir = SegmentDirections.center) |
||||||
|
{ |
||||||
|
return base.AddChild(child, dir); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public enum SegmentDirections |
||||||
|
{ |
||||||
|
center, |
||||||
|
left, |
||||||
|
right, |
||||||
|
forward, |
||||||
|
back, |
||||||
|
} |
||||||
|
|
||||||
|
// Neu: Fixe vorgabe der Childnodes - wildwuchs bringt nix. |
||||||
|
// Für 2D node maximal 3 childnodes. mitte(rauf), links, rechts |
||||||
|
// 3D max 5 - mitte(rauf), links, rechts, vorn, hinten, vll. mehr |
||||||
|
// index 0 = immer: mitte(rauf) |
||||||
|
// array wird max. init bei max_child kein adden mehr |
||||||
|
|
||||||
|
|
||||||
|
public abstract class LifeFormSegmentNode |
||||||
|
{ |
||||||
|
public static int NodeCounter = 0; |
||||||
|
public LifeFormSegmentNode Parent; |
||||||
|
protected LifeFormSegmentNode[] Children; |
||||||
|
public int Order = 1; // 1 = root, 2 - child of root, 3.... |
||||||
|
public readonly bool IsRoot = false; |
||||||
|
private int _maxChildren; |
||||||
|
private int _maxRecursiveChildren; |
||||||
|
private int _childCount = 0; |
||||||
|
|
||||||
|
public int Id; |
||||||
|
// reduce macht im moment noch nix. Nodes bleiben.... |
||||||
|
public int MaxChildren |
||||||
|
{ |
||||||
|
get { return _maxChildren; } |
||||||
|
set |
||||||
|
{ |
||||||
|
if (value > 0) |
||||||
|
{ |
||||||
|
_maxChildren = value; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
value = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public LifeFormSegmentNode(LifeFormSegmentNode parent, int maxChildren = 3, int maxRecursiveChildren = 4096) |
||||||
|
{ |
||||||
|
_maxChildren = maxChildren; |
||||||
|
|
||||||
|
if (parent == null) |
||||||
|
{ |
||||||
|
IsRoot = true; |
||||||
|
Order = 1; |
||||||
|
_maxRecursiveChildren = maxRecursiveChildren; |
||||||
|
GD.Print("Root Node erstellt"); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Parent = parent; |
||||||
|
Order = parent.Order + 1; |
||||||
|
// Count down the values through the child nodes |
||||||
|
_maxRecursiveChildren = parent._maxRecursiveChildren - _maxChildren; |
||||||
|
|
||||||
|
if (_maxRecursiveChildren <= 0) |
||||||
|
{ |
||||||
|
_maxRecursiveChildren = 0; |
||||||
|
_maxChildren = 0; |
||||||
|
} |
||||||
|
else if (_maxRecursiveChildren < _maxChildren) |
||||||
|
_maxChildren = maxRecursiveChildren; |
||||||
|
} |
||||||
|
|
||||||
|
Children = new LifeFormSegmentNode[_maxChildren]; |
||||||
|
|
||||||
|
// Iterate ID |
||||||
|
Id = LifeFormSegmentNode.NodeCounter; |
||||||
|
LifeFormSegmentNode.NodeCounter++; |
||||||
|
} |
||||||
|
|
||||||
|
public bool AddChild(LifeFormSegmentNode childNode, int index) |
||||||
|
{ |
||||||
|
if (!HasFreeChildSlot || Children[index] != null || index > _maxChildren -1) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (childNode.Parent != this) |
||||||
|
throw new Exception("Kukuk Error: Child is not of this node."); |
||||||
|
|
||||||
|
//childNode.Parent = this; |
||||||
|
childNode.Order = this.Order + 1; |
||||||
|
Children[index] = childNode; |
||||||
|
_childCount++; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public bool AddChild(LifeFormSegmentNode childNode, SegmentDirections dir) |
||||||
|
{ |
||||||
|
return AddChild(childNode, (int)dir); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public LifeFormSegmentNode GetChild(SegmentDirections dir) |
||||||
|
{ |
||||||
|
return GetChild((int)dir); |
||||||
|
} |
||||||
|
|
||||||
|
public LifeFormSegmentNode GetChild(int index) |
||||||
|
{ |
||||||
|
if (ChildrenCount < index || index > ChildrenCount) |
||||||
|
return null; |
||||||
|
|
||||||
|
return Children[index]; |
||||||
|
|
||||||
|
if (index < _maxChildren - 1 && Children[index] != null) |
||||||
|
return Children[index]; |
||||||
|
else |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
public bool HasChildren |
||||||
|
{ |
||||||
|
get { return ChildrenCount > 0; } |
||||||
|
} |
||||||
|
public int ChildrenCount |
||||||
|
{ |
||||||
|
//get { return Children.Count(x => x != null); } |
||||||
|
get { return _childCount; } |
||||||
|
} |
||||||
|
|
||||||
|
public bool HasFreeChildSlot |
||||||
|
{ |
||||||
|
get { return ChildrenCount < _maxChildren; } |
||||||
|
} |
||||||
|
|
||||||
|
public int FreeNodeIndex() |
||||||
|
{ |
||||||
|
// nur first ist immer rechtslastig -> rand? |
||||||
|
|
||||||
|
int i = 0; |
||||||
|
if (!HasFreeChildSlot) |
||||||
|
return -1; |
||||||
|
|
||||||
|
if (Children[0] == null) // center zuerst |
||||||
|
return 0; |
||||||
|
if (_maxChildren > 1) // 1 rechts oder 2 links |
||||||
|
{ |
||||||
|
List<int> nodes = new List<int>(); |
||||||
|
for (int j = 0; j < _maxChildren; j++) |
||||||
|
{ |
||||||
|
if (Children[j] == null) |
||||||
|
nodes.Add(j); |
||||||
|
} |
||||||
|
|
||||||
|
int li = 0; |
||||||
|
li = GD.RandRange(0, nodes.Count-1); |
||||||
|
return nodes[li]; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
public abstract void UpdateChildren(); |
||||||
|
} |
||||||
|
|
||||||
|
// public abstract class LifeFormSegmentNode |
||||||
|
// { |
||||||
|
// |
||||||
|
// public LifeFormSegmentNode Parent; |
||||||
|
// protected LifeFormSegmentNode[] Children; |
||||||
|
// public readonly int Order = 1; // 1 = root, 2 - child of root, 3.... |
||||||
|
// public readonly bool IsRoot = false; |
||||||
|
// private int _maxChildren; |
||||||
|
// private int _maxRecursiveChildren; |
||||||
|
// private int _lastChildIndex = 0; |
||||||
|
// |
||||||
|
// public bool HasChildren |
||||||
|
// { |
||||||
|
// get { return (_lastChildIndex > 0); } |
||||||
|
// } |
||||||
|
// |
||||||
|
// public LifeFormSegmentNode(LifeFormSegmentNode parent, int maxChildren = 3, int maxRecursiveChildren = 4096) |
||||||
|
// { |
||||||
|
// _maxChildren = maxChildren; |
||||||
|
// |
||||||
|
// if (parent == null) |
||||||
|
// { |
||||||
|
// IsRoot = true; |
||||||
|
// Order = 1; |
||||||
|
// _maxRecursiveChildren = maxRecursiveChildren; |
||||||
|
// GD.Print("Root Node erstellt"); |
||||||
|
// } |
||||||
|
// else |
||||||
|
// { |
||||||
|
// Parent = parent; |
||||||
|
// Order = parent.Order + 1; |
||||||
|
// // Count down the values through the child nodes |
||||||
|
// _maxRecursiveChildren = parent._maxRecursiveChildren - _maxChildren; |
||||||
|
// |
||||||
|
// if (_maxRecursiveChildren <= 0) |
||||||
|
// { |
||||||
|
// _maxRecursiveChildren = 0; |
||||||
|
// _maxChildren = 0; |
||||||
|
// } |
||||||
|
// else if (_maxRecursiveChildren < _maxChildren) |
||||||
|
// _maxChildren = maxRecursiveChildren; |
||||||
|
// } |
||||||
|
// Children = new LifeFormSegmentNode[_maxChildren]; |
||||||
|
// |
||||||
|
// } |
||||||
|
// |
||||||
|
// public bool HasFreeChildSlot |
||||||
|
// { |
||||||
|
// get { return (_lastChildIndex <= _maxChildren - 1); } |
||||||
|
// } |
||||||
|
// public int ChildrenCount |
||||||
|
// { |
||||||
|
// get { return _lastChildIndex + 1; } |
||||||
|
// } |
||||||
|
// |
||||||
|
// protected bool AddChild(LifeFormSegmentNode child) |
||||||
|
// { |
||||||
|
// if (HasFreeChildSlot) |
||||||
|
// { |
||||||
|
// if (child.Parent != this) |
||||||
|
// throw new Exception("Kukuk Error: Child is not of this node."); |
||||||
|
// if (Children[_lastChildIndex] == null) |
||||||
|
// { |
||||||
|
// Children[_lastChildIndex] = child; |
||||||
|
// _lastChildIndex++; |
||||||
|
// return true; |
||||||
|
// } |
||||||
|
// } |
||||||
|
// return false; |
||||||
|
// } |
||||||
|
// |
||||||
|
// public abstract void UpdateChildren(); |
||||||
|
// |
||||||
|
// |
||||||
|
// } |
||||||
|
|
||||||
|
// public struct LifeformNodeConfiguration |
||||||
|
// { |
||||||
|
// public float MaxSegmentAngle; |
||||||
|
// public int MaxChildren; |
||||||
|
// public Vector2 Size; |
||||||
|
// public Vector2 Direction; |
||||||
|
// public string ConfigName = "empty"; |
||||||
|
// |
||||||
|
// public LifeformNodeConfiguration(string name, Vector2 direction, Vector2 size, int maxChildren = 3) |
||||||
|
// { |
||||||
|
// ConfigName = name; |
||||||
|
// Direction = direction; |
||||||
|
// Size = size; |
||||||
|
// MaxChildren = maxChildren; |
||||||
|
// MaxSegmentAngle = Mathf.DegToRad(45f); |
||||||
|
// } |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// public static LifeformNodeConfiguration GetTree() |
||||||
|
// { |
||||||
|
// Vector2 size = new Vector2(10, 50); |
||||||
|
// return new LifeformNodeConfiguration("tree", Vector2.Up, size); |
||||||
|
// } |
||||||
|
// public static LifeformNodeConfiguration GetDefault() |
||||||
|
// { |
||||||
|
// LifeformNodeConfiguration conf = new LifeformNodeConfiguration(); |
||||||
|
// conf.MaxSegmentAngle = Mathf.DegToRad(45f); |
||||||
|
// conf.MaxChildren = 3; |
||||||
|
// conf.ConfigName = "default"; |
||||||
|
// conf.Size = Vector2.One; |
||||||
|
// conf.Direction = Vector2.Up; |
||||||
|
// return conf; |
||||||
|
// } |
||||||
|
//} |
||||||
Loading…
Reference in new issue