MiniGames/Assets/Scripts/CubeClash/CubeClash_BirdSwoop.cs

299 lines
9.4 KiB
C#
Raw Normal View History

2025-09-01 05:03:00 +05:00
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
2025-09-01 05:03:00 +05:00
[RequireComponent(typeof(Collider))]
2025-09-01 05:03:00 +05:00
public class CubeClash_BirdSwoop : MonoBehaviour
{
[Header("Targeting")]
public bool useStaticList = true;
public string zibuTag = "Zibu";
public bool autoDestroyIfNoTarget = true;
[Header("Bird Movement (Seek)")]
public float birdMoveSpeed = 8f;
public float birdTurnSpeed = 10f;
public float pickupRadius = 1.2f;
public float approachHeightOffset = 2f;
public float followAboveOffset = 1.0f;
public bool faceVelocity = true;
[Header("Lift Profile")]
2025-09-01 05:03:00 +05:00
public float carryHeight = 5f;
public float riseTime = 0.8f;
public float hoverTime = 1.0f;
public float dropKickImpulse = 0f;
[Header("Force Tuning (Physics)")]
public float posGain = 12f;
public float velGain = 2.5f;
public float maxDeltaVPerFixedUpdate = 12f;
[Header("While Carried")]
public bool disableGravityWhileCarried = true;
public float extraDrag = 2f;
public float extraStun = 0.1f;
[Header("Lifecycle")]
public bool destroyOnRelease = true;
private enum SwoopState { Seeking, Carrying, Releasing, Idle }
private SwoopState _state = SwoopState.Idle;
private Rigidbody _targetRb;
private CubeClash_ZibuController _targetController;
// Bird<72>s own body (optional)
private Rigidbody _birdRb; // if present, we<77>ll MovePosition on it
private bool _weMadeKinematic = false;
// Carry bookkeeping
private readonly HashSet<Rigidbody> _carried = new HashSet<Rigidbody>();
private readonly Dictionary<Rigidbody, float> _origDrag = new Dictionary<Rigidbody, float>();
private readonly Dictionary<Rigidbody, bool> _origGrav = new Dictionary<Rigidbody, bool>();
void Reset()
{
var col = GetComponent<Collider>();
if (col) col.isTrigger = true;
}
void Awake()
{
_birdRb = GetComponent<Rigidbody>();
if (_birdRb != null)
{
// Ensure physics doesn<73>t fight our manual movement
if (!_birdRb.isKinematic)
{
_birdRb.isKinematic = true;
_weMadeKinematic = true;
}
_birdRb.interpolation = RigidbodyInterpolation.Interpolate;
}
}
void OnEnable()
{
PickRandomZibuAndSeek();
}
private void PickRandomZibuAndSeek()
{
_targetRb = PickRandomZibuRigidbody();
if (_targetRb == null)
{
if (autoDestroyIfNoTarget) Destroy(gameObject);
return;
}
_targetController = _targetRb.GetComponent<CubeClash_ZibuController>();
_state = SwoopState.Seeking;
}
2025-09-01 05:03:00 +05:00
private Rigidbody PickRandomZibuRigidbody()
2025-09-01 05:03:00 +05:00
{
List<Rigidbody> candidates = new List<Rigidbody>();
if (useStaticList && CubeClash_ZibuController.AllZibus != null && CubeClash_ZibuController.AllZibus.Count > 0)
{
foreach (var z in CubeClash_ZibuController.AllZibus)
{
if (!z || !z.gameObject.activeInHierarchy) continue;
var rb = z.GetComponent<Rigidbody>();
if (rb) candidates.Add(rb);
}
}
else
2025-09-01 05:03:00 +05:00
{
foreach (var go in GameObject.FindGameObjectsWithTag(zibuTag))
{
if (!go || !go.activeInHierarchy) continue;
var rb = go.GetComponent<Rigidbody>();
if (rb) candidates.Add(rb);
}
2025-09-01 05:03:00 +05:00
}
if (candidates.Count == 0) return null;
return candidates[Random.Range(0, candidates.Count)];
2025-09-01 05:03:00 +05:00
}
void FixedUpdate()
2025-09-01 05:03:00 +05:00
{
if (_targetRb == null) return;
switch (_state)
{
case SwoopState.Seeking:
SeekStep();
break;
case SwoopState.Carrying:
EscortFollowStep();
break;
}
}
// --- Movement helpers that work with or without a Rigidbody on the bird ---
private void MoveBirdTo(Vector3 newPos)
{
if (_birdRb != null) _birdRb.MovePosition(newPos);
else transform.position = newPos;
}
private void RotateBirdTo(Quaternion newRot, float slerpFactor)
{
if (faceVelocity)
{
var current = (_birdRb != null) ? _birdRb.rotation : transform.rotation;
var next = Quaternion.Slerp(current, newRot, slerpFactor);
if (_birdRb != null) _birdRb.MoveRotation(next);
else transform.rotation = next;
}
}
private void SeekStep()
{
Vector3 aim = _targetRb.position + Vector3.up * approachHeightOffset;
Vector3 to = aim - ((_birdRb != null) ? _birdRb.position : transform.position);
float dist = to.magnitude;
if (dist > 0.001f)
{
Vector3 dir = to / dist;
Vector3 step = dir * birdMoveSpeed * Time.fixedDeltaTime;
if (step.magnitude > dist) step = to;
MoveBirdTo(((_birdRb != null) ? _birdRb.position : transform.position) + step);
if (faceVelocity && step.sqrMagnitude > 1e-6f)
{
Quaternion targetRot = Quaternion.LookRotation(step.normalized, Vector3.up);
RotateBirdTo(targetRot, birdTurnSpeed * Time.fixedDeltaTime);
}
}
if (Vector3.Distance(((_birdRb != null) ? _birdRb.position : transform.position), _targetRb.position) <= pickupRadius)
{
StartCoroutine(CarryRoutine(_targetRb));
_state = SwoopState.Carrying;
}
}
private void EscortFollowStep()
{
if (_targetRb == null) return;
Vector3 follow = _targetRb.position + Vector3.up * followAboveOffset;
Vector3 to = follow - ((_birdRb != null) ? _birdRb.position : transform.position);
float dist = to.magnitude;
if (dist > 0.001f)
{
Vector3 dir = to / dist;
Vector3 step = dir * birdMoveSpeed * Time.fixedDeltaTime;
if (step.magnitude > dist) step = to;
MoveBirdTo(((_birdRb != null) ? _birdRb.position : transform.position) + step);
if (faceVelocity && step.sqrMagnitude > 1e-6f)
{
Quaternion targetRot = Quaternion.LookRotation(step.normalized, Vector3.up);
RotateBirdTo(targetRot, birdTurnSpeed * Time.fixedDeltaTime);
}
}
}
private IEnumerator CarryRoutine(Rigidbody rb)
{
_carried.Add(rb);
float plannedStun = Mathf.Max(0.05f, riseTime + hoverTime + extraStun);
if (_targetController) _targetController.ApplyPushStun(plannedStun);
if (!_origDrag.ContainsKey(rb)) _origDrag[rb] = rb.drag;
if (!_origGrav.ContainsKey(rb)) _origGrav[rb] = rb.useGravity;
if (disableGravityWhileCarried) rb.useGravity = false;
rb.drag = _origDrag[rb] + Mathf.Max(0f, extraDrag);
Vector3 startPos = rb.position;
float targetY = startPos.y + carryHeight;
// Rise
2025-09-01 05:03:00 +05:00
float t = 0f;
while (t < riseTime)
2025-09-01 05:03:00 +05:00
{
t += Time.fixedDeltaTime;
float a = Mathf.Clamp01(t / Mathf.Max(0.0001f, riseTime));
Vector3 targetPos = new Vector3(rb.position.x, Mathf.Lerp(startPos.y, targetY, a), rb.position.z);
ApplyPDStep(rb, targetPos);
yield return new WaitForFixedUpdate();
2025-09-01 05:03:00 +05:00
}
// Hover
float h = 0f;
while (h < hoverTime)
{
h += Time.fixedDeltaTime;
Vector3 targetPos = new Vector3(rb.position.x, targetY, rb.position.z);
ApplyPDStep(rb, targetPos);
yield return new WaitForFixedUpdate();
}
// Release
if (_origGrav.TryGetValue(rb, out bool g)) rb.useGravity = g;
if (_origDrag.TryGetValue(rb, out float d)) rb.drag = d;
if (dropKickImpulse > 0f)
rb.AddForce(Vector3.down * dropKickImpulse, ForceMode.Impulse);
_carried.Remove(rb);
_origDrag.Remove(rb);
_origGrav.Remove(rb);
_state = SwoopState.Releasing;
_targetRb = null;
_targetController = null;
if (destroyOnRelease) Destroy(gameObject);
else _state = SwoopState.Idle;
}
private void ApplyPDStep(Rigidbody rb, Vector3 targetPos)
{
Vector3 to = targetPos - rb.position;
Vector3 toXZ = new Vector3(to.x, 0f, to.z);
Vector3 toY = new Vector3(0f, to.y, 0f);
Vector3 dvXZ = posGain * toXZ - velGain * new Vector3(rb.velocity.x, 0f, rb.velocity.z);
Vector3 dvY = posGain * toY - velGain * new Vector3(0f, rb.velocity.y, 0f);
Vector3 deltaV = (dvXZ + dvY) * Time.fixedDeltaTime;
if (maxDeltaVPerFixedUpdate > 0f && deltaV.magnitude > maxDeltaVPerFixedUpdate)
deltaV = deltaV.normalized * maxDeltaVPerFixedUpdate;
rb.AddForce(deltaV, ForceMode.VelocityChange);
}
private void OnDisable()
{
foreach (var rb in _carried)
{
if (!rb) continue;
if (_origGrav.TryGetValue(rb, out bool g)) rb.useGravity = g;
if (_origDrag.TryGetValue(rb, out float d)) rb.drag = d;
}
_carried.Clear();
_origDrag.Clear();
_origGrav.Clear();
}
// Debug gizmo to see pickup radius in Scene view
void OnDrawGizmosSelected()
{
Gizmos.color = Color.cyan;
Vector3 pos = (_birdRb != null) ? _birdRb.position : transform.position;
Gizmos.DrawWireSphere(pos, pickupRadius);
2025-09-01 05:03:00 +05:00
}
}