diff --git a/.idea/.idea.Frontiers/.idea/.name b/.idea/.idea.Frontiers/.idea/.name
deleted file mode 100644
index ff0474e..0000000
--- a/.idea/.idea.Frontiers/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-Frontiers
\ No newline at end of file
diff --git a/.idea/.idea.Frontiers/.idea/libraries/GdSdk_Master.xml b/.idea/.idea.Frontiers/.idea/libraries/GdSdk_Master.xml
deleted file mode 100644
index 6875f0b..0000000
--- a/.idea/.idea.Frontiers/.idea/libraries/GdSdk_Master.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Frontiers.csproj b/Frontiers.csproj
index 8eebc1d..f057f47 100644
--- a/Frontiers.csproj
+++ b/Frontiers.csproj
@@ -1,4 +1,4 @@
-
+
net8.0
true
diff --git a/Source/Tile.cs b/Source/Tile.cs
index 7d71cb0..a70043c 100644
--- a/Source/Tile.cs
+++ b/Source/Tile.cs
@@ -48,12 +48,18 @@ public partial class Tile : Node2D
DebugType debugType = _world.DebugType;
- // Run in thread
+ // Use limited parallelism instead of unlimited Task.Run
+ var parallelOptions = new ParallelOptions
+ {
+ MaxDegreeOfParallelism = Math.Max(1, (int)(System.Environment.ProcessorCount * 0.5)),
+ CancellationToken = CancellationToken
+ };
+
Task.Run(() =>
{
try
{
- for (int x = 0; x < _size.X; x++)
+ Parallel.For(0, _size.X, parallelOptions, x =>
{
if (CancellationToken.IsCancellationRequested) return;
@@ -88,7 +94,7 @@ public partial class Tile : Node2D
float neighborHeight = GetValueAtMapPosition(neighborCoords);
- if (IsSeaAtMapPosition(neighborCoords, waterThreshold))
+ if (IsSeaAtMapPosition(neighborCoords))
{
bordersSea = true;
}
@@ -109,14 +115,14 @@ public partial class Tile : Node2D
// Grass (default)
image.SetPixel(x, y, Color.FromString("#bed58a", Colors.Purple));
- if (IsSeaAtMapPosition(pixelOffset, waterThreshold))
+ if (IsSeaAtMapPosition(pixelOffset))
{
// Water
image.SetPixel(x, y, Color.FromString("#4380b0", Colors.Purple));
continue;
}
- if (moistureValue < desertMoistureThreshold && heatValue > desertTemperatureThreshold)
+ if (isDesertAtMapPosition(pixelOffset))
{
// Desert
image.SetPixel(x, y, Color.FromString("#edc9af", Colors.Purple));
@@ -129,7 +135,7 @@ public partial class Tile : Node2D
continue;
}
- if (heightValue > snowHeightThreshold)
+ if (heightValue > snowHeightThreshold && !isDesertAtMapPosition(pixelOffset))
{
// Snow
image.SetPixel(x, y, Color.FromString("#f4f4f4", Colors.Purple));
@@ -165,7 +171,7 @@ public partial class Tile : Node2D
}
- }
+ });
if (!CancellationToken.IsCancellationRequested)
{
@@ -186,13 +192,8 @@ public partial class Tile : Node2D
}
// Terrain Query Methods
- private float GetValueAtMapPosition(Vector2I position)
+ private Vector2I ToHeightMapCoordinates(Vector2I position)
{
-
- // Clamp position to map bounds
- position.X = Mathf.Clamp(position.X, 0, _world.ChunksPerAxis.X * _world.TileSize - 1);
- position.Y = Mathf.Clamp(position.Y, 0, _world.ChunksPerAxis.Y * _world.TileSize - 1);
-
Vector2I heightMapResolution = new Vector2I(
_world.HeightMapImage.GetWidth(),
_world.HeightMapImage.GetHeight()
@@ -206,6 +207,19 @@ public partial class Tile : Node2D
(int)(mapDelta.X * heightMapResolution.X),
(int)(mapDelta.Y * heightMapResolution.Y)
);
+
+ // Clamp to valid range
+ heightMapCoords.X = Mathf.Clamp(heightMapCoords.X, 0, heightMapResolution.X - 1);
+ heightMapCoords.Y = Mathf.Clamp(heightMapCoords.Y, 0, heightMapResolution.Y - 1);
+
+ return heightMapCoords;
+ }
+
+ private float GetValueAtMapPosition(Vector2I position)
+ {
+
+
+ Vector2I heightMapCoords = ToHeightMapCoordinates(position);
Color hmColor = _world.HeightMapImage.GetPixel(heightMapCoords.X, heightMapCoords.Y);
@@ -218,10 +232,17 @@ public partial class Tile : Node2D
return heightValue <= _world.WaterThreshold;
}
- private bool IsSeaAtMapPosition(Vector2I position, float waterThreshold)
+ private bool isDesertAtMapPosition(Vector2I position)
+ {
+ float heatValue = GetHeatAtMapPosition(position);
+ float moistureValue = GetMoistureAtMapPosition(position);
+ return moistureValue < _world.DesertMoistureThreshold && heatValue > _world.DesertTemperatureThreshold;
+ }
+
+ private bool IsSeaAtMapPosition(Vector2I position)
{
float heightValue = GetValueAtMapPosition(position);
- return heightValue <= waterThreshold;
+ return heightValue <= _world.WaterThreshold;
}
private FastNoiseLite _heatNoise = null;
@@ -233,21 +254,21 @@ public partial class Tile : Node2D
_heatNoise = new FastNoiseLite();
_heatNoise.SetNoiseType(FastNoiseLite.NoiseTypeEnum.Perlin);
_heatNoise.SetFrequency(1/_world.TemperatureNoiseFrequency);
+ _heatNoise.SetFractalType(FastNoiseLite.FractalTypeEnum.Fbm);
+ _heatNoise.SetFractalOctaves(12);
_heatNoise.SetSeed(1738);
}
-
- float heat = _heatNoise.GetNoise2D(position.X, position.Y);
- heat *= 0.2f;
+
+ Vector2I heightMapCoords = ToHeightMapCoordinates(position);
+
+ float heat = _heatNoise.GetNoise2D(heightMapCoords.X, heightMapCoords.Y);
+ heat *= _world.TemperatureNoiseAmplitude;
// Fall off towards the poles. Use sine function for a globe effect.
- float mapResolutionY = _world.ChunksPerAxis.Y * _world.TileSize;
- float latitudeFactor = Mathf.Sin(Mathf.Pi * position.Y / mapResolutionY);
+ float mapResolutionY = _world.HeightMapImage.GetHeight();
+ float latitudeFactor = Mathf.Sin(Mathf.Pi * heightMapCoords.Y / mapResolutionY);
heat = latitudeFactor + heat;
- heat = Mathf.Clamp(
- heat,
- 0.0f,
- 1.0f
- );
+
return heat;
}
@@ -261,21 +282,20 @@ public partial class Tile : Node2D
_moistureNoise = new FastNoiseLite();
_moistureNoise.SetNoiseType(FastNoiseLite.NoiseTypeEnum.Perlin);
_moistureNoise.SetFrequency(1/_world.MoistureNoiseFrequency);
+ _moistureNoise.SetFractalType(FastNoiseLite.FractalTypeEnum.Fbm);
+ _moistureNoise.SetFractalOctaves(12);
_moistureNoise.SetSeed(1337);
}
+
+ Vector2I heightMapCoords = ToHeightMapCoordinates(position);
- float moisture = _moistureNoise.GetNoise2D(position.X, position.Y);
- moisture *= 0.2f;
+ float moisture = _moistureNoise.GetNoise2D(heightMapCoords.X, heightMapCoords.Y);
+ moisture *= _world.MoistureNoiseAmplitude;
// Fall off towards the poles. Use sine function for a globe effect.
- float mapResolutionY = _world.ChunksPerAxis.Y * _world.TileSize;
- float latitudeFactor = Mathf.Sin(Mathf.Pi * position.Y / mapResolutionY);
+ float mapResolutionY = _world.HeightMapImage.GetHeight();
+ float latitudeFactor = Mathf.Sin(Mathf.Pi * heightMapCoords.Y / mapResolutionY);
moisture = latitudeFactor + moisture;
- moisture = Mathf.Clamp(
- moisture,
- 0.0f,
- 1.0f
- );
return moisture;
diff --git a/Source/World.cs b/Source/World.cs
index 9f7b88a..ad47a5f 100644
--- a/Source/World.cs
+++ b/Source/World.cs
@@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Godot.Collections;
+using Environment = Godot.Environment;
public enum DebugType
{
@@ -148,6 +149,30 @@ public partial class World : Node2D
}
}
+ private float _TemperatureNoiseAmplitude = 0.1f;
+ [Export]
+ public float TemperatureNoiseAmplitude
+ {
+ get => _TemperatureNoiseAmplitude;
+ set
+ {
+ _TemperatureNoiseAmplitude = value;
+ LoadTiles();
+ }
+ }
+
+ private float _MoistureNoiseAmplitude = 0.1f;
+ [Export]
+ public float MoistureNoiseAmplitude
+ {
+ get => _MoistureNoiseAmplitude;
+ set
+ {
+ _MoistureNoiseAmplitude = value;
+ LoadTiles();
+ }
+ }
+
private DebugType _debugType = DebugType.None;
[Export]
public DebugType DebugType
@@ -162,8 +187,10 @@ public partial class World : Node2D
+
ConcurrentDictionary _tiles = new ConcurrentDictionary();
private CancellationTokenSource _cancellationTokenSource;
+ private static readonly int MaxDegreeOfParallelism = Math.Max(1, (int)(System.Environment.ProcessorCount * 0.5));
public World()
{
@@ -233,7 +260,7 @@ public partial class World : Node2D
{
if (cancellationToken.IsCancellationRequested) return;
- Parallel.For(0, chunksY, new ParallelOptions { CancellationToken = cancellationToken }, y =>
+ for (int y = 0; y < chunksY; y++)
{
if (cancellationToken.IsCancellationRequested) return;
@@ -248,14 +275,14 @@ public partial class World : Node2D
{
CallDeferred("add_child", tile);
_tiles[chunkId] = tile;
- GD.Print("Loaded tile at chunk " + chunkId);
+ // GD.Print("Loaded tile at chunk " + chunkId);
}
- });
+ }
}
}
catch (OperationCanceledException)
{
- GD.Print("Tile generation cancelled");
+ // GD.Print("Tile generation cancelled");
}
}, cancellationToken);
diff --git a/main.tscn b/main.tscn
index 80adc4e..2d6742d 100644
--- a/main.tscn
+++ b/main.tscn
@@ -1,7 +1,7 @@
[gd_scene load_steps=4 format=3 uid="uid://c10nqwr7qp0ai"]
-[ext_resource type="Script" uid="uid://btqabtn0awg6k" path="res://Source/World.cs" id="1_0xm2m"]
-[ext_resource type="Script" uid="uid://ceahximwi24jm" path="res://Source/CameraController.cs" id="3_camera"]
+[ext_resource type="Script" path="res://Source/World.cs" id="1_0xm2m"]
+[ext_resource type="Script" path="res://Source/CameraController.cs" id="3_camera"]
[ext_resource type="Texture2D" uid="uid://dteqog3a5k8qx" path="res://assets/World_Elevation_Map_8_bit_(World_Height_map)_(alterative_version).png" id="3_h2yge"]
[node name="Main" type="Node2D"]
@@ -17,5 +17,7 @@ HeightMapTexture = ExtResource("3_h2yge")
RockThreshold = 0.04
SnowHeightThreshold = 0.32
WaterThreshold = 0.24
-DesertTemperatureThreshold = 0.82
-DesertMoistureThreshold = 0.985
+DesertTemperatureThreshold = 0.84
+DesertMoistureThreshold = 0.96
+TemperatureNoiseFrequency = 1500.0
+MoistureNoiseFrequency = 1500.0
diff --git a/project.godot b/project.godot
index 13840c6..ef45cbb 100644
--- a/project.godot
+++ b/project.godot
@@ -12,7 +12,7 @@ config_version=5
config/name="Frontiers"
run/main_scene="uid://c10nqwr7qp0ai"
-config/features=PackedStringArray("4.4", "C#", "Forward Plus")
+config/features=PackedStringArray("4.3", "C#", "Forward Plus")
config/icon="res://icon.svg"
[dotnet]