TO SQUASH: Jesus!!
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class WorldFiller : MonoBehaviour
|
||||
{
|
||||
@@ -8,16 +9,27 @@ public class WorldFiller : MonoBehaviour
|
||||
|
||||
public GameObject wallPrefab;
|
||||
|
||||
[SerializeField]
|
||||
private float fallbackBarThicknessWorldUnits = 0.08f;
|
||||
|
||||
[SerializeField]
|
||||
private float geometryEpsilon = 0.0001f;
|
||||
|
||||
private Bounds gameBoardBounds;
|
||||
|
||||
private Vector2?[] wallPositions;
|
||||
|
||||
private int wallCount = 0;
|
||||
|
||||
private List<Rect> freeAreas = new List<Rect>();
|
||||
|
||||
private float boardArea;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
gameBoardBounds = gameBoardSpriteRenderer.bounds;
|
||||
InitializeFreeAreas();
|
||||
ClearWalls();
|
||||
}
|
||||
|
||||
@@ -50,15 +62,159 @@ public class WorldFiller : MonoBehaviour
|
||||
{
|
||||
Debug.Log($"Spawning wall with wall line: ({wallPositions[0]}, {wallPositions[1]})");
|
||||
|
||||
Vector2 wallPointA = wallPositions[0].Value;
|
||||
Vector2 wallPointB = wallPositions[1].Value;
|
||||
|
||||
if (Vector2.Distance(wallPointA, wallPointB) <= geometryEpsilon)
|
||||
{
|
||||
Debug.LogWarning("Wall points are too close. Skipping wall spawn.");
|
||||
ClearWalls();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all the balls in the scene.
|
||||
var ballSpawner = FindAnyObjectByType<BallSpawner>();
|
||||
var balls = ballSpawner.GetBalls();
|
||||
var balls = ballSpawner != null ? ballSpawner.GetBalls() : null;
|
||||
|
||||
// There are three possible scenarios for the wall positions:
|
||||
// 1. Only one side of the wall line is empty (either left or right).
|
||||
// 2. All sides of the wall line are occupied.
|
||||
// 3. All sides of the wall line are empty.
|
||||
bool isVerticalLine = Mathf.Abs(wallPointA.x - wallPointB.x) <= Mathf.Abs(wallPointA.y - wallPointB.y);
|
||||
float lineCenter = isVerticalLine ? (wallPointA.x + wallPointB.x) * 0.5f : (wallPointA.y + wallPointB.y) * 0.5f;
|
||||
float lineMin = isVerticalLine ? Mathf.Min(wallPointA.y, wallPointB.y) : Mathf.Min(wallPointA.x, wallPointB.x);
|
||||
float lineMax = isVerticalLine ? Mathf.Max(wallPointA.y, wallPointB.y) : Mathf.Max(wallPointA.x, wallPointB.x);
|
||||
|
||||
float barThickness = GetCurrentBarThickness(isVerticalLine);
|
||||
float halfBarThickness = barThickness * 0.5f;
|
||||
|
||||
Rect activeArea = FindActiveArea((wallPointA + wallPointB) * 0.5f, isVerticalLine, lineCenter, lineMin, lineMax);
|
||||
DetermineSideOccupancy(
|
||||
balls,
|
||||
activeArea,
|
||||
isVerticalLine,
|
||||
lineCenter,
|
||||
halfBarThickness,
|
||||
out bool sideAHasBall,
|
||||
out bool sideBHasBall
|
||||
);
|
||||
|
||||
Rect targetWallRect;
|
||||
|
||||
// Spawn scenarios:
|
||||
// 1) Both sides empty => fill the whole available region.
|
||||
// 2) Only one side empty => fill that free side plus the bar strip.
|
||||
// 3) Both sides occupied => spawn only along the wall line, with bar thickness.
|
||||
if (!sideAHasBall && !sideBHasBall)
|
||||
{
|
||||
targetWallRect = activeArea;
|
||||
}
|
||||
else if (sideAHasBall && sideBHasBall)
|
||||
{
|
||||
if (isVerticalLine)
|
||||
{
|
||||
targetWallRect = Rect.MinMaxRect(
|
||||
lineCenter - halfBarThickness,
|
||||
lineMin,
|
||||
lineCenter + halfBarThickness,
|
||||
lineMax
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetWallRect = Rect.MinMaxRect(
|
||||
lineMin,
|
||||
lineCenter - halfBarThickness,
|
||||
lineMax,
|
||||
lineCenter + halfBarThickness
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isVerticalLine)
|
||||
{
|
||||
// Side A is left, side B is right for vertical lines.
|
||||
targetWallRect = !sideAHasBall
|
||||
? Rect.MinMaxRect(activeArea.xMin, activeArea.yMin, lineCenter + halfBarThickness, activeArea.yMax)
|
||||
: Rect.MinMaxRect(lineCenter - halfBarThickness, activeArea.yMin, activeArea.xMax, activeArea.yMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Side A is bottom, side B is top for horizontal lines.
|
||||
targetWallRect = !sideAHasBall
|
||||
? Rect.MinMaxRect(activeArea.xMin, activeArea.yMin, activeArea.xMax, lineCenter + halfBarThickness)
|
||||
: Rect.MinMaxRect(activeArea.xMin, lineCenter - halfBarThickness, activeArea.xMax, activeArea.yMax);
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryGetRectIntersection(targetWallRect, activeArea, out Rect clampedWallRect))
|
||||
{
|
||||
Debug.LogWarning("Could not create a valid wall rectangle inside the active free area.");
|
||||
ClearWalls();
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject wall = Instantiate(wallPrefab, Vector3.zero, Quaternion.identity);
|
||||
var wallRenderer = wall.GetComponentInChildren<SpriteRenderer>();
|
||||
|
||||
if (wallRenderer == null)
|
||||
{
|
||||
Debug.LogError("Wall prefab is missing a SpriteRenderer in children.");
|
||||
Destroy(wall);
|
||||
ClearWalls();
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyWallTransform(wall.transform, wallRenderer, clampedWallRect);
|
||||
var wallBounds = wallRenderer.bounds;
|
||||
|
||||
UpdateFreeAreas(clampedWallRect);
|
||||
|
||||
// Check if there are yummies, multipliers or the shark intersecting the spawned wall.
|
||||
// Yummies and multipliers are awarded to the player if they are intersected by the wall.
|
||||
// The shark is killed if it is intersected by the wall.
|
||||
var yummies = GameObject.FindGameObjectsWithTag("Yummy");
|
||||
var multipliers = GameObject.FindGameObjectsWithTag("Multiplier");
|
||||
var shark = GameObject.FindGameObjectWithTag("Shark");
|
||||
|
||||
foreach (var yummy in yummies)
|
||||
{
|
||||
if (yummy != null && wallBounds.Intersects(yummy.GetComponent<SpriteRenderer>().bounds))
|
||||
{
|
||||
// Award the yummy to the player.
|
||||
YummyBase yummyBase = yummy.GetComponent<YummyBase>();
|
||||
|
||||
if (yummyBase != null)
|
||||
{
|
||||
yummyBase.AwardToPlayer(player);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("YummyBase component not found on yummy object.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var multiplier in multipliers)
|
||||
{
|
||||
if (multiplier != null && wallBounds.Intersects(multiplier.GetComponent<SpriteRenderer>().bounds))
|
||||
{
|
||||
// Award the multiplier to the player.
|
||||
Multiplier multiplierComponent = multiplier.GetComponent<Multiplier>();
|
||||
|
||||
if (multiplierComponent != null)
|
||||
{
|
||||
multiplierComponent.AwardToPlayer(player);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Multiplier component not found on multiplier object.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shark != null && wallBounds.Intersects(shark.GetComponent<SpriteRenderer>().bounds))
|
||||
{
|
||||
// Kill the shark.
|
||||
shark.GetComponent<Shark>().KillShark();
|
||||
}
|
||||
|
||||
// Destroy all bar segments after we are done.
|
||||
GameObject[] barSegments = GameObject.FindGameObjectsWithTag("Bar");
|
||||
@@ -68,8 +224,271 @@ public class WorldFiller : MonoBehaviour
|
||||
Destroy(barSegment);
|
||||
}
|
||||
|
||||
// Let the player know that the wall has been spawned.
|
||||
int areaPercentage = boardArea > geometryEpsilon
|
||||
? Mathf.RoundToInt((clampedWallRect.width * clampedWallRect.height / boardArea) * 100.0f)
|
||||
: 0;
|
||||
player.OnWallSpawned(areaPercentage);
|
||||
|
||||
// Clear the pending wall line.
|
||||
ClearWalls();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFreeAreas()
|
||||
{
|
||||
gameBoardBounds = gameBoardSpriteRenderer.bounds;
|
||||
|
||||
Rect boardRect = Rect.MinMaxRect(
|
||||
gameBoardBounds.min.x,
|
||||
gameBoardBounds.min.y,
|
||||
gameBoardBounds.max.x,
|
||||
gameBoardBounds.max.y
|
||||
);
|
||||
|
||||
freeAreas.Clear();
|
||||
freeAreas.Add(boardRect);
|
||||
boardArea = boardRect.width * boardRect.height;
|
||||
}
|
||||
|
||||
private float GetCurrentBarThickness(bool isVerticalLine)
|
||||
{
|
||||
var barSegments = GameObject.FindGameObjectsWithTag("Bar");
|
||||
|
||||
foreach (var barSegment in barSegments)
|
||||
{
|
||||
if (barSegment == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var spriteRenderer = barSegment.GetComponent<SpriteRenderer>();
|
||||
if (spriteRenderer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float thickness = isVerticalLine
|
||||
? spriteRenderer.bounds.size.x
|
||||
: spriteRenderer.bounds.size.y;
|
||||
|
||||
if (thickness > geometryEpsilon)
|
||||
{
|
||||
return thickness;
|
||||
}
|
||||
}
|
||||
|
||||
return fallbackBarThicknessWorldUnits;
|
||||
}
|
||||
|
||||
private Rect FindActiveArea(
|
||||
Vector2 lineMidpoint,
|
||||
bool isVerticalLine,
|
||||
float lineCenter,
|
||||
float lineMin,
|
||||
float lineMax)
|
||||
{
|
||||
if (freeAreas.Count == 0)
|
||||
{
|
||||
InitializeFreeAreas();
|
||||
}
|
||||
|
||||
for (int i = 0; i < freeAreas.Count; i++)
|
||||
{
|
||||
if (RectContainsWithTolerance(freeAreas[i], lineMidpoint))
|
||||
{
|
||||
return freeAreas[i];
|
||||
}
|
||||
}
|
||||
|
||||
Rect lineRect = isVerticalLine
|
||||
? Rect.MinMaxRect(lineCenter - geometryEpsilon, lineMin, lineCenter + geometryEpsilon, lineMax)
|
||||
: Rect.MinMaxRect(lineMin, lineCenter - geometryEpsilon, lineMax, lineCenter + geometryEpsilon);
|
||||
|
||||
float bestOverlap = -1.0f;
|
||||
Rect fallback = freeAreas[0];
|
||||
|
||||
for (int i = 0; i < freeAreas.Count; i++)
|
||||
{
|
||||
if (!TryGetRectIntersection(freeAreas[i], lineRect, out Rect overlap))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float overlapArea = overlap.width * overlap.height;
|
||||
if (overlapArea > bestOverlap)
|
||||
{
|
||||
bestOverlap = overlapArea;
|
||||
fallback = freeAreas[i];
|
||||
}
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private void DetermineSideOccupancy(
|
||||
Queue<GameObject> balls,
|
||||
Rect activeArea,
|
||||
bool isVerticalLine,
|
||||
float lineCenter,
|
||||
float halfBarThickness,
|
||||
out bool sideAHasBall,
|
||||
out bool sideBHasBall)
|
||||
{
|
||||
sideAHasBall = false;
|
||||
sideBHasBall = false;
|
||||
|
||||
if (balls == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var ball in balls)
|
||||
{
|
||||
if (ball == null || !ball.activeInHierarchy)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 ballPosition = ball.transform.position;
|
||||
|
||||
if (!RectContainsWithTolerance(activeArea, ballPosition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isVerticalLine)
|
||||
{
|
||||
if (ballPosition.x < lineCenter - halfBarThickness)
|
||||
{
|
||||
sideAHasBall = true;
|
||||
}
|
||||
else if (ballPosition.x > lineCenter + halfBarThickness)
|
||||
{
|
||||
sideBHasBall = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ballPosition.x <= lineCenter)
|
||||
{
|
||||
sideAHasBall = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sideBHasBall = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ballPosition.y < lineCenter - halfBarThickness)
|
||||
{
|
||||
sideAHasBall = true;
|
||||
}
|
||||
else if (ballPosition.y > lineCenter + halfBarThickness)
|
||||
{
|
||||
sideBHasBall = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ballPosition.y <= lineCenter)
|
||||
{
|
||||
sideAHasBall = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sideBHasBall = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sideAHasBall && sideBHasBall)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyWallTransform(Transform wallTransform, SpriteRenderer wallRenderer, Rect targetRect)
|
||||
{
|
||||
Vector2 baseSize = wallRenderer.bounds.size;
|
||||
|
||||
if (baseSize.x <= geometryEpsilon || baseSize.y <= geometryEpsilon)
|
||||
{
|
||||
Debug.LogError("Cannot scale wall: base sprite size is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 currentScale = wallTransform.localScale;
|
||||
float scaleX = currentScale.x * (targetRect.width / baseSize.x);
|
||||
float scaleY = currentScale.y * (targetRect.height / baseSize.y);
|
||||
|
||||
wallTransform.localScale = new Vector3(scaleX, scaleY, currentScale.z);
|
||||
wallTransform.position = new Vector3(targetRect.center.x, targetRect.center.y, wallTransform.position.z);
|
||||
}
|
||||
|
||||
private void UpdateFreeAreas(Rect occupiedArea)
|
||||
{
|
||||
List<Rect> updatedAreas = new List<Rect>();
|
||||
|
||||
for (int i = 0; i < freeAreas.Count; i++)
|
||||
{
|
||||
Rect freeArea = freeAreas[i];
|
||||
|
||||
if (!TryGetRectIntersection(freeArea, occupiedArea, out Rect overlap))
|
||||
{
|
||||
updatedAreas.Add(freeArea);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Left remainder.
|
||||
AddValidRect(updatedAreas, Rect.MinMaxRect(freeArea.xMin, freeArea.yMin, overlap.xMin, freeArea.yMax));
|
||||
|
||||
// Right remainder.
|
||||
AddValidRect(updatedAreas, Rect.MinMaxRect(overlap.xMax, freeArea.yMin, freeArea.xMax, freeArea.yMax));
|
||||
|
||||
// Bottom remainder.
|
||||
AddValidRect(updatedAreas, Rect.MinMaxRect(overlap.xMin, freeArea.yMin, overlap.xMax, overlap.yMin));
|
||||
|
||||
// Top remainder.
|
||||
AddValidRect(updatedAreas, Rect.MinMaxRect(overlap.xMin, overlap.yMax, overlap.xMax, freeArea.yMax));
|
||||
}
|
||||
|
||||
freeAreas = updatedAreas;
|
||||
}
|
||||
|
||||
private void AddValidRect(List<Rect> target, Rect candidate)
|
||||
{
|
||||
if (candidate.width <= geometryEpsilon || candidate.height <= geometryEpsilon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
target.Add(candidate);
|
||||
}
|
||||
|
||||
private bool TryGetRectIntersection(Rect a, Rect b, out Rect intersection)
|
||||
{
|
||||
float xMin = Mathf.Max(a.xMin, b.xMin);
|
||||
float xMax = Mathf.Min(a.xMax, b.xMax);
|
||||
float yMin = Mathf.Max(a.yMin, b.yMin);
|
||||
float yMax = Mathf.Min(a.yMax, b.yMax);
|
||||
|
||||
if (xMax - xMin <= geometryEpsilon || yMax - yMin <= geometryEpsilon)
|
||||
{
|
||||
intersection = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
intersection = Rect.MinMaxRect(xMin, yMin, xMax, yMax);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool RectContainsWithTolerance(Rect rect, Vector2 point)
|
||||
{
|
||||
return point.x >= rect.xMin - geometryEpsilon
|
||||
&& point.x <= rect.xMax + geometryEpsilon
|
||||
&& point.y >= rect.yMin - geometryEpsilon
|
||||
&& point.y <= rect.yMax + geometryEpsilon;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user