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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|