Added dynamic spawning and despawning of objects
This commit is contained in:
parent
adbb208436
commit
e0cf8d9755
23 changed files with 794 additions and 350 deletions
|
|
@ -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<GameObject, Node3D> spawnedObjects = [];
|
||||
private readonly ConcurrentQueue<GameObject> 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<Sector> 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<Task> 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<Sector> 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<Sector> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue