448 lines
17 KiB
C#
448 lines
17 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using Games.GlobeDefine;
|
||
using Games.LogicObj;
|
||
using GCGame.Table;
|
||
using Module.Log;
|
||
using UnityEngine;
|
||
|
||
/// <summary>
|
||
/// 独立特效管理器
|
||
/// </summary>
|
||
// 负责处理不依赖于任何角色的特效类型,例如子弹爆炸等
|
||
public sealed class IndependentEffectManager : BaseEffectLogic
|
||
{
|
||
// 最大缓存镜像数目
|
||
private List<CharacterCloneReference> _activeCloneList;
|
||
private List<MyTuple<Delay, float>> _delayParticleList;
|
||
|
||
// private List<CharacterCloneReference> _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<MyTuple<Delay, float>>();
|
||
_activeCloneList = new List<CharacterCloneReference>();
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 注册粒子延迟激活效果
|
||
/// </summary>
|
||
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, float>(delay, default(float));
|
||
Instance._delayParticleList.Add(record);
|
||
}
|
||
|
||
record.second = Time.time + delay.delayTime;
|
||
record.first.gameObject.SetActive(false);
|
||
}
|
||
else
|
||
{
|
||
LogModule.ErrorLog("试图在IndependentEffectManager不存在的时候使用粒子Delay组件");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移除粒子延迟激活效果,是否激活由调用者自行决定
|
||
/// </summary>
|
||
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<EffectLoadData> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 角色克隆镜像的数据
|
||
/// </summary>
|
||
// 注:同一般特效不同,这个玩意是创建然后加载,而不是加载完成创建。
|
||
// 由于特效机制底层会走加载创建的逻辑,使用新的类型来处理这个
|
||
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<Obj_CharacterClone>();
|
||
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;
|
||
}
|
||
}
|
||
} |