From f7de1c67af530863c307c98ffc617e6e48484cd5 Mon Sep 17 00:00:00 2001 From: ImBenji Date: Sat, 18 Oct 2025 18:26:55 +0100 Subject: [PATCH] Update project to use Godot.NET.Sdk 4.3.0 and improve terrain generation performance --- .idea/.idea.Frontiers/.idea/.name | 1 - .../.idea/libraries/GdSdk_Master.xml | 10 --- Frontiers.csproj | 2 +- Source/Tile.cs | 88 ++++++++++++------- Source/World.cs | 35 +++++++- main.tscn | 10 ++- project.godot | 2 +- 7 files changed, 93 insertions(+), 55 deletions(-) delete mode 100644 .idea/.idea.Frontiers/.idea/.name delete mode 100644 .idea/.idea.Frontiers/.idea/libraries/GdSdk_Master.xml 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]