基于unity event,unity action,script object实现的跨场景的事件订阅传递(实现血量变化传递给UI)
主题代码部分
PlayerStatBar
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerStatBar : MonoBehaviour
{
public Image healthImage;
public Image healthDelayImage;
public Image powerImage;
private void Update()
{
if(healthDelayImage.fillAmount>healthImage.fillAmount)
{
healthDelayImage.fillAmount-=Time.deltaTime;
}
}
/// <summary>
/// 接收health的变更百分比
/// </summary>
/// <param name="persentage">百分比:Current/Max</param>
public void OnHealthChange(float persentage)
{
healthImage.fillAmount = persentage;
}
}
script object(CharacterEventSO)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(menuName ="Event/CharacterEventSO")]
public class CharacterEventSO : ScriptableObject
{
public UnityAction<Character> OnEventRaised;
public void RaiseEvent(Character character)
{
OnEventRaised?.Invoke(character);
}
}
UIManager
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIManager : MonoBehaviour
{
public PlayerStatBar playerStatBar;
[Header("事件监听")]
public CharacterEventSO healthEvent;
public void OnEnable()
{
healthEvent.OnEventRaised += OnHealthEvent;
}
public void OnDisable()
{
healthEvent.OnEventRaised -= OnHealthEvent;
}
private void OnHealthEvent(Character character)
{
var persentage = character.currentHealth / character.maxHealth;
playerStatBar.OnHealthChange(persentage);
}
}
character(部分)
public void TakeDamage(Attack attacker)
{
if (invulnerable)
return;
//Debug.Log(attacker.damage);
if (currentHealth - attacker.damage > 0)
{
currentHealth -= attacker.damage;
TriggerInvulnerable();
//执行受伤
OnTakeDamage?.Invoke(attacker.transform);
}
else
{
currentHealth = 0;
//触发死亡
OnDie?.Invoke();
}
OnHealthChange?.Invoke(this);
}
关于ScriptableObject(SO)
ScriptableObject是存储在我们项目中的一个资产性文件类型, unity官方手册给出的解释是:ScriptableObject 是一个可独立于类实例来保存大量数据的数据容器。
创建、使用ScriptableObject的方法
1.首先在文件夹下创建 一个脚本,然后双击打开这个脚本,之后删除MonoBehaviour(它不需要挂在在物体上),然后继承ScriptableObject
2.加上CreateAssetMenu特性方便日后的使用 menuName(目录名字:鼠标右键创建时的位置)
代码实现逻辑
按照我当前的理解,SO像是一个中间容器(SO的实例是Unity靠assets文件反序列化出来的,但全局共享这一个实例,它可以在运行时被修改,但是不能被持久化下来,比如存档不能依靠SO机制做,必须手动序列化进本地文件)依靠SO的特性和优点我们把想要传递的内容用SO进行存储。通过订阅事件把需要的数据传递出去。(本文中传递给UIManager)
把characterEventSO比作报纸的话,character就是写入报纸的内容。注册character改变这一事件,如果character发生改变所有订阅这个事件的对象就会呼叫,把内容传递出去。最后UIManager接受内容,通过函数方法进行处理。
单项平台
Platform Effector 2D
利用Platform Effector 2D组件,实现单项平台。
然后在组件Platform Effector 2D中取消使用碰撞器遮罩,然后点击使用单向这样我们就实现了一个可以从下面跳上去的平台。
(Use One Way:勾选此选项后,平台将变为单向平台,角色可以从平台上方穿过,但无法从平台下方穿过。
Use Side Friction:勾选此选项后,角色在平台上将受到侧面的摩擦力,可以防止角色因为平台的移动而滑落。
Surface Arc:这是一个角度值,表示角色在平台上的接触面积大小。如果值较小,角色将紧贴着平台移动;如果值较大,角色将有一定的间隙,可以让角色跳跃等动作更加灵活。
Side Arc:和 Surface Arc 类似,但是作用于平台的侧面。
Rotational Offset:旋转偏移量,可以用来调整平台的旋转角度。
Use Collider Mask:勾选此选项后,可以指定一个碰撞层,只有和该层的物体才能和平台碰撞。
Use Global Angle:勾选此选项后,将会使用全局角度,而不是相对于平台自身的角度。
Use One Way Grouping:勾选此选项后,同一组的平台将共享同一个单向平台属性,可以方便批量设置。)
但是这样不能实现S+空格落下平台
想到使用S+空格 短暂关闭角色的碰撞器。导致角色穿模,卡在地面。
最后改成短暂关闭特定平台的碰撞器,当角色触发器和平台不在重叠时恢复平台。代码改的比较凌乱就不展示了。
平台移动
在平台上方设置一个触发器,当平台上升到触发器就消失。
using UnityEngine;
public class PlatFormMove : MonoBehaviour
{
Vector3 movement;//transform是3D组件所以用Vector3
GameObject topline;//获取标志物
public float speed;//定义平台上升速度
void Start()
{
movement.y = speed;
topline = GameObject.Find("TopLine");
}
void Update()
{
MovePlantForm();
}
void MovePlantForm()
{
transform.position += movement * Time.deltaTime;//平台上升
if (transform.position.y >= topline.transform.position.y)//如果到达标记位就消失
Destroy(gameObject);
}
}
类似的写了一个不会消失而是回到初始位置,这样子循环往复。
Unity event,unity action的学习
总结半天发现还是不如大佬总结的好,贴上博客先【Unity】Delegate, Event, UnityEvent, Action, UnityAction, Func 傻傻分不清_unity action-CSDN博客
UntiyAction,它是 Unity 对 C# Action 委托的一个封装(佬写的特别好,跟着他的博客回顾了C#委托,
C#事件)
优化血瓶,精力条
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class BloodVials : MonoBehaviour, IInteractable
{
public Character character;
public UnityEvent<Character> OnHealthChange;
private void Awake()
{
GameObject Player = GameObject.Find("Player");
character = Player.GetComponent<Character>();
}
public void TriggerAction()
{
if (character.currentHealth + 50 > 100)
character.currentHealth = 100;
else
character.currentHealth += 50;
OnHealthChange?.Invoke(character);
}
}
把血瓶回血的代码优化(其实就是加深理解了利用SO进行事件订阅后的优化),用事件的方式更新血量UI。
给血瓶加了一个慢慢喝完的动画。
学习进度
在b站看完了唐老师的unity入门和基础
行为树学习【Unity教程搬运】使用行为树在UNITY中重现大黄蜂Boss战(空洞骑士)_哔哩哔哩_bilibili
算法题。。。
刷题,爽 !