Unity 从0开始编写一个技能编辑器_02_Buff系统的Handler

BuffHandler可以是用于处理角色身上buff的Mono类,任何具备跟Buff交互的角色,都要携带这个BuffHandler脚本。如果你的Buff有额外的处理机制,比如互斥Buff(如:免疫负面效果的霸体),需要在AddBuff方法中额外处理。当然 也可以不作为mono转为BaseFightObj的一个引用的实例,在BaseFightObj这一类中的生命周期进行调用

    /// <summary>/// Buff处理器的接口/// </summary>public interface BuffSystem_IBuffHandler{/// <summary>/// 添加Buff /// </summary>/// <param name="buffId">施加Buff的ID</param>/// <param name="caster">施加Buff者</param>public void AddBuff(int buffId, GameObject caster);/// <summary>/// 移除Buff/// </summary>/// <param name="buffId">要移除的Buff id</param>/// <param name="removeAll">如果对象同时存在多个同id的buff,是否将所有一并移除</param>public void RemoveBuff(int buffId, bool removeAll = true);/// <summary>/// 移除Buff(不执行OnBuffRemove)/// </summary>/// <param name="buffId">要移除的Buff id</param>/// <param name="removeAll">如果对象同时存在多个同id的buff,是否将所有一并移除</param>public void InterruptBuff(int buffId, bool interuptAll = true);/// <summary>/// 注册事件:添加Buff时/// </summary>/// <param name="act">注册的行为</param>public void RegisterOnAddBuff(Action act);/// <summary>/// 删除事件:添加Buff时/// </summary>/// <param name="act">注册的行为</param>public void RemoveOnAddBuff(Action act);/// <summary>/// 注册事件:删除Buff时/// </summary>/// <param name="act">注册的行为</param>public void RegisterOnRemoveBuff(Action act);/// <summary>/// 删除事件:删除Buff时/// </summary>/// <param name="act">注册的行为</param>public void RemoveOnRemoveBuff(Action act);}

接口实现如上所示,可以看到实现了一些 “trigger” 可以在特定需求下减轻一些主线程的压力。
依照接口,我们可以实现具体的BuffHandler类
1.AddBuff 方法如下 可以看到 对上文的 重复添加同一种Buff时的行为 做了处理。这是按照策划的需求给出的,不必要求在初次编写时考虑所有情况 便于扩展即可

AddBuff的多种情况
public enum BuffMutilAddType
{resetTime,                     //重置Buff时间multipleLayer,                 //增加Buff层数multipleLayerAndResetTime,     //增加Buff层数且重置Buff时间multipleCount                  //同种Buff同时存在多个,互不影响
}
AddBuff方法的具体实现
private void AddBuff(IBuff buff, GameObject caster){if (!updated) Update();Buff bf = (Buff)buff;if (bf.IsEmpty()){Debug.LogError("尝试加入空Buff");return;}//无论是否可以添加都执行初始化和BuffAwakebf.Initialize(this, caster);bf.OnBuffAwake();//确定能添加Buff时onAddBuff?.Invoke();//检查是否已有同样的BuffBuff previous = buffs.Find(p => p.Equals(bf));//没有则直接添加if (previous == null){//结算Tag效果if (bf.BuffTag != BuffTag.none){//首先:如果有已有buff能抵消新buff,则直接抵消if (buffs.Any(b => BuffManager.GetInstance().TagManager.IsTagCanAddWhenHaveOther(bf.BuffTag, b.BuffTag))){bf.SetEffective(false);bf.OnBuffDestroy();return;}for (int i = buffs.Count - 1; i >= 0; i--){//之后:如果新buff没有被抵消,则新buff抵消已有的buff//Debug.Log("Running:" + bf.BuffTag + ":" + buffs[i].BuffTag);if (BuffManager.GetInstance().TagManager.IsTagRemoveOther(bf.BuffTag, buffs[i].BuffTag)){RemoveBuff(buffs[i]);}}}buffs.Add(bf);forOnBuffStart += bf.OnBuffStart;return;}//有则根据重复添加的类型处理。//一个Buff对象的Start不会重复执行//只有mutilCount类型会同时存在多个同id Buffswitch (previous.MutilAddType){case BuffMutilAddType.resetTime:previous.ResetTimer();//forOnBuffStart += previous.OnBuffStart;break;case BuffMutilAddType.multipleLayer:previous.ModifyLayer(1);//forOnBuffStart += previous.OnBuffStart;break;case BuffMutilAddType.multipleLayerAndResetTime:previous.ResetTimer();previous.ModifyLayer(1);//forOnBuffStart += previous.OnBuffStart;break;case BuffMutilAddType.multipleCount:buffs.Add(bf);forOnBuffStart += bf.OnBuffStart;break;default:break;}}

然后是RemoveBuff的两种情况

 /// <summary>/// 移除一个Buff,移除后执行OnBuffRemove/// </summary>/// <param name="buff">要移除的Buff</param>private void RemoveBuff(IBuff buff){Buff bf = (Buff)buff;bf.SetEffective(false);}/// <summary>/// 移除一个Buff,移除后不执行OnBuffRemove/// </summary>/// <param name="buff">要移除的Buff</param>private void InteruptBuff(IBuff buff){Buff bf = (Buff)buff;bf.SetEffective(false);buffs.Remove(bf);forOnBuffDestroy += ((Buff)buff).OnBuffDestroy;}

之所以这么设计是因为 有些负面Buff效果在onRemoveBuff执行 然而 互斥buff或者说实际的净化buff 不希望onRemoveBuff执行,这会导致一些预期之外的后果 可能不便于实现一些需求,所以编写这两种本质上均为RemoveBuff的方法,为了方便理解 命名为InteruptBuff 打断buff

上述的私有方法AddBuff(IBuff buff, GameObject caster),RemoveBuff(IBuff buff),和InteruptBuff(IBuff buff)封装了实际的Buff处理逻辑。这些方法实现了添加、移除和打断Buff的具体细节。
将复杂的逻辑封装在私有方法中,可以更容易地进行维护和修改。任何更改都只需要在私有方法内部进行,而不影响公有接口

  public void AddBuff(int buffId, GameObject caster){var b = BuffManager.GetInstance().GetBuff(buffId);AddBuff(b, caster);}public void RemoveBuff(int buffId, bool removeAll = true){var b = buffs.FirstOrDefault(b => b.ID == buffId);if (b == null){Debug.Log("尝试从" + this.name + "移除没有添加的Buff。 id:" + buffId);return;}else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll){var bs = buffs.Where(b => b.ID == buffId);foreach (var bf in bs){RemoveBuff(bf);}}else RemoveBuff(b);}public void InterruptBuff(int buffId, bool removeAll = true){var b = buffs.FirstOrDefault(b => b.ID == buffId);if (b == null){Debug.Log("尝试从" + this.name + "打断没有添加的Buff。 id:" + buffId);return;}else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll){var bs = buffs.Where(b => b.ID == buffId);foreach (var bf in bs){InteruptBuff(bf);}}else InteruptBuff(b);}

然后我们用公有方法将内部复杂的逻辑封装起来,外部只需简单调用。例如,外部调用者只需提供Buff ID和施法者(caster),而不需要关心具体的Buff对象和其处理细节。
完整代码如下

public class BuffHandler : MonoBehaviour, BuffSystem_IBuffHandler{private List<Buff> buffs = new List<Buff>();private Action onAddBuff;private Action onRemoveBuff;public List<Buff> GetBuffs => new List<Buff>(buffs);public void RegisterOnAddBuff(Action act) { onAddBuff += act; }public void RemoveOnAddBuff(Action act) { onRemoveBuff -= act; }public void RegisterOnRemoveBuff(Action act) { onRemoveBuff += act; }public void RemoveOnRemoveBuff(Action act) { onRemoveBuff -= act; }#region 私有方法private void AddBuff(IBuff buff, GameObject caster){if (!updated) Update();Buff bf = (Buff)buff;if (bf.IsEmpty()){Debug.LogError("尝试加入空Buff");return;}//无论是否可以添加都执行初始化和BuffAwakebf.Initialize(this, caster);bf.OnBuffAwake();//确定能添加Buff时onAddBuff?.Invoke();//检查是否已有同样的BuffBuff previous = buffs.Find(p => p.Equals(bf));//没有则直接添加if (previous == null){//结算Tag效果if (bf.BuffTag != BuffTag.none){//首先:如果有已有buff能抵消新buff,则直接抵消if (buffs.Any(b => BuffManager.GetInstance().TagManager.IsTagCanAddWhenHaveOther(bf.BuffTag, b.BuffTag))){bf.SetEffective(false);bf.OnBuffDestroy();return;}for (int i = buffs.Count - 1; i >= 0; i--){//之后:如果新buff没有被抵消,则新buff抵消已有的buff//Debug.Log("Running:" + bf.BuffTag + ":" + buffs[i].BuffTag);if (BuffManager.GetInstance().TagManager.IsTagRemoveOther(bf.BuffTag, buffs[i].BuffTag)){RemoveBuff(buffs[i]);}}}buffs.Add(bf);forOnBuffStart += bf.OnBuffStart;return;}//有则根据重复添加的类型处理。//一个Buff对象的Start不会重复执行//只有mutilCount类型会同时存在多个同id Buffswitch (previous.MutilAddType){case BuffMutilAddType.resetTime:previous.ResetTimer();//forOnBuffStart += previous.OnBuffStart;break;case BuffMutilAddType.multipleLayer:previous.ModifyLayer(1);//forOnBuffStart += previous.OnBuffStart;break;case BuffMutilAddType.multipleLayerAndResetTime:previous.ResetTimer();previous.ModifyLayer(1);//forOnBuffStart += previous.OnBuffStart;break;case BuffMutilAddType.multipleCount:buffs.Add(bf);forOnBuffStart += bf.OnBuffStart;break;default:break;}}/// <summary>/// 移除一个Buff,移除后执行OnBuffRemove/// </summary>/// <param name="buff">要移除的Buff</param>private void RemoveBuff(IBuff buff){Buff bf = (Buff)buff;bf.SetEffective(false);}/// <summary>/// 移除一个Buff,移除后不执行OnBuffRemove/// </summary>/// <param name="buff">要移除的Buff</param>private void InteruptBuff(IBuff buff){Buff bf = (Buff)buff;bf.SetEffective(false);buffs.Remove(bf);forOnBuffDestroy += ((Buff)buff).OnBuffDestroy;}#endregionpublic void AddBuff(int buffId, GameObject caster){var b = BuffManager.GetInstance().GetBuff(buffId);AddBuff(b, caster);}public void RemoveBuff(int buffId, bool removeAll = true){var b = buffs.FirstOrDefault(b => b.ID == buffId);if (b == null){Debug.Log("尝试从" + this.name + "移除没有添加的Buff。 id:" + buffId);return;}else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll){var bs = buffs.Where(b => b.ID == buffId);foreach (var bf in bs){RemoveBuff(bf);}}else RemoveBuff(b);}public void InterruptBuff(int buffId, bool removeAll = true){var b = buffs.FirstOrDefault(b => b.ID == buffId);if (b == null){Debug.Log("尝试从" + this.name + "打断没有添加的Buff。 id:" + buffId);return;}else if (b.MutilAddType == BuffMutilAddType.multipleCount && removeAll){var bs = buffs.Where(b => b.ID == buffId);foreach (var bf in bs){InteruptBuff(bf);}}else InteruptBuff(b);}private bool updated = false;private Action forOnBuffDestroy;    //用于在下一帧执行onBuffDestoryprivate Action forOnBuffStart;private void Update(){if (updated) return;updated = true;forOnBuffDestroy?.Invoke();forOnBuffStart?.Invoke();forOnBuffDestroy = null;forOnBuffStart = null;}private void LateUpdate(){updated = false;Buff bf;bool buffRemoved = false;for (int i = buffs.Count - 1; i >= 0; i--){bf = buffs[i];//Debug.Log(bf);bf.OnBuffUpdate();if (!bf.IsEffective){bf.OnBuffRemove();buffRemoved = true;buffs.Remove(bf);forOnBuffDestroy += bf.OnBuffDestroy;}}if (buffRemoved) onRemoveBuff?.Invoke();}}
}

Update 每帧更新Buff状态,执行所有记录的销毁和开始方法。
LateUpdate检查并更新每个Buff的状态,如果Buff无效,则移除并记录其销毁方法。
BuffHandler类通过管理游戏对象上的所有Buff,实现了Buff的添加、移除、打断和更新等功能。其核心逻辑包括Buff的初始化、处理相同Buff的多种添加类型、管理Buff的生命周期以及调用相关回调方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/31934.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Anthropic AI模型Claude 3.5 Sonnet在Amazon Bedrock上正式可用

Claude 3.5 Sonnet是Anthropic最先进的Claude系列AI模型的新成员&#xff0c;比Claude 3 Opus更智能且价格只有其五分之一 北京——2024年6月21日 亚马逊云科技宣布&#xff0c;Anthropic最新、最强大的模型Claude 3.5 Sonnet现已在Amazon Bedrock上正式可用&#xff0c;该模型…

增强-MIGO物料消耗需要将物料描述写到会计凭证的摘要里面

财务比较闲提的需求&#xff0c;有些物料消耗需要将物料描述写到会计凭证的摘要里面&#xff0c; 找了一下增强点&#xff0c;随便搞了一下&#xff0c;可以了。

20240622 每日AI必读资讯

&#x1f916;力压GPT-4o&#xff01;新王Claude 3.5 Sonnet来了&#xff0c;直接免费可用 - 新模型在推理、知识和编码能力评估方面超越了以前的版本和竞争对手GPT 4o模型&#xff0c;同时其运行速度是Claude 3 Opus的两倍。 - 该模型可在http://Claude.ai和Claude iOS应用上…

Spring Bean 生命周期详解

Spring Bean 生命周期详解 在 Spring 框架中&#xff0c;Bean 的生命周期由 Spring 容器全权管理。了解和掌握 Bean 的生命周期对于使用 Spring 开发稳定且高效的应用程序至关重要。本文将详细介绍 Spring Bean 生命周期的五个主要阶段&#xff1a;实例化、属性注入、初始化、…

如何用vscode编写一个GO语言的入门代码

本章教程,主要介绍如何在VSCODE中,运行GO语言的入门代码。 一、准备一个GO语言代码 一个很简单的代码,用GO语言在控制台打印输出:Hello, World! package mainimport "fmt"func main() {fmt.Println("Hello, World!") }二、安装GO语言SDK 下载地址:

云服务与低代码开发的结合:重塑现代软件开发模式

随着数字化转型的深入推进&#xff0c;越来越多的企业开始将业务迁移到云端&#xff0c;以实现更高的灵活性、可靠性和成本效益。云服务已经成为企业数字化战略的重要组成部分。与此同时&#xff0c;低代码开发作为一种新兴的编程模式&#xff0c;也逐渐受到企业的关注。那么&a…

keepalive+nginx高可用架构

keepalivenginx架构 一.配置真实服务器web1和web2 1.关闭防火墙&#xff0c;并在真实服务器下载http服务 [rootlocalhost ~]# systemctl stop firewalld.service [rootlocalhost ~]# setenforce 0 [rootlocalhost ~]# yum install httpd -y 2.分别在web1和web2上制作网页…

【Redis】List的常用命令以及常用场景

Redis List 是一个简单的链表&#xff0c;支持在两端进行插入和删除操作。这种数据结构在许多场景下非常有用&#xff0c;例如任务队列、消息队列等。Redis 提供了一系列针对 List 的操作命令&#xff0c;帮助我们更高效地操作链表。 1. List常用命令 操作类型命令时间复杂度…

Ubuntu系统使用快速入门实践(八)—— git 命令使用

Ubuntu系统使用快速入门实践系列文章 下面是Ubuntu系统使用系列文章的总链接&#xff0c;本人发表这个系列的文章链接均收录于此 Ubuntu系统使用快速入门实践系列文章总链接 下面是专栏地址&#xff1a; Ubuntu系统使用快速入门实践系列文章专栏 文章目录 Ubuntu系统使用快速…

高温工况下雷达物位计连续冷却方法

高温工况下雷达物位计连续冷却方法 1、提出前状况: 雷达物位计是一种新型物位测量仪表,它采用与介质非接触的测量方式,具有准确度高(分辨率可达1mm)、测量范围大、无可动部件,适用于粘度大的介质、有毒或无毒卫生型介质,有腐蚀性、磨蚀性介质的物位测量,它是通过天线…

【Qt6.3 基础教程 13】 掌握数据展示:使用QTableView和QStandardItemModel

文章目录 前言QTableView&#xff1a;表格式数据的强力工具主要特性 QStandardItemModel&#xff1a;灵活的数据模型主要特性 结合使用QTableView和QStandardItemModel步骤一&#xff1a;初始化模型步骤二&#xff1a;填充数据步骤三&#xff1a;创建视图并设置模型 结论 前言 …

JupyterLab使用指南(八):更改JupterLab左侧默认打开目录

在JupyterLab中&#xff0c;默认打开路径通常是由其配置文件中的root_dir设置决定的。如果你没有特意设置这个配置项&#xff0c;JupyterLab可能会使用当前用户的主目录或者上一次关闭时的路径作为默认打开路径。 更改JupyterLab默认路径的操作在不同操作系统下大体相似&…

Autoencoder(AE)、Variational Autoencoder(VAE)和Diffusion Models(DM)了解

Autoencoder (AE) 工作原理&#xff1a; Autoencoder就像一个数据压缩机器。它由两部分组成&#xff1a; 编码器&#xff1a;将输入数据压缩成一个小小的代码。解码器&#xff1a;将这个小代码还原成尽可能接近原始输入的数据。 优点和应用&#xff1a; 简单易懂&#xff1…

windows使用curl命令出现乱码的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

海口注册公司代理记账的服务优势与流程解析

在海口注册公司加入代理记账服务有多种优势。代理记账公司提供专业的财务服务&#xff0c;帮助企业节约成本、提高效率&#xff0c;实现财务管理的合规性。以下是代理记账服务的主要优势和流程解析&#xff1a; https://www.9733.cn/news/detail/173.html 一、代理记账服务的…

分布式光纤测温DTS在工程现场中稳定性与可靠性如何?

20年前&#xff0c;分布式光纤测温(Distributed Temperature Sensing&#xff0c;DTS)技术的发展尚不成熟&#xff0c;设备成本高昂&#xff0c;其稳定性与可靠性也存在一定问题。然而&#xff0c;经过二十多年的不断发展与创新&#xff0c;DTS技术在工程现场应用中取得了显著进…

企业智慧办公管理平台

摘要 在之前的疫情中&#xff0c;大多数企业都受到了较大的冲击&#xff0c;然而一些公司却因为工作的特殊性可以居家远程办公&#xff0c;不过这些企业在管理员工的过程中却遇到了较大的困难&#xff0c;这是因为这些企业的管理系统根本大多都无法管理员工的工作项目&#xf…

Dockerfile-php7.4.33

# 使用一个包含基本编译工具的基础镜像 FROM ubuntu:latestRUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \echo "Asia/Shanghai" > /etc/timezone# 更新包列表并安装必要的编译工具和库 RUN apt-get update && apt-get i…

AI学习指南机器学习篇-多项式朴素贝叶斯算法简介

AI学习指南机器学习篇-多项式朴素贝叶斯算法简介 前言 随着人工智能技术的快速发展&#xff0c;机器学习作为其中的一个重要分支已经成为各个领域的热门话题。而在机器学习算法中&#xff0c;朴素贝叶斯算法因其简单易懂、效果不俗而备受青睐。本文将针对多项式朴素贝叶斯算法…

YOLOv8目标跟踪model.track的封装

YOLOv8目标跟踪model.track的封装 flyfish 在使用目标跟踪时&#xff0c; 调用model.track整个步骤就完成&#xff0c;track封装了内部运行的步骤。这里主要说回调部分。 使用model.track import cv2from ultralytics import YOLO from collections import defaultdict impo…