using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; namespace DuckHunter { public class Duck : MonoBehaviour { public static List DuckList = new List(); RectTransform _arrowRectTransform; RectTransform _parentRectTransform; RectTransform _rectTransform; DragonBones.UnityArmatureComponent _armature; [NonSerialized] public bool dead = false; float _direction = 1; public Action onExit; public Action onHitDead; public Action onFallDonwStart; public Action onFallDonwEnd; public DuckConfig config; void Start() { DuckList.Add(this); //获取基本组件 _arrowRectTransform = transform.Find("Arrow") as RectTransform; _parentRectTransform = transform.parent as RectTransform; _rectTransform = transform as RectTransform; //初始化皮肤 int useSkinIndex = config.type - 1; for (int i = 0; i < _rectTransform.childCount; i++) { GameObject skin = _rectTransform.GetChild(i).gameObject; bool needActive = i == useSkinIndex; skin.SetActive(needActive); //获取龙骨动画组件 if (needActive) _armature = skin.GetComponent(); } //初始化角度 _flyAngle = config.flyAngle; //初始化速度 _flySpeed = config.flySpeed; //初始化坐标 _rectTransform.anchoredPosition = Vector2.right * config.positionX; //初始化动作 StartActionCoroutine(Fly()); } void OnDestroy() { DuckList.Remove(this); } void Update() { } Sequence _sequenceRotateForever; string _lastPlayAnimationName; void PlayAnimation(string animationName = null) { if (animationName == _lastPlayAnimationName) return; _lastPlayAnimationName = animationName; _armature.animation.Play(animationName, 0); //不是射箭状态不处理箭 if (GlobalData.MyDeviceMode != DeviceMode.Archery) return; if (_sequenceRotateForever != null) { _sequenceRotateForever.Kill(); _sequenceRotateForever = null; } if (animationName == "03") { _arrowRectTransform.gameObject.SetActive(true); _arrowRectTransform.localPosition = new Vector3(-20, -55); _arrowRectTransform.localEulerAngles = new Vector3(0, 0, 145); } else if (animationName == "04") { _arrowRectTransform.gameObject.SetActive(true); _arrowRectTransform.localPosition = new Vector3(0, 35); _arrowRectTransform.localEulerAngles = new Vector3(0, 0, 0); _sequenceRotateForever = DOTween.Sequence(); _sequenceRotateForever.Append(_arrowRectTransform.DOLocalRotate(Vector3.up * 180, 0.2f)); _sequenceRotateForever.Append(_arrowRectTransform.DOLocalRotate(Vector3.up * 360, 0.2f)); _sequenceRotateForever.SetLoops(-1); } else if (animationName == "05") { _arrowRectTransform.gameObject.SetActive(true); _arrowRectTransform.localPosition = new Vector3(34, -77); _arrowRectTransform.localEulerAngles = new Vector3(0, 0, 232); } else { _arrowRectTransform.gameObject.SetActive(false); }; } static string Event_HitDead_Finish = "Event_HitDead_Finish"; static string Event_FallDown_Finish = "Event_FallDown_Finish"; static string Event_FlyTouchBound = "Event_FlyTouchBound"; static string Event_FlyAway = "Event_FlyAway"; void OnEvent(string eventName) { if (eventName == Event_HitDead_Finish) { StartActionCoroutine(FallDown()); } else if (eventName == Event_FallDown_Finish) { onExit?.Invoke(this); } else if (eventName == Event_FlyTouchBound) { if (_touchBoundCount >= config.touchBoundCount) { StartActionCoroutine(FlyAway()); } else { _flyAngle = CalculateTurnbackAngle(); StartActionCoroutine(Fly()); } } else if (eventName == Event_FlyAway) { Destroy(gameObject); onExit?.Invoke(this); } } Coroutine _actionCoroutine = null; void StartActionCoroutine(IEnumerator routine) { StopActionCoroutine(); _actionCoroutine = StartCoroutine(routine); } void StopActionCoroutine() { if (_actionCoroutine != null) { StopCoroutine(_actionCoroutine); _actionCoroutine = null; } } float _flyAngle = 60; float _flySpeed = 60; Vector2 _flyVelocity; /// /// 计算飞行速度 /// Vector2 CalculateFlyVelocity() { return new Vector2(Mathf.Cos(_flyAngle * Mathf.Deg2Rad), Mathf.Sin(_flyAngle * Mathf.Deg2Rad)) * _flySpeed; } /// /// 计算折返后的角度 /// float CalculateTurnbackAngle() { Vector2 dc = _BoundChangeVelocityDirections[_touchBoundType]; Vector3 v = _flyVelocity; Vector3 v_dc = v * dc; Vector3 c = Vector3.Cross(v, v_dc); return _flyAngle + 180 + (c.z > 0 ? -1 : 1) * 90; } /// /// 飞行动作表现 /// IEnumerator Fly() { _flyVelocity = CalculateFlyVelocity(); if (_flyVelocity.y > 0) _armature.animation.Play("01", 0); else _armature.animation.Play("02", 0); Vector2 ap = _rectTransform.anchoredPosition; UpdateTouchBoundType(); int firstTouchBoundType = _touchBoundType; float t = 0; while (_touchBoundType == firstTouchBoundType || _touchBoundType == 0) { t += Time.deltaTime; _rectTransform.anchoredPosition = ap + _flyVelocity * t; _direction = _flyVelocity.x > 0 ? 1 : -1; UpdateAnglesByDirection(); UpdateTouchBoundType(); yield return null; } CorrectWithinTheBound(); _touchBoundCount++; OnEvent(Event_FlyTouchBound); } /// /// 碰到边界后用它来改变速度方向 /// static Vector2[] _BoundChangeVelocityDirections = { Vector2.one, new Vector2(-1, 1), new Vector2(1, -1), new Vector2(-1, 1), new Vector2(1, -1), }; int _touchBoundCount; int _touchBoundType; /// /// 更新触碰边界的标志 /// void UpdateTouchBoundType() { Vector2 ap = _rectTransform.anchoredPosition; Rect mr = _rectTransform.rect; Rect pr = _parentRectTransform.rect; Vector2 mrMin = ap + mr.min; Vector2 mrMax = ap + mr.max; bool hitBottom = false; Collider2D[] hitColliders = Physics2D.OverlapPointAll(transform.position); foreach (var item in hitColliders) { if (item.name.StartsWith("BottomCollider")) { hitBottom = true; break; } } if (hitBottom) _touchBoundType = 2; else if (mrMin.x <= 0) _touchBoundType = 1; //left else if (mrMin.y <= 0) _touchBoundType = 2; //bottom else if (mrMax.x >= pr.width) _touchBoundType = 3; //right else if (mrMax.y >= pr.height) _touchBoundType = 4; //top else _touchBoundType = 0; //within } /// /// 把自己纠正回边界内,避免下一次边界检测可能会出问题 /// void CorrectWithinTheBound() { Vector2 ap = _rectTransform.anchoredPosition; Rect mr = _rectTransform.rect; Rect pr = _parentRectTransform.rect; Vector2 mrMin = ap + mr.min; Vector2 mrMax = ap + mr.max; if (mrMin.x <= 0) ap.x = -mr.min.x + 0.1f; if (mrMin.y <= 0) ap.y = -mr.min.y + 0.1f; if (mrMax.x >= pr.width) ap.x = pr.width - mr.max.x - 0.1f; if (mrMax.y >= pr.height) ap.y = pr.height - mr.max.y - 0.1f; _rectTransform.anchoredPosition = ap; } void UpdateAnglesByDirection() { var ag = _rectTransform.localEulerAngles; ag.y = _direction < 0 ? -180 : 0; _rectTransform.localEulerAngles = ag; } IEnumerator HitDead() { PlayAnimation("03"); UpdateAnglesByDirection(); onHitDead?.Invoke(this); yield return new WaitForSeconds(0.1f); AudioManager.Instance.PlayDuckHit(gameObject); yield return new WaitForSeconds(0.4f); OnEvent(Event_HitDead_Finish); } IEnumerator FallDown() { onFallDonwStart?.Invoke(this); PlayAnimation("04"); UpdateAnglesByDirection(); Vector2 speed = Vector2.zero; while (_rectTransform.anchoredPosition.y > 0) { speed += Vector2.down * 500f * Time.deltaTime; _rectTransform.anchoredPosition += speed * Time.deltaTime; yield return null; } OnEvent(Event_FallDown_Finish); onFallDonwEnd?.Invoke(this); } bool _hasFlyAway = false; public Action onStartFlyAway; IEnumerator FlyAway() { _hasFlyAway = true; onStartFlyAway?.Invoke(); bool intersect = true; while (intersect) { _rectTransform.anchoredPosition += _flyVelocity * Time.deltaTime; Rect mr = _rectTransform.rect; mr.position = _rectTransform.anchoredPosition + mr.min; Rect pr = _parentRectTransform.rect; pr.position = Vector2.zero; intersect = Intersects(mr, pr); yield return null; } OnEvent(Event_FlyAway); } bool Intersects(Rect a, Rect b) { float maxax = a.x + a.width, maxay = a.y + a.height, maxbx = b.x + b.width, maxby = b.y + b.height; return !(maxax < b.x || maxbx < a.x || maxay < b.y || maxby < a.y); } //活着的时候击中才会返回ture public bool Hit() { if (_hasFlyAway) return false; if (dead) return false; dead = true; StartActionCoroutine(HitDead()); return true; } public void PlayDeadForDogHand(int direction) { _direction = direction; UpdateAnglesByDirection(); PlayAnimation("05"); } public void NotifyFlyAway() { if (_hasFlyAway) return; if (dead) return; _flyVelocity *= 1000f / _flyVelocity.magnitude; StartActionCoroutine(FlyAway()); } } }