using System; using System.Collections.Generic; using Games.GlobeDefine; using Games.LogicObj; using GCGame.Table; using Module.Log; using UnityEngine; /// /// 独立特效管理器 /// // 负责处理不依赖于任何角色的特效类型,例如子弹爆炸等 public sealed class IndependentEffectManager : BaseEffectLogic { // 最大缓存镜像数目 private List _activeCloneList; private List> _delayParticleList; // private List _inactiveCloneList; // 锁链类型Impact的Id; //public const int chainImpactLogicId = 16; public static IndependentEffectManager Instance { get; private set; } // public CentralEffectPool EffectPool { get; private set; } private void Awake() { Init(); _delayParticleList = new List>(); _activeCloneList = new List(); } protected override void OnEnable() { base.OnEnable(); if (Instance == null) Instance = this; #if UNITY_EDITOR else LogModule.ErrorLog("独立特效管理器试图播放两个!"); #endif } protected override void OnDisable() { base.OnDisable(); Instance = null; } private void Update() { for (var i = _delayParticleList.Count - 1; i >= 0; i--) if (_delayParticleList[i].second <= Time.time) { // 有其他系统外的操作,可能造成特效物体被意外摧毁,因此增加一个检查 if (_delayParticleList[i].first != null) _delayParticleList[i].first.DelayActive(); _delayParticleList.RemoveAt(i); } } /// /// 注册粒子延迟激活效果 /// public static void AddParticleDelay(Delay delay) { if (Instance != null) { var record = Instance._delayParticleList.Find(a => a.first == delay); if (record == null) { record = new MyTuple(delay, default(float)); Instance._delayParticleList.Add(record); } record.second = Time.time + delay.delayTime; record.first.gameObject.SetActive(false); } else { LogModule.ErrorLog("试图在IndependentEffectManager不存在的时候使用粒子Delay组件"); } } /// /// 移除粒子延迟激活效果,是否激活由调用者自行决定 /// public static void RemoveParticleDelay(Delay delay) { if (Instance != null) { var index = Instance._delayParticleList.FindIndex(a => a.first == delay); if (index >= 0) Instance._delayParticleList.RemoveAt(index); } } public static void PushEffect(Tab_Effect data, Transform effect) { GameManager.gameManager.effectPool.PushEffect(data, effect); } public static void PushEffect(BaseEffectReference effectReference) { effectReference.EndEffect(); PushEffect(effectReference.data, effectReference.effect); } public static void CleanEffectLogic(BaseEffectLogic effectLogic) { if (GameManager.gameManager != null) GameManager.gameManager.effectPool.CleanEffectLogic(effectLogic); } public static void CancelLoad(Predicate predicate) { GameManager.gameManager.effectPool.RemoveLoadTask(predicate); } public static void CancelLoadByHandleId(int handleId) { GameManager.gameManager.effectPool.CancelLoadByHandleId(handleId); } public static void PullEffectItem(EffectLoadData loadData) { GameManager.gameManager.effectPool.PullEffect(loadData); } public void PreloadEffect(int effectId) { var tableData = TableManager.GetEffectByID(effectId, 0); if (tableData == null) LogModule.ErrorLog(string.Format("无法找到Id = {0}的特效!", effectId)); else GameManager.gameManager.effectPool.PreloadEffect(tableData); } public void ShowEffect(int effectId, int layer, GameDefine_Globe.OBJ_TYPE objType, Vector3 position, Quaternion rotation, float duration = -1f, PlayEffectDelegate playCallback = null, object playParameter = null, int? serverHandle = null) { var tableData = TableManager.GetEffectByID(effectId, 0); if (tableData == null) LogModule.ErrorLog(string.Format("无法找到Id = {0}的特效!", effectId)); else ShowEffect(tableData, layer, objType, position, rotation, duration, playCallback, playParameter, serverHandle); } public void ShowEffect(Tab_Effect tableData, int layer, GameDefine_Globe.OBJ_TYPE objType, Vector3 position, Quaternion rotation, float duration = -1f, PlayEffectDelegate playCallback = null, object playParameter = null, int? serverHandle = null) { if (ValidEffectByCount(tableData, objType)) { var customData = new NormalEffectCustomData(position, rotation, duration); var loadData = new EffectLoadData(this, layer, tableData, serverHandle, playCallback, playParameter, customData); GameManager.gameManager.effectPool.PullEffect(loadData); } } public void ShowBullet(int bulletId, int sourceId, int targetId, int layer, GameDefine_Globe.OBJ_TYPE objType, PlayEffectDelegate playCallback = null, object playParameter = null, int? serverHandle = null) { Tab_Effect effectData; Tab_Bullet bulletData; if (bulletId > 0 && GetBulletData(bulletId, out effectData, out bulletData) && ValidEffectByCount(effectData, objType)) { var customData = new BulletCustomData(bulletData, sourceId, targetId); var loadData = new EffectLoadData(this, layer, effectData, serverHandle, playCallback, playParameter, customData); GameManager.gameManager.effectPool.PullEffect(loadData); } } public void ShowBulletToPos(int bulletId, Vector3 startPos, Vector3 endPos, int layer, GameDefine_Globe.OBJ_TYPE objType, PlayEffectDelegate playCallback = null, object playParameter = null, int? serverHandle = null) { Tab_Effect effectData; Tab_Bullet bulletData; if (bulletId > 0 && GetBulletData(bulletId, out effectData, out bulletData) && ValidEffectByCount(effectData, objType)) { var offset = new Vector3(bulletData.StartPosX, bulletData.StartPosY, bulletData.StartPosZ); startPos += offset; endPos += offset; var customData = new BulletToPosData(bulletData, startPos, endPos); var loadData = new EffectLoadData(this, layer, effectData, serverHandle, playCallback, playParameter, customData); GameManager.gameManager.effectPool.PullEffect(loadData); } } // 注:特殊处理锁链 - 锁链句柄相同时,会重置而不是重新生成,因此需要传入两个句柄以保证客户端句柄正确执行 public void ShowChain(int effectId, int layer, GameDefine_Globe.OBJ_TYPE objType, ChainCustomData chainData, PlayEffectDelegate playCallback = null, object playParameter = null, int? serverHandle = null) { var tableData = TableManager.GetEffectByID(effectId, 0); if (tableData == null) LogModule.ErrorLog(string.Format("无法找到Id = {0}的特效!", effectId)); else ShowChain(tableData, layer, objType, chainData, playCallback, playParameter, serverHandle); } public void ShowChain(Tab_Effect tableData, int layer, GameDefine_Globe.OBJ_TYPE objType, ChainCustomData chainData, PlayEffectDelegate playCallback = null, object playParameter = null, int? serverHandle = null) { var create = true; if (serverHandle != null) { var activeChain = GetReferenceByHandle(serverHandle) as ChainEffectReference; if (activeChain != null) { create = false; activeChain.ResetParameters(chainData); } else { var delayChain = GetDelayByHandle(serverHandle); if (delayChain != null) { create = false; delayChain.loadData.customData = chainData; } else { CancelLoadByHandleId(serverHandle.Value); } } } if (create && ValidEffectByCount(tableData, objType)) { var loadData = new EffectLoadData(this, layer, tableData, serverHandle, playCallback, playParameter, chainData); GameManager.gameManager.effectPool.PullEffect(loadData); } } // 用于Boss特殊效果,不执行数量屏蔽 public void ShowMeteor(int effectId, Vector2 holdPos,GameDefine_Globe.OBJ_TYPE objType, Vector2 fallPos, Vector2 rollPos, float fallTime, float rollTime, int? serverHandle) { var effectData = CommonUtility.TryGetTable(effectId, a => TableManager.GetEffectByID(a, 0)); if (effectData != null) { var meteorData = new MeteorEffectData(effectData.OffsetY, 0f, fallTime, rollTime, holdPos, fallPos, rollPos); var loadData = new EffectLoadData(this, gameObject.layer, effectData, serverHandle, null, null, meteorData); GameManager.gameManager.effectPool.PullEffect(loadData); var glowId = effectData.GetParamValuebyIndex(0); if (glowId >= 0) { var z = meteorData.fallPosition - meteorData.holdPosition; var x = Vector3.Cross(Vector3.up, z); var y = Vector3.Cross(z, x); var rotation = y == Vector3.zero ? Quaternion.identity : Quaternion.LookRotation(z, y); ShowEffect(glowId, gameObject.layer, objType, meteorData.fallPosition, rotation); } } } private bool GetBulletData(int bulletId, out Tab_Effect effectData, out Tab_Bullet bulletData) { var result = false; bulletData = TableManager.GetBulletByID(bulletId, 0); if (bulletData == null) { LogModule.ErrorLog("无法找到子弹数据表id={0}!", bulletId); effectData = null; } else { effectData = TableManager.GetEffectByID(bulletData.EffectId, 0); if (effectData == null) LogModule.ErrorLog("无法找到特效数据表id={0}!", bulletData.EffectId); else result = true; } return result; } public CharacterCloneReference ShowCharacterClone(int serverId, Vector2 serverPos, Quaternion rotation, Obj_Character_Clone_Init_Data initData) { // // 特殊处理,如果服务器id同某个激活状态的影子一致,直接替换影子状态 // // 服务器id一致,或者重用模型参数一致的 // var index = _inactiveCloneList.FindIndex(a => a.cloneObj.Profession == initData.m_nProfession // && a.cloneObj.ModelVisualID == initData.m_ModelVisualID // && a.cloneObj.WeaponDataID == initData.m_WeaponDataID); var reference = new CharacterCloneReference(serverId, initData, serverPos, rotation); // if (index < 0) // { // reference // } // else // { // reference = _inactiveCloneList[index]; // _inactiveCloneList.RemoveAt(index); // reference.cloneObj.gameObject.SetActive(true); // reference.SetCloneInfo(serverId, initData, serverPos, rotation); // } _activeCloneList.Add(reference); return reference; } public void RemoveCharacterClone(int serverId) { CharacterCloneReference clone = null; for (var i = _activeCloneList.Count - 1; i >= 0; i--) { if (_activeCloneList[i].ServerId == serverId) { clone = _activeCloneList[i]; _activeCloneList.RemoveAt(i); break; } } if (clone != null) { clone.cloneObj.DestroyObj(); Destroy(clone.cloneObj.gameObject); } } // 服务器没有提供唯一句柄,暂时使用这种策略做一个临时句柄 // 有低概率和服务器特效句柄重复,出现率应该不高,也不会引发其他逻辑混乱 private int CreateWuChangHandle(int effectId, int sourceId, int targetId) { return Animator.StringToHash(effectId + "_" + sourceId + "_" + targetId); } // 用于Boss特殊效果,不执行数量屏蔽 public void CreateWuChangLink(int effectId, int sourceId, int targetId) { var handle = CreateWuChangHandle(effectId, sourceId, targetId); if (CheckCreateByHandle(handle)) { var effectData = CommonUtility.TryGetTable(effectId, a => TableManager.GetEffectByID(a, 0)); if (effectData != null) { var customData = new WuChangLinkData(sourceId, targetId); var loadData = new EffectLoadData(this, gameObject.layer, effectData, handle, null, null, customData); GameManager.gameManager.effectPool.PullEffect(loadData); } } } public void BreakWuChangLink(int effectId, int sourceId, int targetId) { var handle = CreateWuChangHandle(effectId, sourceId, targetId); var effectReference = ActiveList.Find(a => a.effectHandle == handle) as WuChangLinkReference; // 未激活时,可能处于加载或者等待状态 if (effectReference == null) RemoveEffectByHandle(handle); else effectReference.BreakLink(); } // 用于Boss特殊效果,不执行数量屏蔽 public void AddWuChangAirport(int effectId, int sourceId, int count) { // 已知问题,sourceId低概率同其他特效id重叠,不过万年难遇; var handle = CreateGhostAirportHandle(sourceId); if (CheckCreateByHandle(handle)) { var effectData = CommonUtility.TryGetTable(effectId, a => TableManager.GetEffectByID(a, 0)); if (effectData != null) { var airportData = new WuChangAirportData(sourceId, count); var loadData = new EffectLoadData(this, gameObject.layer, effectData, handle, null, null, airportData); GameManager.gameManager.effectPool.PullEffect(loadData); } } } public void RemoveWuChangAirport(int sourceId) { // 已知问题,sourceId低概率同其他特效id重叠,不过万年难遇; var handle = CreateGhostAirportHandle(sourceId); RemoveEffectByHandle(handle); } public void AttackFromWuChangAirport(int sourceId, Obj_Character target, float delay) { // 已知问题,sourceId低概率同其他特效id重叠,不过万年难遇; var handle = CreateGhostAirportHandle(sourceId); var effectReference = ActiveList.Find(a => a.effectHandle == handle) as WuChangAirportReference; if (effectReference != null) { var targetPoint = target.ObjEffectLogic == null ? target.ObjTransform : target.ObjEffectLogic.GetBindPoint(EffectLogic.centerName); effectReference.AttackTarget(targetPoint, Vector3.zero, delay); } } private int CreateGhostAirportHandle(int id) { return Animator.StringToHash(id + "_GhostAirport"); } private bool CheckCreateByHandle(int handle) { var create = true; var effectReference = ActiveList.Find(a => a.effectHandle == handle); if (effectReference != null) create = false; else { var delayChain = GetDelayByHandle(handle); if (delayChain != null) create = false; else CancelLoadByHandleId(handle); } return create; } /// /// 角色克隆镜像的数据 /// // 注:同一般特效不同,这个玩意是创建然后加载,而不是加载完成创建。 // 由于特效机制底层会走加载创建的逻辑,使用新的类型来处理这个 public class CharacterCloneReference { public const string shadowNameFormat = "CharacterClone_{0}"; public readonly Obj_CharacterClone cloneObj; public CharacterCloneReference(int serverId, Obj_Character_Clone_Init_Data initData, Vector3 position, Quaternion rotation) { var gameObject = new GameObject(); cloneObj = gameObject.AddComponent(); SetCloneInfo(serverId, initData, position, rotation); } public int ServerId { get; private set; } public void SetCloneInfo(int serverId, Obj_Character_Clone_Init_Data initData, Vector2 position, Quaternion rotation) { ServerId = serverId; cloneObj.gameObject.name = string.Format(shadowNameFormat, serverId); cloneObj.Init(initData); cloneObj.SyncSetPos(position); cloneObj.ObjTransform.localRotation = rotation; } } }