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;
[Header("Piece info")]
[SerializeField] float pierceAmount;
[Header("Bounce info")]
[SerializeField]private float bounceSpeed;//设置弹跳速度
private bool isBouncing;//判断是否可以弹跳
private int bounceAmount;//弹跳的次数
public List<Transform> enemyTargets;//保存所有在剑范围内的敌人的列表
private int targetIndex;//设置targetIndex作为敌人计数器
[Header("Spin info")]
private float maxTravelDistance;//最大攻击距离
private float spinDuration;//持续时间
private float spinTimer;//计时器
private bool wasStopped;//是否停止
private bool isSpinning;
private float hitTimer;
private float hitColldown;
private float spinDirection;//用来不断推进剑在x轴上移动的值
private void Awake()
{
anim = GetComponentInChildren<Animator>();
rb = GetComponent<Rigidbody2D>();
cd = GetComponent<CircleCollider2D>();
}
public void ReturnSword()
{
rb.constraints = RigidbodyConstraints2D.FreezeAll;//修复剑只要不触碰到物体就无法收回的bug
//rb.isKinematic = false;
transform.parent = null;
isReturning = true;
}
public void SetupSword(Vector2 _dir,float _gravityScale,Player _player)
{
player = _player;
rb.velocity = _dir;
rb.gravityScale = _gravityScale;
if(pierceAmount <= 0)
anim.SetBool("Rotation", true); //其次在pierceAmount > 0时不启动旋转动画
spinDirection = Mathf.Clamp(rb.velocity.x, -1, 1);用来不断推进剑在x轴上移动的值
//Mathf.Clamp,当剑使向右飞的时候,direction为1,反之为-1
//https://docs.unity3d.com/cn/current/ScriptReference/Mathf.Clamp.html
}
public void SetupBounce(bool _isBouncing,int _bounceAmount)
{
isBouncing = _isBouncing;
bounceAmount = _bounceAmount;
}
public void SetupPierce(int _pierceAomunt)
{
pierceAmount = _pierceAomunt;
}
public void SetupSpin(bool _isSpinning, float _maxTravelDistance,float _spinDuration,float _hitCooldown)
{
isSpinning = _isSpinning;
maxTravelDistance = _maxTravelDistance;
spinDuration = _spinDuration;
hitColldown = _hitCooldown;
}
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.CatchTheSword();
}
}
BounceLogic();
SpinLogic();
}
private void SpinLogic()
{
if (isSpinning)//首先当isSpining为true才可进入
{
if (Vector2.Distance(player.transform.position, transform.position) > maxTravelDistance)//当剑与角色到达最大攻击距离,stop为true,停止剑运动,给剑一个倒计时
{
StopWhenSpinning();
}
if (wasStopped)//当stop为true,倒计时过了,回归角色
{
spinTimer -= Time.deltaTime; //spinDirection一直是1或者-1,但position是变化的
transform.position = Vector2.MoveTowards(transform.position, new Vector2(transform.position.x + spinDirection, transform.position.y), 1.5f * Time.deltaTime);//让剑在x轴上移动的函数
if (spinTimer < 0)
{
isReturning = true;
isSpinning = false;
}
hitTimer -= Time.deltaTime;
if (hitTimer < 0)
{
hitTimer = hitColldown;
Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 1);
foreach (var hit in colliders)
{
if (hit.GetComponent<Enemy>() != null)
{
hit.GetComponent<Enemy>().Damage();
}
}
}
}
}
}
private void StopWhenSpinning()
{
wasStopped = true;
rb.constraints = RigidbodyConstraints2D.FreezeAll;
spinTimer = spinDuration;
}
private void BounceLogic()
{
if (isBouncing && enemyTargets.Count > 0)
{
transform.position = Vector2.MoveTowards(transform.position, enemyTargets[targetIndex].position, bounceSpeed * Time.deltaTime);
//3.当可以弹跳且列表内数量大于0,调用ToWords,这将使剑能够跳到敌人身上
if (Vector2.Distance(transform.position, enemyTargets[targetIndex].position) < .1f)
{
enemyTargets[targetIndex].GetComponent<Enemy>().Damage();
targetIndex++;
bounceAmount--;//设置弹跳次数
if (bounceAmount <= 0)
{
isBouncing = false;
isReturning = true;
}//这样会使当弹跳次数为0时,返回到角色手中
if (targetIndex >= enemyTargets.Count)
{
targetIndex = 0;
}
}//利用与目标距离的判断,使剑接近目标距离时切换到下一个目标。
//且如果所有目标都跳完了,切回第一个
}
}
private void OnTriggerEnter2D(Collider2D collision)//传入碰到的物体的碰撞器
{
if (isReturning)
{
return;
}//修复在返回时扔出时没有碰到任何物体,但返回时碰到了物体导致剑的一些性质发生改变的问题,及回来的时候调用了OnTriggerEnter2D
collision.GetComponent<Enemy>()?.Damage();//设置一下敌人受击动画
SetupTargetsForBounce(collision);
StuckInto(collision);
}//打开IsTrigger时才可使用该函数
private void SetupTargetsForBounce(Collider2D collision)
{
if (collision.GetComponent<Enemy>() != null)//首先得碰到敌人,只有碰到敌人才会跳
{
if (isBouncing && enemyTargets.Count <= 0)
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 10);
foreach (var hit in colliders)
{
if (hit.GetComponent<Enemy>() != null)
{
enemyTargets.Add(hit.transform);
}
}
}
}
}
//https://docs.unity3d.com/cn/current/ScriptReference/Collider2D.OnTriggerEnter2D.html
private void StuckInto(Collider2D collision)
{
if(pierceAmount > 0 && collision.GetComponent<Enemy>()!= null)//本质就是能穿过敌人,在amount>0时不执行能让剑卡在敌人里的语句就行
{
pierceAmount--;
return;
}
if (isSpinning)
{
StopWhenSpinning();
return;
}//防止卡在敌人身上
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;//冻结所有移动
if (isBouncing&&enemyTargets.Count>0)//修复剑卡不到墙上的bug
return;
//终止对动画的改变和终止附在敌人上
transform.parent = collision.transform;//设置sword的父组件为碰撞到的物体
anim.SetBool("Rotation", false);
}
}
Sword_Skill.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SwordType
{
Regular,
Bounce,
Pierce,
Spin
}
public class Sword_Skill : Skill
{
public SwordType swordType = SwordType.Regular;//创建应一个enum列表,后面用来判断切换状态
[Header("Bounce Info")]
[SerializeField] private int bounceAmount;
[SerializeField] private float bounceGravity;
[Header("Pierce info")]
[SerializeField] private int pierceAmount;
[SerializeField] private float pierceGravity;
[Header("Skill Info")]
[SerializeField] private GameObject swordPrefab;//sword预制体
[SerializeField] private Vector2 launchForce;//发射力度
[SerializeField] private float swordGravity;//发射体的重力
[Header("Spin info")]
[SerializeField] private float hitCooldown = .35f;//攻击冷却
[SerializeField] private float maxTravelDistance = 7;//最大攻击距离
[SerializeField] private float spinDuration = 2;//持续时间
[SerializeField] private float spinGravity = 1;
[Header("Aim dots")]
[SerializeField] private int numberOfDots;//需要的点的数量
[SerializeField] private float spaceBetweenDots;//相隔的距离
[SerializeField] private GameObject dotPrefab;//dot预制体
[SerializeField] private Transform dotsParent;//不是很懂
private GameObject[] dots;//dot组
private Vector2 finalDir;
protected override void Start()
{
base.Start();
GenerateDots();//生成点函数
SetupGravity();
}
private void SetupGravity()//每个剑的状态都对应了不同的剑重力
{
if(swordType == SwordType.Pierce)
{
swordGravity = pierceGravity;
}
if(swordType == SwordType.Bounce)
{
swordGravity = bounceGravity;
}
if(swordType == SwordType.Spin)
{
swordGravity = spinGravity;
}
}
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
if(swordType == SwordType.Bounce)
{
newSwordScript.SetupBounce(true, bounceAmount);
}
else if(swordType == SwordType.Pierce)
{
newSwordScript.SetupPierce(pierceAmount);
}
else if(swordType == SwordType.Spin)
{
newSwordScript.SetupSpin(true, maxTravelDistance, spinDuration,hitCooldown);
}
newSwordScript.SetupSword(finalDir, swordGravity, player);//调用Controller里的SetupSword函数,给予其速度和重力和player实例
player.AssignNewSword(newSword);//调用在player中保存通过此方法创造出的sword实例
DotsActive(false);//关闭点的显示
}
#region Aim region
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;//返回位置
}//设置点间距函数
#endregion
}