Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
HealthBar_UI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//using UnityEngine.UIElements;
using UnityEngine.UI;
public class HealthBar_UI : MonoBehaviour
{
private Entity entity;
private CharacterStats myStats;
private RectTransform myTransform;
private Slider slider;
private void Start()
{
myTransform = GetComponent<RectTransform>();
entity = GetComponentInParent<Entity>();
slider = GetComponentInChildren<Slider>();
myStats = GetComponentInParent<CharacterStats>();
UpdateHealthUI();
entity.onFlipped += FlipUI;
myStats.onHealthChanged += UpdateHealthUI;
}
private void Update()
{
//UpdateHealthUI();
}
private void UpdateHealthUI()//更新血量条函数,此函数由Event触发
{
slider.maxValue = myStats.GetMaxHealthValue();
slider.value = myStats.currentHealth;
}
private void FlipUI()//让UI不随着角色翻转
{
myTransform.Rotate(0, 180, 0);
}
private void OnDisable()
{
entity.onFlipped -= FlipUI;
myStats.onHealthChanged -= UpdateHealthUI;
}
}
CharacterStats.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterStats : MonoBehaviour
{
[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; // 降低敌人命中率
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();
}
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 = 2;
}
if (_chill)
{
isChilded = _chill;
chilledTimer = 2;
}
if (_shock)
{
isShocked = _shock;
shockedTimer = 2;
}
}
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;
}//统计生命值函数
}
Entity.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Entity : MonoBehaviour
{
[Header("Knockback info")]
[SerializeField] protected Vector2 knockbackDirection;//被击打后的速度信息
[SerializeField] protected float knockbackDuration;//被击打的时间
protected bool isKnocked;//此值通过卡住SetVelocity函数的方式用来阻止当一个角色被攻击时,会乱动的情况
[Header("Collision Info")]
public Transform attackCheck;//transform类,代表的时物体的位置,用来控制攻击检测的位置
public float attackCheckRadius;//检测半径
[SerializeField] protected Transform groundCheck;//transform类,代表的时物体的位置,后面会来定位子组件的位置
[SerializeField] protected float groundCheckDistance;
[SerializeField] protected Transform wallCheck;//transform类,代表的时物体的位置,后面会来定位子组件的位置
[SerializeField] protected float wallCheckDistance;
[SerializeField] protected LayerMask whatIsGround;//LayerMask类,与Raycast配合,https://docs.unity3d.com/cn/current/ScriptReference/Physics.Raycast.html
#region 定义Unity组件
public SpriteRenderer sr { get; private set; }
public Animator anim { get; private set; }//这样才能配合着拿到自己身上的animator的控制权
public Rigidbody2D rb { get; private set; }//配合拿到身上的Rigidbody2D组件控制权
public EntityFX fx { get; private set; }//拿到EntityFX
public CharacterStats stats { get; private set; }
public CapsuleCollider2D cd { get; private set; }
#endregion
public int facingDir { get; private set; } = 1;
protected bool facingRight = true;//判断是否朝右
public System.Action onFlipped;//一个自身不用写函数,只是接受其他函数并调用他们的函数
//https://blog.csdn.net/weixin_44299531/article/details/131343583
protected virtual void Awake()
{
}
protected virtual void Start()
{
anim = GetComponentInChildren<Animator>();//拿到自己子组件身上的animator的控制权
sr = GetComponentInChildren<SpriteRenderer>();
fx = GetComponent<EntityFX>();拿到的组件上的EntityFX控制权
rb = GetComponent<Rigidbody2D>();
stats = GetComponent<CharacterStats>();
cd = GetComponent<CapsuleCollider2D>();
}
protected virtual void Update()
{
}
protected virtual void Exit()
{
}
public virtual void DamageEffect()
{
fx.StartCoroutine("FlashFX");//IEnumertor本质就是将一个函数分块执行,只有满足某些条件才能执行下一段代码,此函数有StartCoroutine调用
//https://www.zhihu.com/tardis/bd/art/504607545?source_id=1001
StartCoroutine("HitKnockback");//调用被击打后产生后退效果的函数
Debug.Log(gameObject.name+"was damaged");
}
protected virtual IEnumerator HitKnockback()
{
isKnocked = true;//此值通过卡住SetVelocity函数的方式用来阻止当一个角色被攻击时,会乱动的情况
rb.velocity = new Vector2(knockbackDirection.x * -facingDir, knockbackDirection.y);
yield return new WaitForSeconds(knockbackDuration);
isKnocked = false;
}
//被击打后产生后退效果的函数
#region 速度函数Velocity
public virtual void SetZeroVelocity()
{
if(isKnocked)
{
return;
}
rb.velocity = new Vector2(0, 0);
}//设置速度为0函数
public virtual void SetVelocity(float _xVelocity, float _yVelocity)
{
if(isKnocked)
return;此值通过卡住SetVelocity函数的方式用来阻止当一个角色被攻击时,会乱动的情况
rb.velocity = new Vector2(_xVelocity, _yVelocity);//将rb的velocity属性设置为对应的想要的二维向量。因为2D游戏的速度就是二维向量
FlipController(_xVelocity);//在其他设置速度的时候调用翻转控制器
}//控制速度的函数,此函数在其他State中可能会使用,但仅能通过player.SeVelocity调用
#endregion
#region 翻转函数Flip
public virtual void Flip()
{
facingDir = facingDir * -1;
facingRight = !facingRight;
transform.Rotate(0, 180, 0);//旋转函数,transform不需要额外定义,因为他是自带的
if(onFlipped != null)
onFlipped();
}//翻转函数
public virtual void FlipController(float _x)//目前设置x,目的时能在空中时也能转身
{
if (_x > 0 && !facingRight)//当速度大于0且没有朝右时,翻转
{
Flip();
}
else if (_x < 0 && facingRight)
{
Flip();
}
}
#endregion
#region 碰撞函数Collision
public virtual bool IsGroundDetected()
{
return Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
}//通过RayCast检测是否挨着地面,https://docs.unity3d.com/cn/current/ScriptReference/Physics2D.Raycast.html
//xxxxxxxx() => xxxxxxxx == xxxxxxxxxx() return xxxxxxxxx;
public virtual bool IsWallDetected()
{
return Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);
}//通过RayCast检测是否挨着地面,https://docs.unity3d.com/cn/current/ScriptReference/Physics2D.Raycast.html
//xxxxxxxx() => xxxxxxxx == xxxxxxxxxx() return xxxxxxxxx;
protected virtual void OnDrawGizmos()
{
Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));//绘制一条从 from(前面的) 开始到 to(后面的) 的线。
Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));//绘制一条从 from(前面的) 开始到 to(后面的) 的线。
Gizmos.DrawWireSphere(attackCheck.position, attackCheckRadius);//https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Gizmos.DrawWireSphere.html
//绘制具有中心和半径的线框球体。
}//画图函数
#endregion
public void MakeTransprent(bool isClear)
{
if (isClear)
sr.color = Color.clear;
else
sr.color = Color.white;
}
public virtual void Die()
{
}
}