类银河恶魔城 P20-1 Slime enemy
与Skeleton 差不多,并无太多的变化。
SlimeBattleState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SlimeBattleState : EnemyState
{private Enemy_Slime enemy;private Transform player;private int moveDir;public SlimeBattleState(Enemy _enemyBase, EnemyStateMachine _StateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _StateMachine, _animBoolName){this.enemy = _enemy;}public override void Enter(){base.Enter();player = PlayerManager.instance.player.transform;if (player.GetComponent<PlayerStats>().isDead)stateMachine.ChangeState(enemy.moveState);}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (enemy.IsPlayerDetected()){stateTimer = enemy.battleTime;if (enemy.IsPlayerDetected().distance < enemy.attackDistance){if (CanAttack())stateMachine.ChangeState(enemy.attackState);}}else{if (stateTimer < 0 || Vector2.Distance(player.transform.position, enemy.transform.position) > 19)stateMachine.ChangeState(enemy.idleState);}if (player.position.x > enemy.transform.position.x){moveDir = 1;}else if (player.position.x < enemy.transform.position.x) { moveDir = -1; }enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);}private bool CanAttack(){if (Time.time >= enemy.lastTimeAttacked + enemy.attackCooldown){enemy.attackCooldown = Random.Range(enemy.minAttackCooldown, enemy.maxAttackCooldown);enemy.lastTimeAttacked = Time.time;return true;}return false;}
}
Enemy_AnimationTriggers.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_AnimationTriggers : MonoBehaviour
{//private Enemy_Skeleton enemy => GetComponentInParent<Enemy_Skeleton>();private Enemy enemy => GetComponentInParent<Enemy>();private void AniationTrigger(){enemy.AnimationFinishTrigger();}private void AttackTrigger(){Collider2D[] colliders = Physics2D.OverlapCircleAll(enemy.attackCheck.position, enemy.attackCheckRadius);foreach (var hit in colliders){if (hit.GetComponent<Player>() != null){PlayerStats target = hit.GetComponent<PlayerStats>();enemy.stats.DoDamage(target);//hit.GetComponent<Player>().Damage();}}}private void OpenCounterWindow() => enemy.OpenCounterAttackWindow();private void CloseCounterWindow() => enemy.CloseCounterAttackWindow();
}
SkeletonBattleState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonBattleState : EnemyState
{private Transform player;private Enemy_Skeleton enemy;private int moveDir;public SkeletonBattleState(Enemy _enemyBase, EnemyStateMachine _StateMachine, string _animBoolName, Enemy_Skeleton _enemy) : base(_enemyBase, _StateMachine, _animBoolName){this.enemy = _enemy;}public override void Enter(){base.Enter();player = PlayerManager.instance.player.transform;if (player.GetComponent<PlayerStats>().isDead)stateMachine.ChangeState(enemy.moveState);}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (enemy.IsPlayerDetected()){stateTimer = enemy.battleTime;if (enemy.IsPlayerDetected().distance < enemy.attackDistance){if(CanAttack())stateMachine.ChangeState(enemy.attackState);}}else{if (stateTimer < 0 || Vector2.Distance(player.transform.position,enemy.transform.position) > 19 )stateMachine.ChangeState(enemy.idleState);}if (player.position.x > enemy.transform.position.x){moveDir = 1;}else if (player.position.x < enemy.transform.position.x) { moveDir = -1; }enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);}private bool CanAttack(){if (Time.time >= enemy.lastTimeAttacked + enemy.attackCooldown){enemy.attackCooldown = Random.Range(enemy.minAttackCooldown, enemy.maxAttackCooldown);enemy.lastTimeAttacked = Time.time;return true;}return false;}
}
SlimeAttackState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SlimeAttackState : EnemyState
{private Enemy_Slime enemy;public SlimeAttackState(Enemy _enemyBase, EnemyStateMachine _StateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _StateMachine, _animBoolName){this.enemy = _enemy;}public override void Enter(){base.Enter();enemy.lastTimeAttacked = Time.time;}public override void Update(){base.Update();//enemy.SetZeroVelocity();if (triggerCalled){stateMachine.ChangeState(enemy.battleState);}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SlimeGroundState : EnemyState
{protected Transform player;protected Enemy_Slime enemy;public SlimeGroundState(Enemy _enemyBase, EnemyStateMachine _StateMachine, string _animBoolName,Enemy_Slime _enemy) : base(_enemyBase, _StateMachine, _animBoolName){this.enemy = _enemy;}public override void Enter(){base.Enter();player = PlayerManager.instance.player.transform;}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (enemy.IsPlayerDetected() || Vector2.Distance(enemy.transform.position, player.transform.position) < 2){stateMachine.ChangeState(enemy.battleState);}}
}
SlimeMoveState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SlimeMoveState : SlimeGroundState
{public SlimeMoveState(Enemy _enemyBase, EnemyStateMachine _StateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _StateMachine, _animBoolName, _enemy){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();enemy.SetVelocity(enemy.moveSpeed * enemy.facingDir, rb.velocity.y);if (!enemy.IsGroundDetected() || enemy.IsWallDetected()){enemy.Flip();enemy.SetVelocity(0, rb.velocity.y); // 先停止移动stateMachine.ChangeState(enemy.idleState);}}
}
SmileIdleState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SmileIdleState : SlimeGroundState
{public SmileIdleState(Enemy _enemyBase, EnemyStateMachine _StateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _StateMachine, _animBoolName, _enemy){}public override void Enter(){base.Enter();stateTimer = enemy.idleTime;}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (stateTimer < 0){stateMachine.ChangeState(enemy.moveState);}}
}
SlimeDeadState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SlimeDeadState : EnemyState
{private Enemy_Slime enemy;public SlimeDeadState(Enemy _enemyBase, EnemyStateMachine _StateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _StateMachine, _animBoolName){this.enemy = _enemy;}public override void Enter(){base.Enter();enemy.anim.SetBool(enemy.lastAnimBoolName, true);enemy.anim.speed = 0;enemy.cd.enabled = false;stateTimer = .15f;}public override void Update(){base.Update();if (stateTimer > 0){rb.velocity = new Vector2(0, 10);}}
}
Enemy_Slime.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_Slime : Enemy
{#region Statespublic SmileIdleState idleState { get; private set; }public SlimeMoveState moveState { get; private set; }public SlimeBattleState battleState { get; private set; }public SlimeAttackState attackState { get; private set; }public SlimeStunnedState stunnedState { get; private set; }public SlimeDeadState deadState { get; private set; }#endregionprotected override void Awake(){base.Awake();SetupDefailtFacingDir(-1);idleState = new SmileIdleState(this, stateMachine, "Idle", this);moveState = new SlimeMoveState(this, stateMachine, "Move", this);battleState = new SlimeBattleState(this, stateMachine, "Move", this);attackState = new SlimeAttackState(this, stateMachine, "Attack", this);stunnedState = new SlimeStunnedState(this, stateMachine, "Stunned", this);deadState = new SlimeDeadState(this, stateMachine, "Idle", this);}protected override void Start(){base.Start();stateMachine.Initalize(idleState);}public override bool CanBeStunned(){if (base.CanBeStunned()){stateMachine.ChangeState(stunnedState);return true;}return false;}public override void Die(){base.Die();stateMachine.ChangeState(deadState);}
}
Entity.cs
using System.Collections;
using UnityEngine;public class Entity : MonoBehaviour
{#region Componentspublic Animator anim { get; private set; }public Rigidbody2D rb { get; private set; }public SpriteRenderer sr { get; private set; }public CharacterStats stats { get; private set; }public CapsuleCollider2D cd { get; private set; }#endregion[Header("Knockback info")][SerializeField] protected Vector2 knockbackPower;[SerializeField] protected float knockbackDuration;protected bool isKnocked;[Header("Collsion info")]public Transform attackCheck;public float attackCheckRadius;[SerializeField] protected Transform groundCheck;[SerializeField] protected float groundCheckDistance;[SerializeField] protected Transform wallCheck;[SerializeField] protected float wallCheckDistance;[SerializeField] protected LayerMask whatIsGround;public int knockbackDir { get; private set; }public int facingDir { get; private set; } = 1;protected bool facingRight = true;public System.Action onFlipped;protected virtual void Awake(){anim = GetComponentInChildren<Animator>();rb = GetComponent<Rigidbody2D>();}protected virtual void Start(){sr = GetComponentInChildren<SpriteRenderer>();anim = GetComponentInChildren<Animator>();rb = GetComponent<Rigidbody2D>();stats = GetComponentInChildren<CharacterStats>();cd = GetComponent<CapsuleCollider2D>();}protected virtual void Update(){}public virtual void SlowEntityBy(float _slowPercentage, float _SlowDuration){}protected virtual void ReturnDefaultSpeed(){anim.speed = 1;}public virtual void DamageImpact(){//fx.StartCoroutine("FlashFX");StartCoroutine("HitKnockback");//Debug.Log(gameObject.name + "was damaged");}public virtual void SetupKnockbackDir(Transform _damageDirection){if (_damageDirection.position.x > transform.position.x)knockbackDir = -1;else if (_damageDirection.position.x < transform.position.x)knockbackDir = 1;}public void SetupKnockbackPower(Vector2 _knockbackpower) => knockbackPower = _knockbackpower;protected virtual IEnumerator HitKnockback(){isKnocked = true;// 强制停止所有可能存在的惯性//rb.velocity = Vector2.zero;rb.velocity = new Vector2(knockbackPower.x * -facingDir, knockbackPower.y);yield return new WaitForSeconds(knockbackDuration);// 强制停止所有可能存在的惯性//rb.velocity = Vector2.zero;isKnocked = false;SetupZeroKnockbackPower();}protected virtual void SetupZeroKnockbackPower(){}#region Velocity //设置速度public void SetZeroVelocity(){if (isKnocked){return;}rb.velocity = new Vector2(0, 0);}public void SetVelocity(float _xVelocity, float _yVelocity){if (isKnocked){return;}rb.velocity = new Vector2(_xVelocity, _yVelocity);FlipControler(_xVelocity);}#endregion #region Collision // 碰撞检测public virtual bool IsGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);public virtual bool IsWallDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);protected virtual void OnDrawGizmos(){// 地面检测可视化Gizmos.color = Color.green;Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));// 墙壁检测可视化//Gizmos.color = Color.red;Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance * facingDir, wallCheck.position.y));Gizmos.DrawWireSphere(attackCheck.position, attackCheckRadius);}#endregion#region Flip //翻动public virtual void Flip(){facingDir = facingDir * -1;facingRight = !facingRight;//Debug.Log(facingDir);transform.Rotate(0, 180, 0);if (onFlipped != null){onFlipped();}}public virtual void FlipControler(float _x){if (_x > 0 && !facingRight){Flip();}else if (_x < 0 && facingRight){Flip();}}public virtual void SetupDefailtFacingDir(int _dirction){facingDir = _dirction;if (facingDir == -1)facingRight = false;}#endregion public virtual void Die(){ }
}
动画中的事件项,从左到右OpenCounterWindow attackTriggle CloseCounterWindow AnimationTriggle