易名网站备案,西宁高端网站制作,wordpress jianux,免费个人网页制作教程【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
教程源地址#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/
本章节实现了玩家属性栏#xff0c;仓库#xff0c;物品栏UI的制作 UI_StatSlot.cs
这个脚本是用来在Unity的UI上显示玩家属性#xf…【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
教程源地址https://www.udemy.com/course/2d-rpg-alexdev/
本章节实现了玩家属性栏仓库物品栏UI的制作 UI_StatSlot.cs
这个脚本是用来在Unity的UI上显示玩家属性比如生命值或攻击力的。
显示状态名称statName用来存储属性名称比如Health。在编辑器中修改这个名字时它会自动显示在对应的UI文本框里。显示状态值statValueText是用来显示这个属性的数值比如100生命值。初始化UIStart方法会在游戏开始时更新UI确保显示玩家的正确状态。动态更新UpdateStatValueUI方法可以随时调用更新UI上显示的数值。它会通过PlayerManager找到玩家的属性然后把这个值显示在UI上。
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;public class UI_StatSlot : MonoBehaviour
{[SerializeField] private string statName;[SerializeField] private StatType statType;[SerializeField] private TextMeshProUGUI statValueText;[SerializeField] private TextMeshProUGUI statNameText;private void OnValidate(){gameObject.name Stat - statName;if(statNameText ! null)statNameText.text statName;}void Start(){UpdateStatValueUI();}public void UpdateStatValueUI(){PlayerStats playerStats PlayerManager.instance.player.GetComponentPlayerStats();if(playerStats !null){statValueText.text playerStats.GetStat(statType).GetValue().ToString();}}
}CharacterStats.cs
把状态类型移动到这个脚本
using System.Collections;
using UnityEngine;public enum StatType//枚举 StatType 的定义
{strength,agility,intelligence,vitality,damage,critChance,critPower,health,armor,evasion,magicRes,fireDamage,iceDamage,lightingDamage
}//10月25日
//10月26日
public class CharacterStats : MonoBehaviour
{private EntityFX fx;[Header(主属性)]public Stat strength;//力量1点增加1攻击力和%1爆伤public Stat agility;//敏捷1点增加1%闪避和%1暴击率public Stat intelligence;//智力1点增加1法术强度和%1魔抗public Stat vitality;//活力1点增加3生命值[Header(攻击属性)]//offensive statspublic Stat damage;public Stat critChance;//暴击率public Stat critPower;//暴击伤害,默认%150[Header(防守属性)]//defensive statspublic Stat maxHealth;public Stat armor;//护甲public Stat evasion;//闪避public Stat magicResistance;//魔抗[Header(魔法属性)]//magic statspublic Stat fireDamage;public Stat iceDamage;public Stat lightningDamage;public bool isIgnited;//是否燃烧,持续伤害public bool isChilled;//是否冻结削弱护甲20%public bool isShocked;//是否感电减少命中率20%[SerializeField] private float ailmentsDuration 4;//异常状态持续时间private float ignitedTimer;private float chilledTimer;private float shockedTimer;private float igniteDamageCoolDown .3f;//燃烧伤害间隔时间private float igniteDamageTimer;//燃烧伤害计时器private int igniteDamage;//燃烧伤害[SerializeField] private GameObject shockStrikePrefab;private int shockDamage;public int currentHealth;public System.Action onHealthChanged;public bool isDead { get; private set; }protected virtual void Start(){critPower.SetDefaultValue(150);//暴击伤害默认150%currentHealth GetMaxHealthValue();//一开始血条满的fx GetComponentEntityFX();}protected virtual void Update(){ignitedTimer - Time.deltaTime;//燃烧时间减少chilledTimer - Time.deltaTime;shockedTimer - Time.deltaTime;igniteDamageTimer - Time.deltaTime;//燃烧伤害计时器减少if (ignitedTimer 0)isIgnited false;if (chilledTimer 0)isChilled false;if (shockedTimer 0)isShocked false;if (isIgnited)ApplyIgniteDamage();}public virtual void IncreaseStatBy(int _modifier, float _duration, Stat _statToModify){StartCoroutine(StatModCoruntine(_modifier, _duration, _statToModify));}private IEnumerator StatModCoruntine(int _modifier, float _duration, Stat _statToModify)//加buff的协程{_statToModify.AddModifier(_modifier);//添加一个buffyield return new WaitForSeconds(_duration);_statToModify.RemoveModifier(_modifier);//移除一个buff}public virtual void DoDamage(CharacterStats _targetStats)//只是一次物理攻击{if (TargetCanAvoidAttack(_targetStats))return;int totalDamage damage.GetValue() strength.GetValue();if (Cancrit()){//Debug.Log(Crit Hit);totalDamage CalculateCriticalDamage(totalDamage);//Debug.Log( 总的暴击伤害是 totalDamage);//Total crit damage is}totalDamage CheckTargetArmor(_targetStats, totalDamage);_targetStats.TakeDamage(totalDamage);//把造成的给到继承CharacterStats的类DoMagicDamage(_targetStats);//如果普通攻击不想要魔法伤害移除}#region Magic Damage and ailmentspublic virtual void DoMagicDamage(CharacterStats _targetStats)//只是一次魔法攻击{int _fireDamage fireDamage.GetValue();int _iceDamage iceDamage.GetValue();int _lightningDamage lightningDamage.GetValue();int totalMagicalDamage _fireDamage _iceDamage _lightningDamage intelligence.GetValue();totalMagicalDamage CheckTargetResistance(_targetStats, totalMagicalDamage);_targetStats.TakeDamage(totalMagicalDamage);//把造成的给到继承CharacterStats的类//importantif (Mathf.Max(_fireDamage, _iceDamage, _lightningDamage) 0)//可以保证下面的while循环不会无限循环return;AttempToApplyAilements(_targetStats, _fireDamage, _iceDamage, _lightningDamage);}private void AttempToApplyAilements(CharacterStats _targetStats, int _fireDamage, int _iceDamage, int _lightningDamage){//判断魔法伤害类型bool canApplyIgnite _fireDamage _iceDamage _fireDamage _lightningDamage;bool canApplyChill _iceDamage _fireDamage _iceDamage _lightningDamage;bool canApplyShock _lightningDamage _fireDamage _lightningDamage _iceDamage;while (!canApplyIgnite !canApplyChill !canApplyShock){//三个if同时判断大小可以完成一个随机属性伤害的bossif (Random.value .33f _fireDamage 0)//Random.value用于生成一个介于 0.0 和 1.0 之间的随机浮点数{canApplyIgnite true;_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);//Debug.Log(ignited );return;}if (Random.value .5f _lightningDamage 0){canApplyShock true;_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);//Debug.Log(shocked );return;}if (Random.value .99f _iceDamage 0){canApplyChill true;_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);//Debug.Log(iced );return;}}if (canApplyIgnite){_targetStats.SetupIgniteDamage(Mathf.RoundToInt(_fireDamage * .2f));}if (canApplyShock){_targetStats.SetupShockDamage(Mathf.RoundToInt(_lightningDamage * .1f));}_targetStats.ApplyAilments(canApplyIgnite, canApplyChill, canApplyShock);}public void ApplyAilments(bool _ignite, bool _chill, bool _shock)//应用异常状态{bool canApplyIgnite !isIgnited !isChilled !isShocked;bool canApplyChill !isIgnited !isChilled !isShocked;bool canApplyShock !isIgnited !isChilled;//没有其他异常状态才能进入一个异常状态//if (isIgnited || isChilled || isShocked)//如果进入一个异常状态就不能进入其他状态了// return;if (_ignite canApplyIgnite){isIgnited _ignite;ignitedTimer ailmentsDuration;fx.IgniteFxFor(ailmentsDuration);}if (_chill canApplyChill){isChilled _chill;chilledTimer ailmentsDuration;float slowPercentage .2f;//减速百分比GetComponentEntity().SlowEntityBy(slowPercentage, ailmentsDuration);//减速20%fx.ChillFxFor(ailmentsDuration);}if (_shock canApplyShock){if (!isShocked){ApplyShock(_shock);}else{if (GetComponentPlayer() ! null)//防止敌人打玩家自己被电return;HitNearsetTargerWithShockStrike();}}}public void ApplyShock(bool _shock){if (isShocked)//已经进入感电就不如再次进入return;isShocked _shock;shockedTimer ailmentsDuration;fx.ShockFxFor(ailmentsDuration);}private void HitNearsetTargerWithShockStrike(){Collider2D[] colliders Physics2D.OverlapCircleAll(transform.position, 25);//碰撞体检测周围的敌人float closestDistance Mathf.Infinity;Transform closestEnemy null;foreach (var hit in colliders){if (hit.GetComponentEnemy() ! null Vector2.Distance(transform.position, hit.transform.position) 1)//如果是敌人并且不是自己防止自己被电{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.GetComponentShockStrike_Controller().Setup(shockDamage, closestEnemy.GetComponentCharacterStats());}}//闪电攻击附近的目标private void ApplyIgniteDamage(){if (igniteDamageTimer 0){DecreaseHealthBy(igniteDamage);//currentHealth - igniteDamage;if (currentHealth 0 !isDead)Die();igniteDamageTimer igniteDamageCoolDown;}}public void SetupIgniteDamage(int _damage) igniteDamage _damage;//设置燃烧伤害public void SetupShockDamage(int _damage) shockDamage _damage;//设置感电伤害#endregionpublic virtual void TakeDamage(int _damage)//造成伤害函数返回伤害值{DecreaseHealthBy(_damage);GetComponentEntity().DamageImpact();fx.StartCoroutine(FlashFX);Debug.Log(_damage);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)//受到伤害的数值变化{currentHealth - _damage;if (onHealthChanged ! null)onHealthChanged();}protected virtual void Die(){isDead true;}#region Stat calculationsprivate int CheckTargetResistance(CharacterStats _targetStats, int totalMagicalDamage)//计算魔法伤害魔抗{totalMagicalDamage - _targetStats.magicResistance.GetValue() (_targetStats.intelligence.GetValue() * 3);//减去魔抗值totalMagicalDamage Mathf.Clamp(totalMagicalDamage, 0, int.MaxValue);//Clamp限制血量数值return totalMagicalDamage;}private int CheckTargetArmor(CharacterStats _targetStats, int totalDamage)//计算物理伤害护甲{if (_targetStats.isChilled)totalDamage - Mathf.RoundToInt(_targetStats.armor.GetValue() * .8f);//减去对方的护甲值elsetotalDamage - _targetStats.armor.GetValue();//减去对方的护甲值totalDamage Mathf.Clamp(totalDamage, 0, int.MaxValue);//最小值是0最大值是int.MaxValue,防止别人打我加血return totalDamage;}private bool TargetCanAvoidAttack(CharacterStats _targetStats)//检测闪避{int totalEvasion _targetStats.evasion.GetValue() _targetStats.agility.GetValue();//总的闪避率if (isShocked)totalEvasion 20;if (Random.Range(0, 100) totalEvasion){Debug.Log(Attack Avoided);return true;}return false;}private bool Cancrit()//暴击检测{int totalCritChance critChance.GetValue() agility.GetValue();//总的暴击率if (Random.Range(0, 100) totalCritChance){return true;}return false;}private int CalculateCriticalDamage(int _damage)//计算爆伤{float totalCritPower (critPower.GetValue() strength.GetValue()) * .01f;//Debug.Log(总的暴击率: totalCritPower);//total crit powerfloat critDamage _damage * totalCritPower;//Debug.Log(取整之后的爆伤 critDamage);//crit damage before round upreturn Mathf.RoundToInt(critDamage);}public int GetMaxHealthValue(){return maxHealth.GetValue() vitality.GetValue() * 5;}//获取最大生命值#endregionpublic Stat GetStat(StatType _statType)//根据 buffType 返回需要修改的属性{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 maxHealth;else if (_statType StatType.armor) return armor;else if (_statType StatType.evasion) return evasion;else if (_statType StatType.magicRes) return magicResistance;else if (_statType StatType.fireDamage) return fireDamage;else if (_statType StatType.iceDamage) return iceDamage;else if (_statType StatType.lightingDamage) return lightningDamage;else return null;}
}Buff_Effect.cs
using UnityEngine;//2024年11月11日[CreateAssetMenu(fileName Buff Effect, menuName Data/Item effect/Buff effect)]
public class Buff_Effect : ItemEffect
{private PlayerStats stats;[SerializeField] private StatType buffType;// Buff 的类型[SerializeField] private int buffAmount; // Buff 的增加量[SerializeField] private float buffDuration;// Buff 持续时间public override void ExcuteEffect(Transform _enemyPositon){stats PlayerManager.instance.player.GetComponentPlayerStats();//相当于获得了CharacterStats的引用stats.IncreaseStatBy(buffAmount, buffDuration, stats.GetStat(buffType));}
}Inventory.cs
更新部分 [SerializeField] private Transform statSlotParent;private UI_StatSlot[] statSlot;private void Start()//初始实例化{inventoryItems new ListInventoryItem();inventoryDictionary new DictionaryItemData, InventoryItem();stash new ListInventoryItem();stashDictionary new DictionaryItemData, InventoryItem();equipment new ListInventoryItem();equipmentDictionary new DictionaryItemData_Equipment, InventoryItem();//同时获取UI中对应的物品槽//获得起始的脚本inventoryItemSlot inventorySlotParent.GetComponentsInChildrenUI_ItemSlot();stashItemSlot stashSlotParent.GetComponentsInChildrenUI_ItemSlot();equipmentSlot equipmentSlotParent.GetComponentsInChildrenUI_EquipmentSlot();statSlot statSlotParent.GetComponentsInChildrenUI_StatSlot();AddStartingItems();}using System.Collections.Generic;
using UnityEngine;//放在创建仓库的empyty对象上就是仓库的运行函数
public class Inventory : MonoBehaviour
{public static Inventory instance;//单例模式public ListItemData startingItems;//初始装备//两种关键的存贮结构//List存储玩家的装备、仓库物品、储藏室物品等//Dictionary存储每个物品的数据以及物品在仓库中的具体信息例如物品的堆叠数量public ListInventoryItem equipment;public DictionaryItemData_Equipment, InventoryItem equipmentDictionary;public ListInventoryItem inventoryItems;public DictionaryItemData, InventoryItem inventoryDictionary;public ListInventoryItem stash;public DictionaryItemData, InventoryItem stashDictionary;//UI物品槽管理通过以下代码将游戏中的物品槽和UI界面上的物品槽关联起来[Header(仓库UI)]//Inventory UI[SerializeField] private Transform inventorySlotParent;//位置[SerializeField] private Transform stashSlotParent;[SerializeField] private Transform equipmentSlotParent;[SerializeField] private Transform statSlotParent;//物品和材料的存贮位置分开private UI_ItemSlot[] inventoryItemSlot;private UI_ItemSlot[] stashItemSlot;//储藏室private UI_EquipmentSlot[] equipmentSlot;//装备private UI_StatSlot[] statSlot;[Header(物品冷却)]private float lastTimeUsedFlask;private float lastTimeUsedArmor;//P122解决一开始不能用物品的问题因为一开始冷却被赋值使用了currentFlask.itemCoolDownprivate float flaskCoolDown;private float armorCoolDown;private void Awake(){if (instance null)instance this;elseDestroy(gameObject);//防止从一个地方到另一个地方}private void Start()//初始实例化{inventoryItems new ListInventoryItem();inventoryDictionary new DictionaryItemData, InventoryItem();stash new ListInventoryItem();stashDictionary new DictionaryItemData, InventoryItem();equipment new ListInventoryItem();equipmentDictionary new DictionaryItemData_Equipment, InventoryItem();//同时获取UI中对应的物品槽//获得起始的脚本inventoryItemSlot inventorySlotParent.GetComponentsInChildrenUI_ItemSlot();stashItemSlot stashSlotParent.GetComponentsInChildrenUI_ItemSlot();equipmentSlot equipmentSlotParent.GetComponentsInChildrenUI_EquipmentSlot();statSlot statSlotParent.GetComponentsInChildrenUI_StatSlot();AddStartingItems();}private void AddStartingItems()//添加初始物品{for (int i 0; i startingItems.Count; i){AddItem(startingItems[i]);}}public void EquipItem(ItemData _item)//把一个物品装备到角色身上{ItemData_Equipment newEquipment _item as ItemData_Equipment;//把 _item 对象转换为 ItemData_Equipment 类型,表示它是一个装备物品InventoryItem newItem new InventoryItem(newEquipment); //把 ItemData 对象转换为 InventoryItem 对象ItemData_Equipment oldEquipment null;//要删除的物品foreach (KeyValuePairItemData_Equipment, InventoryItem item in equipmentDictionary)//遍历装备字典{if (item.Key.equipmentType newEquipment.equipmentType)//如果装备类型相同oldEquipment item.Key;//删除该装备}if (oldEquipment ! null){UnequipItem(oldEquipment);AddItem(oldEquipment);//把要删除的物品放回仓库}equipment.Add(newItem);equipmentDictionary.Add(newEquipment, newItem);newEquipment.AddModifiers();//添加装备属性RemoveItem(_item);UpdataSlotsUI();}public void UnequipItem(ItemData_Equipment itemToRemove)//移除装备函数{if (equipmentDictionary.TryGetValue(itemToRemove, out InventoryItem value)){equipment.Remove(value);equipmentDictionary.Remove(itemToRemove);itemToRemove.RemoveModifiers();}}private void UpdataSlotsUI()//更新UI物体的数量{// 更新装备槽for (int i 0; i equipmentSlot.Length; i)//将装备物品槽与一个装备字典中的物品进行匹配并根据匹配结果更新物品槽的内容{foreach (KeyValuePairItemData_Equipment, InventoryItem item in equipmentDictionary)//遍历装备字典{if (item.Key.equipmentType equipmentSlot[i].slotType)//这个条件用于确保物品能放入正确的槽位中equipmentSlot[i].UpdataSlot(item.Value);}}// 清空并更新仓库和储藏室的物品槽for (int i 0; i inventoryItemSlot.Length; i)//仓库物品槽{inventoryItemSlot[i].CleanUpSlot();}for (int i 0; i stashItemSlot.Length; i)//储藏室中的物品槽{stashItemSlot[i].CleanUpSlot();}// 重新填充仓库和储藏室for (int i 0; i inventoryItems.Count; i){inventoryItemSlot[i].UpdataSlot(inventoryItems[i]);}for (int i 0; i stash.Count; i){stashItemSlot[i].UpdataSlot(stash[i]);}//更新属性槽for (int i 0; i statSlot.Length; i){statSlot[i].UpdateStatValueUI();}}public void AddItem(ItemData _item)//据物品类型将物品添加到仓库inventory或者储藏室stash{if (_item.itemType ItemType.Equipment){AddToInventory(_item);}else if (_item.itemType ItemType.Material){AddToStash(_item);}UpdataSlotsUI();}private void AddToStash(ItemData _item){if (stashDictionary.TryGetValue(_item, out InventoryItem value)){value.AddStack();}else{InventoryItem newItem new InventoryItem(_item);stash.Add(newItem);stashDictionary.Add(_item, newItem);}}private void AddToInventory(ItemData _item){if (inventoryDictionary.TryGetValue(_item, out InventoryItem value))//字典中检查仓库中是否有这个物品具体查找的是ItemDataout InventoryItem value如果找返回与该键相关联的值{value.AddStack();//如果物品已经存在于库存中则增加其堆叠数量}else{InventoryItem newItem new InventoryItem(_item);//如果物品不存在则创建一个新的 InventoryIteminventoryItems.Add(newItem);inventoryDictionary.Add(_item, newItem);}}//检查物品是否已经在仓库里如果存在则增加堆叠数量如果不存在则创建新的物品对象并加入仓库public void RemoveItem(ItemData _item){if (inventoryDictionary.TryGetValue(_item, out InventoryItem value)){if (value.stackSize 1){inventoryItems.Remove(value);inventoryDictionary.Remove(_item);}else{value.RemoveStack();}}if (stashDictionary.TryGetValue(_item, out InventoryItem stashValue)){if (stashValue.stackSize 1)//如果物品的堆叠数量小于等于1则从库存中删除该物品{stash.Remove(stashValue);stashDictionary.Remove(_item);}else{stashValue.RemoveStack();//否则就减少堆寨数量}}UpdataSlotsUI();}public bool CanCraft(ItemData_Equipment _itemToCraft, ListInventoryItem _requiredMaterials)//判断是否可以合成的函数{ListInventoryItem materialsToRemove new ListInventoryItem();for (int i 0; i _requiredMaterials.Count; i){if (stashDictionary.TryGetValue(_requiredMaterials[i].data, out InventoryItem stashValue))//如果储藏室中没有所需材料{if (stashValue.stackSize _requiredMaterials[i].stackSize)//数量是否足够{Debug.Log(没有足够的材料);return false;}else{materialsToRemove.Add(stashValue);}}else{Debug.Log(没有足够的材料);return false;}}for (int i 0; i materialsToRemove.Count; i)//使用了就从临时仓库中移除{RemoveItem(materialsToRemove[i].data);}AddItem(_itemToCraft);Debug.Log(这里是你的物品 _itemToCraft.name);return true;}public ListInventoryItem GetEquipmentList() equipment;//获取装备列表public ListInventoryItem GetStashList() stash;//获取仓库列表public ItemData_Equipment GetEquipment(EquipmentType _type)//获取装备{ItemData_Equipment equipedItem null;foreach (KeyValuePairItemData_Equipment, InventoryItem item in equipmentDictionary)//遍历装备字典{if (item.Key.equipmentType _type)//如果装备类型相同equipedItem item.Key;//删除该装备}return equipedItem;}public void UseFlask()//使用药水的函数{ItemData_Equipment currentFlask GetEquipment(EquipmentType.Flask);//获取当前的药水if (currentFlask null)return;bool canUseFlask Time.time lastTimeUsedFlask flaskCoolDown;//判断是否可以使用药水if (canUseFlask){flaskCoolDown currentFlask.itemCoolDown;//重置冷却时间,一开始就可以使用物品currentFlask.Effect(null);lastTimeUsedFlask Time.time;}elseDebug.Log(药水冷却中);}public bool CanUseArmor(){ItemData_Equipment currentArmor GetEquipment(EquipmentType.Armor);//获取当前装备的护甲信息。if (Time.time lastTimeUsedArmor armorCoolDown){//更新冷却时间和使用时间armorCoolDown currentArmor.itemCoolDown;lastTimeUsedArmor Time.time;return true;}Debug.Log(护甲正在冷却中);return false;}//private void Updata()//{// Debug.Log(Time.time);//}
}