From e0cf8d9755ae08952575376dd4d8a170153fe859c15923fe2989b3d803a803e0 Mon Sep 17 00:00:00 2001 From: Aslan Date: Tue, 27 Jan 2026 07:08:03 -0500 Subject: [PATCH] Added dynamic spawning and despawning of objects --- .gitignore | 1 + prefabs/gameObjects/node.tscn | 6 + prefabs/gameObjects/player.tscn | 31 +++ prefabs/{ => gameObjects}/star.tscn | 0 prefabs/{ => gameObjects}/vessel.tscn | 0 project.godot | 5 + scenes/game.tscn | 47 +--- scripts/FastUniqueList.cs | 2 + scripts/GameManager.cs | 326 +++++++++++++++++--------- scripts/GameObject.cs | 194 ++++++++++----- scripts/GameObjects/Character.cs | 22 ++ scripts/GameObjects/Character.cs.uid | 1 + scripts/GameObjects/Nodes/StarNode.cs | 13 +- scripts/GameObjects/Star.cs | 13 + scripts/Generator/IGenerator.cs | 5 +- scripts/Generator/TestGenerator.cs | 241 +++++++++++-------- scripts/Helpers.cs | 19 +- scripts/Player.cs | 72 +++--- scripts/QueueManager.cs | 43 ++++ scripts/QueueManager.cs.uid | 1 + scripts/Sector.cs | 76 +++++- scripts/Universe.cs | 19 ++ scripts/Vector3Dec.cs | 7 +- 23 files changed, 794 insertions(+), 350 deletions(-) create mode 100644 prefabs/gameObjects/node.tscn create mode 100644 prefabs/gameObjects/player.tscn rename prefabs/{ => gameObjects}/star.tscn (100%) rename prefabs/{ => gameObjects}/vessel.tscn (100%) create mode 100644 scripts/GameObjects/Character.cs create mode 100644 scripts/GameObjects/Character.cs.uid create mode 100644 scripts/QueueManager.cs create mode 100644 scripts/QueueManager.cs.uid diff --git a/.gitignore b/.gitignore index 79d5dce..483fc3b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # Godot 4+ specific ignores .godot/ /android/ +build # Godot-specific ignores .import/ diff --git a/prefabs/gameObjects/node.tscn b/prefabs/gameObjects/node.tscn new file mode 100644 index 0000000..cf463cc --- /dev/null +++ b/prefabs/gameObjects/node.tscn @@ -0,0 +1,6 @@ +[gd_scene format=3 uid="uid://b6c17xutiecq5"] + +[ext_resource type="Script" uid="uid://dknsws58ej0bp" path="res://scripts/GameObject.cs" id="1_gt1sn"] + +[node name="Node" type="StaticBody3D" unique_id=1306600716] +script = ExtResource("1_gt1sn") diff --git a/prefabs/gameObjects/player.tscn b/prefabs/gameObjects/player.tscn new file mode 100644 index 0000000..eb5183f --- /dev/null +++ b/prefabs/gameObjects/player.tscn @@ -0,0 +1,31 @@ +[gd_scene format=3 uid="uid://tlc6h5arfa6p"] + +[ext_resource type="Script" uid="uid://cue3c56axvyja" path="res://scripts/Player.cs" id="1_74mkb"] +[ext_resource type="Script" uid="uid://bwpdtkgmwjs7g" path="res://scripts/GravityReceiver.cs" id="2_y1ton"] + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_8cj0n"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_uwrxv"] + +[node name="Player" type="CharacterBody3D" unique_id=1391068938] +script = ExtResource("1_74mkb") +SprintMultiplier = 500.0 + +[node name="Camera" type="Camera3D" parent="." unique_id=1639995310] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0) +fov = 90.0 + +[node name="PlayerMesh" type="MeshInstance3D" parent="." unique_id=1449639248] +mesh = SubResource("CapsuleMesh_8cj0n") + +[node name="PlayerCollider" type="CollisionShape3D" parent="." unique_id=944580540] +shape = SubResource("CapsuleShape3D_uwrxv") + +[node name="GravityReceiver" type="Node3D" parent="." unique_id=2127509723] +script = ExtResource("2_y1ton") + +[node name="SpotLight3D" type="SpotLight3D" parent="." unique_id=1804770241] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.06334634196567002, -0.29036251889898157) +shadow_enabled = true +spot_range = 25.0 +spot_angle = 90.0 diff --git a/prefabs/star.tscn b/prefabs/gameObjects/star.tscn similarity index 100% rename from prefabs/star.tscn rename to prefabs/gameObjects/star.tscn diff --git a/prefabs/vessel.tscn b/prefabs/gameObjects/vessel.tscn similarity index 100% rename from prefabs/vessel.tscn rename to prefabs/gameObjects/vessel.tscn diff --git a/project.godot b/project.godot index 0a822f5..6e38868 100644 --- a/project.godot +++ b/project.godot @@ -66,6 +66,11 @@ rotate_right={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) ] } +sprint={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} [physics] diff --git a/scenes/game.tscn b/scenes/game.tscn index f642e46..c268d1a 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -1,8 +1,7 @@ [gd_scene format=3 uid="uid://b3yh6h7fdt160"] -[ext_resource type="Script" uid="uid://cue3c56axvyja" path="res://scripts/Player.cs" id="1_uwrxv"] [ext_resource type="Script" uid="uid://cy8nuarxbnd" path="res://scripts/GravityZone.cs" id="1_yqjtg"] -[ext_resource type="Script" uid="uid://bwpdtkgmwjs7g" path="res://scripts/GravityReceiver.cs" id="3_lnu2h"] +[ext_resource type="Script" uid="uid://dr6y711ano07p" path="res://scripts/QueueManager.cs" id="4_iywne"] [ext_resource type="Script" uid="uid://n557xfrv0i6x" path="res://scripts/GameControlManager.cs" id="4_lbhrr"] [ext_resource type="Script" uid="uid://bwgjvm21oi3d6" path="res://scripts/GameManager.cs" id="4_p57ef"] [ext_resource type="Script" uid="uid://betypbypf6bf2" path="res://scripts/GameMenuController.cs" id="5_iywne"] @@ -16,14 +15,14 @@ size = Vector3(10, 0.5, 10) [sub_resource type="BoxShape3D" id="BoxShape3D_uwrxv"] size = Vector3(10, 5, 10) -[sub_resource type="CapsuleMesh" id="CapsuleMesh_8cj0n"] - -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_uwrxv"] - -[node name="Game" type="Node3D" unique_id=1201210338] +[node name="Game" type="Node3D" unique_id=1201210338 node_paths=PackedStringArray("GameMenu", "QueueManager")] +script = ExtResource("4_p57ef") +GameMenu = NodePath("GameMenu") +QueueManager = NodePath("QueueManager") +closeTickInterval = 0.01 [node name="Plane" type="StaticBody3D" parent="." unique_id=1260154250] -transform = Transform3D(1, 0, 0, 0, 0.99999994, 0, 0, 0, 0.99999994, 20, 0, 0) +transform = Transform3D(1, 0, 0, 0, 0.99999994, 0, 0, 0, 0.99999994, 0, -10, 0) [node name="PlaneMesh" type="MeshInstance3D" parent="Plane" unique_id=107049489] transform = Transform3D(5, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0) @@ -41,37 +40,13 @@ script = ExtResource("1_yqjtg") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0) shape = SubResource("BoxShape3D_uwrxv") -[node name="Player" type="CharacterBody3D" parent="." unique_id=612572257 node_paths=PackedStringArray("GameMenu")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 8.823173, 0.7854848) -script = ExtResource("1_uwrxv") -GameMenu = NodePath("../GameMenu") - -[node name="Camera" type="Camera3D" parent="Player" unique_id=1097983892] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0) -fov = 90.0 - -[node name="PlayerMesh" type="MeshInstance3D" parent="Player" unique_id=1440613415] -mesh = SubResource("CapsuleMesh_8cj0n") - -[node name="PlayerCollider" type="CollisionShape3D" parent="Player" unique_id=886478863] -shape = SubResource("CapsuleShape3D_uwrxv") - -[node name="GravityReceiver" type="Node3D" parent="Player" unique_id=1963556576] -script = ExtResource("3_lnu2h") - -[node name="OmniLight" type="OmniLight3D" parent="." unique_id=1887866205] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6497431, -4.41529) -omni_range = 500.0 -omni_attenuation = 0.1 - -[node name="GameManager" type="Node" parent="." unique_id=267630256 node_paths=PackedStringArray("player")] -script = ExtResource("4_p57ef") -player = NodePath("../Player") - [node name="GameControlManager" type="Node" parent="." unique_id=1751385863 node_paths=PackedStringArray("GameMenu")] script = ExtResource("4_lbhrr") GameMenu = NodePath("../GameMenu") +[node name="QueueManager" type="Node" parent="." unique_id=355148200] +script = ExtResource("4_iywne") + [node name="GameMenu" type="Control" parent="." unique_id=223510406] visible = false layout_mode = 3 @@ -102,6 +77,8 @@ offset_bottom = 63.0 layout_mode = 2 text = "Main Menu" +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=226559047] + [connection signal="body_entered" from="Plane/GravityZone" to="Plane/GravityZone" method="OnBodyEntered"] [connection signal="body_exited" from="Plane/GravityZone" to="Plane/GravityZone" method="OnBodyExited"] [connection signal="pressed" from="GameMenu/VBoxContainer/MainMenuButton" to="GameMenu" method="OnMainMenu"] diff --git a/scripts/FastUniqueList.cs b/scripts/FastUniqueList.cs index 3c40ff2..f31815d 100644 --- a/scripts/FastUniqueList.cs +++ b/scripts/FastUniqueList.cs @@ -10,6 +10,8 @@ public class FastUniqueList public T this[int index] => list[index]; + public IEnumerator GetEnumerator() => list.GetEnumerator(); + public bool Contains(T item) { return set.Contains(item); diff --git a/scripts/GameManager.cs b/scripts/GameManager.cs index 201e6c8..aeddbec 100644 --- a/scripts/GameManager.cs +++ b/scripts/GameManager.cs @@ -1,148 +1,246 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Godot; -public partial class GameManager : Node +public partial class GameManager : Node3D { - [Export] Player player; + [Export] public Control GameMenu { get; private set; } + [Export] public QueueManager QueueManager { get; private set; } - [Export] double closeTickInterval = 1; - [Export] double farTickInterval = 10; - [Export] int maxFarThreads = 16; + [Export] public double closeTickInterval = 1; + [Export] public double farTickInterval = 10; + [Export] public int maxFarThreads = 16; - public static GameManager Singleton { get; private set; } - public IGenerator Generator { get; private set; } + public Player MainPlayer { get; private set; } - public Universe GameUniverse { get; private set; } - public Sector CurrentSector { get; private set; } + public static GameManager Singleton { get; private set; } + public IGenerator Generator { get; private set; } - private double closeTickTimer = 0; - private double farTickTimer = 0; + public Universe GameUniverse { get; private set; } - public override void _Ready() - { - Singleton = this; + private double closeTickTimer = 0; + private double farTickTimer = 0; - Generator = new TestGenerator(); - Generator.GenerateUniverse((universe) => - { - GameUniverse = universe; - CurrentSector = universe.Sectors[1, 1, 1]; - }); - } + public bool simulating = false; - public override void _Process(double delta) - { - closeTickTimer += delta; - farTickTimer += delta; + private readonly Dictionary spawnedObjects = []; + private readonly ConcurrentQueue spawnQueue = []; - if (closeTickTimer >= closeTickInterval) - { - SimulateClose(closeTickTimer); - closeTickTimer = 0; - } + public override void _Ready() + { + Singleton = this; - if (farTickTimer >= farTickInterval) - { - SimulateFar(farTickTimer); - farTickTimer = 0; - } - } + Generator = new TestGenerator(); + GameUniverse = Generator.GenerateUniverse(); - private void SimulateClose(double delta) - { - Vector3I currentCoordinates = CurrentSector.Coordinates; + Character character = new(GameUniverse.Sectors[50, 50, 50], new(0, 0, 0)); + MainPlayer = character.InstantiatePlayer(); + MainPlayer.GameMenu = GameMenu; + spawnedObjects.Add(character, MainPlayer); + CallDeferred("add_child", MainPlayer); - Sector[,,] sectors = GameUniverse.Sectors; + SpawnClose(); + } - int sizeX = sectors.GetLength(0); - int sizeY = sectors.GetLength(1); - int sizeZ = sectors.GetLength(2); + public override void _Process(double delta) + { + closeTickTimer += delta; + farTickTimer += delta; - int startX = Mathf.Clamp(currentCoordinates.X - 1, 0, sizeX); - int startY = Mathf.Clamp(currentCoordinates.Y - 1, 0, sizeY); - int startZ = Mathf.Clamp(currentCoordinates.Z - 1, 0, sizeZ); + if (closeTickTimer >= closeTickInterval) + { + SimulateClose(closeTickTimer); + closeTickTimer = 0; + } - int endX = Mathf.Clamp(currentCoordinates.X + 1, 0, sizeX); - int endY = Mathf.Clamp(currentCoordinates.Y + 1, 0, sizeY); - int endZ = Mathf.Clamp(currentCoordinates.Z + 1, 0, sizeZ); + if (simulating) + { + farTickTimer = 0; + } - for (int x = startX; x <= endX; x++) - { - for (int y = startY; y <= endY; y++) - { - for (int z = startZ; z <= endZ; z++) - { - int taskX = x; - int taskY = y; - int taskZ = z; + if (farTickTimer >= farTickInterval) + { + double taskFarTickTimer = farTickTimer; + Task.Run(() => + { + SimulateFar(taskFarTickTimer); + }); - Task.Run(() => - { - sectors[taskX, taskY, taskZ].Simulate(delta); - }); - } - } - } - } + farTickTimer = 0; + } + } - private void SimulateFar(double delta) - { - Sector[,,] sectors = GameUniverse.Sectors; + public Sector GetCurrentSector() + { + return MainPlayer.PlayerData.CurrentSector; + } - int sizeX = sectors.GetLength(0); - int countX = Mathf.Clamp(sizeX / maxFarThreads, 1, int.MaxValue); + private void SimulateClose(double delta) + { + List neighbours = GetCurrentSector().GetNeighbouringSectors(); - for (int x = 0; x < sizeX; x += countX) - { - double taskDelta = delta; - int taskStartX = x; - int taskCountX = countX; + neighbours.ForEach(sector => + { + Task.Run(() => + { + sector.Simulate(delta); + }); + }); + } - _ = SimulateFarThreaded(taskDelta, taskStartX, taskCountX); - } - } + private void SimulateFar(double delta) + { + simulating = true; - private async Task SimulateFarThreaded(double delta, int startX, int countX) - { - Vector3I currentCoordinates = CurrentSector.Coordinates; + List tasks = []; - Sector[,,] sectors = GameUniverse.Sectors; + Sector[,,] sectors = GameUniverse.Sectors; - int sizeX = sectors.GetLength(0); - int sizeY = sectors.GetLength(1); - int sizeZ = sectors.GetLength(2); + int sizeX = sectors.GetLength(0); + int countX = Mathf.Clamp(sizeX / maxFarThreads, 1, int.MaxValue); - int startSkipX = Mathf.Clamp(currentCoordinates.X - 1, 0, sizeX); - int startSkipY = Mathf.Clamp(currentCoordinates.Y - 1, 0, sizeY); - int startSkipZ = Mathf.Clamp(currentCoordinates.Z - 1, 0, sizeZ); + for (int x = 0; x < sizeX; x += countX) + { + double taskDelta = delta; + int taskStartX = x; + int taskCountX = countX; - int endSkipX = Mathf.Clamp(currentCoordinates.X + 1, 0, sizeX); - int endSkipY = Mathf.Clamp(currentCoordinates.Y + 1, 0, sizeY); - int endSkipZ = Mathf.Clamp(currentCoordinates.Z + 1, 0, sizeZ); + Task clusteredTask = Task.Run(() => + { + SimulateFarClustered(taskDelta, taskStartX, taskCountX); + }); - int endX = Mathf.Clamp(startX + countX, 0, sizeX); + tasks.Add(clusteredTask); + } - for (int x = startX; x < endX; x++) - { - for (int y = 0; y < sizeY; y++) - { - for (int z = 0; z < sizeZ; z++) - { - if (Helpers.IsBetweenInclusive(x, startSkipX, endSkipX)) - { - if (Helpers.IsBetweenInclusive(y, startSkipY, endSkipY)) - { - if (Helpers.IsBetweenInclusive(z, startSkipZ, endSkipZ)) - { - continue; - } - } - } + Task.WaitAll([.. tasks]); - sectors[x, y, z].Simulate(delta); - } - } - } - } + simulating = false; + } + + private void SimulateFarClustered(double delta, int startX, int countX) + { + Vector3I currentCoordinates = GetCurrentSector().Coordinates; + + Sector[,,] sectors = GameUniverse.Sectors; + + int sizeX = sectors.GetLength(0); + int sizeY = sectors.GetLength(1); + int sizeZ = sectors.GetLength(2); + + int startSkipX = Mathf.Clamp(currentCoordinates.X - 1, 0, sizeX); + int startSkipY = Mathf.Clamp(currentCoordinates.Y - 1, 0, sizeY); + int startSkipZ = Mathf.Clamp(currentCoordinates.Z - 1, 0, sizeZ); + + int endSkipX = Mathf.Clamp(currentCoordinates.X + 1, 0, sizeX); + int endSkipY = Mathf.Clamp(currentCoordinates.Y + 1, 0, sizeY); + int endSkipZ = Mathf.Clamp(currentCoordinates.Z + 1, 0, sizeZ); + + int endX = Mathf.Clamp(startX + countX, 0, sizeX); + + for (int x = startX; x < endX; x++) + { + QueueManager.LogQueue.Enqueue("Simulating: " + x); + for (int y = 0; y < sizeY; y++) + { + Thread.Sleep(5); + for (int z = 0; z < sizeZ; z++) + { + if (Helpers.IsBetweenInclusive(x, startSkipX, endSkipX)) + { + if (Helpers.IsBetweenInclusive(y, startSkipY, endSkipY)) + { + if (Helpers.IsBetweenInclusive(z, startSkipZ, endSkipZ)) + { + continue; + } + } + } + + try + { + sectors[x, y, z].Simulate(delta); + } + catch (Exception e) + { + QueueManager.LogQueue.Enqueue("EXCEPTION: " + e.Message); + } + } + } + } + } + + private void SpawnClose() + { + List neighbours = GetCurrentSector().GetNeighbouringSectors(); + + neighbours.ForEach(sector => sector.SpawnObjects()); + } + + public void Spawn(GameObject gameObject) + { + spawnQueue.Enqueue(gameObject); + + CallDeferred(nameof(ProcessSpawnQueue)); + } + + private void ProcessSpawnQueue() + { + spawnQueue.TryDequeue(out GameObject gameObject); + if (gameObject == null) + { + return; + } + if (spawnedObjects.ContainsKey(gameObject)) + { + return; + } + + Node3D instance = gameObject.Instantiate(GetCurrentSector()); + spawnedObjects.Add(gameObject, instance); + + CallDeferred("add_child", instance); + } + + public void Despawn(GameObject gameObject) + { + if (!spawnedObjects.ContainsKey(gameObject)) + { + return; + } + + Node3D nodeToDespawn = spawnedObjects.GetValueOrDefault(gameObject); + spawnedObjects.Remove(gameObject); + + CallDeferred("remove_child", nodeToDespawn); + nodeToDespawn.CallDeferred("queue_free"); + } + + public void ApplyOrigin() + { + Sector current = GetCurrentSector(); + + List nearby = current.GetNeighbouringSectors(); + + foreach (Sector sector in nearby) + { + foreach (GameObject gameObject in sector.GameObjects) + { + gameObject.UpdateSectorOffset(current); + } + } + + foreach (GameObject spawned in spawnedObjects.Keys) + { + if (!nearby.Contains(spawned.CurrentSector)) + { + Despawn(spawned); + } + } + + nearby.ForEach(sector => sector.SpawnObjects()); + } } diff --git a/scripts/GameObject.cs b/scripts/GameObject.cs index 571edd5..fbc26aa 100644 --- a/scripts/GameObject.cs +++ b/scripts/GameObject.cs @@ -1,71 +1,157 @@ +using System.Collections.Generic; using Godot; public abstract class GameObject { - public Sector CurrentSector { get; private set; } - public Vector3 LocalCoordinates; - public Vector3Dec GlobalCoordinates; + public Sector CurrentSector { get; protected set; } + public Vector3 LocalCoordinates { get; protected set; } + public Vector3Dec GlobalCoordinates { get; protected set; } - public GameObject(Sector sector, Vector3 localCoordinates) - { - CurrentSector = sector; - LocalCoordinates = localCoordinates; + public Vector3 SectorOffset { get; set; } - GlobalCoordinates = new - ( - sector.GlobalStartCoordinates.X + (decimal)localCoordinates.X, - sector.GlobalStartCoordinates.Y + (decimal)localCoordinates.Y, - sector.GlobalStartCoordinates.Z + (decimal)localCoordinates.Z - ); - } + private bool reassigning = false; - public GameObject(Sector sector, double localX, double localY, double localZ) - { - CurrentSector = sector; - LocalCoordinates = new(localX, localY, localZ); + public GameObject(Sector sector, Vector3 localCoordinates) + { + CurrentSector = sector; + LocalCoordinates = localCoordinates; - GlobalCoordinates = new - ( - sector.GlobalStartCoordinates.X + (decimal)localX, - sector.GlobalStartCoordinates.Y + (decimal)localY, - sector.GlobalStartCoordinates.Z + (decimal)localZ - ); - } + GlobalCoordinates = new + ( + sector.GlobalCenterCoordinates.X + (decimal)localCoordinates.X, + sector.GlobalCenterCoordinates.Y + (decimal)localCoordinates.Y, + sector.GlobalCenterCoordinates.Z + (decimal)localCoordinates.Z + ); + } - public GameObject(Vector3Dec coordinates) - { - GlobalCoordinates = coordinates; - UpdateSector(); - } + public GameObject(Sector sector, double localX, double localY, double localZ) + { + CurrentSector = sector; + LocalCoordinates = new(localX, localY, localZ); - public GameObject(decimal x, decimal y, decimal z) - { - GlobalCoordinates = new(x, y, z); - UpdateSector(); - } + GlobalCoordinates = new + ( + sector.GlobalStartCoordinates.X + (decimal)localX, + sector.GlobalStartCoordinates.Y + (decimal)localY, + sector.GlobalStartCoordinates.Z + (decimal)localZ + ); + } - public Vector3 GetSectorCoordinates() - { - Vector3Dec relativeSectorCoordinates = GlobalCoordinates - CurrentSector.GlobalStartCoordinates; + public GameObject(Vector3Dec coordinates) + { + GlobalCoordinates = coordinates; + UpdateSector(); + } - return relativeSectorCoordinates.ToVector3(); - } + public GameObject(decimal x, decimal y, decimal z) + { + GlobalCoordinates = new(x, y, z); + UpdateSector(); + } - public bool IsInSector() - { - return Helpers.IsInsideArea(CurrentSector.Size, GetSectorCoordinates()); - } + public bool IsInCurrentSector() + { + return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates); + } - public void UpdateSector() - { + public void UpdateSector() + { + List neighbours = CurrentSector.GetNeighbouringSectors(); + foreach (Sector sector in neighbours) + { + if (sector.IsObjectInSector(this)) + { + sector.AssignObject(this); + reassigning = true; + return; + } + } - } + foreach (Sector sector in GameManager.Singleton.GameUniverse.Sectors) + { + if (sector.IsObjectInSector(this)) + { + sector.AssignObject(this); + reassigning = true; + return; + } + } + } - public virtual void Simulate(double delta) - { - if (!IsInSector()) - { - UpdateSector(); - } - } + public void AssignSector(Sector sector) + { + CurrentSector = sector; + ResetLocalCoordinates(); + + SectorOffset = GetSectorOffset(GameManager.Singleton.GetCurrentSector()); + + if (this is Character) + { + GameManager.Singleton.ApplyOrigin(); + } + + if (this is not Character) + { + List neighbours = GameManager.Singleton.GetCurrentSector().GetNeighbouringSectors(); + + if (neighbours.Contains(sector)) + { + GameManager.Singleton.Spawn(this); + } + else + { + GameManager.Singleton.Despawn(this); + } + } + + reassigning = false; + } + + public Vector3 GetSectorOffset(Sector sector) + { + Vector3Dec relative = CurrentSector.GlobalCenterCoordinates - sector.GlobalCenterCoordinates; + return relative.ToVector3(); + } + + public virtual void UpdateSectorOffset(Sector sector) + { + SectorOffset = GetSectorOffset(sector); + } + + public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates) + { + GlobalCoordinates = globalCoordinates; + LocalCoordinates = (globalCoordinates - CurrentSector.GlobalCenterCoordinates).ToVector3(); + + UpdateNodePosition(); + } + + public void SetCoordinatesFromLocal(Vector3 localCoordinates) + { + LocalCoordinates = localCoordinates; + GlobalCoordinates = Vector3Dec.FromVector3(localCoordinates) + CurrentSector.GlobalCenterCoordinates; + + UpdateNodePosition(); + } + + public void ResetLocalCoordinates() + { + SetCoordinatesFromGlobal(GlobalCoordinates); + } + + public virtual void UpdateNodePosition() { } + + public virtual void Simulate(double delta) + { + if (!reassigning && !IsInCurrentSector()) + { + UpdateSector(); + } + } + + public virtual Node3D Instantiate(Sector sector) + { + PackedScene modulePrefab = ResourceLoader.Load("res://prefabs/gameObjects/node.tscn"); + return modulePrefab.Instantiate(); + } } diff --git a/scripts/GameObjects/Character.cs b/scripts/GameObjects/Character.cs new file mode 100644 index 0000000..7930bf6 --- /dev/null +++ b/scripts/GameObjects/Character.cs @@ -0,0 +1,22 @@ +using Godot; + +public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sector, localCoordinates) +{ + private Player player; + + public Player InstantiatePlayer() + { + PackedScene prefab = ResourceLoader.Load("res://prefabs/gameObjects/player.tscn"); + Player instance = prefab.Instantiate(); + + instance.PlayerData = this; + player = instance; + + return instance; + } + + public override void UpdateNodePosition() + { + player.GlobalPosition = LocalCoordinates; + } +} diff --git a/scripts/GameObjects/Character.cs.uid b/scripts/GameObjects/Character.cs.uid new file mode 100644 index 0000000..267fb63 --- /dev/null +++ b/scripts/GameObjects/Character.cs.uid @@ -0,0 +1 @@ +uid://dbl3bjk8yp3eb diff --git a/scripts/GameObjects/Nodes/StarNode.cs b/scripts/GameObjects/Nodes/StarNode.cs index 7207fc8..e48ccf7 100644 --- a/scripts/GameObjects/Nodes/StarNode.cs +++ b/scripts/GameObjects/Nodes/StarNode.cs @@ -1,5 +1,16 @@ using Godot; -public partial class StarNode : Node +public partial class StarNode : StaticBody3D { + public Star StarData { get; set; } + + public override void _Ready() + { + GlobalPosition = StarData.LocalCoordinates + StarData.SectorOffset; + } + + public override void _Process(double delta) + { + GlobalPosition = StarData.LocalCoordinates + StarData.SectorOffset; + } } diff --git a/scripts/GameObjects/Star.cs b/scripts/GameObjects/Star.cs index e0537ea..7e2e143 100644 --- a/scripts/GameObjects/Star.cs +++ b/scripts/GameObjects/Star.cs @@ -5,5 +5,18 @@ public class Star(Sector sector, Vector3 localCoordinates) : GameObject(sector, public override void Simulate(double delta) { base.Simulate(delta); + + SetCoordinatesFromGlobal(new(GlobalCoordinates.X, GlobalCoordinates.Y, GlobalCoordinates.Z + 1 * (decimal)delta)); + } + + public override Node3D Instantiate(Sector sector) + { + PackedScene prefab = ResourceLoader.Load("res://prefabs/gameObjects/star.tscn"); + StarNode instance = prefab.Instantiate(); + + instance.StarData = this; + SectorOffset = GetSectorOffset(sector); + + return instance; } } diff --git a/scripts/Generator/IGenerator.cs b/scripts/Generator/IGenerator.cs index d280816..d95ce7f 100644 --- a/scripts/Generator/IGenerator.cs +++ b/scripts/Generator/IGenerator.cs @@ -1,13 +1,12 @@ using System; -using System.Threading.Tasks; using Godot; public interface IGenerator { public Vector3I GetUniverseSize(); - public Vector3 GetSectorSizeEachDirection(); + public Vector3 GetSectorSize(); - public Task GenerateUniverse(Action callback); + public Universe GenerateUniverse(); public Sector GenerateSector(Vector3I coordinates); public Star GenerateStar(Sector sector, Vector3 localCoordinates); diff --git a/scripts/Generator/TestGenerator.cs b/scripts/Generator/TestGenerator.cs index c3d9667..285c33f 100644 --- a/scripts/Generator/TestGenerator.cs +++ b/scripts/Generator/TestGenerator.cs @@ -1,134 +1,169 @@ -using System; +using System.Collections.Generic; using System.Threading.Tasks; using Godot; public class TestGenerator : IGenerator { - public static Vector3I UNIVERSE_SIZE = new(3, 3, 3); - public static Vector3 SECTOR_SIZE_EACH_DIRECTION = new(1000, 1000, 1000); - public static int STARS_PER_SECTOR = 5; - public static int STARS_PER_SECTOR_VARIANCE = 2; - public static int SHIPS_PER_SECTOR = 0; - public static int SHIPS_PER_SECTOR_VARIANCE = 0; - public static int STATIONS_PER_SECTOR = 0; - public static int STATIONS_PER_SECTOR_VARIANCE = 0; + public static Vector3I UNIVERSE_SIZE = new(100, 100, 100); + public static Vector3 SECTOR_SIZE = new(5000, 5000, 5000); + public static int STARS_PER_SECTOR = 100; + public static int STARS_PER_SECTOR_VARIANCE = 5; + public static int SHIPS_PER_SECTOR = 0; + public static int SHIPS_PER_SECTOR_VARIANCE = 0; + public static int STATIONS_PER_SECTOR = 0; + public static int STATIONS_PER_SECTOR_VARIANCE = 0; - private RandomNumberGenerator rng; + private readonly int threads = 16; - public Vector3I GetUniverseSize() - { - return UNIVERSE_SIZE; - } + private RandomNumberGenerator rng; - public Vector3 GetSectorSizeEachDirection() - { - return SECTOR_SIZE_EACH_DIRECTION; - } + public Vector3I GetUniverseSize() + { + return UNIVERSE_SIZE; + } - public async Task GenerateUniverse(Action callback) - { - rng = new(); + public Vector3I GetSectorOffset() + { + return new( + (int)Mathf.Floor(UNIVERSE_SIZE.X / 2), + (int)Mathf.Floor(UNIVERSE_SIZE.X / 2), + (int)Mathf.Floor(UNIVERSE_SIZE.X / 2) + ); + } - Universe universe = new(UNIVERSE_SIZE); - for (int x = 0; x < UNIVERSE_SIZE.X; x++) - { - for (int y = 0; y < UNIVERSE_SIZE.Y; y++) - { - for (int z = 0; z < UNIVERSE_SIZE.Z; z++) - { - universe.Sectors[x, y, z] = GenerateSector(new(x, y, z)); - } - } - } + public Vector3 GetSectorSize() + { + return SECTOR_SIZE; + } - callback(universe); - } + public Universe GenerateUniverse() + { + rng = new(); - public Sector GenerateSector(Vector3I coordinates) - { - Sector sector = new(coordinates, SECTOR_SIZE_EACH_DIRECTION); + List tasks = []; - int starCount = rng.RandiRange( - STARS_PER_SECTOR - STARS_PER_SECTOR_VARIANCE, - STARS_PER_SECTOR + STARS_PER_SECTOR_VARIANCE - ); - starCount = Mathf.Clamp(starCount, 0, int.MaxValue); + Universe universe = new(UNIVERSE_SIZE); + int countX = Mathf.Clamp(UNIVERSE_SIZE.X / threads, 1, int.MaxValue); - for (int i = 0; i < starCount; i++) - { - Vector3 localCoordinates = GenerateLocalCoordinates(); - Star star = GenerateStar(sector, localCoordinates); + for (int x = 0; x < UNIVERSE_SIZE.X; x += countX) + { + int taskStartX = x; + int taskCountX = countX; - sector.GameObjects.Add(star); - } + Task clusteredTask = Task.Run(() => + { + GenerateClustered(universe, taskStartX, taskCountX); + }); - int shipCount = rng.RandiRange( - SHIPS_PER_SECTOR - SHIPS_PER_SECTOR_VARIANCE, - SHIPS_PER_SECTOR + SHIPS_PER_SECTOR_VARIANCE - ); - shipCount = Mathf.Clamp(shipCount, 0, int.MaxValue); + tasks.Add(clusteredTask); + } - for (int i = 0; i < shipCount; i++) - { - Vector3 localCoordinates = GenerateLocalCoordinates(); - Vessel ship = GenerateShip(sector, localCoordinates); + Task.WaitAll([.. tasks]); - sector.GameObjects.Add(ship); - } + return universe; + } - int stationCount = rng.RandiRange( - STATIONS_PER_SECTOR - STATIONS_PER_SECTOR_VARIANCE, - STATIONS_PER_SECTOR + STATIONS_PER_SECTOR_VARIANCE - ); - stationCount = Mathf.Clamp(stationCount, 0, int.MaxValue); + private void GenerateClustered(Universe universe, int startX, int countX) + { + int endX = Mathf.Clamp(startX + countX, 0, UNIVERSE_SIZE.X); - for (int i = 0; i < stationCount; i++) - { - Vector3 localCoordinates = GenerateLocalCoordinates(); - Vessel station = GenerateStation(sector, localCoordinates); + for (int x = startX; x < endX; x++) + { + for (int y = 0; y < UNIVERSE_SIZE.Y; y++) + { + for (int z = 0; z < UNIVERSE_SIZE.Z; z++) + { + universe.Sectors[x, y, z] = GenerateSector(new(x, y, z)); + } + } + } + } - sector.GameObjects.Add(station); - } + public Sector GenerateSector(Vector3I coordinates) + { + Sector sector = new(coordinates, GetSectorOffset(), SECTOR_SIZE); - return sector; - } + int starCount = rng.RandiRange( + STARS_PER_SECTOR - STARS_PER_SECTOR_VARIANCE, + STARS_PER_SECTOR + STARS_PER_SECTOR_VARIANCE + ); + starCount = Mathf.Clamp(starCount, 0, int.MaxValue); - public Star GenerateStar(Sector sector, Vector3 localCoordinates) - { - return new Star(sector, localCoordinates); - } + for (int i = 0; i < starCount; i++) + { + Vector3 localCoordinates = GenerateLocalCoordinates(); + Star star = GenerateStar(sector, localCoordinates); - public Star GenerateStar(Sector sector) - { - return GenerateStar(sector, GenerateLocalCoordinates()); - } + sector.GameObjects.Add(star); + } - public Vessel GenerateShip(Sector sector, Vector3 localCoordinates) - { - return new Vessel(sector, localCoordinates); - } + int shipCount = rng.RandiRange( + SHIPS_PER_SECTOR - SHIPS_PER_SECTOR_VARIANCE, + SHIPS_PER_SECTOR + SHIPS_PER_SECTOR_VARIANCE + ); + shipCount = Mathf.Clamp(shipCount, 0, int.MaxValue); - public Vessel GenerateShip(Sector sector) - { - return GenerateShip(sector, GenerateLocalCoordinates()); - } + for (int i = 0; i < shipCount; i++) + { + Vector3 localCoordinates = GenerateLocalCoordinates(); + Vessel ship = GenerateShip(sector, localCoordinates); - public Vessel GenerateStation(Sector sector, Vector3 localCoordinates) - { - return new Vessel(sector, localCoordinates); - } + sector.GameObjects.Add(ship); + } - public Vessel GenerateStation(Sector sector) - { - return GenerateStation(sector, GenerateLocalCoordinates()); - } + int stationCount = rng.RandiRange( + STATIONS_PER_SECTOR - STATIONS_PER_SECTOR_VARIANCE, + STATIONS_PER_SECTOR + STATIONS_PER_SECTOR_VARIANCE + ); + stationCount = Mathf.Clamp(stationCount, 0, int.MaxValue); - public Vector3 GenerateLocalCoordinates() - { - double x = rng.Randf() * SECTOR_SIZE_EACH_DIRECTION.X; - double y = rng.Randf() * SECTOR_SIZE_EACH_DIRECTION.Y; - double z = rng.Randf() * SECTOR_SIZE_EACH_DIRECTION.Z; + for (int i = 0; i < stationCount; i++) + { + Vector3 localCoordinates = GenerateLocalCoordinates(); + Vessel station = GenerateStation(sector, localCoordinates); - return new(x, y, z); - } + sector.GameObjects.Add(station); + } + + return sector; + } + + public Star GenerateStar(Sector sector, Vector3 localCoordinates) + { + return new Star(sector, localCoordinates); + } + + public Star GenerateStar(Sector sector) + { + return GenerateStar(sector, GenerateLocalCoordinates()); + } + + public Vessel GenerateShip(Sector sector, Vector3 localCoordinates) + { + return new Vessel(sector, localCoordinates); + } + + public Vessel GenerateShip(Sector sector) + { + return GenerateShip(sector, GenerateLocalCoordinates()); + } + + public Vessel GenerateStation(Sector sector, Vector3 localCoordinates) + { + return new Vessel(sector, localCoordinates); + } + + public Vessel GenerateStation(Sector sector) + { + return GenerateStation(sector, GenerateLocalCoordinates()); + } + + public Vector3 GenerateLocalCoordinates() + { + double x = (rng.Randf() - 0.5) * SECTOR_SIZE.X; + double y = (rng.Randf() - 0.5) * SECTOR_SIZE.Y; + double z = (rng.Randf() - 0.5) * SECTOR_SIZE.Z; + + return new(x, y, z); + } } diff --git a/scripts/Helpers.cs b/scripts/Helpers.cs index 3162076..7858291 100644 --- a/scripts/Helpers.cs +++ b/scripts/Helpers.cs @@ -9,12 +9,27 @@ public static class Helpers public static bool IsInsideArea(Vector3 area, Vector3 coordinates) { - if (coordinates.X >= area.X && coordinates.Y >= area.Y && coordinates.Z >= area.Z) + if (coordinates.X >= area.X || coordinates.Y >= area.Y || coordinates.Z >= area.Z) { return false; } - if (coordinates.X < -area.X && coordinates.Y < -area.Y && coordinates.Z < -area.Z) + if (coordinates.X < -area.X || coordinates.Y < -area.Y || coordinates.Z < -area.Z) + { + return false; + } + + return true; + } + + public static bool IsInsideGlobalArea(Vector3Dec areaStart, Vector3Dec areaEnd, Vector3Dec coordinates) + { + if (coordinates.X >= areaEnd.X || coordinates.Y >= areaEnd.Y || coordinates.Z >= areaEnd.Z) + { + return false; + } + + if (coordinates.X < areaStart.X || coordinates.Y < areaStart.Y || coordinates.Z < areaStart.Z) { return false; } diff --git a/scripts/Player.cs b/scripts/Player.cs index ae7a6c8..e6a4136 100644 --- a/scripts/Player.cs +++ b/scripts/Player.cs @@ -2,15 +2,19 @@ using Godot; public partial class Player : CharacterBody3D { - [Export] Control GameMenu; - [Export] public float Speed = 5f; - [Export] public float JumpForce = 5f; - [Export] public float MouseSensitivity = 0.2f; + [Export] public double Speed = 5; + [Export] public double SprintMultiplier = 2; + [Export] public double JumpForce = 5; + [Export] public double MouseSensitivity = 0.2; + + public Control GameMenu { get; set; } + + public Character PlayerData { get; set; } private Vector3 gravityVelocity = Vector3.Zero; private Vector3 movementVelocity = Vector3.Zero; - private double cameraPitch = 0f; + private double cameraPitch = 0; private Camera3D camera; private GravityReceiver gravityReceiver; @@ -18,6 +22,14 @@ public partial class Player : CharacterBody3D { camera = GetNode("Camera"); gravityReceiver = GetNode("GravityReceiver"); + + PlayerData.UpdateNodePosition(); + } + + public override void _Process(double delta) + { + PlayerData.SetCoordinatesFromLocal(GlobalPosition); + PlayerData.Simulate(delta); } public override void _PhysicsProcess(double delta) @@ -69,7 +81,7 @@ public partial class Player : CharacterBody3D if (IsInGravity()) { - cameraPitch = Mathf.Clamp(cameraPitch + pitchDelta, -90f, 90f); + cameraPitch = Mathf.Clamp(cameraPitch + pitchDelta, -90, 90); } else { @@ -82,7 +94,7 @@ public partial class Player : CharacterBody3D public bool IsInGravity() { - return gravityReceiver.InGravityZone && gravityReceiver.GetGravityStrength() > 0f; + return gravityReceiver.InGravityZone && gravityReceiver.GetGravityStrength() > 0; } public bool IsOnGravityFloor() @@ -94,7 +106,7 @@ public partial class Player : CharacterBody3D double alignment = collisionNormal.Dot(gravityReceiver.GetGravityDirection()); - if (alignment > 0.7f) + if (alignment > 0.7) { return true; } @@ -118,28 +130,28 @@ public partial class Player : CharacterBody3D Vector3 targetUp = gravityReceiver.GetGravityDirection(); Vector3 axis = currentUp.Cross(targetUp); - if (axis.Length() < 0.00001f) + if (axis.Length() < 0.00001) { return; } double angle = currentUp.AngleTo(targetUp); - GlobalRotate(axis.Normalized(), angle * delta * 10f); + GlobalRotate(axis.Normalized(), angle * delta * 10); } private void ProcessCamera(double delta) { - if (cameraPitch > 0.01f) + if (cameraPitch > 0.01) { - cameraPitch -= 90f * delta; + cameraPitch -= 90 * delta; } - else if (cameraPitch < -0.001f) + else if (cameraPitch < -0.001) { - cameraPitch += 90f * delta; + cameraPitch += 90 * delta; } - if (Mathf.Abs(cameraPitch) < 1f) + if (Mathf.Abs(cameraPitch) < 1) { - cameraPitch = 0f; + cameraPitch = 0; } camera.RotationDegrees = new Vector3(cameraPitch, 0, 0); } @@ -149,19 +161,21 @@ public partial class Player : CharacterBody3D float inputX = Input.GetAxis("move_left", "move_right"); float inputZ = Input.GetAxis("move_forward", "move_back"); + bool sprint = Input.IsActionPressed("sprint"); + Vector3 direction = GlobalTransform.Basis.X * inputX + GlobalTransform.Basis.Z * inputZ; if (direction != Vector3.Zero) { - newMovementVelocity.X = direction.X * Speed; - newMovementVelocity.Y = direction.Y * Speed; - newMovementVelocity.Z = direction.Z * Speed; + newMovementVelocity.X = direction.X * Speed * (sprint ? SprintMultiplier : 1); + newMovementVelocity.Y = direction.Y * Speed * (sprint ? SprintMultiplier : 1); + newMovementVelocity.Z = direction.Z * Speed * (sprint ? SprintMultiplier : 1); } else { - newMovementVelocity.X = 0f; - newMovementVelocity.Y = 0f; - newMovementVelocity.Z = 0f; + newMovementVelocity.X = 0; + newMovementVelocity.Y = 0; + newMovementVelocity.Z = 0; } if (Input.IsActionJustPressed("jump") && IsOnGravityFloor()) @@ -176,19 +190,21 @@ public partial class Player : CharacterBody3D float inputY = -Input.GetAxis("move_up", "move_down"); float inputZ = Input.GetAxis("move_forward", "move_back"); + bool sprint = Input.IsActionPressed("sprint"); + Vector3 direction = Transform.Basis.X * inputX + Transform.Basis.Y * inputY + Transform.Basis.Z * inputZ; if (direction != Vector3.Zero) { - newMovementVelocity.X = direction.X * Speed; - newMovementVelocity.Y = direction.Y * Speed; - newMovementVelocity.Z = direction.Z * Speed; + newMovementVelocity.X = direction.X * Speed * (sprint ? SprintMultiplier : 1); + newMovementVelocity.Y = direction.Y * Speed * (sprint ? SprintMultiplier : 1); + newMovementVelocity.Z = direction.Z * Speed * (sprint ? SprintMultiplier : 1); } else { - newMovementVelocity.X = 0f; - newMovementVelocity.Y = 0f; - newMovementVelocity.Z = 0f; + newMovementVelocity.X = 0; + newMovementVelocity.Y = 0; + newMovementVelocity.Z = 0; } double inputRotateZ = Input.GetAxis("rotate_left", "rotate_right"); diff --git a/scripts/QueueManager.cs b/scripts/QueueManager.cs new file mode 100644 index 0000000..7c6f07f --- /dev/null +++ b/scripts/QueueManager.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Concurrent; +using Godot; + +public partial class QueueManager : Node +{ + public static ConcurrentQueue LogQueue = new(); + public static ConcurrentQueue ActionQueue = new(); + + public static ConcurrentQueue<(Sector, GameObject)> SectorReassignQueue = new(); + + private readonly int sectorReassignQueueRateLimit = 500; + + public override void _Process(double delta) + { + while (LogQueue.TryDequeue(out string text)) + { + GD.Print(text); + } + + while (ActionQueue.TryDequeue(out Action action)) + { + action(); + } + + GD.Print(SectorReassignQueue.Count); + + int sectorReassignQueueProcessed = 0; + while ( + !GameManager.Singleton.simulating + && sectorReassignQueueProcessed++ < sectorReassignQueueRateLimit + && SectorReassignQueue.TryDequeue(out var item) + ) + { + var (sector, gameObject) = item; + + gameObject.CurrentSector.GameObjects.Remove(gameObject); + sector.GameObjects.Add(gameObject); + + gameObject.AssignSector(sector); + } + } +} diff --git a/scripts/QueueManager.cs.uid b/scripts/QueueManager.cs.uid new file mode 100644 index 0000000..9439df0 --- /dev/null +++ b/scripts/QueueManager.cs.uid @@ -0,0 +1 @@ +uid://dr6y711ano07p diff --git a/scripts/Sector.cs b/scripts/Sector.cs index bc2d092..224531d 100644 --- a/scripts/Sector.cs +++ b/scripts/Sector.cs @@ -1,37 +1,95 @@ -using System.Threading; +using System.Collections.Generic; using Godot; public class Sector { public Vector3I Coordinates; public Vector3Dec GlobalStartCoordinates; + public Vector3Dec GlobalCenterCoordinates; public Vector3Dec GlobalEndCoordinates; public Vector3 Size; public FastUniqueList GameObjects = new(); - public Sector(Vector3I coordinates, Vector3 size) + public Sector(Vector3I coordinates, Vector3I offset, Vector3 size) { Coordinates = coordinates; - decimal startX = Coordinates.X * (decimal)size.X; - decimal startY = Coordinates.Y * (decimal)size.Y; - decimal startZ = Coordinates.Z * (decimal)size.Z; + Vector3Dec sizeDec = Vector3Dec.FromVector3(size); + + decimal startX = (coordinates.X - offset.X) * sizeDec.X; + decimal startY = (coordinates.Y - offset.Y) * sizeDec.Y; + decimal startZ = (coordinates.Z - offset.Z) * sizeDec.Z; GlobalStartCoordinates = new(startX, startY, startZ); + GlobalCenterCoordinates = new( + startX + sizeDec.X / 2, + startY + sizeDec.Y / 2, + startZ + sizeDec.Z / 2 + ); + + GlobalEndCoordinates = new( + startX + sizeDec.X, + startY + sizeDec.Y, + startZ + sizeDec.Z + ); + Size = size; } - public Sector(int x, int y, int z, Vector3 size) : this(new(x, y, z), size) { } - public void Simulate(double delta) { - //GD.Print(Thread.CurrentThread.ManagedThreadId); - GameObjects.ForEach(gameObject => { gameObject.Simulate(delta); }); } + + public bool IsObjectInSector(GameObject gameObject) + { + return Helpers.IsInsideGlobalArea(GlobalStartCoordinates, GlobalEndCoordinates, gameObject.GlobalCoordinates); + } + + public void AssignObject(GameObject gameObject) + { + QueueManager.SectorReassignQueue.Enqueue((this, gameObject)); + } + + public void SpawnObjects() + { + GameObjects.ForEach(GameManager.Singleton.Spawn); + } + + public List GetNeighbouringSectors() + { + List neighbours = []; + + Sector[,,] allSectors = GameManager.Singleton.GameUniverse.Sectors; + + int sizeX = allSectors.GetLength(0); + int sizeY = allSectors.GetLength(1); + int sizeZ = allSectors.GetLength(2); + + int startX = Mathf.Clamp(Coordinates.X - 1, 0, sizeX - 1); + int startY = Mathf.Clamp(Coordinates.Y - 1, 0, sizeY - 1); + int startZ = Mathf.Clamp(Coordinates.Z - 1, 0, sizeZ - 1); + + int endX = Mathf.Clamp(Coordinates.X + 1, 0, sizeX - 1); + int endY = Mathf.Clamp(Coordinates.Y + 1, 0, sizeY - 1); + int endZ = Mathf.Clamp(Coordinates.Z + 1, 0, sizeZ - 1); + + for (int x = startX; x <= endX; x++) + { + for (int y = startY; y <= endY; y++) + { + for (int z = startZ; z <= endZ; z++) + { + neighbours.Add(allSectors[x, y, z]); + } + } + } + + return neighbours; + } } diff --git a/scripts/Universe.cs b/scripts/Universe.cs index 3831038..aa137d6 100644 --- a/scripts/Universe.cs +++ b/scripts/Universe.cs @@ -1,3 +1,4 @@ +using System; using Godot; public class Universe @@ -23,4 +24,22 @@ public class Universe { return Sectors[x, y, z]; } + + public void ForEachSector(Action action) + { + int sizeX = Sectors.GetLength(0); + int sizeY = Sectors.GetLength(1); + int sizeZ = Sectors.GetLength(2); + + for (int x = 0; x < sizeX; x++) + { + for (int y = 0; y < sizeY; y++) + { + for (int z = 0; z < sizeZ; z++) + { + action(Sectors[x, y, z]); + } + } + } + } } diff --git a/scripts/Vector3Dec.cs b/scripts/Vector3Dec.cs index 4f580b4..032a058 100644 --- a/scripts/Vector3Dec.cs +++ b/scripts/Vector3Dec.cs @@ -1,7 +1,7 @@ using System; using Godot; -public class Vector3Dec(decimal x, decimal y, decimal z) +public readonly struct Vector3Dec(decimal x, decimal y, decimal z) { public static Vector3Dec Zero { get; } = new(0, 0, 0); @@ -14,6 +14,11 @@ public class Vector3Dec(decimal x, decimal y, decimal z) return new((double)X, (double)Y, (double)Z); } + public static Vector3Dec FromVector3(Vector3 vec) + { + return new((decimal)vec.X, (decimal)vec.Y, (decimal)vec.Z); + } + public static Vector3Dec operator +(Vector3Dec vec1, Vector3Dec vec2) { return new(vec1.X + vec2.X, vec1.Y + vec2.Y, vec1.Z + vec2.Z);