using System.Collections; using System.Collections.Generic; using UnityEngine; using Spine; using Spine.Unity; using UnityEngine.LowLevel; using UnityEngine.Playables; using System.ComponentModel; using Unity.VisualScripting; using System; using Base.Common; using cfg; using static UnityEngine.EventSystems.EventTrigger; [Serializable] public struct AttackInfo { public int damage; public Vector3 attackDir; public float force; public bool changeHurt; public float hurtTime; public float repelValue; } public enum PlayerAttackState { Idle = 0, WalkForward = 1, WalkBack = 2, } public class PlayerController : MoveCharacter { public static PlayerController instance; public List demonicPrefabs; public List demonicSummonPos; public Dictionary> demonicDic; public UIHP uiMp; public float jumpSpeed = 10; public float airJumpSpeed = 10; //public float moveAcc = 5f; //public float airMoveAcc = 3f; public float rushSpeed = 100; public float mp; public float totalMp; public float mpReplySpeed = 1; public float rushCostMp = 5; public float sprintCostMp = 5; public float rushInvincibleTime = 0.2f; [HideInInspector] public float canJumpTime; //离开平台后仍然可以跳跃的时间,用于提升手感 public float leaveGroundCanJumpTime = 0.1f; [HideInInspector] public float cacheJumpTime; //即将落地时按下跳跃键不会跳跃,手感不好,缓存几帧,在这几帧内落地会立即跳跃; public float totalCacheJumpTime = 0.1f; [HideInInspector] public float summonTime; public float totalSummonTime = 0.5f; [HideInInspector] public float cacheAttackTime; //无法攻击时按下攻击键不会攻击,手感不好,缓存几帧,在这几帧内落地会立即攻击; public float totalCacheAttackTime = 0.1f; [HideInInspector] public float cacheSummonTime; //无法召唤时按下召唤键不会召唤,手感不好,缓存几帧,在这几帧内落地会立即召唤; public float totalCacheSummonTime = 0.1f; [HideInInspector] public int cacheSummonId; [HideInInspector] public float rushTime; public float totalRushTime = 0.5f; [HideInInspector] public float cacheRushTime; //无法Rush时按下Rush键不会Rush,手感不好,缓存几帧,在这几帧内落地会立即Rush; public float totalCacheRushTime = 0.1f; [HideInInspector] public bool airJumped; public PlayerAttackState attackState; public float attackMoveSpeed = 5f; public bool btnJumpPress { get { return Input.GetKeyDown(KeyCode.Space) || isClickBtnJump; } } [HideInInspector] public bool isClickBtnJump; public bool btnRushPress { get { return Input.GetKeyDown(KeyCode.LeftShift) || isClickBtnRush; } } [HideInInspector] public bool isClickBtnRush; public bool btnRushKeep { get { return Input.GetKey(KeyCode.LeftShift) || isKeepBtnRush; } } [HideInInspector] public bool isKeepBtnRush; public bool btnSouthPress { get { return Input.GetKeyDown(KeyCode.K) || isClickBtnSouth; } } [HideInInspector] public bool isClickBtnSouth; public bool btnEastPress { get { return Input.GetKeyDown(KeyCode.L) || isClickBtnEast; } } [HideInInspector] public bool isClickBtnEast; public bool btnWestPress { get { return Input.GetKeyDown(KeyCode.J) || isClickBtnWest; } } [HideInInspector] public bool isClickBtnWest; public bool btnNorthPress { get { return Input.GetKeyDown(KeyCode.I) || isClickBtnNorth; } } [HideInInspector] public bool isClickBtnNorth; public bool btnNorthKeep { get { return Input.GetKey(KeyCode.I) || isKeepBtnNorth; } } [HideInInspector] public bool isKeepBtnNorth; public Vector2 leftDir { get { int x = 0; int y = 0; if (Input.GetKey(KeyCode.A)) { x--; } if (Input.GetKey(KeyCode.D)) { x++; } if (Input.GetKey(KeyCode.S)) { y--; } if (Input.GetKey(KeyCode.W)) { y++; } return new Vector2(x, y); } } public override void Init() { base.Init(); mp = totalMp; uiMp.Show(mp, totalMp); } private void Awake() { if (!instance) { instance = this; } else { DestroyImmediate(gameObject); return; } demonicDic = new Dictionary>(); Init(); } private void Update() { if (Input.GetKeyDown(KeyCode.LeftShift)) { isClickBtnRush = true; } if (Input.GetKey(KeyCode.LeftShift)) { isKeepBtnRush = true; } if (Input.GetKeyDown(KeyCode.Space)) { isClickBtnJump = true; } if (Input.GetKeyDown(KeyCode.J)) { isClickBtnWest = true; } if (Input.GetKeyDown(KeyCode.K)) { isClickBtnSouth = true; } if (Input.GetKeyDown(KeyCode.L)) { isClickBtnEast = true; } if (Input.GetKeyDown(KeyCode.I)) { isClickBtnNorth = true; } } public void Jump() { SetUpSpeed(jumpSpeed); ani.Play("jump", 0, 0); } public void AirJump() { SetUpSpeed(airJumpSpeed); ani.Play("jump", 0, 0); } public void SetUpSpeed(float speed) { ChangeState(CharacterState.Rise); Vector3 velocity = rb.velocity; CheckTurn(); velocity.y = speed; rb.velocity = velocity; //animalAni.SetInteger("state", (int)PlayerState.Rise); } public bool CheckSummon() { if (cacheSummonTime > 0) { Summon(cacheSummonId); return true; } if (btnWestPress) { Summon(0); return true; } if (btnSouthPress) { Summon(1); return true; } if (btnEastPress) { Summon(2); return true; } return false; } //角色处于可自由活动状态时的通用切换状态逻辑,如Idle、Run状态,以及别的状态结束时准备回到Idle状态前 public bool CheckPlayerChangeState(CharacterState excludeState = CharacterState.None) { if (!foot.TrigGround) { if ((btnRushPress || cacheRushTime > 0) && mp >= rushCostMp) { if (excludeState != CharacterState.Rush) { ChangeState(CharacterState.Rush); return true; } } if (rb.velocity.y > 0) { if (excludeState != CharacterState.Rise) { ChangeState(CharacterState.Rise); return true; } } else { if (excludeState != CharacterState.Fall) { ChangeState(CharacterState.Fall); return true; } } } else { airJumped = false; if (btnNorthPress || cacheAttackTime > 0) { if (excludeState != CharacterState.Attack) { Attack1(); return true; } } if (excludeState != CharacterState.Summon) { if (CheckSummon()) { return true; } } if ((btnRushPress || cacheRushTime > 0) && mp >= rushCostMp) { if (excludeState != CharacterState.Rush) { ChangeState(CharacterState.Rush); return true; } } if (btnJumpPress || cacheJumpTime > 0) { if (excludeState != CharacterState.Rise) { Jump(); ChangeState(CharacterState.Rise); return true; } } if (leftDir.x > 0.3f || leftDir.x < -0.3f) { if (excludeState != CharacterState.Run) { ChangeState(CharacterState.Run); return true; } } else { if (excludeState != CharacterState.Idle) { ChangeState(CharacterState.Idle); return true; } } } return false; } public override Vector3 GetMoveDir() { return leftDir; } public void CachedPlayerInput() { if (btnRushPress) { cacheRushTime = totalCacheRushTime; } if (btnJumpPress) { cacheJumpTime = totalCacheJumpTime; } if (btnNorthPress) { cacheAttackTime = totalCacheAttackTime; } if (btnWestPress) { cacheSummonTime = totalCacheSummonTime; cacheSummonId = 0; } if (btnSouthPress) { cacheSummonTime = totalCacheSummonTime; cacheSummonId = 1; } if (btnEastPress) { cacheSummonTime = totalCacheSummonTime; cacheSummonId = 2; } } public override void OnState() { base.OnState(); cacheJumpTime -= Time.deltaTime; cacheAttackTime -= Time.deltaTime; cacheSummonTime -= Time.deltaTime; canJumpTime -= Time.deltaTime; invincibleTime -= Time.deltaTime; hurtKeepTime -= Time.deltaTime; attackTime -= Time.deltaTime; summonTime -= Time.deltaTime; rushTime -= Time.deltaTime; cacheRushTime -= Time.deltaTime; dieKeepTime -= Time.deltaTime; weakTime -= Time.deltaTime; Vector3 velocity = rb.velocity; switch (state) { case CharacterState.Idle: if (CheckPlayerChangeState(CharacterState.Idle)) { break; } break; case CharacterState.Run: if (CheckPlayerChangeState(CharacterState.Run)) { break; } CheckTurn(); if (leftDir.x > 0.3f) { rb.velocity = Vector3.right * moveSpeed; } else if (leftDir.x < -0.3f) { rb.velocity = Vector3.left * moveSpeed; } break; case CharacterState.Rise: if (CheckSummon()) { break; } if ((btnRushPress || cacheRushTime > 0) && mp >= rushCostMp) { ChangeState(CharacterState.Rush); break; } if (rb.velocity.y <= 0) { ChangeState(CharacterState.Fall); break; } if (btnJumpPress || cacheJumpTime > 0) { if (!airJumped && rb.velocity.y < airJumpSpeed) { airJumped = true; AirJump(); break; } } CachedPlayerInput(); rb.velocity = AirMove(rb.velocity); break; case CharacterState.Fall: if (CheckSummon()) { break; } if ((btnRushPress || cacheRushTime > 0) && mp >= rushCostMp) { ChangeState(CharacterState.Rush); break; } if (foot.TrigGround) { if (CheckPlayerChangeState()) { break; } } //if (foot.canStepPlayers.Count > 0) //{ // Jump(jumpSpeed / 2); // StepOther(); // break; //} //if (foot.canStepEnemyList.Count > 0) //{ // Jump(jumpSpeed / 2); // StepEnemy(); // break; //} if (btnJumpPress || cacheJumpTime > 0) { if (canJumpTime > 0) { Jump(); break; } else if (!airJumped) { airJumped = true; AirJump(); break; } } CachedPlayerInput(); rb.velocity = AirMove(rb.velocity); break; case CharacterState.Hurt: if (hurtKeepTime <= 0) { if (CheckPlayerChangeState()) { break; } } CachedPlayerInput(); break; case CharacterState.Attack: if (attackTime <= 0) { if (btnNorthKeep) { ChangeState(CharacterState.KeepAttack); break; } if (CheckPlayerChangeState()) { break; } } CachedPlayerInput(); break; case CharacterState.KeepAttack: if ((btnRushPress) && mp >= rushCostMp) { ChangeState(CharacterState.Rush); break; } if (btnJumpPress && canJumpTime > 0) { Jump(); break; } if (!btnNorthKeep) { if (CheckPlayerChangeState(CharacterState.Attack)) { break; } } switch (attackState) { case PlayerAttackState.Idle: if (bodyTrans.localScale.x > 0) { if (leftDir.x > 0.3f) { SetAttackState(PlayerAttackState.WalkBack); velocity.x = attackMoveSpeed; rb.velocity = velocity; break; } else if (leftDir.x < -0.3f) { SetAttackState(PlayerAttackState.WalkForward); velocity.x = -attackMoveSpeed; rb.velocity = velocity; break; } } else { if (leftDir.x > 0.3f) { SetAttackState(PlayerAttackState.WalkForward); velocity.x = attackMoveSpeed; rb.velocity = velocity; break; } else if (leftDir.x < -0.3f) { SetAttackState(PlayerAttackState.WalkBack); velocity.x = -attackMoveSpeed; rb.velocity = velocity; break; } } velocity.x = 0; rb.velocity = velocity; break; case PlayerAttackState.WalkForward: if (bodyTrans.localScale.x > 0) { if (leftDir.x > 0.3f) { SetAttackState(PlayerAttackState.WalkBack); velocity.x = attackMoveSpeed; rb.velocity = velocity; break; } else if (leftDir.x > -0.3f && leftDir.x < 0.3f) { SetAttackState(PlayerAttackState.Idle); velocity.x = 0; rb.velocity = velocity; break; } else { velocity.x = -attackMoveSpeed; rb.velocity = velocity; } } else { if (leftDir.x < -0.3f) { SetAttackState(PlayerAttackState.WalkBack); velocity.x = -attackMoveSpeed; rb.velocity = velocity; break; } else if (leftDir.x > -0.3f && leftDir.x < 0.3f) { SetAttackState(PlayerAttackState.Idle); velocity.x = 0; rb.velocity = velocity; break; } else { velocity.x = attackMoveSpeed; rb.velocity = velocity; } } break; case PlayerAttackState.WalkBack: if (bodyTrans.localScale.x > 0) { if (leftDir.x < -0.3f) { SetAttackState(PlayerAttackState.WalkForward); velocity.x = -attackMoveSpeed; rb.velocity = velocity; break; } else if (leftDir.x > -0.3f && leftDir.x < 0.3f) { SetAttackState(PlayerAttackState.Idle); velocity.x = 0; rb.velocity = velocity; break; } else { velocity.x = attackMoveSpeed; rb.velocity = velocity; } } else { if (leftDir.x > 0.3f) { SetAttackState(PlayerAttackState.WalkForward); velocity.x = attackMoveSpeed; rb.velocity = velocity; break; } else if (leftDir.x > -0.3f && leftDir.x > 0.3f) { SetAttackState(PlayerAttackState.Idle); velocity.x = 0; rb.velocity = velocity; break; } else { velocity.x = -attackMoveSpeed; rb.velocity = velocity; } } break; default: break; } break; case CharacterState.Summon: if (summonTime <= 0) { if (CheckPlayerChangeState()) { break; } } break; case CharacterState.Rush: if (rushTime <= 0) { if (btnRushKeep) { ChangeState(CharacterState.Sprint); break; } if (CheckPlayerChangeState()) { break; } } CachedPlayerInput(); if (bodyTrans.localScale.x > 0) { rb.velocity = Vector3.left * rushSpeed; } else { rb.velocity = Vector3.right * rushSpeed; } break; case CharacterState.Sprint: if (!btnRushKeep) { if (CheckPlayerChangeState(CharacterState.Rush)) { break; } } if (mp < sprintCostMp * Time.deltaTime) { if (CheckPlayerChangeState(CharacterState.Rush)) { break; } } mp -= sprintCostMp * Time.deltaTime; uiMp.Show(mp, totalMp); CachedPlayerInput(); CheckTurn(); if (bodyTrans.localScale.x > 0) { rb.velocity = Vector3.left * rushSpeed; } else { rb.velocity = Vector3.right * rushSpeed; } break; case CharacterState.Die: if (dieKeepTime <= 0) { gameObject.SetActive(false); break; } break; case CharacterState.Weak: if (weakTime <= 0) { ChangeState(CharacterState.Idle); break; } break; default: break; } if (!foot.TrigGround) { if (rb.velocity.y > 0) { rb.velocity += Vector3.up * extraRiseGravity * Time.deltaTime; } else { rb.velocity += Vector3.up * extraFallGravity * Time.deltaTime; } } isClickBtnRush = false; isKeepBtnRush = false; isClickBtnJump = false; isClickBtnSouth = false; isClickBtnEast = false; isClickBtnNorth = false; isClickBtnWest = false; if (foot.TrigGround) { canJumpTime = leaveGroundCanJumpTime; } SearchTarget(); attackTarget = targetCharacter; if (mp < totalMp) { mp += mpReplySpeed * Time.deltaTime; } if (mp > totalMp) { mp = totalMp; } uiMp.Show(mp, totalMp); } public override void ChangeState(CharacterState newState) { Vector3 velocity = rb.velocity; switch (state) { case CharacterState.Idle: break; case CharacterState.Run: velocity.x = 0; break; case CharacterState.Rise: break; case CharacterState.Fall: break; case CharacterState.Hurt: break; case CharacterState.Attack: aniCollider.Play("NotAttack", 1, 0); break; case CharacterState.KeepAttack: aniCollider.Play("NotAttack", 1, 0); break; case CharacterState.Summon: break; case CharacterState.Rush: velocity = Vector3.zero; break; case CharacterState.Sprint: velocity = Vector3.zero; break; case CharacterState.Die: isDie = false; break; case CharacterState.Weak: break; default: break; } CharacterState oldState = state; state = newState; switch (newState) { case CharacterState.Idle: aniCollider.Play("Idle", 0, 0); if (oldState == CharacterState.Fall) { ani.Play("fall_end", 0, 0); } else { ani.Play("idle", 0, 0); } velocity = Vector3.zero; //animalAni.SetInteger("state", (int)PlayerState.Idle); break; case CharacterState.Run: aniCollider.Play("Run", 0, 0); ani.Play("run_start", 0, 0); //animalAni.SetInteger("state", (int)PlayerState.Walk); break; case CharacterState.Rise: aniCollider.Play("Rise", 0, 0); canJumpTime = 0; break; case CharacterState.Fall: aniCollider.Play("Fall", 0, 0); ani.Play("fall", 0, 0); //animalAni.SetInteger("state", (int)PlayerState.Fall); break; case CharacterState.Hurt: aniCollider.Play("Hurt", 0, 0); ani.Play("hitted", 0, 0); invincibleTime = totalInvincibleTime; //ani.Play("Invincible", 2, 0); break; case CharacterState.Attack: attackTime = totalAttack1Time; break; case CharacterState.KeepAttack: aniCollider.Play("Attack1Keep", 1, 0); break; case CharacterState.Summon: aniCollider.Play("Summon", 0, 0); ani.Play("summon", 0, 0); summonTime = totalSummonTime; break; case CharacterState.Rush: aniCollider.Play("Rush", 0, 0); ani.Play("rush_loop", 0, 0); rushTime = totalRushTime; invincibleTime = rushInvincibleTime; if (bodyTrans.localScale.x > 0) { velocity = Vector3.left * rushSpeed; } else { velocity = Vector3.right * rushSpeed; } mp -= rushCostMp; uiMp.Show(mp, totalMp); break; case CharacterState.Sprint: aniCollider.Play("Sprint", 0, 0); ani.Play("rush_loop", 0, 0); if (bodyTrans.localScale.x > 0) { velocity = Vector3.left * rushSpeed; } else { velocity = Vector3.right * rushSpeed; } break; case CharacterState.Die: aniCollider.Play("Die", 0, 0); ani.Play("die", 0, 0); isDie = true; dieKeepTime = totalDieKeepTime; break; case CharacterState.Weak: aniCollider.Play("Weak", 0, 0); ani.Play("weak", 0, 0); velocity.y = weakUpSpeed; weakTime = totalWeakTime; break; default: break; } rb.velocity = velocity; } public void CheckTurn() { if (leftDir.x > 0.3f && bodyTrans.localScale.x > 0) { Turn(); } else if (leftDir.x < -0.3f && bodyTrans.localScale.x < 0) { Turn(); } } public Vector3 AirMove(Vector3 velocity) { CheckTurn(); if (leftDir.x > 0.3f) { velocity = new Vector3(moveSpeed, velocity.y, velocity.z); } else if (leftDir.x < -0.3f) { velocity = new Vector3(-moveSpeed, velocity.y, velocity.z); } else { velocity = new Vector3(0, velocity.y, velocity.z); } return velocity; } public void Summon(int id) { if (id >= demonicPrefabs.Count) { Debug.LogError("未配置" + id + "号使魔"); return; } if (id >= demonicSummonPos.Count) { Debug.LogError("未配置" + id + "号使魔召唤位置"); return; } GameObject prefab = demonicPrefabs[id]; if (!CheckCanSummon(id)) { return; } ChangeState(CharacterState.Summon); float costMp = prefab.GetComponent().costMp; mp -= costMp; uiMp.Show(mp, totalMp); GameObject demonicObj = PoolManager.Instantiate(prefab); Demonic demonic = demonicObj.GetComponent(); demonic.id = id; if (!demonicDic.ContainsKey(id)) { demonicDic.Add(id, new List()); } demonicDic[id].Add(demonic); demonicObj.transform.parent = null; demonicObj.transform.localEulerAngles = Vector3.zero; demonicObj.transform.localScale = new Vector3(1, 1, 1); Vector3 offset = demonicSummonPos[id]; if (bodyTrans.localScale.x > 0) { demonicObj.transform.position = transform.position + offset; if (demonic.bodyTrans.localScale.x < 0) { demonic.Turn(); } } else { demonicObj.transform.position = transform.position + new Vector3(-offset.x, offset.y, offset.z); if (demonic.bodyTrans.localScale.x > 0) { demonic.Turn(); } } demonic.totalHp = 100; demonic.Init(); demonic.SetSortingOrder(id * 1000 + demonicDic[id].Count); demonic.Attack1(); } public void OnDemonicRecycle(Demonic demonic) { if (!demonicDic.ContainsKey(demonic.id)) { return; } demonicDic[demonic.id].Remove(demonic); for (int i = 0; i < demonicDic[demonic.id].Count; i++) { demonicDic[demonic.id][i].SetSortingOrder(demonic.id * 1000 + i); } } public bool CheckCanSummon(int id) { GameObject prefab = demonicPrefabs[id]; float costMp = prefab.GetComponent().costMp; if (mp < costMp) { Debug.Log("mp不足召唤失败, 还得加个动画或者音效啥的"); return false; } return true; } public override void Attack1() { base.Attack1(); if (leftDir.x > 0.3f) { if (bodyTrans.localScale.x > 0) { Turn(); } SetAttackState(PlayerAttackState.WalkForward); } else if (leftDir.x < -0.3f) { if (bodyTrans.localScale.x < 0) { Turn(); } SetAttackState(PlayerAttackState.WalkForward); } else { SetAttackState(PlayerAttackState.Idle); } } public void SetAttackState(PlayerAttackState value) { attackState = value; ani.SetInteger("attackState", (int)value); aniCollider.Play("Attack1Keep", 1, 0); } public void SearchTarget() { targetCharacter = searchTrigger.GetMinDisTarget(targetTypes, canHitFly); } }