Revamp the settings system (#7)
- A Settings Provider system to utilize addressables for loading settings at runtime - An editor UI for easy modifications of the settings objects - A split out developer settings functionality to keep gameplay and nitty-gritty details separately - Most settings migrated out of game objects and into the new system - An additional Editor utility for fetching the settings at editor runtime, for gizmos, visualization etc Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Co-authored-by: AlexanderT <alexander@foolhardyhorizons.com> Reviewed-on: #7
This commit is contained in:
@@ -4,6 +4,7 @@ using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Serialization;
|
||||
using Pooling;
|
||||
using AppleHills.Core.Settings;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
@@ -16,21 +17,6 @@ namespace Minigames.DivingForPictures
|
||||
[Tooltip("List of possible trench tile prefabs.")]
|
||||
[SerializeField] private List<GameObject> tilePrefabs;
|
||||
|
||||
[Header("Tile Settings")]
|
||||
[SerializeField] private int initialTileCount = 3;
|
||||
[SerializeField] private float tileSpawnBuffer = 1f;
|
||||
|
||||
[Header("Movement Settings")]
|
||||
[SerializeField] private float moveSpeed = 3f;
|
||||
[SerializeField] private float speedUpFactor = 0.2f;
|
||||
[SerializeField] private float speedUpInterval = 10f;
|
||||
[SerializeField] private float maxMoveSpeed = 12f;
|
||||
|
||||
[Header("Object Pooling")]
|
||||
[SerializeField] private bool useObjectPooling = true;
|
||||
[SerializeField] private int maxPerPrefabPoolSize = 2;
|
||||
[SerializeField] private int totalMaxPoolSize = 10;
|
||||
|
||||
[Header("Events")]
|
||||
[FormerlySerializedAs("OnTileSpawned")]
|
||||
public UnityEvent<GameObject> onTileSpawned;
|
||||
@@ -38,6 +24,10 @@ namespace Minigames.DivingForPictures
|
||||
[FormerlySerializedAs("OnTileDestroyed")]
|
||||
public UnityEvent<GameObject> onTileDestroyed;
|
||||
|
||||
// Settings references
|
||||
private IDivingMinigameSettings _settings;
|
||||
private DivingDeveloperSettings _devSettings;
|
||||
|
||||
// Private fields
|
||||
private readonly Dictionary<GameObject, float> _tileHeights = new Dictionary<GameObject, float>();
|
||||
private readonly List<GameObject> _activeTiles = new List<GameObject>();
|
||||
@@ -52,9 +42,6 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Current velocity for tile movement
|
||||
private float _currentVelocity;
|
||||
|
||||
// Interval for velocity calculations (seconds)
|
||||
[SerializeField] private float velocityCalculationInterval = 0.5f;
|
||||
|
||||
private const float TileSpawnZ = -1f;
|
||||
private const float DefaultTileHeight = 5f;
|
||||
@@ -73,7 +60,22 @@ namespace Minigames.DivingForPictures
|
||||
private void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
_baseMoveSpeed = moveSpeed; // Store the original base speed
|
||||
|
||||
// Get settings from GameManager
|
||||
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||
_devSettings = GameManager.GetDeveloperSettings<DivingDeveloperSettings>();
|
||||
|
||||
if (_settings == null)
|
||||
{
|
||||
Debug.LogError("[TrenchTileSpawner] Failed to load diving minigame settings!");
|
||||
}
|
||||
|
||||
if (_devSettings == null)
|
||||
{
|
||||
Debug.LogError("[TrenchTileSpawner] Failed to load diving developer settings!");
|
||||
}
|
||||
|
||||
_baseMoveSpeed = _settings?.MoveSpeed ?? 3f; // Store the original base speed
|
||||
|
||||
// Calculate tile heights for each prefab
|
||||
CalculateTileHeights();
|
||||
@@ -81,7 +83,7 @@ namespace Minigames.DivingForPictures
|
||||
// Ensure all prefabs have Tile components
|
||||
ValidateTilePrefabs();
|
||||
|
||||
if (useObjectPooling)
|
||||
if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling)
|
||||
{
|
||||
InitializeObjectPool();
|
||||
}
|
||||
@@ -110,7 +112,7 @@ namespace Minigames.DivingForPictures
|
||||
SpawnInitialTiles();
|
||||
|
||||
// Initialize velocity and start the velocity calculation coroutine
|
||||
_currentVelocity = moveSpeed * Time.fixedDeltaTime;
|
||||
_currentVelocity = _baseMoveSpeed * Time.fixedDeltaTime;
|
||||
StartCoroutine(VelocityCalculationRoutine());
|
||||
}
|
||||
|
||||
@@ -148,7 +150,7 @@ namespace Minigames.DivingForPictures
|
||||
while (true)
|
||||
{
|
||||
CalculateVelocity();
|
||||
yield return new WaitForSeconds(velocityCalculationInterval);
|
||||
yield return new WaitForSeconds(_settings.VelocityCalculationInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +159,7 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
private void CalculateVelocity()
|
||||
{
|
||||
_currentVelocity = moveSpeed * Time.fixedDeltaTime;
|
||||
_currentVelocity = _baseMoveSpeed * Time.fixedDeltaTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -197,9 +199,9 @@ namespace Minigames.DivingForPictures
|
||||
poolGO.transform.SetParent(transform);
|
||||
_tilePool = poolGO.AddComponent<TrenchTilePool>();
|
||||
|
||||
// Set up the pool configuration
|
||||
_tilePool.maxPerPrefabPoolSize = maxPerPrefabPoolSize;
|
||||
_tilePool.totalMaxPoolSize = totalMaxPoolSize;
|
||||
// Set up the pool configuration using developer settings
|
||||
_tilePool.maxPerPrefabPoolSize = _devSettings.TrenchTileMaxPerPrefabPoolSize;
|
||||
_tilePool.totalMaxPoolSize = _devSettings.TrenchTileTotalMaxPoolSize;
|
||||
|
||||
// Convert the GameObject list to a Tile list
|
||||
List<Tile> prefabTiles = new List<Tile>(tilePrefabs.Count);
|
||||
@@ -247,7 +249,7 @@ namespace Minigames.DivingForPictures
|
||||
// Move starting position up by 2 tile heights
|
||||
startingY += tileHeightEstimate * 2;
|
||||
|
||||
for (int i = 0; i < initialTileCount; i++)
|
||||
for (int i = 0; i < _settings.InitialTileCount; i++)
|
||||
{
|
||||
float y = startingY;
|
||||
// Calculate proper Y position based on previous tiles
|
||||
@@ -291,12 +293,12 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Update the actual move speed based on the velocity factor
|
||||
// This keeps the original move speed intact for game logic
|
||||
moveSpeed = _baseMoveSpeed * Mathf.Abs(_velocityFactor);
|
||||
_baseMoveSpeed = _settings.MoveSpeed * Mathf.Abs(_velocityFactor);
|
||||
|
||||
// Recalculate velocity immediately
|
||||
CalculateVelocity();
|
||||
|
||||
Debug.Log($"[TrenchTileSpawner] Velocity factor updated to {_velocityFactor:F2}, moveSpeed: {moveSpeed:F2}");
|
||||
Debug.Log($"[TrenchTileSpawner] Velocity factor updated to {_velocityFactor:F2}, moveSpeed: {_baseMoveSpeed:F2}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -362,12 +364,12 @@ namespace Minigames.DivingForPictures
|
||||
if (_isSurfacing)
|
||||
{
|
||||
// When surfacing, destroy tiles at the bottom
|
||||
shouldDestroy = topTile.transform.position.y + tileHeight / 2 < _screenBottom - tileSpawnBuffer;
|
||||
shouldDestroy = topTile.transform.position.y + tileHeight / 2 < _screenBottom - _settings.TileSpawnBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When descending, destroy tiles at the top
|
||||
shouldDestroy = topTile.transform.position.y - tileHeight / 2 > _screenTop + tileSpawnBuffer;
|
||||
shouldDestroy = topTile.transform.position.y - tileHeight / 2 > _screenTop + _settings.TileSpawnBuffer;
|
||||
}
|
||||
|
||||
if (shouldDestroy)
|
||||
@@ -375,7 +377,7 @@ namespace Minigames.DivingForPictures
|
||||
_activeTiles.RemoveAt(0);
|
||||
onTileDestroyed?.Invoke(topTile);
|
||||
|
||||
if (useObjectPooling && _tilePool != null)
|
||||
if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling && _tilePool != null)
|
||||
{
|
||||
// Find the prefab index for this tile
|
||||
int prefabIndex = GetPrefabIndex(topTile);
|
||||
@@ -431,12 +433,12 @@ namespace Minigames.DivingForPictures
|
||||
if (_isSurfacing)
|
||||
{
|
||||
// When surfacing, check if the bottom of the tile is above the top of the screen
|
||||
isLastTileLeaving = bottomTile.transform.position.y - tileHeight / 2 > _screenTop + tileSpawnBuffer;
|
||||
isLastTileLeaving = bottomTile.transform.position.y - tileHeight / 2 > _screenTop + _settings.TileSpawnBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When descending, check if the top of the tile is below the bottom of the screen
|
||||
isLastTileLeaving = bottomTile.transform.position.y + tileHeight / 2 < _screenBottom - tileSpawnBuffer;
|
||||
isLastTileLeaving = bottomTile.transform.position.y + tileHeight / 2 < _screenBottom - _settings.TileSpawnBuffer;
|
||||
}
|
||||
|
||||
if (isLastTileLeaving)
|
||||
@@ -454,14 +456,14 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
// When surfacing, spawn new tiles at the top
|
||||
float topEdge = bottomTile.transform.position.y + tileHeight / 2;
|
||||
shouldSpawn = topEdge < _screenTop + tileSpawnBuffer;
|
||||
shouldSpawn = topEdge < _screenTop + _settings.TileSpawnBuffer;
|
||||
newY = bottomTile.transform.position.y + tileHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When descending, spawn new tiles at the bottom
|
||||
float bottomEdge = bottomTile.transform.position.y - tileHeight / 2;
|
||||
shouldSpawn = bottomEdge > _screenBottom - tileSpawnBuffer;
|
||||
shouldSpawn = bottomEdge > _screenBottom - _settings.TileSpawnBuffer;
|
||||
newY = bottomTile.transform.position.y - tileHeight;
|
||||
}
|
||||
|
||||
@@ -477,9 +479,9 @@ namespace Minigames.DivingForPictures
|
||||
private void HandleSpeedRamping()
|
||||
{
|
||||
_speedUpTimer += Time.deltaTime;
|
||||
if (_speedUpTimer >= speedUpInterval)
|
||||
if (_speedUpTimer >= _settings.SpeedUpInterval)
|
||||
{
|
||||
moveSpeed = Mathf.Min(moveSpeed + speedUpFactor, maxMoveSpeed);
|
||||
_baseMoveSpeed = Mathf.Min(_baseMoveSpeed + _settings.SpeedUpFactor, _settings.MaxMoveSpeed);
|
||||
_speedUpTimer = 0f;
|
||||
}
|
||||
}
|
||||
@@ -506,7 +508,7 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
GameObject tile;
|
||||
|
||||
if (useObjectPooling && _tilePool != null)
|
||||
if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling && _tilePool != null)
|
||||
{
|
||||
tile = _tilePool.GetTile(prefabIndex);
|
||||
if (tile == null)
|
||||
@@ -518,11 +520,25 @@ namespace Minigames.DivingForPictures
|
||||
tile.transform.position = new Vector3(0f, y, TileSpawnZ);
|
||||
tile.transform.rotation = prefab.transform.rotation;
|
||||
tile.transform.SetParent(transform);
|
||||
|
||||
// Set the layer to the configured trench tile layer
|
||||
if (_devSettings != null)
|
||||
{
|
||||
tile.layer = _devSettings.TrenchTileLayer;
|
||||
SetLayerRecursively(tile, _devSettings.TrenchTileLayer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the prefab's original rotation
|
||||
tile = Instantiate(prefab, new Vector3(0f, y, TileSpawnZ), prefab.transform.rotation, transform);
|
||||
|
||||
// Set the layer to the configured trench tile layer
|
||||
if (_devSettings != null)
|
||||
{
|
||||
tile.layer = _devSettings.TrenchTileLayer;
|
||||
SetLayerRecursively(tile, _devSettings.TrenchTileLayer);
|
||||
}
|
||||
}
|
||||
|
||||
_activeTiles.Add(tile);
|
||||
@@ -538,7 +554,7 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
int prefabCount = tilePrefabs.Count;
|
||||
List<float> weights = new List<float>(prefabCount);
|
||||
|
||||
|
||||
for (int i = 0; i < prefabCount; i++)
|
||||
{
|
||||
int lastUsed = _tileLastUsed.TryGetValue(i, out var value) ? value : -prefabCount;
|
||||
@@ -546,13 +562,13 @@ namespace Minigames.DivingForPictures
|
||||
float weight = Mathf.Clamp(age, 1, prefabCount * 2); // More unused = higher weight
|
||||
weights.Add(weight);
|
||||
}
|
||||
|
||||
|
||||
float totalWeight = 0f;
|
||||
foreach (var weight in weights)
|
||||
{
|
||||
totalWeight += weight;
|
||||
}
|
||||
|
||||
|
||||
float randomValue = Random.value * totalWeight;
|
||||
for (int i = 0; i < prefabCount; i++)
|
||||
{
|
||||
@@ -562,10 +578,10 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
randomValue -= weights[i];
|
||||
}
|
||||
|
||||
|
||||
return Random.Range(0, prefabCount); // fallback
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of a tile based on its prefab or renderer bounds
|
||||
/// </summary>
|
||||
@@ -593,18 +609,18 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If not found, calculate it from the renderer
|
||||
Renderer renderer = tile.GetComponentInChildren<Renderer>();
|
||||
if (renderer != null)
|
||||
{
|
||||
return renderer.bounds.size.y;
|
||||
}
|
||||
|
||||
|
||||
// Fallback
|
||||
return DefaultTileHeight;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the prefab that was used to create this tile
|
||||
/// </summary>
|
||||
@@ -620,7 +636,7 @@ namespace Minigames.DivingForPictures
|
||||
for (int i = 0; i < tilePrefabs.Count; i++)
|
||||
{
|
||||
if (tilePrefabs[i] == null) continue;
|
||||
|
||||
|
||||
if (tile.name.StartsWith(tilePrefabs[i].name))
|
||||
{
|
||||
return i;
|
||||
@@ -656,18 +672,41 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
// Draw tile bounds for debugging
|
||||
Gizmos.color = Color.cyan;
|
||||
for (int i = 0; i < initialTileCount; i++)
|
||||
if (_settings != null)
|
||||
{
|
||||
float height = DefaultTileHeight;
|
||||
if (tilePrefabs != null && tilePrefabs.Count > 0 && tilePrefabs[0] != null &&
|
||||
_tileHeights.TryGetValue(tilePrefabs[0], out float h))
|
||||
for (int i = 0; i < _settings.InitialTileCount; i++)
|
||||
{
|
||||
height = h;
|
||||
float height = DefaultTileHeight;
|
||||
if (tilePrefabs != null && tilePrefabs.Count > 0 && tilePrefabs[0] != null &&
|
||||
_tileHeights.TryGetValue(tilePrefabs[0], out float h))
|
||||
{
|
||||
height = h;
|
||||
}
|
||||
Vector3 center = new Vector3(0f, _screenBottom + i * height, 0f);
|
||||
Gizmos.DrawWireCube(center, new Vector3(10f, height, 1f));
|
||||
}
|
||||
Vector3 center = new Vector3(0f, _screenBottom + i * height, 0f);
|
||||
Gizmos.DrawWireCube(center, new Vector3(10f, height, 1f));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Set the layer of a GameObject and all its children recursively
|
||||
/// </summary>
|
||||
/// <param name="obj">The GameObject to set the layer for</param>
|
||||
/// <param name="layer">The layer index to set</param>
|
||||
private void SetLayerRecursively(GameObject obj, int layer)
|
||||
{
|
||||
if (obj == null) return;
|
||||
|
||||
obj.layer = layer;
|
||||
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
if (child != null)
|
||||
{
|
||||
SetLayerRecursively(child.gameObject, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user