Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
UI.cs
using UnityEngine;
public class UI : MonoBehaviour
{
[SerializeField] private GameObject characterUI;
[SerializeField] private GameObject skillTreeUI;
[SerializeField] private GameObject craftUI;
[SerializeField] private GameObject optionsUI;
public UI_itemTooltip itemToolTip;
public UI_statToolTip statToopTip;
public Ui_SkillToolTip skillToolTip;
public UI_CraftWindow craftWindow;
public void Awake()
{
SwitchTo(skillTreeUI);//修复可能出现skill没法加载成功的bug
}
public void Start()
{
SwitchTo(null);
itemToolTip.gameObject.SetActive(false);
statToopTip.gameObject.SetActive(false);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.C))
{
SwitchWithKeyTo(characterUI);
}
if(Input.GetKeyDown(KeyCode.B))
{
SwitchWithKeyTo(craftUI);
}
if(Input.GetKeyDown(KeyCode.K))
{
SwitchWithKeyTo(skillTreeUI);
}
if(Input.GetKeyDown(KeyCode.O))
{
SwitchWithKeyTo(optionsUI);
}
}
public void SwitchTo(GameObject _menu)//切换窗口函数
{
for (int i = 0; i < transform.childCount; i++)
{
transform.GetChild(i).gameObject.SetActive(false);
}
if (_menu != null)
{
_menu.SetActive(true);
}
}
public void SwitchWithKeyTo(GameObject _menu)//键盘切换窗口函数
{
if (_menu != null && _menu.activeSelf)//通过判断是否传入mune和mune是否激活来决定使设置为可视或不可使
{
_menu.SetActive(false);
return;
}
SwitchTo(_menu);
}
}
CharacterStats.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting.Antlr3.Runtime.Misc;
using UnityEngine;
public enum StatType
{
strength,
agility,
intelligence,
vitality,
damage,
critChance,
critPower,
Health,
armor,
evasion,
magicResistance,
fireDamage,
iceDamage,
lightingDamage
}
public class CharacterStats : MonoBehaviour
{
private EntityFX fx;
[Header("Major stats")]
public Stat strength; // 力量 增伤1点 爆伤增加 1% 物抗
public Stat agility;// 敏捷 闪避 1% 闪避几率增加 1%
public Stat intelligence;// 1 点 魔法伤害 1点魔抗
public Stat vitality;//加血的
[Header("Offensive stats")]
public Stat damage;
public Stat critChance; // 暴击率
public Stat critPower; //150% 爆伤
[Header("Defensive stats")]
public Stat Health;
public Stat armor;
public Stat evasion;//闪避值
public Stat magicResistance;
[Header("Magic stats")]
public Stat fireDamage;
public Stat iceDamage;
public Stat lightingDamage;
public bool isIgnited; // 持续烧伤
public bool isChilded; // 削弱护甲 20%
public bool isShocked; // 降低敌人命中率
[SerializeField] private float ailmentsDuration = 4;
private float ignitedTimer;
private float chilledTimer;
private float shockedTimer;
private float igniteDamageCooldown = .3f;
private float ignitedDamageTimer;
private int igniteDamage;
[SerializeField] private GameObject shockStrikePrefab;
private int shockDamage;
public System.Action onHealthChanged;//使角色在Stat里调用UI层的函数
//此函数调用了更新HealthUI函数
public bool isDead { get; private set; }
private bool isVulnerable;//脆弱效果
[SerializeField] public int currentHealth;
protected virtual void Start()
{
critPower.SetDefaultValue(150);//设置默认爆伤
currentHealth = GetMaxHealthValue();
fx = GetComponent<EntityFX>();
}
public void MakeVulnerableFor(float _duration) => StartCoroutine(VulnerableCorutine(_duration));//脆弱效果函数
private IEnumerator VulnerableCorutine(float _duration)
{
isVulnerable = true;
yield return new WaitForSeconds(_duration);
isVulnerable = false;
}
protected virtual void Update()
{
//所有的状态都设置上默认持续时间,持续过了就结束状态
ignitedTimer -= Time.deltaTime;
chilledTimer -= Time.deltaTime;
shockedTimer -= Time.deltaTime;
ignitedDamageTimer -= Time.deltaTime;
if (ignitedTimer < 0)
isIgnited = false;
if (chilledTimer < 0)
isChilded = false;
if (shockedTimer < 0)
isShocked = false;
//被点燃后,出现多段伤害后点燃停止
if(isIgnited)
ApplyIgnitedDamage();
}
public virtual void IncreaseStatBy(int _modifier, float _duration,Stat _statToModify)
{
StartCoroutine(StatModCoroutine(_modifier, _duration, _statToModify));
}
private IEnumerator StatModCoroutine(int _modifier, float _duration, Stat _statToModify)
{
_statToModify.AddModifier(_modifier);
yield return new WaitForSeconds(_duration);
_statToModify.RemoveModifier(_modifier);
}
public virtual void DoDamage(CharacterStats _targetStats)//计算后造成伤害函数
{
if (TargetCanAvoidAttack(_targetStats))设置闪避
{
return;
}
int totleDamage = damage.GetValue() + strength.GetValue();
//爆伤设置
if (CanCrit())
{
totleDamage = CalculateCriticalDamage(totleDamage);
}
totleDamage = CheckTargetArmor(_targetStats, totleDamage);//设置防御
_targetStats.TakeDamage(totleDamage);
DoMagicaDamage(_targetStats); // 可以去了也可以不去
}
protected virtual void Die()
{
isDead = true;
}
public virtual void TakeDamage(int _damage)//造成伤害是出特效
{
fx.StartCoroutine("FlashFX");//IEnumertor本质就是将一个函数分块执行,只有满足某些条件才能执行下一段代码,此函数有StartCoroutine调用
//https://www.zhihu.com/tardis/bd/art/504607545?source_id=1001
DecreaseHealthBy(_damage);
GetComponent<Entity>().DamageImpact();
if (currentHealth < 0 && !isDead)
Die();
}
public virtual void IncreaseHealthBy(int _amount)//添加回血函数
{
currentHealth += _amount;
if (currentHealth > GetMaxHealthValue())
currentHealth = GetMaxHealthValue();
if (onHealthChanged != null)
onHealthChanged();
}
protected virtual void DecreaseHealthBy(int _damage)//此函数用来改变当前生命值,不调用特效
{
if (isVulnerable)
_damage = Mathf.RoundToInt(_damage * 1.1f);
currentHealth -= _damage;
if (onHealthChanged != null)
{
onHealthChanged();
}
}
#region Magical damage and ailements
private void ApplyIgnitedDamage()
{
if (ignitedDamageTimer < 0 )
{
DecreaseHealthBy(igniteDamage);
if (currentHealth < 0 && !isDead)
Die();
ignitedDamageTimer = igniteDamageCooldown;
}
}被点燃后,出现多段伤害后点燃停止
public virtual void DoMagicaDamage(CharacterStats _targetStats)//法伤计算和造成元素效果调用的地方
{
int _fireDamage = fireDamage.GetValue();
int _iceDamage = iceDamage.GetValue();
int _lightingDamage = lightingDamage.GetValue();
int totleMagicalDamage = _fireDamage + _iceDamage + _lightingDamage + intelligence.GetValue();
totleMagicalDamage = CheckTargetResistance(_targetStats, totleMagicalDamage);
_targetStats.TakeDamage(totleMagicalDamage);
//防止循环在所有元素伤害为0时出现死循环
if (Mathf.Max(_fireDamage, _iceDamage, _lightingDamage) <= 0)
return;
//让元素效果取决与伤害
//为了防止出现元素伤害一致而导致无法触发元素效果
//循环判断触发某个元素效果
AttemptyToApplyAilement(_targetStats, _fireDamage, _iceDamage, _lightingDamage);
}
private void AttemptyToApplyAilement(CharacterStats _targetStats, int _fireDamage, int _iceDamage, int _lightingDamage)
{
bool canApplyIgnite = _fireDamage > _iceDamage && _fireDamage > _lightingDamage;
bool canApplyChill = _iceDamage > _lightingDamage && _iceDamage > _fireDamage;
bool canApplyShock = _lightingDamage > _fireDamage && _lightingDamage > _iceDamage;
while (!canApplyIgnite && !canApplyChill && !canApplyShock)
{
if (Random.value < .25f)
{
canApplyIgnite = true;
Debug.Log("Ignited");
_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
return;
}
if (Random.value < .35f)
{
canApplyChill = true;
Debug.Log("Chilled");
_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
return;
}
if (Random.value < .55f)
{
canApplyShock = true;
Debug.Log("Shocked");
_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
return;
}
}
if (canApplyIgnite)
{
_targetStats.SetupIgniteDamage(Mathf.RoundToInt(_fireDamage * .2f));
}
if (canApplyShock)
_targetStats.SetupShockStrikeDamage(Mathf.RoundToInt(_lightingDamage * .1f));
//给点燃伤害赋值
_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
}//造成元素效果
public void ApplyAilments(bool _ignite, bool _chill, bool _shock)//判断异常状态
{
bool canApplyIgnite = !isIgnited && !isChilded && !isShocked;
bool canApplyChill = !isIgnited && !isChilded && !isShocked;
bool canApplyShock = !isIgnited && !isChilded;
//使当isShock为真时Shock里的函数仍然可以调用
if (_ignite && canApplyIgnite)
{
isIgnited = _ignite;
ignitedTimer = ailmentsDuration;
fx.IgniteFxFor(ailmentsDuration);
}
if (_chill && canApplyChill)
{
isChilded = _chill;
chilledTimer = ailmentsDuration;
float slowPercentage = .2f;
GetComponent<Entity>().SlowEntityBy(slowPercentage, ailmentsDuration);
fx.ChillFxFor(ailmentsDuration);
}
if (_shock && canApplyShock)
{
if(!isShocked)
{
ApplyShock(_shock);
}
else
{
if (GetComponent<Player>() != null)//防止出现敌人使玩家进入shock状态后也出现闪电
return;
HitNearestTargetWithShockStrike();
}//isShock为真时反复执行的函数为寻找最近的敌人,创建闪电实例并传入数据
}
}
public void ApplyShock(bool _shock)
{
if (isShocked)
return;
isShocked = _shock;
shockedTimer = ailmentsDuration;
fx.ShockFxFor(ailmentsDuration);
}//触电变色效果
private void HitNearestTargetWithShockStrike()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 25);//找到环绕自己的所有碰撞器
float closestDistance = Mathf.Infinity;//正无穷大的表示形式(只读)
Transform closestEnemy = null;
//https://docs.unity3d.com/cn/current/ScriptReference/Mathf.Infinity.html
foreach (var hit in colliders)
{
if (hit.GetComponent<Enemy>() != null && Vector2.Distance(transform.position, hit.transform.position) > 1)// 防止最近的敌人就是Shock状态敌人自己
{
float distanceToEnemy = Vector2.Distance(transform.position, hit.transform.position);//拿到与敌人之间的距离
if (distanceToEnemy < closestDistance)//比较距离,如果离得更近,保存这个敌人的位置,更改最近距离
{
closestDistance = distanceToEnemy;
closestEnemy = hit.transform;
}
}
if (closestEnemy == null)
closestEnemy = transform;
}
if (closestEnemy != null)
{
GameObject newShockStrike = Instantiate(shockStrikePrefab, transform.position, Quaternion.identity);
newShockStrike.GetComponent<ShockStrike_Controller>().Setup(shockDamage, closestEnemy.GetComponent<CharacterStats>());
}
}//给最近的敌人以雷劈
public void SetupIgniteDamage(int _damage) => igniteDamage = _damage;//给点燃伤害赋值
public void SetupShockStrikeDamage(int _damage) => shockDamage = _damage;//雷电伤害赋值
#endregion
#region Stat calculations
private int CheckTargetResistance(CharacterStats _targetStats, int totleMagicalDamage)//法抗计算
{
totleMagicalDamage -= _targetStats.magicResistance.GetValue() + (_targetStats.intelligence.GetValue() * 3);
totleMagicalDamage = Mathf.Clamp(totleMagicalDamage, 0, int.MaxValue);
return totleMagicalDamage;
}
protected static int CheckTargetArmor(CharacterStats _targetStats, int totleDamage)//防御计算
{
//被冰冻后,角色护甲减少
if (_targetStats.isChilded)
totleDamage -= Mathf.RoundToInt(_targetStats.armor.GetValue() * .8f);
else
totleDamage -= _targetStats.armor.GetValue();
totleDamage = Mathf.Clamp(totleDamage, 0, int.MaxValue);
return totleDamage;
}
public virtual void OnEvasion()
{
}//可继承成功闪避触发的函数
protected bool TargetCanAvoidAttack(CharacterStats _targetStats)//闪避计算
{
int totleEvation = _targetStats.evasion.GetValue() + _targetStats.agility.GetValue();
//我被麻痹后
//敌人的闪避率提升
if (isShocked)
totleEvation += 20;
if (Random.Range(0, 100) < totleEvation)
{
_targetStats.OnEvasion();
return true;
}
return false;
}
protected bool CanCrit()//判断是否暴击
{
int totleCriticalChance = critChance.GetValue() + agility.GetValue();
if (Random.Range(0, 100) <= totleCriticalChance)
{
return true;
}
return false;
}
protected int CalculateCriticalDamage(int _damage)//计算暴击后伤害
{
float totleCirticalPower = (critPower.GetValue() + strength.GetValue()) * .01f;
float critDamage = _damage * totleCirticalPower;
return Mathf.RoundToInt(critDamage);//返回舍入为最近整数的
}
public int GetMaxHealthValue()
{
return Health.GetValue() + vitality.GetValue() * 10;
}//统计生命值函数
public Stat GetStats(StatType _statType)
{
if (_statType == StatType.strength) return strength;
else if (_statType == StatType.agility) return agility;
else if (_statType == StatType.intelligence) return intelligence;
else if (_statType == StatType.vitality) return vitality;
else if (_statType == StatType.damage) return damage;
else if (_statType == StatType.critChance) return critChance;
else if (_statType == StatType.critPower) return critPower;
else if (_statType == StatType.Health) return Health;
else if (_statType == StatType.armor) return armor;
else if (_statType == StatType.evasion) return evasion;
else if (_statType == StatType.magicResistance) return magicResistance;
else if (_statType == StatType.fireDamage) return fireDamage;
else if (_statType == StatType.iceDamage) return iceDamage;
else if (_statType == StatType.lightingDamage) return lightingDamage;
return null;
}
#endregion
}
PlayerStat.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerStats : CharacterStats
{
private Player player;
protected override void Start()
{
player = GetComponent<Player>();
base.Start();
}
public override void DoDamage(CharacterStats _targetStats)
{
base.DoDamage(_targetStats);
}
public override void TakeDamage(int _damage)
{
base.TakeDamage(_damage);
}
protected override void Die()
{
base.Die();
player.Die();
GetComponent<PlayerItemDrop>()?.GenerateDrop();
}
protected override void DecreaseHealthBy(int _damage)
{
base.DecreaseHealthBy(_damage);
ItemData_Equipment currentArmor = Inventory.instance.GetEquipment(EquipmentType.Armor);
if(currentArmor != null)
{
currentArmor.Effect(player.transform);
}
}
public override void OnEvasion()
{
player.skill.dogge.CreateMirageOnDoDogge();
}
public void CloneDoDamage(CharacterStats _targetStats,float _multiplier)
{
if (TargetCanAvoidAttack(_targetStats))设置闪避
{
return;
}
int totleDamage = damage.GetValue() + strength.GetValue();
if(_multiplier > 0)
{
totleDamage = Mathf.RoundToInt(totleDamage * _multiplier);
}
//爆伤设置
if (CanCrit())
{
totleDamage = CalculateCriticalDamage(totleDamage);
}
totleDamage = CheckTargetArmor(_targetStats, totleDamage);//设置防御
_targetStats.TakeDamage(totleDamage);
DoMagicaDamage(_targetStats); // 可以去了也可以不去
}
}
Blackhole_Skill.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Blackhole_Skill : Skill
{
[SerializeField] private UI_SkillTreeSlot balckholeUnlockButton;
public bool blackholeUnlocked { get; private set; }
[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;
private void UnlockBlackhole()
{
if(balckholeUnlockButton.unlocked)
{
blackholeUnlocked = true;
}
}
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();
balckholeUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockBlackhole);
}
protected override void Update()
{
base.Update();
}
public bool SkillCompleted()
{
if(currentBlackhole == null)
return false;
if (currentBlackhole.playerCanExitState)
{
return true;
}
else
{
return false;
}
}
//把随机敌人半径改成黑洞半径的一半就行
public float GetBlackholeRadius()
{
return maxSize / 2;
}
}
Clone_Skill_Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Clone_Skill_Controller是绑在Clone体上的也就是Prefah上的
public class Clone_Skill_Controller : MonoBehaviour
{
private Player player;
private Animator anim;//声明animator
private SpriteRenderer sr;//定义Sr
[SerializeField] private float colorLoosingSpeed;//加速消失时间
private float cloneTimer;//定时器
private float attackMultiplier;
[SerializeField]private Transform attackCheck;
[SerializeField] private float attackCheckRadius = .8f;
private Transform closestEnemy;
private int facingDir = 1;//这个是控制位置的,产生的克隆体的位置能在敌人外侧
private bool canDuplicateClone;
private float chanceToDuplicate;
private void Awake()
{
sr = GetComponent<SpriteRenderer>();//拿到Sr
anim = GetComponent<Animator>();//拿到anim
}
private void Update()
{
cloneTimer -= Time.deltaTime;
if(cloneTimer < 0)
{
sr.color = new Color(1, 1, 1,sr.color.a-(Time.deltaTime * colorLoosingSpeed));//设置sr消失
}
if(sr.color.a<0)
{
Destroy(gameObject);
}
}
public void SetupClone(Transform _newTransform,float _cloneDuration,bool _canAttack,Vector3 _offset,Transform _closestEnemy,bool _canDuplicateClone,float _chanceToDuplicate,Player _player,float _attackMultiplier)
{
if(_canAttack)
{
anim.SetInteger("AttackNumber", Random.Range(1, 3));//返回[minInclusive..maxInclusive](范围包括在内)内的随机浮点值。如果minInclusive大于maxInclusive,则数字会自动交换。
}
player = _player;
//Random.Range()//https://docs.unity3d.com/cn/current/ScriptReference/Random.Range.html
transform.position = _newTransform.position+_offset;//这个函数实现了将克隆出来的对象的位置与Dash之前的位置重合的效果
attackMultiplier = _attackMultiplier;
cloneTimer = _cloneDuration;
closestEnemy = _closestEnemy;
canDuplicateClone = _canDuplicateClone;
chanceToDuplicate = _chanceToDuplicate;
FaceCloseTarget();
}
private void AnimationTrigger()
{
cloneTimer = -.1f;
}
private void AttackTrigger()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(attackCheck.position, attackCheckRadius);//创建一个碰撞器组,保存所有圈所碰到的碰撞器
//https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Physics2D.OverlapCircleAll.html
foreach (var hit in colliders)//https://blog.youkuaiyun.com/m0_52358030/article/details/121722077
{
if (hit.GetComponent<Enemy>() != null)
{
PlayerStats playerStats = player.GetComponent<PlayerStats>();
EnemyStats enemyStats = hit.GetComponent<EnemyStats>();
playerStats.CloneDoDamage(enemyStats, attackMultiplier);
if(player.skill.clone.canApplyOnHitEffect)
{
if(Inventory.instance.GetEquipment(EquipmentType.Weapon) != null)//攻击带装备特效
Inventory.instance.GetEquipment(EquipmentType.Weapon).Effect(hit.transform);
}
//使角色克隆体的攻击有概率产生新的克隆体
if (canDuplicateClone)
{
if(Random.Range(1,100)<chanceToDuplicate)
{
SkillManager.instance.clone.CreateClone(hit.transform, new Vector3(1.5f*facingDir, 0));
}
}
}
}
}
private void FaceCloseTarget()
{
if(closestEnemy != null)
{
if(transform.position.x>closestEnemy.position.x)//敌人在左面,转一圈
{
facingDir = -1;//这个是控制位置的
transform.Rotate(0,180,0);
}
}
}
}
Clone_Skill.cs
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class Clone_Skill : Skill
{
[Header("Clone Info")]
[SerializeField] private GameObject clonePrefab;//克隆原型
[SerializeField] private float cloneDuration;//克隆持续时间
[Space]
[Header("Clone attack")]//使克隆可以攻击
[SerializeField] private UI_SkillTreeSlot cloneAttackUnlockButton;
[SerializeField] private float cloneAttackMultiplier;//攻击倍率
[SerializeField] private bool canAttack;// 判断是否可以攻击
[Header("Aggresive clone")]//攻击带特效
[SerializeField] private UI_SkillTreeSlot aggresiveCloneUnlockButton;
[SerializeField] private float aggresiveCloneMultiplier;//没有用,不知道干什么的
public bool canApplyOnHitEffect { get; private set; }
[Header("Multiple clone")]//攻击产生克隆体
[SerializeField] private UI_SkillTreeSlot multipleCloneUnlockButton;
[SerializeField] private float multipleCloneMultiplier;//没有用,不知道干什么的
[SerializeField] private bool canDuplicateClone;
[SerializeField] private float chanceToDuplicate;
[Header("Crystal instead of clone")]
[SerializeField] private UI_SkillTreeSlot crystalInsteadOfCloneButton;
public bool crystalInsteadOfClone;
protected override void Start()
{
base.Start();
cloneAttackUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockCloneAttack);
aggresiveCloneUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockAggresiveClone);
multipleCloneUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockMultipleClone);
crystalInsteadOfCloneButton.GetComponent<Button>().onClick.AddListener(UnlockCrystalInsteadOfClone);
}
#region Unlock region
private void UnlockCloneAttack()
{
if(cloneAttackUnlockButton.unlocked)
{
canAttack = true;
}
}
private void UnlockAggresiveClone()
{
if (aggresiveCloneUnlockButton.unlocked)
{
canApplyOnHitEffect = true;
}
}
private void UnlockMultipleClone()
{
if (multipleCloneUnlockButton.unlocked)
{
canDuplicateClone = true;
}
}
private void UnlockCrystalInsteadOfClone()
{
if (crystalInsteadOfCloneButton.unlocked)
{
crystalInsteadOfClone = true;
}
}
#endregion
public void CreateClone(Transform _clonePosition, Vector3 _offset)//传入克隆位置
{
if (crystalInsteadOfClone)
{
SkillManager.instance.crystal.CreateCrystal();
return;
}//让所有的生成克隆的技能都变成生成水晶
GameObject newClone = Instantiate(clonePrefab);//创建新的克隆//克隆 original 对象并返回克隆对象。
//https://docs.unity3d.com/cn/current/ScriptReference/Object.Instantiate.html
newClone.GetComponent<Clone_Skill_Controller>().SetupClone(_clonePosition, cloneDuration, canAttack, _offset, FindClosestEnemy(newClone.transform), canDuplicateClone, chanceToDuplicate, player,cloneAttackMultiplier);//调试clone的位置,同时调试克隆持续时间 //Controller绑在克隆原型上的,所以用GetComponent
}
//反击后产生一个克隆背刺敌人
public void CanCreateCloneWithDelay(Transform _enemyTransform)
{
StartCoroutine(CreateDelayCoroutine(_enemyTransform, new Vector3(1 * player.facingDir, 0, 0)));
}
//整个延迟生成
private IEnumerator CreateDelayCoroutine(Transform _enemyTransform, Vector3 _offset)
{
yield return new WaitForSeconds(.4f);
CreateClone(_enemyTransform, _offset);
}
}
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.R)&& player.skill.blackhole.blackholeUnlocked)
{
stateMachine.ChangeState(player.blackhole);
}
if(Input.GetKeyDown(KeyCode.Mouse1)&&HasNoSword() && player.skill.sword.swordUnlocked)//点击右键进入瞄准状态,当sword存在时,不能进入aim状态
{
stateMachine.ChangeState(player.aimSword);
}
if(Input.GetKeyDown(KeyCode.Q) && player.skill.parry.parryUnlocked)//摁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;
}
}
PlayerAnimationTrigger.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerAnimationTriggers : MonoBehaviour
{
private Player player => GetComponentInParent<Player>();//获得夫组件上的实际存在的Player组件
private void AnimationTrigger()
{
player.AnimationTrigger();
}
private void AttackTrigger()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(player.attackCheck.position, player.attackCheckRadius);//创建一个碰撞器组,保存所有圈所碰到的碰撞器
//https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Physics2D.OverlapCircleAll.html
foreach(var hit in colliders)//https://blog.youkuaiyun.com/m0_52358030/article/details/121722077
{
if(hit.GetComponent<Enemy>()!=null)
{
EnemyStats _target = hit.GetComponent<EnemyStats>();
if(_target != null)//修复无敌人时触发DoDamge出错的bug
player.stats.DoDamage(_target);//提供一个可以从player调用敌人受伤函数的入口,通过传入受伤敌人的组件
if(Inventory.instance.GetEquipment(EquipmentType.Weapon) != null)//攻击带装备特效
Inventory.instance.GetEquipment(EquipmentType.Weapon).Effect(_target.transform);
// hit.GetComponent<Enemy>().DamageImpact();
}
}
}
private void ThrowSword()
{
SkillManager.instance.sword.CreateSword();
}
}