Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
Sword_Skill_Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sword_Skill_Controller : MonoBehaviour
{
[SerializeField] private float returnSpeed = 12;
private bool isReturning;
private Animator anim;
private Rigidbody2D rb;
private CircleCollider2D cd;
private Player player;
private bool canRotate = true;
private void Awake()
{
anim = GetComponentInChildren<Animator>();
rb = GetComponent<Rigidbody2D>();
cd = GetComponent<CircleCollider2D>();
}
public void ReturnSword()
{
rb.isKinematic = true;
transform.parent = null;
isReturning = true;
}
public void SetupSword(Vector2 _dir,float _gravityScale,Player _player)
{
player = _player;
rb.velocity = _dir;
rb.gravityScale = _gravityScale;
anim.SetBool("Rotation", true);
}
private void Update()
{
if(canRotate)
transform.right = rb.velocity;//使武器随着速度而改变朝向
if (isReturning)
{
transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);//从原来的位置返回到player的位置
//并且随着时间增加而增加速度
if(Vector2.Distance(transform.position,player.transform.position)<1)//当剑与player的距离小于一定距离,清除剑
{
player.ClearTheSword();
}
}
}
private void OnTriggerEnter2D(Collider2D collision)//传入碰到的物体的碰撞器
{
canRotate = false;
cd.enabled = false;//相当于关闭碰撞后触发函数。//https://docs.unity3d.com/cn/current/ScriptReference/Collision2D-enabled.html
rb.isKinematic = true;//开启物理学反应 https://docs.unity3d.com/cn/current/ScriptReference/Rigidbody2D-isKinematic.html
rb.constraints = RigidbodyConstraints2D.FreezeAll;//冻结所有移动
transform.parent = collision.transform;//设置sword的父组件为碰撞到的物体
anim.SetBool("Rotation", false);
}//打开IsTrigger时才可使用该函数
//https://docs.unity3d.com/cn/current/ScriptReference/Collider2D.OnTriggerEnter2D.html
}
Sword_Skill.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sword_Skill : Skill
{
[Header("Skill Info")]
[SerializeField] private GameObject swordPrefab;//sword预制体
[SerializeField] private Vector2 launchForce;//发射力度
[SerializeField] private float swordGravity;//发射体的重力
private Vector2 finalDir;//发射方向
[Header("Aim dots")]
[SerializeField] private int numberOfDots;//需要的点的数量
[SerializeField] private float spaceBetweenDots;//相隔的距离
[SerializeField] private GameObject dotPrefab;//dot预制体
[SerializeField] private Transform dotsParent;//不是很懂
private GameObject[] dots;//dot组
protected override void Start()
{
base.Start();
GenerateDots();//生成点函数
}
protected override void Update()
{
base.Update();
if (Input.GetKeyUp(KeyCode.Mouse1))
{
finalDir = new Vector2(AimDirection().normalized.x * launchForce.x, AimDirection().normalized.y * launchForce.y);
//将位移量改为单位向量分别与力度的x,y相乘作为finalDir
}
if(Input.GetKey(KeyCode.Mouse1))
{
for (int i = 0; i < dots.Length;i++)
{
dots[i].transform.position = DotsPosition(i * spaceBetweenDots);//用循环为每个点以返回值赋值(传入值为每个点的顺序i*点间距
}
}
}
public void CreateSword()
{
GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);//创造实例,初始位置为此时player的位置
Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();//获得Controller
newSwordScript.SetupSword(finalDir, swordGravity,player);//调用Controller里的SetupSword函数,给予其速度和重力和player实例
player.AssignNewSword(newSword);//调用在player中保存通过此方法创造出的sword实例
DotsActive(false);//关闭点的显示
}
public Vector2 AimDirection()
{
Vector2 playerPosition = player.transform.position;//拿到玩家此时的位置
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);//https://docs.unity3d.com/cn/current/ScriptReference/Camera.ScreenToWorldPoint.html
//大概就是返回屏幕上括号里的参数的位置,这里返回了鼠标的位置
//拿到此时鼠标的位置
Vector2 direction = mousePosition - playerPosition;//获得距离的绝对向量
return direction;//返回距离向量
}
public void DotsActive(bool _isActive)
{
for(int i = 0;i<dots.Length;i++)
{
dots[i].SetActive(_isActive);//设置每个点是否显示函数
}
}
private void GenerateDots()//生成点函数
{
dots = new GameObject[numberOfDots];//为dot赋予实例数量
for(int i = 0;i < numberOfDots;i++)
{
dots[i] = Instantiate(dotPrefab, player.transform.position, Quaternion.identity,dotsParent);//对象与世界轴或父轴完全对齐
//https://docs.unity3d.com/cn/current/ScriptReference/Quaternion-identity.html
dots[i].SetActive(false);//关闭dot
}
}
private Vector2 DotsPosition(float t)//传入顺序相关的点间距
{
Vector2 position = (Vector2)player.transform.position +
new Vector2
(AimDirection().normalized.x * launchForce.x,
AimDirection().normalized.y * launchForce.y) * t //基本间距
+ .5f * (Physics2D.gravity * swordGravity) * (t * t)//重力影响
;
//t是控制之间点间距的
return position;//返回位置
}//设置点间距函数
}
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;
[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 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");
//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 ClearTheSword()
{
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切换设置成一个函数,使其在所以情况下都能使用
}
PlayerGroundState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//GroundState用于保证只有在Idle和Move这两个地面状态下才能调用某些函数,并且稍微减少一点代码量
public class PlayerGroundState : PlayerState
{
public PlayerGroundState(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(Input.GetKeyDown(KeyCode.Mouse1)&&HasNoSword())//点击右键进入瞄准状态,当sword存在时,不能进入aim状态
{
stateMachine.ChangeState(player.aimSword);
}
if(Input.GetKeyDown(KeyCode.Q))//摁Q进入反击状态
{
stateMachine.ChangeState(player.counterAttack);
}
if(Input.GetKeyDown(KeyCode.Mouse0))//p38 2.从ground进入攻击状态
{
stateMachine.ChangeState(player.primaryAttack);
}
if(player.IsGroundDetected()==false)
{
stateMachine.ChangeState(player.airState);
}// 写这个是为了防止在空中直接切换为moveState了。
if (Input.GetKeyDown(KeyCode.Space) && player.IsGroundDetected())
{
stateMachine.ChangeState(player.jumpState);
}//空格切换为跳跃状态
}
private bool HasNoSword()//用这个函数同时控制了是否能进入aimSword和如果sword存在便使他回归player的功能
{
if(!player.sword)
{
return true;
}
player.sword.GetComponent<Sword_Skill_Controller>().ReturnSword();
return false;
}
}