Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码、
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
Blackhole_Skill_Controller.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class Blackhole_Skill_Controller : MonoBehaviour
{
[SerializeField] private GameObject hotKeyPrefab;
[SerializeField] private List<KeyCode> KeyCodeList;
private float maxSize;//最大尺寸
private float growSpeed;//变大速度
private float shrinkSpeed;//缩小速度
private float blackholeTimer;
private bool canGrow = true;//是否可以变大
private bool canShrink;//缩小
private bool canCreateHotKeys = true;专门控制后面进入的没法生成热键
private bool cloneAttackReleased;
private int amountOfAttacks = 4;
private float cloneAttackCooldown = .3f;
private float cloneAttackTimer;
private List<Transform> targets = new List<Transform>();
private List<GameObject> createdHotKey = new List<GameObject>();
public bool playerCanExitState { get; private set; }
public void SetupBlackhole(float _maxSize,float _growSpeed,float _shrinkSpeed,int _amountOfAttacks,float _cloneAttackCooldown,float _blackholeDuration)
{
maxSize = _maxSize;
growSpeed = _growSpeed;
shrinkSpeed = _shrinkSpeed;
amountOfAttacks = _amountOfAttacks;
cloneAttackCooldown = _cloneAttackCooldown;
blackholeTimer = _blackholeDuration;
}
private void Update()
{
blackholeTimer -= Time.deltaTime;
cloneAttackTimer -= Time.deltaTime;
if(blackholeTimer <= 0)
{
blackholeTimer = Mathf.Infinity;//防止重复检测
if (targets.Count > 0)//只有有target才释放攻击
{
ReleaseCloneAttack();//释放攻击
}
else
FinishBlackholeAbility();//缩小黑洞
}
if (Input.GetKeyDown(KeyCode.R)&& targets.Count > 0)
{
ReleaseCloneAttack();
}
CloneAttackLogic();
if (canGrow && !canShrink)
{
//这是控制物体大小的参数
transform.localScale = Vector2.Lerp(transform.localScale, new Vector2(maxSize, maxSize), growSpeed * Time.deltaTime);
//类似MoveToward,不过是放大到多少大小 https://docs.unity3d.com/cn/current/ScriptReference/Vector2.Lerp.html
}
if (canShrink)
{
transform.localScale = Vector2.Lerp(transform.localScale, new Vector2(0, 0), shrinkSpeed * Time.deltaTime);
if (transform.localScale.x <= 1f)
{
Destroy(gameObject);
}
}
}
private void ReleaseCloneAttack()
{
cloneAttackReleased = true;
canCreateHotKeys = false;
DestroyHotKeys();
PlayerManager.instance.player.MakeTransprent(true);
}
private void CloneAttackLogic()
{
if (cloneAttackTimer < 0 && cloneAttackReleased&&amountOfAttacks>0)
{
cloneAttackTimer = cloneAttackCooldown;
int randomIndex = Random.Range(0, targets.Count);
//限制攻击次数和设置攻击偏移量
float _offset;
if (Random.Range(0, 100) > 50)
_offset = 1.5f;
else
_offset = -1.5f;
SkillManager.instance.clone.CreateClone(targets[randomIndex], new Vector3(_offset, 0, 0));
amountOfAttacks--;
if (amountOfAttacks <= 0)
{
Invoke("FinishBlackholeAbility", 0.5f);
}
}
}
private void FinishBlackholeAbility()
{
DestroyHotKeys();
canShrink = true;
cloneAttackReleased = false;
playerCanExitState = true;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.GetComponent<Enemy>()!=null)
{
collision.GetComponent<Enemy>().FreezeTime(true);
CreateHotKey(collision);
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.GetComponent<Enemy>() != null)
{
collision.GetComponent<Enemy>().FreezeTime(false);
}
}
private void CreateHotKey(Collider2D collision)
{
if(KeyCodeList.Count == 0)//当所有的KeyCode都被去除,就不在创建实例
{
return;
}
if(!canCreateHotKeys)//这是当角色已经开大了,不在创建实例
{
return;
}
//创建实例
GameObject newHotKey = Instantiate(hotKeyPrefab, collision.transform.position + new Vector3(0, 2), Quaternion.identity);
//将实例添加进列表
createdHotKey.Add(newHotKey);
//随机KeyCode传给HotKey,并且传过去一个毁掉一个
KeyCode choosenKey = KeyCodeList[Random.Range(0, KeyCodeList.Count)];
KeyCodeList.Remove(choosenKey);
Blackhole_Hotkey_Controller newHotKeyScript = newHotKey.GetComponent<Blackhole_Hotkey_Controller>();
newHotKeyScript.SetupHotKey(choosenKey, collision.transform, this);
}
public void AddEnemyToList(Transform _myEnemy)
{
targets.Add(_myEnemy);
}
//销毁Hotkey
private void DestroyHotKeys()
{
if(createdHotKey.Count <= 0)
{
return;
}
for (int i = 0; i < createdHotKey.Count; i++)
{
Destroy(createdHotKey[i]);
}
}
}
Blackhole_Skill.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Blackhole_Skill : Skill
{
[SerializeField]private float maxSize;//最大尺寸
[SerializeField] private float growSpeed;//变大速度
[SerializeField] private float shrinkSpeed;//缩小速度
[SerializeField] private GameObject blackholePrefab;
[Space]
[SerializeField] private float blackholeDuration;
[SerializeField] int amountOfAttacks = 4;
[SerializeField] float cloneAttackCooldown = .3f;
Blackhole_Skill_Controller currentBlackhole;
public override bool CanUseSkill()
{
return base.CanUseSkill();
}
public override void UseSkill()
{
base.UseSkill();
GameObject newBlackhole = Instantiate(blackholePrefab,player.transform.position,Quaternion.identity);
currentBlackhole = newBlackhole.GetComponent<Blackhole_Skill_Controller>();
currentBlackhole.SetupBlackhole(maxSize,growSpeed,shrinkSpeed,amountOfAttacks,cloneAttackCooldown,blackholeDuration);
}
protected override void Start()
{
base.Start();
}
protected override void Update()
{
base.Update();
}
public bool SkillCompleted()
{
if(currentBlackhole == null)
return false;
if (currentBlackhole.playerCanExitState)
{
return true;
}
else
{
return false;
}
}
}
Player.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作为击退的参数
[Header("Dash Info")]
[SerializeField] private float dashCooldown;
private float dashUsageTimer;//为dash设置冷却时间,在一定时间内不能连续使用
public float dashSpeed;//冲刺速度
public float dashDuration;//持续时间
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 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动画
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;
}
protected override void Update()//在mano中update会自动刷新但其他没有mano的不会故,需要在这个updata中调用其他脚本中的函数stateMachine.currentState.update以实现 //stateMachine中的update
{
base.Update();
stateMachine.currentState.Update();//反复调用CurrentState的Update函数
CheckForDashInput();
}
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 (Input.GetKeyDown(KeyCode.LeftShift) && skill.dash.CanUseSkill())//将DashTimer<0 的判断 改成DashSkill里的判断
{
dashDir = Input.GetAxisRaw("Horizontal");//设置一个值,可以将dash的方向改为你想要的方向而不是你的朝向
if (dashDir == 0)
{
dashDir = facingDir;//只有当玩家没有控制方向时才使用默认朝向
}
stateMachine.ChangeState(dashState);
}
}//将Dash切换设置成一个函数,使其在所以情况下都能使用
}
PlayerBlackholeState.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class PlayerBlackholeState : PlayerState
{
private float flyTime = .4f;//飞行时间
private bool skillUsed;//技能是否在被使用
private float defaultGravity;
public PlayerBlackholeState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
{
}
public override void AnimationFinishTrigger()
{
base.AnimationFinishTrigger();
}
public override void Enter()
{
base.Enter();
skillUsed = false;
stateTimer = flyTime;
defaultGravity = rb.gravityScale;
rb.gravityScale = 0;
}
public override void Exit()
{
base.Exit();
rb.gravityScale = defaultGravity;
player.MakeTransprent(false);
}
public override void Update()
{
base.Update();
//使角色释放技能后能飞起来
if (stateTimer > 0)
{
rb.velocity = new Vector2(0, 15);
}
if(stateTimer < 0)
{
rb.velocity = new Vector2(0, -.1f);
if(!skillUsed)
{
if(player.skill.blackhole.CanUseSkill())//创建实体
skillUsed = true;
}
}
if(player.skill.blackhole.SkillCompleted())
{
stateMachine.ChangeState(player.airState);
}
}
}