using UnityEngine; using System.Collections; using System.Collections.Generic; [RequireComponent(typeof(Collider))] 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")] 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’s own body (optional) private Rigidbody _birdRb; // if present, we’ll MovePosition on it private bool _weMadeKinematic = false; // Carry bookkeeping private readonly HashSet _carried = new HashSet(); private readonly Dictionary _origDrag = new Dictionary(); private readonly Dictionary _origGrav = new Dictionary(); void Reset() { var col = GetComponent(); if (col) col.isTrigger = true; } void Awake() { _birdRb = GetComponent(); if (_birdRb != null) { // Ensure physics doesn’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(); _state = SwoopState.Seeking; } private Rigidbody PickRandomZibuRigidbody() { List candidates = new List(); 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(); if (rb) candidates.Add(rb); } } else { foreach (var go in GameObject.FindGameObjectsWithTag(zibuTag)) { if (!go || !go.activeInHierarchy) continue; var rb = go.GetComponent(); if (rb) candidates.Add(rb); } } if (candidates.Count == 0) return null; return candidates[Random.Range(0, candidates.Count)]; } void FixedUpdate() { 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 float t = 0f; while (t < riseTime) { 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(); } // 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); } }