using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; using Sirenix.OdinInspector; using Spine; using Spine.Unity; using System.Collections; using System.Collections.Generic; using TMPro; using Unity.VisualScripting; using UnityEngine; public enum SearchState { NoTarget = 0, //搜索范围内没有目标 InSearchScope = 1, //在搜索范围内发现目标,但不在攻击范围内 InAttackScope = 2, //目标在攻击范围内 } public class Enemy : MoveCharacter { [Space(30)] [Title("Enemy属性")] [LabelText("击杀提供的经验值")] public int exp; [LabelText("死亡特效")] public GameObject dieEffect; public string name; public int baseSortingOrder; int sortingOrder = 0; public bool isBack = false; //往反方向走 public float jumpSpeed = 10; public float maxMoveSpeed, minMoveSpeed; public float runSpeed; [Header("击飞、屏幕反弹")] public bool isBeBlownUp; //被击飞 public bool isBeReboundedX; //X方向被反弹 public bool isBeReboundedY; //Y方向被反弹 private bool hasBeReboundedX; private bool hasBeReboundedY; public float reboundXSpeed; public float reboundYSpeed; public int wallDamage; [Header("敌方英灵")] public Spirits.SpiritType type; [Header("敌方单位组件")] public SearchState searchState; [Header("攻击")] public float attackRatio; protected int curAttackID; public int len; protected float pastAttackTime; protected bool isConAttack; //连续攻击,不切idle动画 [Header("掉落魂")] public int dropSoulMax = 3; public int dropSoulMin = 1; public int dropProbability = 100; public float dropSoulAngle = 60f; public override void Awake() { base.Awake(); } protected virtual void Start() { len = attackController.attackMethod_march.Length; } protected virtual void OnEnable() { Init(); } protected override void OnDisable() { base.OnDisable(); EnemyCreater.instance.OnEnemyRecycle(this); } public override void Init() { base.Init(); moveSpeed = Random.Range(minMoveSpeed, maxMoveSpeed); spinee.transform.rotation = Quaternion.identity; ChangeSearchState(SearchState.NoTarget); attributeStatus.curSpecialStates = SpecialState.Null; attributeStatus.attributeTime = 0; curAttackID = 0; attackController.curAttackMethod = attackController.attackMethod_march[0]; } public override void FixedUpdate() { OnSearchState(); OnState(); } public override Vector3 GetMoveDir() { Vector3 moveDir = Vector3.zero; switch (searchState) { case SearchState.NoTarget: if (TowerMap.myTowers.Count == 0) { moveDir = Vector3.right; break; } float minDistance = Mathf.Infinity; int id = -1; for (int i = 0; i < TowerMap.myTowers.Count; i++) { Tower myTower = TowerMap.myTowers[i].GetComponent(); if (transform.position.y > myTower.transform.position.y + myTower.height) { continue; } float distance = Vector3.Distance(transform.position, TowerMap.myTowers[i].transform.position); if (distance < minDistance) { minDistance = distance; id = i; } } if (id == -1) { moveDir = Vector3.right; break; } if (bodyTrans.position.x > TowerMap.myTowers[id].transform.position.x) { moveDir = Vector3.left; } else { moveDir = Vector3.right; } break; case SearchState.InSearchScope: if (targetCharacter) { if (targetCharacter.transform.position.x - transform.position.x < -1) { moveDir = Vector3.left; } else if(targetCharacter.transform.position.x - transform.position.x >1) { moveDir = Vector3.right; } else { moveDir = bodyTrans.localScale.x > 0 ? Vector3.left : Vector3.right; } } else { moveDir = Vector3.zero; } break; case SearchState.InAttackScope: if (targetCharacter) { if (targetCharacter.transform.position.x - transform.position.x < 0) { moveDir = Vector3.left; } else { moveDir = Vector3.right; } } else { moveDir = Vector3.zero; } break; default: break; } if (!isBack) { return moveDir; } return -moveDir; } public virtual bool GetAttack() { if (searchState == SearchState.InAttackScope) { return true; } return false; } public bool GetJump() { return false; } public override void OnState() { base.OnState(); if (state == CharacterState.FramePause) { return; } //hurtKeepTime -= Time.deltaTime; invincibleTime -= Time.deltaTime; pastAttackTime += Time.deltaTime; Vector3 leftDir = GetMoveDir(); bool isAttack = GetAttack(); Vector3 velocity = rb.velocity; Quaternion targetQt = Quaternion.Euler(Vector3.zero); switch (state) { case CharacterState.Idle: if (isAdjustHeight == 1) { ChangeState(CharacterState.Rise); break; } else if (isAttack) { if (pastAttackTime >= attackController.attackInterval) { Attack_march(); } } else { if (!foot.TrigGround && !canFly) { if (rb.velocity.y > 0) { ChangeState(CharacterState.Rise); break; } else { ChangeState(CharacterState.Fall); break; } } if (leftDir.x > 0.3f || leftDir.x < -0.3f) { ChangeState(CharacterState.Run); break; } velocity.y = 0; if (!foot.haveGravity) { transform.position = new Vector3(transform.position.x, platformPosY, transform.position.z); targetQt = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, platformRotZ); } if (RotLerpTime < 1) { RotLerpTime += RotLerpSpeed * Time.deltaTime; transform.rotation = Quaternion.Lerp(transform.rotation, targetQt, RotLerpTime); } else { transform.rotation = targetQt; } rb.velocity = velocity * moveSpeedScale; } break; case CharacterState.Run: if (isAttack) { if (pastAttackTime >= attackController.attackInterval) { Attack_march(); } else { ChangeState(CharacterState.Idle); } } else { if (!foot.TrigGround && !canFly) { if (rb.velocity.y > 0) { ChangeState(CharacterState.Rise); break; } else { ChangeState(CharacterState.Fall); break; } } if (leftDir.x < 0.3f && leftDir.x > -0.3f) { ChangeState(CharacterState.Idle); break; } if (leftDir.x > 0.3f) { velocity.x = moveSpeed; if (bodyTrans.localScale.x > 0) { Turn(); } } else if (leftDir.x < -0.3f) { velocity.x = -moveSpeed; if (bodyTrans.localScale.x < 0) { Turn(); } } velocity.y = 0; if (!foot.haveGravity) { transform.position = new Vector3(transform.position.x, platformPosY, transform.position.z); targetQt = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, platformRotZ); } if (RotLerpTime < 1) { RotLerpTime += RotLerpSpeed * Time.deltaTime; transform.rotation = Quaternion.Lerp(transform.rotation, targetQt, RotLerpTime); } else { transform.rotation = targetQt; } rb.velocity = velocity * moveSpeedScale; AdjustHeight(); } break; case CharacterState.Rush: if (isAttack) { if (pastAttackTime >= attackController.attackInterval) { Attack_march(); } else { ChangeState(CharacterState.Idle); } } else { if (!foot.TrigGround && !canFly) { if (rb.velocity.y > 0) { ChangeState(CharacterState.Rise); break; } else { ChangeState(CharacterState.Attack); break; } } if (leftDir.x < 0.3f && leftDir.x > -0.3f) { ChangeState(CharacterState.Idle); break; } if (leftDir.x > 0.3f) { //rb.velocity += Vector3.right * moveAcc * Time.deltaTime; rb.velocity = Vector3.right * runSpeed * moveSpeedScale; //if (rb.velocity.x > maxMoveSpeed) //{ // rb.velocity = new Vector3(maxMoveSpeed, rb.velocity.y, rb.velocity.z); //} if (bodyTrans.localScale.x > 0) { Turn(); } } else if (leftDir.x < -0.3f) { //rb.velocity -= Vector3.right * moveAcc * Time.deltaTime; rb.velocity = Vector3.left * runSpeed * moveSpeedScale; //if (rb.velocity.x < -maxMoveSpeed) //{ // rb.velocity = new Vector3(-maxMoveSpeed, rb.velocity.y, rb.velocity.z); //} if (bodyTrans.localScale.x < 0) { Turn(); } } //AdjustHeight(); } break; case CharacterState.Rise: if (isAdjustHeight == 1) { AdjustHeight(); if (!foot.haveGravity) { targetQt = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, platformRotZ); } if (RotLerpTime < 1) { RotLerpTime += RotLerpSpeed * Time.deltaTime; transform.rotation = Quaternion.Lerp(transform.rotation, targetQt, RotLerpTime); } else { transform.rotation = targetQt; } } else if (isAdjustHeight == 2) { ChangeState(CharacterState.Idle); isAdjustHeight = 0; } else { if (rb.velocity.y <= 0) { ChangeState(CharacterState.Fall); break; } velocity.y += extraRiseGravity * Time.deltaTime; if (leftDir.x > 0.3f) { velocity.x = moveSpeed; if (bodyTrans.localScale.x > 0) { Turn(); } } else if (leftDir.x < -0.3f) { velocity.x = -moveSpeed; if (bodyTrans.localScale.x < 0) { Turn(); } } rb.velocity = velocity * moveSpeedScale; } break; case CharacterState.Fall: if (foot.TrigGround || canFly) { ChangeState(CharacterState.Idle); break; } velocity.y += extraFallGravity * Time.deltaTime; if (leftDir.x > 0.3f) { velocity.x = moveSpeed; if (bodyTrans.localScale.x > 0) { Turn(); } } else if (leftDir.x < -0.3f) { velocity.x = -moveSpeed; if (bodyTrans.localScale.x < 0) { Turn(); } } rb.velocity = velocity * moveSpeedScale; break; case CharacterState.Attack: attackController.JudgeTriggerOnOff(); if (attackController.attackTime <= 0) { isAttack = GetAttack(); if (isAttack) { isConAttack = true; } ChangeState(CharacterState.Idle); break; } if (!foot.haveGravity) { targetQt = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, platformRotZ); } if (RotLerpTime < 1) { RotLerpTime += RotLerpSpeed * Time.deltaTime; transform.rotation = Quaternion.Lerp(transform.rotation, targetQt, RotLerpTime); } else { transform.rotation = targetQt; } break; case CharacterState.Die: //if (killer != null && killer.GetComponent()) //{ // SoldierType st = killer.GetComponent().soldierType; // SoldierEXP.expInstance.AddEXP(st, (int)(exp * LevelSelect.EXPRatio + 0.5f)); //} //if (dieEffect) //{ // PoolManager.Instantiate(dieEffect, transform.position); //} //GameManager gameManager = GameManager.instance; //if (gameManager.gameType == GameType.GameEnd) //{ // GameObject fx = PoolManager.Instantiate(gameManager.dropGoldFX); // fx.transform.position = transform.position; // GameObject injuryNum = PoolManager.Instantiate(gameManager.dropGoldText); // injuryNum.transform.position = new Vector3( // transform.position.x + injuryNumPos_march.x + Random.Range(-injuryNumRandom_march.x / 2f, injuryNumRandom_march.x / 2f), // transform.position.y + injuryNumPos_march.y + Random.Range(-injuryNumRandom_march.y / 2f, injuryNumRandom_march.y / 2f), // transform.position.z); // TextMeshProUGUI text = injuryNum.GetComponentInChildren(); // text.text = $"+{gameManager.enemyGoldDrop}"; //} gameObject.SetActive(false); //dieKeepTime -= Time.deltaTime; //if (dieKeepTime <= 0) //{ // if (killer!=null && killer.GetComponent()) // { // SoldierType st = killer.GetComponent().soldierType; // SoldierEXP.expInstance.AddEXP(st, (int)(exp * LevelSelect.EXPRatio + 0.5f)); // } // if (dieEffect) // { // PoolManager.Instantiate(dieEffect, transform.position); // } // GameManager gameManager = GameManager.instance; // if (gameManager.gameType == GameType.GameEnd) // { // GameObject fx = PoolManager.Instantiate(gameManager.dropGoldFX); // fx.transform.position = transform.position; // GameObject injuryNum = PoolManager.Instantiate(gameManager.dropGoldText); // injuryNum.transform.position = new Vector3( // transform.position.x + injuryNumPos_march.x + Random.Range(-injuryNumRandom_march.x / 2f, injuryNumRandom_march.x / 2f), // transform.position.y + injuryNumPos_march.y + Random.Range(-injuryNumRandom_march.y / 2f, injuryNumRandom_march.y / 2f), // transform.position.z); // TextMeshProUGUI text = injuryNum.GetComponentInChildren(); // text.text = $"+{gameManager.enemyGoldDrop}"; // } // gameObject.SetActive(false); // break; //} break; case CharacterState.HitStun: hitFeedbackSystem.HitStunUpdate(); break; case CharacterState.SpecialStatus_Float: attributeStatus.SpecialStateEffect(SpecialState.FloatState); break; case CharacterState.SpecialStatus_BlowUp: attributeStatus.SpecialStateEffect(SpecialState.BlownUp); break; case CharacterState.SpecialStatus_ShotDown: attributeStatus.SpecialStateEffect(SpecialState.ShotDown); break; case CharacterState.SpecialStatus_Weak: attributeStatus.SpecialStateEffect(SpecialState.Weak); break; default: break; } } public override void ChangeState(CharacterState newState) { if (state == newState || newState == CharacterState.FramePause) { state = newState; return; } //Debug.Log("从" + state + "转向" + newState); switch (state) { case CharacterState.Idle: //transform.rotation = Quaternion.Euler(Vector3.zero); break; case CharacterState.Run: rb.velocity = Vector3.zero; //transform.rotation = Quaternion.Euler(Vector3.zero); break; case CharacterState.Rush: rb.velocity = Vector3.zero; break; case CharacterState.Rise: if (!canFly) { bodyCollider.SetActive(true); } break; case CharacterState.Fall: rb.velocity = Vector3.zero; break; //case CharacterState.Hurt: // break; case CharacterState.Attack: attackController.isAttackTriggerOn = false; attackController.curAttackMethod.attackTrigger.gameObject.SetActive(false); break; case CharacterState.Die: isDie = false; break; case CharacterState.FramePause: state = hitFeedbackSystem.curCharacterState; ChangeState(newState); return; default: break; } CharacterState oldState = state; state = newState; switch (newState) { case CharacterState.Idle: if (!isConAttack || attackController.attackInterval > 0) { if (!IsPlayingAnimation(AnimatorHash.ANIMATOR_idle)) { ani.CrossFade(AnimatorHash.ANIMATOR_idle, 1); } } rb.velocity = Vector3.zero; break; case CharacterState.Run: ani.Play(AnimatorHash.ANIMATOR_walk, 0, 0); break; case CharacterState.Rush: ani.Play(AnimatorHash.ANIMATOR_rush, 0, 0); break; case CharacterState.Fall: ani.Play(AnimatorHash.ANIMATOR_fall, 0, 0); break; case CharacterState.Attack: break; case CharacterState.Rise: ani.Play(AnimatorHash.ANIMATOR_jump, 0, 0); nowCanFly = canFly; break; case CharacterState.Die: ani.Play(AnimatorHash.ANIMATOR_die, 0, 0); isDie = true; dieKeepTime = totalDieKeepTime; if(GameManager.instance.gameType != GameType.GameEnd) { DropSouls(); } if (killer != null && killer.GetComponent()) { SoldierType st = killer.GetComponent().soldierType; SoldierEXP.expInstance.AddEXP(st, (int)(exp * LevelSelect.EXPRatio + 0.5f)); } if (dieEffect) { PoolManager.Instantiate(dieEffect, transform.position); } GameManager gameManager = GameManager.instance; if (gameManager.gameType == GameType.GameEnd) { GameObject fx = PoolManager.Instantiate(gameManager.dropGoldFX); fx.transform.position = transform.position; GameObject injuryNum = PoolManager.Instantiate(gameManager.dropGoldText); injuryNum.transform.position = new Vector3( transform.position.x + injuryNumPos_march.x + Random.Range(-injuryNumRandom_march.x / 2f, injuryNumRandom_march.x / 2f), transform.position.y + injuryNumPos_march.y + Random.Range(-injuryNumRandom_march.y / 2f, injuryNumRandom_march.y / 2f), transform.position.z); TextMeshProUGUI text = injuryNum.GetComponentInChildren(); text.text = $"+{gameManager.enemyGoldDrop}"; } if (GameManager.instance.isRockEnable) { int randomInt = Random.Range(0, 100); if (randomInt < GameManager.instance.rockProbability + GameManager.instance.myTreasuresTag[3] * GameManager.instance.rockLabelEffectRatio) { StoneStatue stoneStatue = PoolManager.Instantiate(Resources.Load("Prefab/StoneStatue"), transform.position).GetComponentInChildren(); GameManager.instance.player.rebornSkills.Add(stoneStatue); if(charactertag == CharacterTag.Tank) { stoneStatue.bulletCount = 6; stoneStatue.transform.parent.localScale = Vector3.one * 1.5f; } else { stoneStatue.bulletCount = 3; stoneStatue.transform.parent.localScale = Vector3.one * 0.8f; } } } //if (GameManager.instance.isWoodEnable) //{ // int randomInt = Random.Range(0, 100); // if (randomInt < GameManager.instance.woodProbability + GameManager.instance.myTreasuresTag[7] * GameManager.instance.woodLabelEffectRatio) // { // PoolManager.Instantiate(Resources.Load("Prefab/SoulFlower"), transform.position).GetComponent().Init(false); // } //} break; default: break; } } private bool IsPlayingAnimation(int animHash) { AnimatorStateInfo stateInfo = ani.GetCurrentAnimatorStateInfo(0); return stateInfo.shortNameHash == animHash; } public void DropSouls() { int dropSoulNum = Random.Range(dropSoulMin, dropSoulMax + 1); if (dropSoulNum > 1) { for (int i = 0; i < dropSoulNum; i++) { float angleInterval = dropSoulAngle / (float)(dropSoulNum - 1); float angle = 90 + ((float)i - (float)(dropSoulNum - 1) / 2) * angleInterval; angle = angle / 180 * Mathf.PI; GameObject soulObj = PoolManager.Instantiate(soulPrefab, transform.position); Vector3 dir = new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0); Soul soul = soulObj.GetComponent(); soul.Burst(dir * soulStartSpeed); soul.type = type; } } else { int randomInt = Random.Range(0, 100); if (randomInt >= dropProbability) { return; } GameObject soulObj = PoolManager.Instantiate(soulPrefab, transform.position); Vector3 dir = Vector3.up; Soul soul = soulObj.GetComponent(); soul.Burst(dir * soulStartSpeed); soul.type = type; } } public void Jump() { SetUpSpeed(jumpSpeed); ani.Play(AnimatorHash.ANIMATOR_jump, 0, 0); } public void SetUpSpeed(float speed) { ChangeState(CharacterState.Rise); Vector3 velocity = rb.velocity; Vector3 leftDir = GetMoveDir(); if (leftDir.x > 0.3f) { if (bodyTrans.localScale.x > 0) { Turn(); } } else if (leftDir.x < -0.3f) { if (bodyTrans.localScale.x < 0) { Turn(); } } velocity.y = speed; rb.velocity = velocity * moveSpeedScale; //animalAni.SetInteger("state", (int)PlayerState.Rise); } public virtual void ChangeSearchState(SearchState newState) { switch (searchState) { case SearchState.NoTarget: break; case SearchState.InSearchScope: break; case SearchState.InAttackScope: break; default: break; } searchState = newState; switch (searchState) { case SearchState.NoTarget: Character character0 = PlayersInput.instance[0]; if (character0 && character0.attackController.beTargetCharacter.Exists(t => t == this)) { character0.attackController.beTargetCharacter.Remove(this); } targetCharacter = null; break; case SearchState.InSearchScope: break; case SearchState.InAttackScope: break; default: break; } } public virtual bool SearchTarget() { targetCharacter = searchTrigger.GetTarget(attackController.targetTypes, attackController.curAttackMethod.canHitFly, attackController.curAttackMethod.searchMode); if (targetCharacter != null) { Character character0 = PlayersInput.instance[0]; Character character1 = PlayersInput.instance[1]; if (targetCharacter == character0 && !character0.attackController.beTargetCharacter.Exists(t => t == this)) { character0.attackController.beTargetCharacter.Add(this); } if (targetCharacter == character1 && !character1.attackController.beTargetCharacter.Exists(t => t == this)) { character1.attackController.beTargetCharacter.Add(this); } return true; } else { return false; } } public virtual void OnSearchState() { switch (searchState) { case SearchState.NoTarget: if (SearchTarget()) { ChangeSearchState(SearchState.InSearchScope); break; } //向玩家基地移动 break; case SearchState.InSearchScope: if (!SearchTarget()) { targetCharacter = null; ChangeSearchState(SearchState.NoTarget); break; } attackDis = attackController.curAttackMethod.attackDistance + targetCharacter.beHitDistance; if (targetCharacter != null && Mathf.Abs(targetCharacter.transform.position.x - transform.position.x) <= attackDis) { ChangeSearchState(SearchState.InAttackScope); break; } break; case SearchState.InAttackScope: if (targetCharacter != null) { //判断是否在射程夹角内 AttackController.AttackMethod am = attackController.curAttackMethod; if (am.attackType == AttackController.AttackType.Shoot && attackController.curAttackMethod.attackInfo.attackMethod_Type == AttackMethod_Type.Attack_March) { Vector3 dir = targetCharacter.beSearchTrigger.transform.position - transform.position; float angle = Vector3.Angle(dir, Vector3.left * bodyTrans.localScale.x); if ((dir.y > 0 && angle > am.maxUpAngle) || (dir.y < 0 && angle > am.maxDownAngle)) { ChangeSearchState(SearchState.NoTarget); } } attackDis = attackController.curAttackMethod.attackDistance + targetCharacter.beHitDistance; if (!targetCharacter.gameObject.activeInHierarchy || targetCharacter.isDie || Mathf.Abs(targetCharacter.transform.position.x - transform.position.x) > attackDis) { ChangeSearchState(SearchState.NoTarget); } } else { ChangeSearchState(SearchState.NoTarget); } break; default: break; } } public void Attack_summon() { CheckTurn(GetMoveDir().x); attackController.Attack_summon(); attackTarget = targetCharacter; } public virtual void Attack_march() { CheckTurn(GetMoveDir().x); attackController.Attack_march(); if (curAttackID + 1 < len) { curAttackID += 1; } else if (attackController.curAttackMethod.attackInfo.attackMethod_Type == AttackMethod_Type.Attack_Summon) { curAttackID = 1; } else { curAttackID = 0; } attackTarget = targetCharacter; pastAttackTime = 0; } public void ChosePlayer() { float distance0 = Mathf.Infinity; float distance1 = Mathf.Infinity; PlayerController player0 = PlayersInput.instance[0]; PlayerController player1 = PlayersInput.instance[1]; if (player0 != null && !player0.isRevive) { distance0 = Mathf.Abs(player0.transform.position.x - transform.position.x); } if (player1 != null && !player1.isRevive) { distance1 = Mathf.Abs(player1.transform.position.x - transform.position.x); } if (distance0 == Mathf.Infinity && distance1 == Mathf.Infinity) { targetCharacter = null; return; } if (distance0 <= distance1) { targetCharacter = player0; if (!player0.attackController.beTargetCharacter.Exists(t => t == this)) { player0.attackController.beTargetCharacter.Add(this); } } else { targetCharacter = player1; if (!player1.attackController.beTargetCharacter.Exists(t => t == this)) { player1.attackController.beTargetCharacter.Add(this); } } } public override void BeHit(int damage) { base.BeHit(damage); } public override void BeHit(AttackController.AttackMethod attackMethod, Character attackFrom, int damage = -1) { base.BeHit(attackMethod, attackFrom, damage); } }