TO SQUASH: Fucking magnets, how do they work?

This commit is contained in:
2026-08-05 05:36:38 -04:00
parent a9db378127
commit 46198d6c44
10 changed files with 472 additions and 32 deletions
+149
View File
@@ -16,6 +16,20 @@ public class BallBase : MonoBehaviour
protected bool affectedByMagnet = true;
protected bool magnetActiveLeftRight = false;
protected bool magnetActiveTopBottom = false;
protected struct MagnetBounceAxisState
{
public bool magnetWasActive;
public bool bouncingToAnchor;
public float anchor;
public float target;
public float bounceTimer;
public int sideDirection;
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.CompareTag("Bar"))
@@ -42,4 +56,139 @@ public class BallBase : MonoBehaviour
}
}
}
public void MagnetActive(bool flipped)
{
if (flipped)
{
magnetActiveTopBottom = true;
}
else
{
magnetActiveLeftRight = true;
}
}
public void MagnetInactive()
{
magnetActiveTopBottom = false;
magnetActiveLeftRight = false;
}
protected float ApplyMagnetBounceAxis(
float currentValue,
float minBound,
float maxBound,
bool magnetIsActive,
ref MagnetBounceAxisState state,
float colliderExtent,
float bounceDuration,
float centerLeeway)
{
if (!magnetIsActive)
{
state.magnetWasActive = false;
state.sideDirection = 0;
state.bounceTimer = 0.0f;
return currentValue;
}
float center = (minBound + maxBound) * 0.5f;
float halfSpan = (maxBound - minBound) * 0.5f;
float maxLeeway = Mathf.Max(0.0f, halfSpan - colliderExtent);
float safeLeeway = Mathf.Clamp(centerLeeway, 0.0f, maxLeeway);
float leftAnchorMax = center - safeLeeway;
float rightAnchorMin = center + safeLeeway;
if (!state.magnetWasActive)
{
state.magnetWasActive = true;
state.anchor = currentValue;
state.sideDirection = state.anchor <= center ? -1 : 1;
state.target = state.sideDirection < 0 ? minBound + colliderExtent : maxBound - colliderExtent;
state.bouncingToAnchor = false;
state.bounceTimer = 0.0f;
}
// Keep the selected side locked while magnet is active.
if (state.sideDirection < 0)
{
state.anchor = Mathf.Min(state.anchor, leftAnchorMax);
}
else
{
state.anchor = Mathf.Max(state.anchor, rightAnchorMin);
}
state.bounceTimer += Time.fixedDeltaTime;
float t = Mathf.Clamp01(state.bounceTimer / bounceDuration);
if (state.bouncingToAnchor)
{
// Mirror NukeBall ascent: ease-out from target back to anchor.
float easedT = 1.0f - Mathf.Pow(1.0f - t, 2.0f);
currentValue = Mathf.Lerp(state.target, state.anchor, easedT);
if (t >= 1.0f)
{
state.bouncingToAnchor = false;
state.bounceTimer = 0.0f;
currentValue = state.anchor;
}
}
else
{
// Mirror NukeBall descent: ease-in from anchor down to side target.
float easedT = t * t;
currentValue = Mathf.Lerp(state.anchor, state.target, easedT);
if (t >= 1.0f)
{
state.bouncingToAnchor = true;
state.bounceTimer = 0.0f;
currentValue = state.target;
}
}
return currentValue;
}
protected bool TryCacheGameBoardBounds(out Bounds bounds)
{
var gameBoard = FindAnyObjectByType<GameBoard>();
if (gameBoard != null)
{
return TryGetWorldBounds(gameBoard.gameObject, out bounds);
}
bounds = default;
return false;
}
protected static bool TryGetWorldBounds(GameObject target, out Bounds bounds)
{
Collider2D collider2D = target.GetComponent<Collider2D>();
if (collider2D != null)
{
bounds = collider2D.bounds;
return true;
}
Renderer renderer = target.GetComponent<Renderer>();
if (renderer != null)
{
bounds = renderer.bounds;
return true;
}
Collider collider3D = target.GetComponent<Collider>();
if (collider3D != null)
{
bounds = collider3D.bounds;
return true;
}
bounds = default;
return false;
}
}