本章节修复了玩家滑墙bug,实现了玩家在受到较高伤害时的击退效果
源代码
PlayerWallSlideState.cs
新增代码
//未检测到墙
if (!player.IsWallDetected())
stateMachine.ChangeState(player.idleState);
完整代码
using UnityEngine;
public class PlayerWallSlideState : PlayerState
{
public PlayerWallSlideState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
{
}
public override void Enter()
{
base.Enter();
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
//未检测到墙
if (!player.IsWallDetected())
stateMachine.ChangeState(player.idleState);
if (Input.GetKeyDown(KeyCode.Space))
{
stateMachine.ChangeState(player.wallJumpState);
return;
}
if (xInput != 0 && xInput != player.facingDir)
stateMachine.ChangeState(player.idleState);
player.SetVelocity(0, rb.velocity.y * 0.7f);
//着地
if (player.IsGroundDetected())
{
stateMachine.ChangeState(player.idleState);
}
}
}
Entity.cs
新增代码
public void SetupKnockbackPower(Vector2 _knockbackPower) => knockbackPower = _knockbackPower;
protected virtual void SetupZeroKnockbackPower()
{
}
完整代码
using System.Collections;
using UnityEngine;
public class Entity : MonoBehaviour
{
#region Component
public Animator anim { get; private set; }
public Rigidbody2D rb { get; private set; }
public EntityFX fx { 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("Collision 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()
{
}
protected virtual void Start()
{
sr = GetComponentInChildren<SpriteRenderer>();
anim = GetComponentInChildren<Animator>();
rb = GetComponent<Rigidbody2D>();
fx = GetComponent<EntityFX>();
stats = GetComponentInChildren<CharacterStats>();
cd = GetComponent<CapsuleCollider2D>();
}
protected virtual void Update()
{
}
public virtual void SlowEnitityBy(float _slowPercentage, float _slowDuration)
{
}
public virtual void ReturnDefaultSpeed()
{
anim.speed = 1;
}
public virtual void DamageImpact() => StartCoroutine("HitKnockback");
//设置击退方向
public virtual void SetupKnockbackDir(Transform _damageDir)
{
if (_damageDir.position.x > transform.position.x)
knockbackDir = -1;
else
knockbackDir = 1;
}
public void SetupKnockbackPower(Vector2 _knockbackPower) => knockbackPower = _knockbackPower;
protected virtual IEnumerator HitKnockback()
{
isKnocked = true;
rb.velocity = new Vector2(knockbackPower.x * knockbackDir, knockbackPower.y);
yield return new WaitForSeconds(knockbackDuration);
isKnocked = false;
SetupZeroKnockbackPower();
}
protected virtual void SetupZeroKnockbackPower()
{
}
#region Velocity
public virtual void SetZeroVelocity()
{
if (isKnocked)
return;
rb.velocity = new Vector2(0, 0);
}
public virtual void SetVelocity(float _xVelocity, float _yVelocity)
{
if (isKnocked)
return;
rb.velocity = new Vector2(_xVelocity, _yVelocity);
FlipController(_xVelocity);
}
#endregion
#region Collosion
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.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));
Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));
Gizmos.DrawWireSphere(attackCheck.position, attackCheckRadius);
}
#endregion
#region Flip
public virtual void Flip()
{
facingDir *= -1;
facingRight = !facingRight;
transform.Rotate(0, 180, 0);
if (onFlipped != null)
onFlipped();
}
public virtual void FlipController(float _x)
{
if (_x > 0 && !facingRight)
{
Flip();
}
else if (_x < 0 && facingRight)
{
Flip();
}
}
#endregion
public virtual void Die()
{
}
}
Player.cs
新增代码
protected override void SetupZeroKnockbackPower()
{
knockbackPower = new Vector2(0, 0);
}
完整代码
using System.Collections;
using UnityEngine;
public class Player : Entity
{
[Header("Attack details")]
public Vector2[] attackMovement;
public float counterAttackDuration = .2f;
public bool isBusy { get; private set; }
[Header("Move info")]
public float moveSpeed = 8f;
public float jumpForce = 12f;
public float wallJumpForce = 5f;
public float swordReturnImpact;
private float defaultMoveSpeed;
private float defaultJumpForce;
[Header("Dash info")]
public float dashSpeed = 25f;
public float dashDuration = 0.2f;
private float defaultDashSpeed;
public float dashDir { get; private set; }
public SkillManager skill { get; private set; }
public GameObject sword { get; private set; }
#region State
public PlayerStateMachine stateMachine { get; private set; }
public PlayerIdleState idleState { get; private set; }
public PlayerMoveState moveState { get; private set; }
public PlayerJumpState jumpState { get; private set; }
public PlayerAirState airState { get; private set; }
public PlayerDashState dashState { get; private set; }
public PlayerWallSlideState wallSlideState { get; private set; }
public PlayerWallJumpState wallJumpState { get; private set; }
public PlayerPrimaryAttackState primaryAttack { get; private set; }
public PlayerCounterAttackState counterAttack { get; private set; }
public PlayerAimSwordState aimSwordState { get; private set; }
public PlayerCatchSwordState catchSwordState { get; private set; }
public PlayerBlackholeState blackholeState { get; private set; }
public PlayerDeadState deadState { get; private set; }
#endregion
protected override void Awake()
{
base.Awake();
stateMachine = new PlayerStateMachine();
idleState = new PlayerIdleState(this, stateMachine, "Idle");
moveState = new PlayerMoveState(this, stateMachine, "Move");
jumpState = new PlayerJumpState(this, stateMachine, "Jump");
airState = new PlayerAirState(this, stateMachine, "Jump");
dashState = new PlayerDashState(this, stateMachine, "Dash");
wallSlideState = new PlayerWallSlideState(this, stateMachine, "WallSlide");
wallJumpState = new PlayerWallJumpState(this, stateMachine, "Jump");
primaryAttack = new PlayerPrimaryAttackState(this, stateMachine, "Attack");
counterAttack = new PlayerCounterAttackState(this, stateMachine, "CounterAttack");
aimSwordState = new PlayerAimSwordState(this, stateMachine, "AimSword");
catchSwordState = new PlayerCatchSwordState(this, stateMachine, "CatchSword");
blackholeState = new PlayerBlackholeState(this, stateMachine, "Jump");
deadState = new PlayerDeadState(this, stateMachine, "Die");
}
protected override void Start()
{
base.Start();
skill = SkillManager.instance;
stateMachine.Initialize(idleState);
defaultMoveSpeed = moveSpeed;
defaultJumpForce = jumpForce;
defaultDashSpeed = dashSpeed;
}
protected override void Update()
{
base.Update();
stateMachine.currentState.Update();
CheckForDashInput();
if (Input.GetKeyDown(KeyCode.F) && skill.crystal.crystalUnlocked)
skill.crystal.CanUseSkill();
if (Input.GetKeyDown(KeyCode.Alpha1))
Inventory.instance.UseFlask();
}
public override void SlowEnitityBy(float _slowPercentage, float _slowDuration)
{
moveSpeed = moveSpeed * (1 - _slowPercentage);
jumpForce = jumpForce * (1 - _slowPercentage);
dashSpeed = dashSpeed * (1 - _slowPercentage);
anim.speed = anim.speed * (1 - _slowPercentage);
Invoke("ReturnDefaultSpeed", _slowDuration);
}
public override void ReturnDefaultSpeed()
{
base.ReturnDefaultSpeed();
moveSpeed = defaultMoveSpeed;
jumpForce = defaultJumpForce;
dashSpeed = defaultDashSpeed;
}
public void AssignNewSword(GameObject _newSword)
{
sword = _newSword;
}
public void CatchTheSword()
{
stateMachine.ChangeState(catchSwordState);
Destroy(sword);
}
public IEnumerator BusyFor(float _seconds)
{
isBusy = true;
yield return new WaitForSeconds(_seconds);
isBusy = false;
}
public void AnimationTrigger() => stateMachine.currentState.AnimationFinishTrigger();
private void CheckForDashInput()
{
if (IsWallDetected())
{
return;
}
if (!skill.dash.dashUnlocked)
return;
if (Input.GetKeyDown(KeyCode.LeftShift) && SkillManager.instance.dash.CanUseSkill())
{
dashDir = Input.GetAxisRaw("Horizontal");
if (dashDir == 0)
{
dashDir = facingDir;
}
stateMachine.ChangeState(dashState);
}
}
public override void Die()
{
base.Die();
stateMachine.ChangeState(deadState);
}
protected override void SetupZeroKnockbackPower()
{
knockbackPower = new Vector2(0, 0);
}
}
PlayerStats.cs
新增代码
//收到超过30%最大生命值的伤害时被击退
if(_damage > GetMaxHealth() * .3f)
{
player.SetupKnockbackPower(new Vector2(6, 6));
//播放收集音效
int random = Random.Range(34, 35);
AudioManager.instance.PlaySFX(random, null);
}
完整代码
using UnityEngine;
public class PlayerStats : CharacterStats
{
private Player player;
protected override void Start()
{
base.Start();
player = GetComponent<Player>();
}
public override void TakeDamage(int _damage)
{
base.TakeDamage(_damage);
}
protected override void Die()
{
base.Die();
player.Die();
//死亡时货币清空
GameManager.instance.lostCurrencyAmount = PlayerManager.instance.currency;
PlayerManager.instance.currency = 0;
GetComponent<PlayerItemDrop>()?.GenerateDrop();
}
protected override void DecreaseHealthBy(int _damage)
{
base.DecreaseHealthBy(_damage);
//收到超过30%最大生命值的伤害时被击退
if(_damage > GetMaxHealth() * .3f)
{
player.SetupKnockbackPower(new Vector2(6, 6));
//播放收集音效
int random = Random.Range(34, 35);
AudioManager.instance.PlaySFX(random, null);
}
ItemData_Equipment currentArmor = Inventory.instance.GetEquipment(EquipmentType.护甲);
if (currentArmor != null)
currentArmor.Effect(player.transform);
}
public override void OnEvasion(Transform _enemy)
{
player.skill.dodge.CreatMirageOnDodge(_enemy);
}
public void CloneDoDamage(CharacterStats _targetStats, float _multiplier)
{
if (TargetCanAvoidAttack(_targetStats))
return;
int totalDamage = damage.GetValue() + strength.GetValue();
if (_multiplier > 0)
totalDamage = Mathf.RoundToInt(totalDamage * _multiplier);
if (CanCrit())
{
totalDamage = CalculaterCriticalDamage(totalDamage);
}
totalDamage = CheckTargetArmor(_targetStats, totalDamage);
_targetStats.TakeDamage(totalDamage);
DoMagicDamage(_targetStats); //测试魔法伤害
}
}