Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
同上一P
Player.cs
cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Player : Entity
{
[Header("Attack Details")]
public Vector2[] attackMovement;//每个攻击时获得的速度组
public float counterAttackDuration = .2f;
public bool isBusy{ get; private set; }//防止在攻击间隔中进入move
//
[Header("Move Info")]
public float moveSpeed;//定义速度,与xInput相乘控制速度的大小
public float jumpForce;
public float swordReturnImpact;//在player里设置swordReturnImpact作为击退的参数
private float defaultMoveSpeed;
private float defaultJumpForce;
[Header("Dash Info")]
[SerializeField] private float dashCooldown;
private float dashUsageTimer;//为dash设置冷却时间,在一定时间内不能连续使用
public float dashSpeed;//冲刺速度
public float dashDuration;//持续时间
private float defaultDashSpeed;
public float dashDir { get; private set; }
#region 定义States
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 wallSlide { get; private set; }
public PlayerWallJumpState wallJump { get; private set; }
public PlayerDeadState deadState { get; private set; }
public PlayerPrimaryAttackState primaryAttack { get; private set; }
public PlayerCounterAttackState counterAttack { get; private set; }
public PlayerAimSwordState aimSword { get; private set; }
public PlayerCatchSwordState catchSword { get; private set; }
public PlayerBlackholeState blackhole { get; private set; }
public SkillManager skill { get; private set; }
public GameObject sword{ get; private set; }//声明sword
#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");
wallSlide = new PlayerWallSlideState(this, stateMachine, "WallSlide");
wallJump = new PlayerWallJumpState(this, stateMachine, "Jump");//wallJump也是Jump动画
deadState = new PlayerDeadState(this, stateMachine, "Die");
primaryAttack = new PlayerPrimaryAttackState(this, stateMachine, "Attack");
counterAttack = new PlayerCounterAttackState(this, stateMachine, "CounterAttack");
aimSword = new PlayerAimSwordState(this,stateMachine, "AimSword");
catchSword = new PlayerCatchSwordState(this, stateMachine, "CatchSword");
blackhole = new PlayerBlackholeState(this, stateMachine, "Jump");
//this 就是 Player这个类本身
}//Awake初始化所以State,为所有State传入各自独有的参数,及animBool,以判断是否调用此动画(与animatoin配合完成)
protected override void Start()
{
base.Start();
stateMachine.Initialize(idleState);
skill = SkillManager.instance;
defaultMoveSpeed = moveSpeed;
defaultJumpForce = jumpForce;
defaultDashSpeed = dashSpeed;
}
protected override void Update()//在mano中update会自动刷新但其他没有mano的不会故,需要在这个updata中调用其他脚本中的函数stateMachine.currentState.update以实现 //stateMachine中的update
{
base.Update();
stateMachine.currentState.Update();//反复调用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 SlowEntityBy(float _slowPercentage, float flowDuration)//减缓一切速度函数
{
base.SlowEntityBy(_slowPercentage, flowDuration);
moveSpeed = moveSpeed * (1 - _slowPercentage);
jumpForce = jumpForce * (1 - _slowPercentage);
dashSpeed = dashSpeed * (1 - _slowPercentage);
anim.speed = anim.speed * (1 - _slowPercentage);
Invoke("ReturnDefaultSpeed", flowDuration);
}
protected override void ReturnDefaultSpeed()//全部速度恢复正常函数
{
base.ReturnDefaultSpeed();
moveSpeed = defaultMoveSpeed;
jumpForce = defaultJumpForce;
dashSpeed = defaultDashSpeed;
}
public void AssignNewSword(GameObject _newSword)//保持创造的sword实例的函数
{
sword = _newSword;
}
public void CatchTheSword()//通过player的CatchTheSword进入,及当剑消失的瞬间进入
{
stateMachine.ChangeState(catchSword);
Destroy(sword);
}
public IEnumerator BusyFor(float _seconds)//https://www.zhihu.com/tardis/bd/art/504607545?source_id=1001
{
isBusy = true;
yield return new WaitForSeconds(_seconds);
isBusy = false;
}//p39 4.防止在攻击间隔中进入move,通过设置busy值,在使用某些状态时,使其为busy为true,抑制其进入其他state
//IEnumertor本质就是将一个函数分块执行,只有满足某些条件才能执行下一段代码,此函数有StartCoroutine调用
public void AnimationTrigger() => stateMachine.currentState.AnimationFinishTrigger();
//从当前状态拿到AnimationTrigger进行调用的函数
public void CheckForDashInput()
{
if (IsWallDetected())
{
return;
}//修复在wallslide可以dash的BUG
if (skill.dash.dashUnlocked == false)
return;
if (Input.GetKeyDown(KeyCode.LeftShift) && skill.dash.CanUseSkill())//将DashTimer<0 的判断 改成DashSkill里的判断
{
dashDir = Input.GetAxisRaw("Horizontal");//设置一个值,可以将dash的方向改为你想要的方向而不是你的朝向
if (dashDir == 0)
{
dashDir = facingDir;//只有当玩家没有控制方向时才使用默认朝向
}
stateMachine.ChangeState(dashState);
}
}//将Dash切换设置成一个函数,使其在所以情况下都能使用
public override void Die()
{
base.Die();
stateMachine.ChangeState(deadState);
}
}
Crystal_Skill.cs
cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
public class Crystal_Skill : Skill
{
[SerializeField] private GameObject crystalPrefab;
[SerializeField] private float crystalDuration;
private GameObject currentCrystal;
[Header("Crystal simple")]
[SerializeField] private UI_SkillTreeSlot unlockedCrystalButton;
public bool crystalUnlocked { get; private set; }
[Header("Crystal mirage")]
[SerializeField] private UI_SkillTreeSlot unlockedCloneInsteadButton;
[SerializeField] private bool cloneInsteadOfCrystal = false;
[Header("Explosive crystal")]
[SerializeField] private UI_SkillTreeSlot unlockedExplosion;
[SerializeField] private bool canExplode;
[Header("Moving crystal")]
[SerializeField] private UI_SkillTreeSlot unlockedMovingCrystalButton;
[SerializeField] private bool canMoveToEnemy;
[SerializeField] private float moveSpeed;
[Header("Multi stacking crystal")]
[SerializeField] private UI_SkillTreeSlot unlockedMultiStackButton;
[SerializeField] private bool canUseMultiStacks;
[SerializeField] private int amountOfStacks;
[SerializeField] private float multiStackCooldown;
[SerializeField] private List<GameObject> crystalLeft = new List<GameObject>();//水晶列表
[SerializeField] private float useTimeWindow;
public override bool CanUseSkill()
{
return base.CanUseSkill();
}
public override void UseSkill()
{
base.UseSkill();
if (CanUseMultiCrystal())
return;
if (currentCrystal == null)
{
CreateCrystal();
}
else
{
//限制玩家在水晶可以移动时瞬移
if (canMoveToEnemy)
return;
//爆炸前与角色交换位置
Vector2 playerPos = player.transform.position;
player.transform.position = currentCrystal.transform.position;
currentCrystal.transform.position = playerPos;
//水晶互换时在水晶处出现clone,水晶消失
if (cloneInsteadOfCrystal)
{
SkillManager.instance.clone.CreateClone(currentCrystal.transform,Vector3.zero);
Destroy(currentCrystal);
}
else
{
currentCrystal.GetComponent<Crystal_Skill_Controller>()?.FinishCrystal();
}
}
}
public void CreateCrystal()//创建水晶函数
{
currentCrystal = Instantiate(crystalPrefab, player.transform.position, Quaternion.identity);
Crystal_Skill_Controller currentCrystalScripts = currentCrystal.GetComponent<Crystal_Skill_Controller>();
currentCrystalScripts.SetupCrystal(crystalDuration, canExplode, canMoveToEnemy, moveSpeed, FindClosestEnemy(currentCrystal.transform),player);
}
public void CurrentCrystalChooseRandomTarget() => currentCrystal.GetComponent<Crystal_Skill_Controller>().ChooseRandomEnemy();//选择随机敌人函数
protected override void Start()
{
base.Start();
unlockedCrystalButton.GetComponent<Button>().onClick.AddListener(UnlockCrystal);
unlockedCloneInsteadButton.GetComponent<Button>().onClick.AddListener(UnlockCloneInstead);
unlockedExplosion.GetComponent<Button>().onClick.AddListener(UnlockExplosion);
unlockedMovingCrystalButton.GetComponent<Button>().onClick.AddListener(UnlockMovingCrystal);
unlockedMultiStackButton.GetComponent<Button>().onClick.AddListener(UnlockedMultiStack);
}
protected override void Update()
{
base.Update();
}
private bool CanUseMultiCrystal()//将List里的东西实例化函数
{
if(canUseMultiStacks)
{
if(crystalLeft.Count > 0&&cooldownTimer<0)
{
if(crystalLeft.Count == amountOfStacks)
{
Invoke("ResetAbility", useTimeWindow);// 设置自动补充水晶函数
}
cooldown = 0;
GameObject crystalToSpawn = crystalLeft[crystalLeft.Count - 1];
GameObject newCrystal = Instantiate(crystalToSpawn, player.transform.position, Quaternion.identity);
crystalLeft.Remove(crystalToSpawn);
newCrystal.GetComponent<Crystal_Skill_Controller>().SetupCrystal(crystalDuration, canExplode, canMoveToEnemy, moveSpeed, FindClosestEnemy(newCrystal.transform), player);
//当水晶发射完设置冷却时间和使用补充水晶
if (crystalLeft.Count<=0)
{
cooldown = multiStackCooldown;
RefilCrystal();
}
}
return true;
}
return false;
}
private void RefilCrystal()//给List填充Prefab函数
{
int amountToAdd = amountOfStacks - crystalLeft.Count;
for (int i = 0;i < amountToAdd; i++)
{
crystalLeft.Add(crystalPrefab);
}
}
private void ResetAbility()//自动补充水晶函数
{
if (cooldownTimer > 0)
return;
cooldown = multiStackCooldown;
RefilCrystal();
}
#region Unlock skill region
private void UnlockCrystal()
{
if (unlockedCrystalButton.unlocked)
crystalUnlocked = true;
}
private void UnlockCloneInstead()
{
if (unlockedCloneInsteadButton.unlocked)
cloneInsteadOfCrystal = true;
}
private void UnlockExplosion()
{
if (unlockedExplosion.unlocked)
canExplode = true;
}
private void UnlockMovingCrystal()
{
if (unlockedMovingCrystalButton.unlocked)
canMoveToEnemy = true;
}
private void UnlockedMultiStack()
{
if (unlockedMultiStackButton.unlocked)
canUseMultiStacks = true;
}
#endregion
}