Commmit minigame and tile logic
This commit is contained in:
@@ -6,6 +6,29 @@
|
||||
/// </summary>
|
||||
public class Tile : MonoBehaviour
|
||||
{
|
||||
[Header("Path In")]
|
||||
public bool pathInLeft;
|
||||
public bool pathInCenter;
|
||||
public bool pathInRight;
|
||||
|
||||
[Header("Path Out")]
|
||||
public bool pathOutLeft;
|
||||
public bool pathOutCenter;
|
||||
public bool pathOutRight;
|
||||
|
||||
[Header("Difficulty")]
|
||||
public int difficultyLevel; // 1 (easy) to 5 (hard)
|
||||
|
||||
// --- FLOATING AREA LOGIC ---
|
||||
[Header("Middle Obstacle")]
|
||||
// Indicates if this tile has a floating area in the middle
|
||||
public bool hasFloatingAreaMiddle;
|
||||
// Indicates if this tile continues a floating area in the middle
|
||||
public bool continuesFloatingAreaMiddle;
|
||||
// Indicates if this tile ends a floating area in the middle
|
||||
public bool endsFloatingAreaMiddle;
|
||||
// You can set these in the Inspector for each tile prefab to control floating area behavior
|
||||
|
||||
// This is primarily a marker component, but we could add tile-specific properties here if needed
|
||||
|
||||
// Optional: Add properties that might be useful for all tiles
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace Minigames.DivingForPictures
|
||||
[Tooltip("List of possible trench tile prefabs.")]
|
||||
[SerializeField] private List<GameObject> tilePrefabs;
|
||||
|
||||
[Header("Initial Tile")]
|
||||
[Tooltip("Prefab for the initial trench tile. This will always be spawned first.")]
|
||||
[SerializeField] private GameObject initialTilePrefab;
|
||||
|
||||
[Header("Events")]
|
||||
[FormerlySerializedAs("OnTileSpawned")]
|
||||
public UnityEvent<GameObject> onTileSpawned;
|
||||
@@ -67,6 +71,44 @@ namespace Minigames.DivingForPictures
|
||||
// Screen normalization
|
||||
private float _screenNormalizationFactor = 1.0f;
|
||||
|
||||
// Tracks if a floating area in the middle is currently active
|
||||
private bool isFloatingAreaActive = false;
|
||||
|
||||
// Current depth of the trench
|
||||
private int _currentDepth = 0;
|
||||
|
||||
[System.Serializable]
|
||||
public struct DepthDifficultyRange
|
||||
{
|
||||
public int minDepth;
|
||||
public int maxDepth;
|
||||
public int difficulty;
|
||||
}
|
||||
|
||||
[Header("Difficulty Depths")]
|
||||
[Tooltip("Configure depth ranges and their corresponding difficulty levels.")]
|
||||
[SerializeField] private List<DepthDifficultyRange> depthDifficultyRanges = new List<DepthDifficultyRange>
|
||||
{
|
||||
new DepthDifficultyRange { minDepth = 0, maxDepth = 10, difficulty = 1 },
|
||||
new DepthDifficultyRange { minDepth = 11, maxDepth = 20, difficulty = 2 },
|
||||
new DepthDifficultyRange { minDepth = 21, maxDepth = 30, difficulty = 3 },
|
||||
new DepthDifficultyRange { minDepth = 31, maxDepth = 40, difficulty = 4 },
|
||||
new DepthDifficultyRange { minDepth = 41, maxDepth = int.MaxValue, difficulty = 5 }
|
||||
};
|
||||
|
||||
public int CurrentDifficulty
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var range in depthDifficultyRanges)
|
||||
{
|
||||
if (_currentDepth >= range.minDepth && _currentDepth <= range.maxDepth)
|
||||
return range.difficulty;
|
||||
}
|
||||
return 1; // Default fallback
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
@@ -213,7 +255,7 @@ namespace Minigames.DivingForPictures
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError("Null prefab found in tilePrefabs list!");
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
Renderer renderer = prefab.GetComponentInChildren<Renderer>();
|
||||
@@ -300,7 +342,14 @@ namespace Minigames.DivingForPictures
|
||||
float prevHeight = GetTileHeight(prevTile);
|
||||
y = prevTile.transform.position.y - prevHeight;
|
||||
}
|
||||
SpawnTileAtY(y);
|
||||
if (i == 0 && initialTilePrefab != null)
|
||||
{
|
||||
SpawnSpecificTileAtY(initialTilePrefab, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnTileAtY(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,6 +643,89 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two tiles are fully compatible for spawning, following clarified floating area rules.
|
||||
/// </summary>
|
||||
private bool AreTilesFullyCompatible(Tile prev, Tile next)
|
||||
{
|
||||
// Path compatibility: at least one path is open in both Out (prev) and In (next)
|
||||
bool pathCompatible =
|
||||
(prev.pathOutLeft && next.pathInLeft) ||
|
||||
(prev.pathOutCenter && next.pathInCenter) ||
|
||||
(prev.pathOutRight && next.pathInRight);
|
||||
|
||||
// --- Updated floating area rules ---
|
||||
// Continue tile: can only spawn after Start, and must be followed by End
|
||||
if (next.continuesFloatingAreaMiddle)
|
||||
{
|
||||
if (!prev.hasFloatingAreaMiddle)
|
||||
return false;
|
||||
}
|
||||
// End tile: can only spawn after Start or Continue
|
||||
if (next.endsFloatingAreaMiddle)
|
||||
{
|
||||
if (!(prev.hasFloatingAreaMiddle || prev.continuesFloatingAreaMiddle))
|
||||
return false;
|
||||
}
|
||||
// After a Start tile, only Continue or End can follow
|
||||
if (prev.hasFloatingAreaMiddle)
|
||||
{
|
||||
if (!(next.continuesFloatingAreaMiddle || next.endsFloatingAreaMiddle))
|
||||
return false;
|
||||
}
|
||||
// After a Continue tile, only End can follow
|
||||
if (prev.continuesFloatingAreaMiddle)
|
||||
{
|
||||
if (!next.endsFloatingAreaMiddle)
|
||||
return false;
|
||||
}
|
||||
// Otherwise, normal tiles are always allowed
|
||||
return pathCompatible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a weighted random index among fully compatible tiles, prioritizing least recently used.
|
||||
/// </summary>
|
||||
private int GetWeightedCompatibleTileIndex(Tile prevTile)
|
||||
{
|
||||
var compatibleIndices = new List<int>();
|
||||
int currentDifficulty = CurrentDifficulty;
|
||||
for (int i = 0; i < tilePrefabs.Count; i++)
|
||||
{
|
||||
var candidateGO = tilePrefabs[i];
|
||||
var candidate = candidateGO?.GetComponent<Tile>();
|
||||
if (candidate == null) continue;
|
||||
if (candidate.difficultyLevel > currentDifficulty) continue;
|
||||
bool compatible = AreTilesFullyCompatible(prevTile, candidate);
|
||||
if (compatible)
|
||||
compatibleIndices.Add(i);
|
||||
}
|
||||
if (compatibleIndices.Count == 0)
|
||||
{
|
||||
Debug.LogError("No compatible tile found for previous tile and current difficulty. Spawning aborted.");
|
||||
return -1;
|
||||
}
|
||||
List<float> weights = new List<float>(compatibleIndices.Count);
|
||||
foreach (var i in compatibleIndices)
|
||||
{
|
||||
int lastUsed = _tileLastUsed.TryGetValue(i, out var value) ? value : -tilePrefabs.Count;
|
||||
int age = _spawnCounter - lastUsed;
|
||||
float weight = Mathf.Clamp(age, 1, tilePrefabs.Count * 2);
|
||||
weights.Add(weight);
|
||||
}
|
||||
float totalWeight = 0f;
|
||||
foreach (var weight in weights)
|
||||
totalWeight += weight;
|
||||
float randomValue = Random.value * totalWeight;
|
||||
for (int idx = 0; idx < compatibleIndices.Count; idx++)
|
||||
{
|
||||
if (randomValue < weights[idx])
|
||||
return compatibleIndices[idx];
|
||||
randomValue -= weights[idx];
|
||||
}
|
||||
return compatibleIndices[Random.Range(0, compatibleIndices.Count)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn a new tile at the specified Y position
|
||||
/// </summary>
|
||||
@@ -606,7 +738,32 @@ namespace Minigames.DivingForPictures
|
||||
return;
|
||||
}
|
||||
|
||||
int prefabIndex = GetWeightedRandomTileIndex();
|
||||
int prefabIndex;
|
||||
// First tile: any tile
|
||||
if (_activeTiles.Count == 0)
|
||||
{
|
||||
prefabIndex = GetWeightedRandomTileIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject prevTileGO = _activeTiles[_activeTiles.Count - 1];
|
||||
Tile prevTile = prevTileGO != null ? prevTileGO.GetComponent<Tile>() : null;
|
||||
if (prevTile != null)
|
||||
{
|
||||
// Use weighted compatible selection
|
||||
prefabIndex = GetWeightedCompatibleTileIndex(prevTile);
|
||||
if (prefabIndex == -1)
|
||||
{
|
||||
Debug.LogError("No compatible tile can be spawned after previous tile. Aborting spawn.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prefabIndex = GetWeightedRandomTileIndex();
|
||||
}
|
||||
}
|
||||
|
||||
GameObject prefab = tilePrefabs[prefabIndex];
|
||||
if (prefab == null)
|
||||
{
|
||||
@@ -615,7 +772,6 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
|
||||
GameObject tile;
|
||||
|
||||
if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling && _tilePool != null)
|
||||
{
|
||||
tile = _tilePool.GetTile(prefabIndex);
|
||||
@@ -624,12 +780,9 @@ namespace Minigames.DivingForPictures
|
||||
Debug.LogError("Failed to get tile from pool!");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -638,20 +791,112 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
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);
|
||||
_tileLastUsed[prefabIndex] = _spawnCounter++;
|
||||
_currentDepth++;
|
||||
Debug.Log($"[TrenchTileSpawner] Current Depth: {_currentDepth}");
|
||||
onTileSpawned?.Invoke(tile);
|
||||
|
||||
// --- FLOATING AREA STATE MANAGEMENT ---
|
||||
Tile spawnedTile = tile.GetComponent<Tile>();
|
||||
if (spawnedTile != null)
|
||||
{
|
||||
if (spawnedTile.hasFloatingAreaMiddle || spawnedTile.continuesFloatingAreaMiddle)
|
||||
{
|
||||
isFloatingAreaActive = true;
|
||||
}
|
||||
if (spawnedTile.endsFloatingAreaMiddle)
|
||||
{
|
||||
isFloatingAreaActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn a specific tile at the specified Y position
|
||||
/// </summary>
|
||||
/// <param name="prefab">The tile prefab to spawn</param>
|
||||
/// <param name="y">The Y position to spawn at</param>
|
||||
private void SpawnSpecificTileAtY(GameObject prefab, float y)
|
||||
{
|
||||
GameObject tile;
|
||||
if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling && _tilePool != null)
|
||||
{
|
||||
int prefabIndex = tilePrefabs.IndexOf(prefab);
|
||||
if (prefabIndex >= 0)
|
||||
{
|
||||
tile = _tilePool.GetTile(prefabIndex);
|
||||
if (tile == null)
|
||||
{
|
||||
Debug.LogError("Failed to get initial tile from pool!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tile = Instantiate(prefab, new Vector3(0f, y, TileSpawnZ), prefab.transform.rotation, transform);
|
||||
}
|
||||
tile.transform.position = new Vector3(0f, y, TileSpawnZ);
|
||||
tile.transform.rotation = prefab.transform.rotation;
|
||||
tile.transform.SetParent(transform);
|
||||
if (_devSettings != null)
|
||||
{
|
||||
tile.layer = _devSettings.TrenchTileLayer;
|
||||
SetLayerRecursively(tile, _devSettings.TrenchTileLayer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tile = Instantiate(prefab, new Vector3(0f, y, TileSpawnZ), prefab.transform.rotation, transform);
|
||||
if (_devSettings != null)
|
||||
{
|
||||
tile.layer = _devSettings.TrenchTileLayer;
|
||||
SetLayerRecursively(tile, _devSettings.TrenchTileLayer);
|
||||
}
|
||||
}
|
||||
_activeTiles.Add(tile);
|
||||
_currentDepth++;
|
||||
Debug.Log($"[TrenchTileSpawner] Current Depth: {_currentDepth}");
|
||||
onTileSpawned?.Invoke(tile);
|
||||
// Optionally update floating area state if needed
|
||||
Tile spawnedTile = tile.GetComponent<Tile>();
|
||||
if (spawnedTile != null)
|
||||
{
|
||||
if (spawnedTile.hasFloatingAreaMiddle || spawnedTile.continuesFloatingAreaMiddle)
|
||||
{
|
||||
isFloatingAreaActive = true;
|
||||
}
|
||||
if (spawnedTile.endsFloatingAreaMiddle)
|
||||
{
|
||||
isFloatingAreaActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of allowed tile indices for the current difficulty
|
||||
/// </summary>
|
||||
/// <returns>List of allowed prefab indices</returns>
|
||||
private List<int> GetAllowedTileIndicesForCurrentDifficulty()
|
||||
{
|
||||
var allowedIndices = new List<int>();
|
||||
int currentDifficulty = CurrentDifficulty;
|
||||
for (int i = 0; i < tilePrefabs.Count; i++)
|
||||
{
|
||||
var tileComponent = tilePrefabs[i]?.GetComponent<Tile>();
|
||||
if (tileComponent != null && tileComponent.difficultyLevel <= currentDifficulty)
|
||||
{
|
||||
allowedIndices.Add(i);
|
||||
}
|
||||
}
|
||||
return allowedIndices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -660,14 +905,19 @@ namespace Minigames.DivingForPictures
|
||||
/// <returns>The selected prefab index</returns>
|
||||
private int GetWeightedRandomTileIndex()
|
||||
{
|
||||
int prefabCount = tilePrefabs.Count;
|
||||
List<float> weights = new List<float>(prefabCount);
|
||||
|
||||
for (int i = 0; i < prefabCount; i++)
|
||||
var allowedIndices = GetAllowedTileIndicesForCurrentDifficulty();
|
||||
if (allowedIndices.Count == 0)
|
||||
{
|
||||
int lastUsed = _tileLastUsed.TryGetValue(i, out var value) ? value : -prefabCount;
|
||||
Debug.LogError("No allowed tiles for current difficulty!");
|
||||
return 0;
|
||||
}
|
||||
List<float> weights = new List<float>(allowedIndices.Count);
|
||||
|
||||
for (int i = 0; i < allowedIndices.Count; i++)
|
||||
{
|
||||
int lastUsed = _tileLastUsed.TryGetValue(allowedIndices[i], out var value) ? value : -tilePrefabs.Count;
|
||||
int age = _spawnCounter - lastUsed;
|
||||
float weight = Mathf.Clamp(age, 1, prefabCount * 2); // More unused = higher weight
|
||||
float weight = Mathf.Clamp(age, 1, tilePrefabs.Count * 2); // More unused = higher weight
|
||||
weights.Add(weight);
|
||||
}
|
||||
|
||||
@@ -678,16 +928,16 @@ namespace Minigames.DivingForPictures
|
||||
}
|
||||
|
||||
float randomValue = Random.value * totalWeight;
|
||||
for (int i = 0; i < prefabCount; i++)
|
||||
for (int i = 0; i < allowedIndices.Count; i++)
|
||||
{
|
||||
if (randomValue < weights[i])
|
||||
{
|
||||
return i;
|
||||
return allowedIndices[i];
|
||||
}
|
||||
randomValue -= weights[i];
|
||||
}
|
||||
|
||||
return Random.Range(0, prefabCount); // fallback
|
||||
return Random.Range(0, allowedIndices.Count); // fallback
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1005,13 +1255,13 @@ namespace Minigames.DivingForPictures
|
||||
{
|
||||
StopCoroutine(_speedRampingCoroutine);
|
||||
}
|
||||
_speedRampingCoroutine = StartCoroutine(SpeedRampingCoroutine());
|
||||
_speedRampingCoroutine = StartCoroutine(SpeedRampingRoutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles increasing the movement speed over time
|
||||
/// </summary>
|
||||
private IEnumerator SpeedRampingCoroutine()
|
||||
private IEnumerator SpeedRampingRoutine()
|
||||
{
|
||||
const float checkInterval = 1.0f; // Check once per second as requested
|
||||
Debug.Log($"[TrenchTileSpawner] Started speed ramping coroutine with interval: {checkInterval}s");
|
||||
|
||||
Reference in New Issue
Block a user