Compare commits
34 Commits
a470593ae4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f324122874 | |||
| f932ae1bcd | |||
| c41cdd57c4 | |||
| e653f23a42 | |||
| 6acccefa0a | |||
| 28ada065a6 | |||
| ea34b8ecc0 | |||
| 4462573267 | |||
| 0b97027e00 | |||
| d9bde10c3c | |||
| 2e4fc22263 | |||
| 202e71d97e | |||
| a119e92bf6 | |||
| da3afd22fc | |||
| abf948a310 | |||
| 2cd6a757dc | |||
| 4ce265e722 | |||
| 572e02956c | |||
| de99d54ad6 | |||
| 2bcdeecb81 | |||
| 7f31c8c3d5 | |||
| 882a12862e | |||
| 5045b4421e | |||
| 71db6513f2 | |||
| 4e6b1d642c | |||
| 8ebaa9e987 | |||
| 383e1343ef | |||
| d1dac0b855 | |||
| 34aee98a6a | |||
| 7b2845ee01 | |||
| bb2f498ac5 | |||
| 7388e1a2d7 | |||
| 2d38774341 | |||
| 8c3f7c2964 |
@@ -1,6 +1,9 @@
|
||||
<Project Sdk="Godot.NET.Sdk/4.2.2">
|
||||
<Project Sdk="Godot.NET.Sdk/4.5.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="benchmark.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
12
FOU.csproj.old.1
Normal file
12
FOU.csproj.old.1
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Godot.NET.Sdk/4.4.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="benchmark.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="UI\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -2,7 +2,8 @@ using Godot;
|
||||
|
||||
public partial class FPSLabel : Label
|
||||
{
|
||||
bool ShowFPS = true;
|
||||
[Export]
|
||||
public bool ShowFPS = true;
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
public override void _Process(double delta) {
|
||||
|
||||
1
Scenes/FPSLabel.cs.uid
Normal file
1
Scenes/FPSLabel.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d3cbk8f7lckbr
|
||||
17
Scenes/PerfDetails.cs
Normal file
17
Scenes/PerfDetails.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using FOU.Scripts;
|
||||
using Godot;
|
||||
|
||||
public partial class PerfDetails : Label
|
||||
{
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
Text = "";
|
||||
if (!Main.Instance.DebugMode) return;
|
||||
|
||||
int activeElements = 0;
|
||||
foreach (Chunk c in Main.Instance.Level.GetChunks())
|
||||
activeElements += c.ActiveElementsCount();
|
||||
|
||||
Text += $"Active Elements: {activeElements}";
|
||||
}
|
||||
}
|
||||
1
Scenes/PerfDetails.cs.uid
Normal file
1
Scenes/PerfDetails.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://52grhydeyy0t
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Reflection;
|
||||
using Godot;
|
||||
|
||||
public partial class SettingsController : VBoxContainer
|
||||
@@ -18,7 +17,7 @@ public partial class SettingsController : VBoxContainer
|
||||
slSize.Value = main.BrushSize;
|
||||
|
||||
slRain.ValueChanged += OnRainValueChanged;
|
||||
slRain.Value = main.RainAmount;
|
||||
slRain.Value = main.rainAmount;
|
||||
}
|
||||
|
||||
private void OnSizeValueChanged(double value) {
|
||||
|
||||
1
Scenes/SettingsController.cs.uid
Normal file
1
Scenes/SettingsController.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dxmkbb5f1t368
|
||||
@@ -1,14 +1,14 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://cf34vk5r055dx"]
|
||||
[gd_scene load_steps=5 format=3 uid="uid://cf34vk5r055dx"]
|
||||
|
||||
[ext_resource type="Script" path="res://Scripts/Main.cs" id="1_k1i8e"]
|
||||
[ext_resource type="Script" path="res://Scenes/FPSLabel.cs" id="2_8cb7y"]
|
||||
[ext_resource type="Script" path="res://Scenes/SettingsController.cs" id="3_a4w6m"]
|
||||
[ext_resource type="Script" uid="uid://cmvvubxfvdca7" path="res://Scripts/Main.cs" id="1_k1i8e"]
|
||||
[ext_resource type="Script" uid="uid://d3cbk8f7lckbr" path="res://Scenes/FPSLabel.cs" id="2_8cb7y"]
|
||||
[ext_resource type="Script" uid="uid://dxmkbb5f1t368" path="res://Scenes/SettingsController.cs" id="3_a4w6m"]
|
||||
[ext_resource type="Script" uid="uid://52grhydeyy0t" path="res://Scenes/PerfDetails.cs" id="3_u2p48"]
|
||||
|
||||
[node name="Main" type="Node2D"]
|
||||
script = ExtResource("1_k1i8e")
|
||||
BrushSize = 0
|
||||
TextureResolution = 0.25
|
||||
RainAmount = 5.0
|
||||
DebugMode = true
|
||||
BrushSize = 4
|
||||
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
@@ -34,6 +34,15 @@ text = "FPS: 0
|
||||
"
|
||||
script = ExtResource("2_8cb7y")
|
||||
|
||||
[node name="PerfDetails" type="Label" parent="CanvasLayer/TopUI"]
|
||||
layout_mode = 0
|
||||
offset_top = 30.0
|
||||
offset_right = 76.0
|
||||
offset_bottom = 67.0
|
||||
theme_override_font_sizes/font_size = 10
|
||||
text = "Active Elements: 0"
|
||||
script = ExtResource("3_u2p48")
|
||||
|
||||
[node name="Settings" type="VBoxContainer" parent="CanvasLayer/TopUI"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
@@ -75,6 +84,13 @@ custom_minimum_size = Vector2(150, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
size_flags_vertical = 1
|
||||
max_value = 5.0
|
||||
max_value = 10.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
|
||||
[node name="ToolbarMarginContainer2" parent="CanvasLayer/TopUI" instance=ExtResource("5_kry3j")]
|
||||
layout_mode = 0
|
||||
offset_left = 860.0
|
||||
offset_top = 5.0
|
||||
offset_right = 1060.0
|
||||
offset_bottom = 69.0
|
||||
|
||||
151
Scripts/Chunk.cs
151
Scripts/Chunk.cs
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FOU.Scripts.Elements;
|
||||
using Godot;
|
||||
|
||||
@@ -6,6 +7,7 @@ namespace FOU.Scripts;
|
||||
|
||||
public class Chunk {
|
||||
public Element[,] Elements;
|
||||
public int Index = -1;
|
||||
public Chunk NeighborN = null;
|
||||
public Chunk NeighborE = null;
|
||||
public Chunk NeighborS = null;
|
||||
@@ -14,62 +16,78 @@ public class Chunk {
|
||||
private readonly Image image;
|
||||
private readonly int sizeX;
|
||||
private readonly int sizeY;
|
||||
private int frame;
|
||||
private Chunk _this;
|
||||
|
||||
public Chunk(int x, int y) {
|
||||
_this = this;
|
||||
private readonly HashSet<Element> activeElements;
|
||||
private readonly HashSet<Element> toAddElements = new();
|
||||
private readonly HashSet<Element> toRemoveElements = new();
|
||||
|
||||
public Chunk(int x, int y, int index) {
|
||||
Index = index;
|
||||
sizeX = x;
|
||||
sizeY = y;
|
||||
|
||||
Elements = new Element[x, y];
|
||||
for (int i = 0; i < sizeX; i++) {
|
||||
for (int j = 0; j < sizeY; j++) {
|
||||
Elements[i,j] = new Element(i, j, _this);
|
||||
Elements[i,j] = new Element(i, j, this);
|
||||
}
|
||||
}
|
||||
|
||||
image = Image.Create(sizeX, sizeY, false, Image.Format.Rgb8);
|
||||
activeElements = new HashSet<Element>(sizeX * sizeY);
|
||||
|
||||
image = Image.CreateEmpty(sizeX, sizeY, false, Image.Format.Rgb8);
|
||||
image.Fill(Colors.Black);
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
if (Elements[x,y] != null)
|
||||
Elements[x,y].Update(frame);
|
||||
foreach (Element e in activeElements)
|
||||
e.Update();
|
||||
|
||||
// apply changes:
|
||||
if (toRemoveElements.Count > 0) {
|
||||
foreach (Element removeElement in toRemoveElements) {
|
||||
activeElements.Remove(removeElement);
|
||||
removeElement.ResetColor();
|
||||
}
|
||||
}
|
||||
frame++;
|
||||
// TODO: enable rain again
|
||||
// MakeItRain(_main.RainAmount);
|
||||
toRemoveElements.Clear();
|
||||
}
|
||||
|
||||
public void WritePixel<T>(int x, int y, int size) {
|
||||
int halfsize = size/2;
|
||||
if (toAddElements.Count > 0) {
|
||||
foreach (Element addElement in toAddElements) {
|
||||
activeElements.Add(addElement);
|
||||
addElement.SetDebugColor(addElement.Color * 1.5f);
|
||||
}
|
||||
toAddElements.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetElementActive(Element e, bool active) {
|
||||
if (e.GetType() == typeof(Element)) return;
|
||||
|
||||
if (active) AddToChunk(e);
|
||||
else RemoveFromChunk(e);
|
||||
}
|
||||
|
||||
public void WritePixel<T>(int x, int y) {
|
||||
if (x < 0 || x >= sizeX || y < 0 || y >= sizeY) {
|
||||
GD.PrintErr($"Out of bounds for chunk: {x}:{y}");
|
||||
return;
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
object o = Activator.CreateInstance(type, x, y, this);
|
||||
|
||||
for (int i = -halfsize; i <= halfsize; i++) {
|
||||
for (int j = -halfsize; j <= halfsize; j++) {
|
||||
int X = Mathf.Clamp(x + i, 0, sizeX-1);
|
||||
int Y = Mathf.Clamp(y + j, 0, sizeY-1);
|
||||
object o = Activator.CreateInstance(type, X, Y, _this);
|
||||
|
||||
if (Elements[X,Y].GetType() != type)
|
||||
Elements[X,Y] = o as Element;
|
||||
}
|
||||
}
|
||||
// check if not empty and overwrite not allowed:
|
||||
if (!Elements[x,y].IsEmpty() && !Main.Instance.AllowOverwrite) {
|
||||
return;
|
||||
}
|
||||
|
||||
public Image DrawLevel() {
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
image.SetPixel(x,y, Elements[x,y].Color);
|
||||
Elements[x, y].Chunk.RemoveFromChunk(Elements[x, y]);
|
||||
Elements[x, y] = o as Element;
|
||||
Elements[x, y].Active = true;
|
||||
}
|
||||
}
|
||||
return image;
|
||||
|
||||
public int ActiveElementsCount() {
|
||||
return activeElements.Count;
|
||||
}
|
||||
|
||||
public void Swap(Element what, Vector2I pos) {
|
||||
@@ -77,19 +95,27 @@ public class Chunk {
|
||||
}
|
||||
|
||||
public Element Get(Vector2I pos) {
|
||||
if (pos.Y < 0)
|
||||
if (NeighborN != null) return NeighborN.Get(pos.X, NeighborN.sizeY + pos.Y);
|
||||
Element e = null;
|
||||
|
||||
if (pos.Y >= sizeY)
|
||||
if (NeighborS != null) return NeighborS.Get(pos.X, sizeY - pos.Y);
|
||||
if (pos.Y < 0) {
|
||||
if (NeighborN != null)
|
||||
e = NeighborN.Get(pos.X, NeighborN.sizeY + pos.Y);
|
||||
|
||||
if (pos.X < 0)
|
||||
if (NeighborE != null) return NeighborE.Get(NeighborE.sizeX + pos.X, pos.Y);
|
||||
} else if (pos.Y >= sizeY) {
|
||||
if (NeighborS != null)
|
||||
e = NeighborS.Get(pos.X, sizeY - pos.Y);
|
||||
|
||||
if (pos.X >= sizeX)
|
||||
if (NeighborW != null) return NeighborW.Get(sizeX - pos.X, pos.Y);
|
||||
} else if (pos.X < 0) {
|
||||
if (NeighborE != null)
|
||||
e = NeighborE.Get(NeighborE.sizeX + pos.X, pos.Y);
|
||||
|
||||
return Get(pos.X, pos.Y);
|
||||
} else if (pos.X >= sizeX) {
|
||||
if (NeighborW != null)
|
||||
e = NeighborW.Get(sizeX - pos.X, pos.Y);
|
||||
|
||||
} else e = Get(pos.X, pos.Y);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
private Element Get(int x, int y) {
|
||||
@@ -104,25 +130,44 @@ public class Chunk {
|
||||
}
|
||||
|
||||
private void Swap(Element what, Element swapTo) {
|
||||
Element swap = new Element(what);
|
||||
if (what == null || swapTo == null) {
|
||||
GD.PrintErr("Trying to swap null");
|
||||
return;
|
||||
}
|
||||
|
||||
GD.Print($"Swap: {what} -> {swapTo}");
|
||||
if (swapTo.Position.Y == 0) GD.Print($"");
|
||||
if (what.Chunk != swapTo.Chunk) {
|
||||
what.Chunk.RemoveFromChunk(what);
|
||||
swapTo.Chunk.AddToChunk(what);
|
||||
|
||||
if (what == null || swapTo == null) return;
|
||||
swapTo.Chunk.RemoveFromChunk(swapTo);
|
||||
what.Chunk.AddToChunk(swapTo);
|
||||
}
|
||||
|
||||
Element temp = new Element(what);
|
||||
|
||||
what.Position = swapTo.Position;
|
||||
what.Chunk = swapTo.Chunk;
|
||||
Elements[swapTo.Position.X, swapTo.Position.Y] = what;
|
||||
|
||||
swapTo.Position = swap.Position;
|
||||
swapTo.Chunk = swap.Chunk;
|
||||
Elements[swap.Position.X, swap.Position.Y] = swapTo;
|
||||
what.Chunk.Elements[what.Position.X, what.Position.Y] = what;
|
||||
|
||||
swapTo.Position = temp.Position;
|
||||
swapTo.Chunk = temp.Chunk;
|
||||
swapTo.Chunk.Elements[swapTo.Position.X, swapTo.Position.Y] = swapTo;
|
||||
|
||||
what.Active = true;
|
||||
what.Moved();
|
||||
swapTo.Active = true;
|
||||
what.LastMove = frame;
|
||||
swapTo.LastMove = frame;
|
||||
swapTo.Moved();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"Chunk {Index}";
|
||||
}
|
||||
|
||||
private void RemoveFromChunk(Element e) {
|
||||
toRemoveElements.Add(e);
|
||||
}
|
||||
|
||||
private void AddToChunk(Element e) {
|
||||
toAddElements.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
1
Scripts/Chunk.cs.uid
Normal file
1
Scripts/Chunk.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://r17upkbkmfiy
|
||||
@@ -5,7 +5,7 @@ namespace FOU.Scripts.Elements;
|
||||
public class Dirt : Solid {
|
||||
|
||||
public Dirt(int x, int y, ref Chunk chunk) : base(x, y, ref chunk) {
|
||||
Color = AddColorVariance(Colors.Brown);
|
||||
color = AddColorVariance(Colors.Brown);
|
||||
Density = 10;
|
||||
DiffuseSpeed = 50;
|
||||
}
|
||||
|
||||
1
Scripts/Elements/Dirt.cs.uid
Normal file
1
Scripts/Elements/Dirt.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://80esqy0cardd
|
||||
@@ -2,83 +2,83 @@
|
||||
|
||||
namespace FOU.Scripts.Elements;
|
||||
|
||||
public class Element {
|
||||
public Color Color = Colors.Black;
|
||||
public class Element{
|
||||
|
||||
public Vector2I Position;
|
||||
public Chunk Chunk;
|
||||
public Vector2I Position;
|
||||
public int Density;
|
||||
public int LastUpdate = -1;
|
||||
public int LastMove = 0;
|
||||
|
||||
public int DiffuseSpeed = 10;
|
||||
public const int MAX_DIFFUSE_SPEED = 100;
|
||||
public const int STEPS_UNTIL_INACTIVE = 100;
|
||||
protected int DiffuseSpeed = 10;
|
||||
protected Color color = Colors.Black;
|
||||
protected const int MAX_DIFFUSE_SPEED = 100;
|
||||
protected const int STEPS_UNTIL_INACTIVE = 500;
|
||||
|
||||
protected const float MAX_COLOR_VARIANCE = 0.1f;
|
||||
protected static readonly Vector2I VERTICAL_OPPOSITE = new Vector2I(-1, 1);
|
||||
private const float MAX_COLOR_VARIANCE = 0.1f;
|
||||
|
||||
private bool active = true;
|
||||
private bool active = false;
|
||||
private bool markedForUpdate = true;
|
||||
private int lastUpdate = -1;
|
||||
private int lastMove = 0;
|
||||
private Color originalColor;
|
||||
|
||||
public Element(Element e) {
|
||||
Position = e.Position;
|
||||
Chunk = e.Chunk;
|
||||
Active = e.active;
|
||||
lastMove = e.lastMove;
|
||||
lastUpdate = e.lastUpdate;
|
||||
Chunk.SetElementActive(this, Active);
|
||||
}
|
||||
|
||||
public Element(int x, int y, Chunk chunk) {
|
||||
Position.X = x;
|
||||
Position.Y = y;
|
||||
Chunk = chunk;
|
||||
lastMove = Engine.GetFramesDrawn();
|
||||
Chunk.SetElementActive(this, Active);
|
||||
}
|
||||
|
||||
public bool Active {
|
||||
get => active;
|
||||
set {
|
||||
if (active == value) return;
|
||||
active = value;
|
||||
Chunk.SetElementActive(this, value);
|
||||
|
||||
if (active)
|
||||
Color = originalColor;
|
||||
else if (Main.Instance.DebugVisualization)
|
||||
Color = new Color(0.2f, 0.2f, 0.2f);
|
||||
if (active) {
|
||||
Moved();
|
||||
} else {
|
||||
ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color Color => color;
|
||||
|
||||
/// <summary>
|
||||
/// base update method, checks if anything is to do at all
|
||||
/// </summary>
|
||||
/// <param name="currentFrame"></param>
|
||||
/// <returns>false if there is nothing to do</returns>
|
||||
public virtual bool Update(int currentFrame) {
|
||||
public virtual bool Update() {
|
||||
if (!Active) return false;
|
||||
|
||||
if (LastUpdate == currentFrame) return false; // already updated this frame
|
||||
LastUpdate = currentFrame;
|
||||
int frame = Engine.GetFramesDrawn();
|
||||
if (lastMove + STEPS_UNTIL_INACTIVE < frame)
|
||||
Active = false;
|
||||
if (lastUpdate == frame)
|
||||
return false; // already updated this frame
|
||||
|
||||
lastUpdate = frame;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"{GetType()} {Position}";
|
||||
return $"{GetType()} {Position}[{Chunk.Index}] {active}";
|
||||
}
|
||||
|
||||
protected virtual void Tick() {
|
||||
Vector2I randomDirection = RandomDirectionDown();
|
||||
|
||||
if (Chunk.IsEmpty(Position + Vector2I.Down))
|
||||
Chunk.Swap(this, Position + Vector2I.Down);
|
||||
|
||||
else if (Chunk.IsEmpty(Position + randomDirection))
|
||||
Chunk.Swap(this, Position + randomDirection);
|
||||
|
||||
else if (Chunk.IsEmpty(Position + randomDirection * VERTICAL_OPPOSITE))
|
||||
Chunk.Swap(this, Position + randomDirection * VERTICAL_OPPOSITE);
|
||||
|
||||
if (GD.Randi() % MAX_DIFFUSE_SPEED > DiffuseSpeed) return; // ascend slower
|
||||
|
||||
if (Chunk.Get(Position + randomDirection)?.Density < Density)
|
||||
Chunk.Swap(this, Position + Vector2I.Down);
|
||||
}
|
||||
protected virtual void Tick() { }
|
||||
|
||||
protected Vector2I RandomDirectionDown() {
|
||||
int randomDirection = GD.Randi() % 2 != 0 ? 1 : -1;
|
||||
@@ -87,6 +87,13 @@ public class Element {
|
||||
+ (GD.Randi() % 2 != 0 ? Vector2I.Zero : Vector2I.Right * randomDirection);
|
||||
}
|
||||
|
||||
/// <returns>-1, 0 or 1</returns>
|
||||
protected Vector2I RandomDirection() {
|
||||
int randomDirection = GD.Randi() % 2 != 0 ? 1 : -1;
|
||||
|
||||
return (GD.Randi() % 2 != 0 ? Vector2I.Zero : Vector2I.Right * randomDirection);
|
||||
}
|
||||
|
||||
protected Color AddColorVariance(Color baseColor) {
|
||||
Color c = baseColor;
|
||||
c.R += (GD.Randf() - 1) * MAX_COLOR_VARIANCE;
|
||||
@@ -97,4 +104,31 @@ public class Element {
|
||||
return c;
|
||||
}
|
||||
|
||||
public void ResetColor() {
|
||||
color = originalColor;
|
||||
MarkForUpdate();
|
||||
}
|
||||
|
||||
public void SetDebugColor(Color color) {
|
||||
if (!Main.Instance.DebugMode) return;
|
||||
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void Moved() {
|
||||
lastMove = Engine.GetFramesDrawn();
|
||||
MarkForUpdate();
|
||||
}
|
||||
|
||||
public bool IsMarkedForUpdate() {
|
||||
return markedForUpdate;
|
||||
}
|
||||
|
||||
public void MarkForUpdate(bool mark = true) {
|
||||
markedForUpdate = true;
|
||||
}
|
||||
|
||||
public bool IsEmpty() {
|
||||
return GetType() == typeof(Element);
|
||||
}
|
||||
}
|
||||
|
||||
1
Scripts/Elements/Element.cs.uid
Normal file
1
Scripts/Elements/Element.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b5lme1c4rx15n
|
||||
@@ -5,18 +5,19 @@ namespace FOU.Scripts.Elements;
|
||||
public abstract class Liquid : Element {
|
||||
public const int MAX_VERTICAL_SPREAD = 3;
|
||||
|
||||
protected Liquid(int x, int y, ref Chunk chunk) : base(x, y, chunk) { }
|
||||
protected Liquid(int x, int y, ref Chunk chunk) : base(x, y, chunk) {
|
||||
MarkForUpdate();
|
||||
}
|
||||
|
||||
public override bool Update(int currentFrame) {
|
||||
if (!base.Update(currentFrame)) return false;
|
||||
if (LastMove + STEPS_UNTIL_INACTIVE < currentFrame) Active = false;
|
||||
public override bool Update() {
|
||||
if (!base.Update()) return false;
|
||||
|
||||
Tick();
|
||||
|
||||
return true; // not necessarily end, subclasses could do some more things
|
||||
}
|
||||
|
||||
protected override void Tick() {
|
||||
protected virtual void Tick() {
|
||||
Vector2I randomDirection = RandomDirectionDown();
|
||||
if (randomDirection.Y != 0)
|
||||
randomDirection *= Vector2I.Left * (int)(GD.Randi() % MAX_VERTICAL_SPREAD);
|
||||
|
||||
1
Scripts/Elements/Liquid.cs.uid
Normal file
1
Scripts/Elements/Liquid.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bqx36wisy7127
|
||||
@@ -3,14 +3,40 @@
|
||||
namespace FOU.Scripts.Elements;
|
||||
|
||||
public abstract class Solid : Element {
|
||||
protected Solid(int x, int y, ref Chunk chunk) : base(x, y, chunk) { }
|
||||
protected Solid(int x, int y, ref Chunk chunk) : base(x, y, chunk) {
|
||||
MarkForUpdate();
|
||||
}
|
||||
|
||||
public override bool Update(int currentFrame) {
|
||||
if (!base.Update(currentFrame)) return false;
|
||||
if (LastMove + STEPS_UNTIL_INACTIVE < currentFrame) Active = false;
|
||||
public override bool Update() {
|
||||
if (!base.Update()) return false;
|
||||
|
||||
Tick();
|
||||
|
||||
return true; // not necessarily end, subclasses could do some more things
|
||||
}
|
||||
|
||||
protected virtual void Tick() {
|
||||
Vector2I randomDirection = RandomDirectionDown();
|
||||
|
||||
// fall speed reduction:
|
||||
if (GD.Randi() % MAX_DIFFUSE_SPEED > DiffuseSpeed) return; // descend slower
|
||||
|
||||
if (Chunk.IsEmpty(Position + Vector2I.Down))
|
||||
Chunk.Swap(this, Position + Vector2I.Down);
|
||||
|
||||
else if (Chunk.IsEmpty(Position + Vector2I.Down + randomDirection))
|
||||
Chunk.Swap(this, Position + Vector2I.Down + randomDirection);
|
||||
|
||||
else if (Chunk.IsEmpty(Position + Vector2I.Down + randomDirection * VERTICAL_OPPOSITE))
|
||||
Chunk.Swap(this, Position + Vector2I.Down + randomDirection * VERTICAL_OPPOSITE);
|
||||
|
||||
// density check
|
||||
if (Chunk.Get(Position + Vector2I.Down)?.Density < Density)
|
||||
Chunk.Swap(this, Position + Vector2I.Down);
|
||||
else if (Chunk.Get(Position + Vector2I.Down + randomDirection)?.Density < Density)
|
||||
Chunk.Swap(this, Position + Vector2I.Down + randomDirection);
|
||||
else if (Chunk.Get(Position + Vector2I.Down + randomDirection * VERTICAL_OPPOSITE)?.Density < Density)
|
||||
Chunk.Swap(this, Position + Vector2I.Down + randomDirection * VERTICAL_OPPOSITE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1
Scripts/Elements/Solid.cs.uid
Normal file
1
Scripts/Elements/Solid.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ur56t06r7n4l
|
||||
@@ -5,7 +5,7 @@ namespace FOU.Scripts.Elements;
|
||||
public class Water : Liquid {
|
||||
|
||||
public Water(int x, int y, ref Chunk chunk) : base(x, y, ref chunk) {
|
||||
Color = AddColorVariance(Colors.Blue);
|
||||
color = AddColorVariance(Colors.Blue);
|
||||
Density = 1;
|
||||
}
|
||||
|
||||
|
||||
1
Scripts/Elements/Water.cs.uid
Normal file
1
Scripts/Elements/Water.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://k8id5egjc7x8
|
||||
155
Scripts/Level.cs
155
Scripts/Level.cs
@@ -1,4 +1,6 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using FOU.Scripts.Elements;
|
||||
using Godot;
|
||||
|
||||
namespace FOU.Scripts;
|
||||
|
||||
@@ -9,75 +11,164 @@ public class Level {
|
||||
private Chunk[,] chunks;
|
||||
private Image image;
|
||||
|
||||
private int chunksX;
|
||||
private int chunksY;
|
||||
private int chunksPerX;
|
||||
private int chunksPerY;
|
||||
|
||||
// per chunk:
|
||||
private int chunkResX;
|
||||
private int chunkResY;
|
||||
|
||||
private float rainAmount = 0;
|
||||
|
||||
public Level(Main main, int sizeX, int sizeY) {
|
||||
GD.Print($"Generating level ({sizeX}:{sizeY})");
|
||||
|
||||
Resolution = new Vector2I(sizeX, sizeY);
|
||||
chunksX = main.ChunksPerAxis;
|
||||
chunksY = main.ChunksPerAxis;
|
||||
chunksPerX = main.ChunksPerAxis;
|
||||
chunksPerY = main.ChunksPerAxis;
|
||||
|
||||
chunkResX = sizeX / chunksX;
|
||||
chunkResY = sizeY / chunksY;
|
||||
chunkResX = sizeX / chunksPerX;
|
||||
chunkResY = sizeY / chunksPerY;
|
||||
|
||||
GD.Print($"{chunksX}x{chunksY} chunks. {chunkResX}:{chunkResY} res each.");
|
||||
GD.Print($"Generating level ({sizeX}:{sizeY}) with {chunksPerX*chunksPerY} chunks ({chunkResX} * {chunkResY})");
|
||||
|
||||
image = Image.Create(sizeX, sizeY, false, Image.Format.Rgb8);
|
||||
image = Image.CreateEmpty(sizeX, sizeY, false, Image.Format.Rgb8);
|
||||
|
||||
chunks = new Chunk[chunksX, chunksY];
|
||||
chunks = new Chunk[chunksPerX, chunksPerY];
|
||||
int index = 0;
|
||||
// create all chunks
|
||||
for (int x = 0; x < chunksX; x++) {
|
||||
for (int y = 0; y < chunksY; y++) {
|
||||
chunks[x, y] = new Chunk(chunkResX, chunkResY);
|
||||
for (int x = 0; x < chunksPerX; x++) {
|
||||
for (int y = 0; y < chunksPerY; y++) {
|
||||
chunks[x, y] = new Chunk(chunkResX, chunkResY, index++);
|
||||
}
|
||||
}
|
||||
|
||||
// assign neighbors
|
||||
for (int x = 0; x < chunksX; x++) {
|
||||
for (int y = 0; y < chunksY; y++) {
|
||||
for (int x = 0; x < chunksPerX; x++) {
|
||||
for (int y = 0; y < chunksPerY; y++) {
|
||||
|
||||
if (y > 0) chunks[x, y].NeighborN = chunks[x, y-1];
|
||||
if (y < chunksY-1) chunks[x, y].NeighborS = chunks[x, y+1];
|
||||
if (y < chunksPerY-1) chunks[x, y].NeighborS = chunks[x, y+1];
|
||||
|
||||
if (x > 0) chunks[x, y].NeighborE = chunks[x-1, y];
|
||||
if (x < chunksX-1) chunks[x, y].NeighborW = chunks[x+1, y];
|
||||
|
||||
if (x < chunksPerX-1) chunks[x, y].NeighborW = chunks[x+1, y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
MakeItRain();
|
||||
|
||||
foreach (Chunk c in chunks)
|
||||
c.Update();
|
||||
}
|
||||
|
||||
// TODO!
|
||||
private void MakeItRain(float rainAmount) {
|
||||
// int rainDrops = (int)Math.Round(rainAmount);
|
||||
//
|
||||
// for (int i = 0; i <= rainDrops; i++) {
|
||||
// if (GD.Randf() < rainAmount)
|
||||
// WritePixel<Water>((int)(GD.Randi() % SizeX), 0, 1);
|
||||
// }
|
||||
public void SetRainAmount(float amount) {
|
||||
rainAmount = amount;
|
||||
}
|
||||
|
||||
public void StartBenchmark() {
|
||||
GD.Print("benchmark");
|
||||
for (int x = 0; x < chunksPerX; x++) {
|
||||
for (int y = 0; y < chunksPerY; y++) {
|
||||
WritePixel<Dirt>((x * chunkResX) + chunkResX/2, (y * chunkResY) + chunkResY/2, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Chunk[,] GetChunks() {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
private void MakeItRain() {
|
||||
if (rainAmount < .1f) return;
|
||||
|
||||
int rainDrops = (int)Math.Round(rainAmount);
|
||||
|
||||
for (int i = 0; i <= rainDrops; i++) {
|
||||
if (GD.Randf() < rainAmount) {
|
||||
int d = (int)(GD.Randi() % (chunkResX * chunksPerX));
|
||||
if (GetElement(d, 0).IsEmpty())
|
||||
WritePixel<Water>(d, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WritePixel<T>(int x, int y, int size) {
|
||||
chunks[x/chunkResX, y/chunkResY].WritePixel<T>(x % chunkResX, y % chunkResY, size);
|
||||
int halfsize = size/2;
|
||||
|
||||
int startPtX = x % chunkResX;
|
||||
int startPtY = y % chunkResY;
|
||||
|
||||
for (int i = -halfsize; i <= halfsize; i++) {
|
||||
for (int j = -halfsize; j <= halfsize; j++) {
|
||||
|
||||
// calculate in-chunk coordinates
|
||||
int iteratorX = i;
|
||||
int iteratorY = j;
|
||||
int ptX = startPtX;
|
||||
int ptY = startPtY;
|
||||
|
||||
// calculate chunk to write to
|
||||
int chunkX = x/chunkResX;
|
||||
|
||||
// left of chunk
|
||||
if (startPtX + iteratorX < 0) {
|
||||
chunkX--;
|
||||
iteratorX = chunkResX + startPtX + iteratorX;
|
||||
ptX = startPtX + iteratorX;
|
||||
|
||||
} else {
|
||||
ptX = startPtX + iteratorX;
|
||||
}
|
||||
if (startPtX + iteratorX >= chunkResX) {
|
||||
chunkX++;
|
||||
ptX = startPtX + iteratorX - chunkResX;
|
||||
}
|
||||
int chunkY = y/chunkResY;
|
||||
|
||||
// above chunk
|
||||
if (startPtY + iteratorY < 0) {
|
||||
chunkY--;
|
||||
ptY = chunkResY + (startPtY + iteratorY);
|
||||
} else {
|
||||
ptY = startPtY + iteratorY;
|
||||
}
|
||||
if (startPtY + iteratorY >= chunkResY) {
|
||||
chunkY++;
|
||||
ptY = startPtY + iteratorY - chunkResY;
|
||||
}
|
||||
|
||||
// ignore everything outside
|
||||
if (chunkX < 0) continue;
|
||||
if (chunkX >= chunksPerX) continue;
|
||||
if (chunkY < 0) continue;
|
||||
if (chunkY >= chunksPerY) continue;
|
||||
|
||||
chunks[chunkX, chunkY].WritePixel<T>(ptX, ptY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Element GetElement(int x, int y) {
|
||||
if (x < 0 || x > chunkResX * chunkResX) return null;
|
||||
if (y < 0 || y > chunkResY * chunkResY) return null;
|
||||
|
||||
return chunks[x / chunkResX, y / chunkResY].Elements[x % chunkResX, y % chunkResY];
|
||||
}
|
||||
|
||||
public Image DrawLevel() {
|
||||
for (int cx = 0; cx < chunksX; cx++) {
|
||||
for (int cy = 0; cy < chunksY; cy++) {
|
||||
// chunk
|
||||
for (int cx = 0; cx < chunksPerX; cx++) {
|
||||
for (int cy = 0; cy < chunksPerY; cy++) {
|
||||
|
||||
// pixel in chunk
|
||||
for (int x = 0; x < chunkResX; x++) {
|
||||
for (int y = 0; y < chunkResY; y++) {
|
||||
image.SetPixel(cx*chunkResX + x, cy*chunkResY + y, chunks[cx,cy].Elements[x, y].Color);
|
||||
// TODO: multithreading here! use Chunk.DrawLevel() and stitch images together
|
||||
|
||||
if (chunks[cx, cy].Elements[x, y].IsMarkedForUpdate()) {
|
||||
image.SetPixel(cx * chunkResX + x, cy * chunkResY + y, chunks[cx, cy].Elements[x, y].Color);
|
||||
chunks[cx, cy].Elements[x, y].MarkForUpdate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
Scripts/Level.cs.uid
Normal file
1
Scripts/Level.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bkwbjagt8s01s
|
||||
@@ -1,32 +1,47 @@
|
||||
using System.Reflection;
|
||||
using FOU.Scripts;
|
||||
using FOU.Scripts.Elements;
|
||||
using Godot;
|
||||
|
||||
public partial class Main : Node2D {
|
||||
[Export] public bool DebugVisualization = false;
|
||||
[Export] public bool DebugMode = false;
|
||||
[Export] public bool AllowOverwrite = true;
|
||||
|
||||
[Export] public int BrushSize = 5;
|
||||
[Export] public float TextureResolution = 0.5f;
|
||||
[Export] public int ChunksPerAxis = 2;
|
||||
[Export] public float RainAmount = 0.01f;
|
||||
[Export] public float rainAmount = 0;
|
||||
|
||||
public static Main Instance;
|
||||
public Level Level;
|
||||
|
||||
private TextureRect mLevelDrawer;
|
||||
private Level mLevel;
|
||||
private bool enableRain;
|
||||
|
||||
public float RainAmount {
|
||||
get => rainAmount;
|
||||
set {
|
||||
rainAmount = value;
|
||||
Level?.SetRainAmount(rainAmount);
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Ready() {
|
||||
mLevel = new Level(this, (int)(GetViewportRect().Size.X * TextureResolution),
|
||||
Level = new Level(this, (int)(GetViewportRect().Size.X * TextureResolution),
|
||||
(int)(GetViewportRect().Size.Y * TextureResolution));
|
||||
Level.SetRainAmount(rainAmount);
|
||||
mLevelDrawer = GetNode<TextureRect>("CanvasLayer/LevelDrawer");
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta) {
|
||||
base._PhysicsProcess(delta);
|
||||
}
|
||||
|
||||
public override void _Process(double delta) {
|
||||
mLevel.Update();
|
||||
mLevelDrawer.Texture = ImageTexture.CreateFromImage(mLevel.DrawLevel());
|
||||
Level.Update();
|
||||
mLevelDrawer.Texture?.Dispose();
|
||||
mLevelDrawer.Texture = ImageTexture.CreateFromImage(Level.DrawLevel());
|
||||
}
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event) {
|
||||
@@ -35,13 +50,15 @@ public partial class Main : Node2D {
|
||||
if (eventMouseButton.IsPressed()) {
|
||||
Vector2 mouse = GetViewport().GetMousePosition();
|
||||
|
||||
float mappedX = mouse.X / (GetViewportRect().Size.X / mLevel.Resolution.X);
|
||||
float mappedY = mouse.Y / (GetViewportRect().Size.Y / mLevel.Resolution.Y);
|
||||
float mappedX = mouse.X / (GetViewportRect().Size.X / Level.Resolution.X);
|
||||
float mappedY = mouse.Y / (GetViewportRect().Size.Y / Level.Resolution.Y);
|
||||
|
||||
mLevel.WritePixel<Dirt>((int)mappedX, (int)mappedY, BrushSize);
|
||||
}
|
||||
} else base._UnhandledInput(@event);
|
||||
Level.WritePixel<Dirt>((int)mappedX, (int)mappedY, BrushSize);
|
||||
}
|
||||
} else if (@event is InputEventKey keyEvent && keyEvent.Pressed) {
|
||||
if (keyEvent.Keycode == Key.F9)
|
||||
Level.StartBenchmark();
|
||||
}
|
||||
else base._UnhandledInput(@event);
|
||||
}
|
||||
}
|
||||
|
||||
1
Scripts/Main.cs.uid
Normal file
1
Scripts/Main.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cmvvubxfvdca7
|
||||
22
benchmark.txt
Normal file
22
benchmark.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
# Benchmark
|
||||
|
||||
Start from editor not rider!
|
||||
|
||||
## Settings
|
||||
|
||||
- Texture Resolution: 0.5
|
||||
- CHunks per Axis: 2
|
||||
- Rain Amount: 0
|
||||
|
||||
## Measurements:
|
||||
|
||||
### original (no optimization):
|
||||
|
||||
- idle: 42 - 44 fps
|
||||
- benchmark: 24 fps minimum
|
||||
|
||||
### performance-improvements (71db6513f22094e7dd67bd32c7ab86b326f9ad9e)
|
||||
|
||||
- idle before: 89
|
||||
- benchmark: 33
|
||||
- idle after: 75
|
||||
@@ -12,7 +12,7 @@ config_version=5
|
||||
|
||||
config/name="FOU"
|
||||
run/main_scene="res://Scenes/main.tscn"
|
||||
config/features=PackedStringArray("4.2", "C#", "Forward Plus")
|
||||
config/features=PackedStringArray("4.5", "C#", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[dotnet]
|
||||
|
||||
Reference in New Issue
Block a user