Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
EntityFX.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EntityFX : MonoBehaviour
{
private SpriteRenderer sr;//定义SR组件来保持要用的组件
[Header("Flash FX")]
[SerializeField] private Material hitMat;//要改成的材料
[SerializeField] private float flashDuration;//闪光的时间
private Material originalMat;//原来的材料
[Header("Aliment colors")]
[SerializeField] private Color[] chillColor;
[SerializeField] private Color[] igniteColor;
[SerializeField] private Color[] shockColor;
private void Start()
{
sr = GetComponentInChildren<SpriteRenderer>();//从子组件中拿到SR组件
originalMat = sr.material;//拿到原来的材料
}
private IEnumerator FlashFX()//被打后该触发的函数
{
sr.material = hitMat;
//修复在元素效果期间击中,颜色变红的情况
Color currentColor = sr.color;
sr.color = Color.white;
yield return new WaitForSeconds(flashDuration);
sr.color = currentColor;
sr.material = originalMat;
} //IEnumertor本质就是将一个函数分块执行,只有满足某些条件才能执行下一段代码,此函数有StartCoroutine调用
//https://www.zhihu.com/tardis/bd/art/504607545?source_id=1001
private void RedColorBlink()//使角色闪烁的函数
{
if (sr.color != Color.white)
{
sr.color = Color.white;
}
else
{
sr.color = Color.red;
}
}
private void CancelColorChange()//使角色停止闪烁的函数
{
CancelInvoke();//取消该 MonoBehaviour 上的所有 Invoke 调用。
//https://docs.unity3d.com/cn/current/ScriptReference/MonoBehaviour.CancelInvoke.html
sr.color = Color.white;
}
public void ShockFxFor(float _second)
{
InvokeRepeating("ShockColorFx", 0, .3f);
Invoke("CancelColorChange", _second);
}
public void ChillFxFor(float _second)
{
InvokeRepeating("ChillColor", 0, .3f);
Invoke("CancelColorChange", _second);
}
public void IgniteFxFor(float _second)
{
InvokeRepeating("IgniteColorFX", 0, .3f);
Invoke("CancelColorChange", _second);
}
private void IgniteColorFX()
{
if (sr.color != igniteColor[0])
sr.color = igniteColor[0];
else
sr.color = igniteColor[1];
}
private void ShockColorFx()
{
if (sr.color != shockColor[0])
sr.color = shockColor[0];
else
sr.color = shockColor[1];
}
private void ChillColor()
{
if (sr.color != chillColor[0])
sr.color = chillColor[0];
else
sr.color = chillColor[1];
}
}
CharacterStats.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
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 maxHealth;
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;
public System.Action onHealthChanged;//使角色在Stat里调用UI层的函数
[SerializeField] public int currentHealth;
protected virtual void Start()
{
critPower.SetDefaultValue(150);//设置默认爆伤
currentHealth = GetMaxHealthValue();
fx = GetComponent<EntityFX>();
}
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 (ignitedDamageTimer < 0 && isIgnited)
{
Debug.Log("Take Burning Damage" + igniteDamage);
DecreaseHealthBy(igniteDamage);
if (currentHealth < 0)
Die();
ignitedDamageTimer = igniteDamageCooldown;
}
}
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);
}
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);
//让元素效果取决与伤害
bool canApplyIgnite = _fireDamage > _iceDamage && _fireDamage > _lightingDamage;
bool canApplyChill = _iceDamage > _lightingDamage && _iceDamage > _fireDamage;
bool canApplyShock = _lightingDamage > _fireDamage && _lightingDamage > _iceDamage;
//防止循环在所有元素伤害为0时出现死循环
if (Mathf.Max(_fireDamage, _iceDamage, _lightingDamage) <= 0)
return;
//为了防止出现元素伤害一致而导致无法触发元素效果
//循环判断触发某个元素效果
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));
}
_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);
}
private static int CheckTargetResistance(CharacterStats _targetStats, int totleMagicalDamage)//法抗计算
{
totleMagicalDamage -= _targetStats.magicResistance.GetValue() + (_targetStats.intelligence.GetValue() * 3);
totleMagicalDamage = Mathf.Clamp(totleMagicalDamage, 0, int.MaxValue);
return totleMagicalDamage;
}
public void ApplyAilments(bool _ignite, bool _chill, bool _shock)//判断异常状态
{
if (isIgnited || isChilded || isShocked)
{
return;
}
if (_ignite)
{
isIgnited = _ignite;
ignitedTimer = ailmentsDuration;
fx.IgniteFxFor(ailmentsDuration);
}
if (_chill)
{
isChilded = _chill;
chilledTimer = ailmentsDuration;
fx.ChillFxFor(ailmentsDuration);
}
if (_shock)
{
isShocked = _shock;
shockedTimer = ailmentsDuration;
fx.ShockFxFor(ailmentsDuration);
}
}
public void SetupIgniteDamage(int _damage) => igniteDamage = _damage;//给点燃伤害赋值
protected virtual void TakeDamage(int _damage)//造成伤害是出特效
{
DecreaseHealthBy(_damage);
if (currentHealth < 0)
Die();
}
protected virtual void DecreaseHealthBy(int _damage)//此函数用来改变当前声明值,不调用特效
{
currentHealth -= _damage;
if(onHealthChanged != null)
{
onHealthChanged();
}
}
protected virtual void Die()
{
}
private 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;
}
private bool TargetCanAvoidAttack(CharacterStats _targetStats)//设置闪避
{
int totleEvation = _targetStats.evasion.GetValue() + _targetStats.agility.GetValue();
//我被麻痹后
//敌人的闪避率提升
if (isShocked)
totleEvation += 20;
if (Random.Range(0, 100) < totleEvation)
{
return true;
}
return false;
}
private bool CanCrit()//判断是否暴击
{
int totleCriticalChance = critChance.GetValue() + agility.GetValue();
if (Random.Range(0, 100) <= totleCriticalChance)
{
return true;
}
return false;
}
private int CalculateCriticalDamage(int _damage)//计算暴击后伤害
{
float totleCirticalPower = (critPower.GetValue() + strength.GetValue()) * .01f;
float critDamage = _damage * totleCirticalPower;
return Mathf.RoundToInt(critDamage);//返回舍入为最近整数的
}
public int GetMaxHealthValue()
{
return maxHealth.GetValue() + vitality.GetValue() * 10;
}//统计生命值函数
}
SkeletonStunnedState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SkeletonStunnedState : EnemyState
{
private Enemy_Skeleton enemy;
public SkeletonStunnedState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Skeleton _enemy) : base(_enemyBase, _stateMachine, _animBoolName)
{
enemy = _enemy;
}
public override void Enter()
{
base.Enter();
enemy.fx.InvokeRepeating("RedColorBlink", 0, .1f);//在 time 秒后调用 methodName 方法,然后每 repeatRate 秒调用一次。
//https://docs.unity3d.com/cn/current/ScriptReference/MonoBehaviour.InvokeRepeating.html
stateTimer = enemy.stunnedDuration;//stunned持续时间
rb.velocity = new Vector2(-enemy.facingDir*enemy.stunnedDirection.x, enemy.stunnedDirection.y);//stunned改变后的速度,由于SetVelocity有FlipCheck,所有这个用rb.velocity设置速度
}
public override void Exit()
{
base.Exit();
enemy.fx.Invoke("CancelColorChange",0); //在 time 秒后调用 methodName 方法。
//https://docs.unity3d.com/cn/current/ScriptReference/MonoBehaviour.Invoke.html
}
public override void Update()
{
base.Update();
if(stateTimer < 0)
{
stateMachine.ChangeState(enemy.idleState);
}
}
}