MiniGames/Assets/Scripts/CubeClash/CubeClash_BirdSwoop.cs

299 lines
9.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
// Birds own body (optional)
private Rigidbody _birdRb; // if present, well 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 doesnt 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;
}
private Rigidbody PickRandomZibuRigidbody()
{
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
{
foreach (var go in GameObject.FindGameObjectsWithTag(zibuTag))
{
if (!go || !go.activeInHierarchy) continue;
var rb = go.GetComponent<Rigidbody>();
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);
}
}