using System;
using System.Collections.Generic;
using Games.AnimationModule;
using Games.Events;
using Games.GlobeDefine;
using Games.LogicObj;
using Games.Scene;
using GCGame.Table;
using Module.Log;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
///
///
/// 最基本的特效管理器
///
public abstract class BaseEffectLogic : MonoBehaviour
{
///
/// 最小加载容忍时间,加载时间超过该值会忽略特效
///
public const float minLoadTolerance = 0.5f;
///
/// 最小加载容忍时间比例,加载时间超过特效总时长比例会忽略特效
///
public const float minLoadRatio = 0.3f;
private bool _enable;
public List ActiveList { get; private set; }
public List DelayList { get; private set; }
public bool IsInited { get; private set; }
///
/// 获得特效后,如果是需要延迟出现的情况,特效将会延迟出现
///
/// 特效物体
/// 加载数据
public void OnCreateEffect(Transform effect, EffectLoadData loadData)
{
// 加载超过容忍时间后,直接忽略当前特效
var loadDelay = Time.time - loadData.startTime;
var effectDelay = Mathf.Max(0f, loadData.data.DelayTime + loadData.delayModifier);
var tolerance = effectDelay + Mathf.Max(minLoadTolerance, minLoadRatio * loadData.data.Duration);
if (loadDelay > tolerance)
{
IndependentEffectManager.PushEffect(loadData.data, effect);
}
else
{
// 从加载开始所计算的延迟时间
effectDelay = Mathf.Max(0f, effectDelay - loadDelay);
if (effectDelay > 0)
{
effect.gameObject.SetActive(false);
DelayList.Add(new DelayEffectReference(loadData, effect, Time.time + effectDelay));
}
else
{
ActiveEffect(effect, loadData);
}
}
}
public virtual Transform GetBindPoint(string pointName, out Vector3 offset)
{
// 故意保留的强制报错,实际只有角色的需要拥有获取绑定点的功能,其他的只保留一个编译接口
throw new InvalidOperationException(string.Format("{0}未实现获取绑定点的功能,不应该使用绑定类型特效!", GetType()));
}
public virtual Obj_Character GetOwnerCharacter()
{
// 故意保留的强制报错,实际只有角色才能获得拥有者,其他的只保留一个编译接口
throw new InvalidOperationException(string.Format("{0}未实现获取拥有者角色的功能,不应该使用神像类型特效!", GetType()));
}
private void ActiveEffect(Transform effect, EffectLoadData loadData)
{
CommonUtility.SetLayersRecursively(effect, loadData.layer);
var activeResult = ActiveEffectInherited(effect, loadData);
if (loadData.playCallback != null)
loadData.playCallback(activeResult ? effect.gameObject : null, loadData.playParameter);
}
protected bool ActiveEffectInherited(Transform effect, EffectLoadData loadData)
{
var result = false;
var customData = (CustomEffectLoadData) loadData.customData;
switch (customData.EffectType)
{
case CommonEffectType.Bound:
{
var duration = loadData.data.Duration;
if (duration < 0)
duration = float.PositiveInfinity;
var activeEffectData = new CommonEffectReference(effect, loadData.data, loadData.handle,
Time.time + duration);
Vector3 offset;
var bindPoint = GetBindPoint(loadData.data.ParentName, out offset);
activeEffectData.SetBindPoint(bindPoint,
offset + new Vector3(loadData.data.OffsetX, loadData.data.OffsetY, loadData.data.OffsetZ));
ActiveList.Add(activeEffectData);
// Hack:给预警圈的特别处理
SetWarningData(effect, loadData.data);
result = true;
}
break;
case CommonEffectType.Position:
{
var effectData = (NormalEffectCustomData) customData;
var duration = effectData.duration > 0f ? effectData.duration : loadData.data.Duration;
if (duration < 0)
duration = float.PositiveInfinity;
var activeEffectData =
new CommonEffectReference(effect, loadData.data, loadData.handle, Time.time + duration);
effect.position = effectData.position;
// NoRotation不需要旋转
var rotation = loadData.data.RotationType == EffectRotationType.noRotation
? Quaternion.identity
: effectData.rotation;
if (loadData.data.Yaw > 0)
rotation = rotation * Quaternion.Euler(0f, loadData.data.Yaw, 0f);
effect.rotation = rotation;
effect.localScale = loadData.data.Scale > 0 ? Vector3.one * loadData.data.Scale : Vector3.one;
ActiveList.Add(activeEffectData);
// Hack:给预警圈的特别处理
SetWarningData(effect, loadData.data);
result = true;
}
break;
case CommonEffectType.Bullet:
{
var effectData = (BulletCustomData) customData;
var source = Singleton.Instance.FindObjCharacterInScene(effectData.sourceId);
if (source != null)
{
var activeBulletData = new BulletEffectReference(effect, effectData.targetId, source,
effectData.bulletData, loadData.data, loadData.handle);
effect.position = source.transform.position +
source.transform.rotation * activeBulletData.GetStartOffset();
var delay = Mathf.Max(0f, Time.time - loadData.startTime - loadData.data.DelayTime);
// 模拟子弹加载期间的位移
if (activeBulletData.InitBullet(delay))
{
ActiveList.Add(activeBulletData);
result = true;
}
}
if (!result)
IndependentEffectManager.PushEffect(loadData.data, effect);
}
break;
case CommonEffectType.BulletToPos:
{
var effectData = (BulletToPosData) customData;
var activeBulletData = new BulletToPosReference(effect, effectData.startPos, effectData.endPos,
effectData.bulletData, loadData.data, loadData.handle);
ActiveList.Add(activeBulletData);
result = true;
}
break;
case CommonEffectType.Chain:
{
var effectData = (ChainCustomData) customData;
var activeChainData = new ChainEffectReference(effect, effectData, loadData.data, loadData.handle);
ActiveList.Add(activeChainData);
result = true;
}
break;
case CommonEffectType.Stamp:
{
var effectData = (StampCustomData) customData;
var activeEffectData =
new StampEffectReference(effect, loadData.data, effectData.stampCount, loadData.handle);
Vector3 offset;
var bindPoint = GetBindPoint(loadData.data.ParentName, out offset);
activeEffectData.SetBindPoint(bindPoint,
offset + new Vector3(loadData.data.OffsetX, loadData.data.OffsetY, loadData.data.OffsetZ));
ActiveList.Add(activeEffectData);
result = true;
}
break;
case CommonEffectType.WuChangLink:
{
var effectData = (WuChangLinkData) customData;
var activeLinkRef = new WuChangLinkReference(effect, effectData, loadData.data, loadData.handle);
ActiveList.Add(activeLinkRef);
result = true;
}
break;
case CommonEffectType.WuChangAirport:
{
var effectData = (WuChangAirportData) customData;
var airportReference = new WuChangAirportReference(effect, effectData, loadData.data, loadData.handle);
ActiveList.Add(airportReference);
result = true;
}
break;
case CommonEffectType.Meteor:
{
var effectData = (MeteorEffectData) customData;
var meteorRef = new MeteorEffectReference(effect, effectData, loadData.data, loadData.handle);
ActiveList.Add(meteorRef);
result = true;
}
break;
case CommonEffectType.Avatar:
{
var character = GetOwnerCharacter();
if (character != null)
{
var duration = loadData.data.Duration;
if (duration < 0)
duration = float.PositiveInfinity;
var activeEffectData = new AvatarEffectReference(effect, loadData.data, loadData.handle,
Time.time + duration);
activeEffectData.Init(character);
ActiveList.Add(activeEffectData);
result = true;
}
}
break;
default:
LogModule.ErrorLog(string.Format("未处理类型为{0}的特效!", customData.EffectType));
break;
}
return result;
}
protected void Init()
{
IsInited = true;
ActiveList = new List();
DelayList = new List();
TryAddEvent();
}
protected virtual void OnEnable()
{
_enable = true;
TryAddEvent();
}
protected virtual void OnDisable()
{
_enable = false;
TryAddEvent();
}
private void SetWarningData(Transform effect, Tab_Effect data)
{
// 特殊处理技能警告指示器 - 暂时简单做法是用普通加载流程,然后只在配置位置时配置效果
var effectType = (EffectLogic.EffectType) data.Type;
switch (effectType)
{
case EffectLogic.EffectType.TYPE_CIRCLE:
effect.GetComponent().SetEffectData(data);
break;
case EffectLogic.EffectType.TYPE_RECTANGLE:
effect.GetComponent().SetEffectData(data);
break;
case EffectLogic.EffectType.TYPE_SECTOR:
effect.GetComponent().SetEffectData(data);
break;
}
}
private void OnDestroy()
{
if (GameManager.gameManager != null && GameManager.gameManager.effectPool != null)
GameManager.gameManager.effectPool.DestroyEffectLogic(this);
}
private void TryAddEvent()
{
if (IsInited && _enable)
EventDispatcher.Instance.Add(Games.Events.EventId.PostMainCameraMove, AfterCameraMovement);
else
EventDispatcher.Instance.Remove(Games.Events.EventId.PostMainCameraMove,
AfterCameraMovement);
}
protected virtual void AfterCameraMovement(object args)
{
for (var i = DelayList.Count - 1; i >= 0; i--)
try
{
if (DelayList[i].effect == null)
{
#if UNITY_EDITOR
LogModule.ErrorLog(string.Format("一个于{1}的特效{0}被使用不正常的方式删除!", DelayList[i].loadData.data.EffectID,
transform.GetHierarchyName()));
#endif
DelayList.RemoveAt(i);
}
// 处于延迟发布的状态
else if (DelayList[i].CheckActive())
{
var delayReference = DelayList[i];
DelayList.RemoveAt(i);
// 可能某个主角模型在Delay阶段被析构,导致绑定的特效被析构
if (delayReference.effect != null)
{
delayReference.effect.gameObject.SetActive(true);
ActiveEffect(delayReference.effect, delayReference.loadData);
}
}
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
for (var i = ActiveList.Count - 1; i >= 0; i--)
try
{
// 处于延迟回收状态
if (ActiveList[i].effect == null)
{
#if UNITY_EDITOR
LogModule.ErrorLog(string.Format("一个于{1}的特效{0}被使用不正常的方式删除!", ActiveList[i].data.EffectID,
transform.GetHierarchyName()));
#endif
ActiveList.RemoveAt(i);
}
else if (ActiveList[i].DelayRecovery)
{
if (!ActiveList[i].UpdateForDelayRecovery())
RemoveEffectAt(i);
}
else
{
if (!ActiveList[i].Update())
if (!ActiveList[i].CheckDelayRecovery())
RemoveEffectAt(i);
}
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
}
public void RemoveEffectByHandle(int handleId)
{
if (handleId != 0)
{
IndependentEffectManager.CancelLoadByHandleId(handleId);
for (var i = ActiveList.Count - 1; i >= 0; i--)
try
{
if (ActiveList[i].effectHandle == handleId)
RemoveEffectAt(i);
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
for (var i = DelayList.Count - 1; i >= 0; i--)
try
{
if (DelayList[i].loadData.handle == handleId)
RemoveDelayAt(i);
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
}
}
public void RemoveEffectByEffectId(int effectId)
{
IndependentEffectManager.CancelLoad(a => a.data.EffectID == effectId);
for (var i = ActiveList.Count - 1; i >= 0; i--)
try
{
if (ActiveList[i].data.EffectID == effectId)
RemoveEffectAt(i);
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
for (var i = DelayList.Count - 1; i >= 0; i--)
try
{
if (DelayList[i].loadData.data.EffectID == effectId)
RemoveDelayAt(i);
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
}
///
/// 清除全部播放中的特效
///
public void CleanEffect()
{
// 清除加载中的特效
IndependentEffectManager.CleanEffectLogic(this);
}
protected void RemoveEffectAt(int index)
{
var item = ActiveList[index];
ActiveList.RemoveAt(index);
IndependentEffectManager.PushEffect(item);
}
protected void RemoveDelayAt(int index)
{
var delayReference = DelayList[index];
DelayList.RemoveAt(index);
IndependentEffectManager.PushEffect(delayReference.loadData.data, delayReference.effect);
}
///
/// 从延迟列表获得HandleId符合条件的物体
///
// 特殊处理HandleId为0的情况 - 0视为不可用id
public DelayEffectReference GetDelayByHandle(int? handleId)
{
if (handleId == null)
return null;
return DelayList.Find(a => a.loadData.handle == handleId);
}
///
/// 从激活列表获得HandleId符合条件的物体
///
// 延迟回收的特效不允许获得
public BaseEffectReference GetReferenceByHandle(int? handleId)
{
return handleId == null ? null : ActiveList.Find(a => a.effectHandle == handleId && !a.DelayRecovery);
}
///
/// 从激活列表获得EffectId符合条件的物体
///
// 延迟回收的特效不允许获得
public BaseEffectReference GetReferenceByEffectId(int effectId)
{
var result = ActiveList.Find(a => a.data.EffectID == effectId && !a.DelayRecovery);
return result;
}
public static bool ValidEffectByCount(Tab_Effect data, GameDefine_Globe.OBJ_TYPE objType)
{
var result = true;
if (objType == GameDefine_Globe.OBJ_TYPE.OBJ_OTHER_PLAYER || //其他玩家
objType == GameDefine_Globe.OBJ_TYPE.OBJ_NPC || //NPC
objType == GameDefine_Globe.OBJ_TYPE.OBJ_FELLOW || //伙伴
objType == GameDefine_Globe.OBJ_TYPE.OBJ_ZOMBIE_PLAYER || //僵尸玩家
objType == GameDefine_Globe.OBJ_TYPE.OBJ_DROP_ITEM)//掉落包
{
result = false;
if (data.CountLimit >= EffectLimitLevel.unlimit)
result = true;
// 原始逻辑,一般认为unlimit的是非技能特效
else if (PlayerPreferenceData.SystemSkillEffectEnable)
{
var limit = data.CountLimit < EffectLimitLevel.important
? PlayerPreferenceData.effectCountLimit.commonCount
: PlayerPreferenceData.effectCountLimit.importantCount;
if (limit > GameManager.gameManager.effectPool.inUseCount)
result = true;
}
}
return result;
}
}
public class DelayEffectReference
{
public readonly Transform effect;
public readonly EffectLoadData loadData;
public readonly float startTime;
public DelayEffectReference(EffectLoadData loadData, Transform effect, float startTime)
{
this.loadData = loadData;
this.effect = effect;
this.startTime = startTime;
}
public bool CheckActive()
{
return Time.time > startTime;
}
}
public abstract class BaseEffectReference
{
public readonly Tab_Effect data;
public readonly Transform effect;
public readonly int? effectHandle;
protected BaseEffectReference(Tab_Effect data, Transform effect, int? effectHandle)
{
this.data = data;
this.effect = effect;
this.effectHandle = effectHandle;
}
// 特效是否需要延迟回收 - 有拖尾等特效效果等待效果结束
public bool DelayRecovery { get; private set; }
// 特效延迟回收时间
public float DelayRecoveryTime { get; private set; }
///
/// 更新特效状态
///
/// 特效是否继续存在
public virtual bool Update()
{
return effect != null;
}
///
/// 检查特效是否需要延迟回收
///
public bool CheckDelayRecovery()
{
var cleaner = effect.GetComponent();
if (cleaner == null)
{
LogModule.ErrorLog(string.Format("特效{0}上没有ParticleCleaner!", effect.GetHierarchyName()));
}
else if (cleaner.DelayRecoveryTime > 0f)
{
DelayRecovery = true;
DelayRecoveryTime = Time.time + cleaner.DelayRecoveryTime;
cleaner.StartDelayRecovery();
}
return DelayRecovery;
}
///
/// 检查特效是否延迟回收尚未结束
///
public virtual bool UpdateForDelayRecovery()
{
return Time.time < DelayRecoveryTime;
}
///
/// 特效播放结束时统一处理
///
public virtual void EndEffect()
{
}
}
public delegate void PlayEffectDelegate(GameObject effectObj, object param);
#region 特效物品通用管理池
public class EffectPool : AutoLoadPool
{
public EffectPool(Transform root, UnityAction createAction) : base(root, true, createAction)
{
}
public static string GetBundleName(string assetPath)
{
return LoadAssetBundle.FixBundleName(LoadAssetBundle.BUNDLE_PATH_EFFECT + assetPath);
}
public void PreloadEffect(Tab_Effect data)
{
var bundleName = GetBundleName(data.Path);
Preload(bundleName, data.Path);
}
public void PullEffect(EffectLoadData taskStarter)
{
var bundleName = GetBundleName(taskStarter.data.Path);
PullItem(taskStarter, bundleName, taskStarter.data.Path);
}
public void PushEffect(BaseEffectReference effect)
{
PushEffect(effect.data, effect.effect);
}
public void PushEffect(Tab_Effect data, Transform effect)
{
var bundleName = GetBundleName(data.Path);
if (data.IsOnlyDeactive)
{
PushItem(bundleName, data.Path, effect);
}
else
{
// 不退回物体,但是要求减少使用记录
ReduceCount(bundleName, data.Path);
Object.Destroy(effect.gameObject);
}
}
///
/// 按照特效句柄取消一个特效加载任务
///
/// 特效句柄
// 注:主要用于特效在完成加载前,服务器要求停止播放的情况
public void CancelLoadByHandleId(int handleId)
{
//if (!Killed)
for (var i = loadTaskList.Count - 1; i >= 0; i--)
loadTaskList[i].CancelByHandle(handleId);
}
}
///
/// 全局唯一的特效管理池,统一处理所有特效GameObject的回收和再利用
///
public class CentralEffectPool : EffectPool
{
public CentralEffectPool(Transform root) : base(root, EnsureParticleCleaner)
{
}
private static void EnsureParticleCleaner(Transform child)
{
// 如果没有自定义粒子回收器,就添加通用回收器
var cleaner = child.GetComponent();
if (cleaner == null)
child.gameObject.AddComponent();
}
///
/// EffectLogic被销毁时,通知池子取消加载注册
///
public void CleanEffectLogic(BaseEffectLogic effectLogic)
{
// if (!Killed)
// {
// 移除对加载事件的监听
for (var i = 0; i < loadTaskList.Count; i++)
try
{
loadTaskList[i].RemoveListener(a => a.callback == effectLogic.OnCreateEffect);
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
// 回收使用中的特效
for (var i = 0; i < effectLogic.ActiveList.Count; i++)
if (effectLogic.ActiveList[i].effect != null)
PushEffect(effectLogic.ActiveList[i]);
for (var i = 0; i < effectLogic.DelayList.Count; i++)
if (effectLogic.DelayList[i].effect != null)
{
var delayItem = effectLogic.DelayList[i];
PushEffect(delayItem.loadData.data, delayItem.effect);
}
effectLogic.ActiveList.Clear();
effectLogic.DelayList.Clear();
}
public void DestroyEffectLogic(BaseEffectLogic effectLogic)
{
if (!GameManager.applicationQuit)
{
// 移除对加载事件的监听
for (var i = 0; i < loadTaskList.Count; i++)
try
{
loadTaskList[i].RemoveListener(a => a.callback == effectLogic.OnCreateEffect);
}
catch (Exception e)
{
LogModule.ErrorLog(e.ToString());
}
// 回收会导致报错,因此跳过回收流程
for (var i = 0; i < effectLogic.ActiveList.Count; i++)
if (effectLogic.ActiveList[i].effect != null)
{
var assetName = effectLogic.ActiveList[i].data.Path;
var bundleName = GetBundleName(assetName);
ReduceCount(bundleName, assetName);
}
for (var i = 0; i < effectLogic.DelayList.Count; i++)
if (effectLogic.DelayList[i].effect != null)
{
var assetName = effectLogic.DelayList[i].loadData.data.Path;
var bundleName = GetBundleName(assetName);
ReduceCount(bundleName, assetName);
}
}
}
// 主角技能特效预加载
// 预加载方式为在这个地方添加一次引用防止成为Unused
#region MainCharacter Preload Effect
// 注:其他角色的技能表信息是无法获得的,因此只能预加载主角特效
private readonly List _holdEffects = new List();
public void SetMainPlayerPreload()
{
var preloadList = new HashSet();
var playerDataPool = GameManager.gameManager.PlayerDataPool;
foreach (var ownSkill in playerDataPool.OwnSkillInfo)
{
if (ownSkill.SkillExTable != null)
{
var preload = ownSkill.SkillExTable.PreloadEffect;
if (!string.IsNullOrEmpty(preload) && !"-1".Equals(preload) && !"0".Equals(preload))
{
var preloads = preload.Split(';');
for (var i = 0; i < preloads.Length; i++)
{
int effectId;
if (int.TryParse(preloads[i], out effectId))
{
if (effectId > GlobeVar.INVALID_ID)
{
var effectData = TableManager.GetEffectByID(effectId, 0);
if (effectData != null)
preloadList.Add(effectData.Path);
}
}
}
}
}
}
SetHoldPreload(preloadList);
}
public void SetHoldPreload(ICollection effects)
{
for (var i = _holdEffects.Count - 1; i >= 0; i--)
{
if (!effects.Contains(_holdEffects[i]))
{
var assetName = _holdEffects[i];
var bundleName = GetBundleName(assetName);
ReduceCount(bundleName, assetName, true);
_holdEffects.RemoveAt(i);
}
}
foreach (var effect in effects)
if (!_holdEffects.Contains(effect))
{
_holdEffects.Add(effect);
var assetName = effect;
var bundleName = GetBundleName(assetName);
// 如果已经有池,就直接添加数量
if (!Preload(bundleName, assetName))
AddCount(bundleName, assetName, true);
}
}
// 只有Kill会清空Pool;Kill后也不会重用池,因此不需要额外调用这个。
public void CleanHoldPreload()
{
for (var i = 0; i < _holdEffects.Count; i++)
{
var assetName = _holdEffects[i];
var bundleName = GetBundleName(assetName);
ReduceCount(bundleName, assetName, true);
}
_holdEffects.Clear();
}
// 底层锁死Pool只在资源加载完成才构造,因此只能监听加载完成后,再执行Hold;
// 修改底层风险过高,因此采用加载完成加Token的流程
protected override void OnLoadTaskFinished(EffectLoadTask loadTask)
{
base.OnLoadTaskFinished(loadTask);
if (_holdEffects.Contains(loadTask.PrefabName))
AddCount(loadTask.BundleName, loadTask.PrefabName, true);
}
#endregion
}
public class EffectLoadTask : BaseLoadTask
{
///
/// 按照特效句柄Id取消一个加载需求
///
/// 特效句柄Id
/// 是否加载任务已空
public void CancelByHandle(int handle)
{
for (var i = taskListeners.Count - 1; i >= 0; i--)
if (taskListeners[i].handle == handle)
taskListeners.RemoveAt(i);
}
}
public class EffectLoadData : BaseLoadData
{
public readonly Tab_Effect data;
///
/// 特效播放延迟的修正数值
///
public readonly float delayModifier;
public readonly int? handle;
public readonly int layer;
public readonly PlayEffectDelegate playCallback;
public readonly object playParameter;
public readonly float startTime;
public object customData;
public EffectLoadData(BaseEffectLogic effectLogic, int layer, Tab_Effect data, int? handle,
PlayEffectDelegate playCallback, object playParameter, object customData, float delayModifier = 0f) : base(
effectLogic.transform, effectLogic.OnCreateEffect)
{
this.layer = layer;
this.data = data;
this.handle = handle;
this.playCallback = playCallback;
this.playParameter = playParameter;
this.customData = customData;
this.delayModifier = delayModifier;
startTime = Time.time;
}
}
#endregion
#region 特效物品自定义参数
public class BulletCustomData : CustomEffectLoadData
{
public readonly Tab_Bullet bulletData;
public readonly int sourceId;
public readonly int targetId;
public BulletCustomData(Tab_Bullet bulletData, int sourceId, int targetId)
{
this.bulletData = bulletData;
this.sourceId = sourceId;
this.targetId = targetId;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.Bullet; }
}
}
public class BulletToPosData : CustomEffectLoadData
{
public readonly Tab_Bullet bulletData;
public readonly Vector3 endPos;
public readonly Vector3 startPos;
public BulletToPosData(Tab_Bullet bulletData, Vector3 startPos, Vector3 endPos)
{
this.bulletData = bulletData;
this.startPos = startPos;
this.endPos = endPos;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.BulletToPos; }
}
}
public class NormalEffectCustomData : CustomEffectLoadData
{
public readonly float duration;
public readonly Vector3 position;
public readonly Quaternion rotation;
public NormalEffectCustomData(Vector3 position, Quaternion rotation, float duration = -1f)
{
this.position = position;
this.rotation = rotation;
this.duration = duration;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.Position; }
}
}
public class ChainCustomData : CustomEffectLoadData
{
public readonly float duration;
public readonly int sourceId;
public readonly float speed;
public ChainCustomData(int sourceId, int targetId, float duration, float speed)
{
this.sourceId = sourceId;
TargetId = targetId;
this.duration = duration;
this.speed = speed;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.Chain; }
}
public int TargetId { get; private set; }
public void ResetTarget(int targetId)
{
TargetId = targetId;
}
}
public class BoundCustomData : CustomEffectLoadData
{
public override CommonEffectType EffectType
{
get { return CommonEffectType.Bound; }
}
}
public class StampCustomData : CustomEffectLoadData
{
public readonly int stampCount;
public StampCustomData(int stampCount)
{
this.stampCount = stampCount;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.Stamp; }
}
}
public class WuChangLinkData : CustomEffectLoadData
{
public readonly int sourceId;
public readonly int targetId;
public WuChangLinkData(int sourceId, int targetId)
{
this.sourceId = sourceId;
this.targetId = targetId;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.WuChangLink; }
}
}
public class WuChangAirportData : CustomEffectLoadData
{
public readonly int count;
public readonly int sourceId;
public WuChangAirportData(int sourceId, int count)
{
this.sourceId = sourceId;
this.count = count;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.WuChangAirport; }
}
}
public class MeteorEffectData : CustomEffectLoadData
{
public readonly Vector3 fallPosition;
public readonly float fallTime;
public readonly Vector3 holdPosition;
public readonly float holdTime;
public readonly Vector3 rollPosition;
public readonly float rollTime;
public MeteorEffectData(float offsetY, float holdTime, float fallTime, float rollTime, Vector2 holdPos,
Vector2 fallPos, Vector2 rollPos)
{
this.holdTime = holdTime;
this.fallTime = fallTime + this.holdTime;
this.rollTime = rollTime + this.fallTime;
holdPosition = ActiveScene.GetTerrainPosition(holdPos.InsertY());
fallPosition = ActiveScene.GetTerrainPosition(fallPos.InsertY());
rollPosition = ActiveScene.GetTerrainPosition(rollPos.InsertY());
holdPosition.y += offsetY;
}
public override CommonEffectType EffectType
{
get { return CommonEffectType.Meteor; }
}
}
public class AvatarEffectData : CustomEffectLoadData
{
public override CommonEffectType EffectType
{
get { return CommonEffectType.Avatar; }
}
}
public abstract class CustomEffectLoadData
{
public abstract CommonEffectType EffectType { get; }
}
#endregion
#region 特效物品引用参数
///
/// 标准绑定位置的特效基类
///
public abstract class BoundEffectReferenceBase : BaseEffectReference
{
private readonly Quaternion _localRotation;
public BoundEffectReferenceBase(Transform effect, Tab_Effect data, int? effectHandle) : base(data, effect,
effectHandle)
{
_localRotation = data.Yaw > 0 ? Quaternion.Euler(0f, data.Yaw, 0f) : Quaternion.identity;
}
// 特效绑定点
public Transform BindPoint { get; private set; }
// 特效绑定偏移值
public Vector3 Offset { get; private set; }
public void SetBindPoint(Transform bindPoint, Vector3 offset)
{
BindPoint = bindPoint;
Offset = offset;
effect.SetParent(bindPoint, false);
effect.transform.localPosition = offset;
effect.transform.localRotation = _localRotation;
var scale = data.Scale > 0 ? data.Scale * Vector3.one : Vector3.one;
effect.transform.localScale = scale;
}
public override bool Update()
{
if (base.Update())
{
SetRotation();
return true;
}
return false;
}
private void SetRotation()
{
switch (data.RotationType)
{
case EffectRotationType.noRotation:
// 需要校正LocalPosition到世界类型
effect.rotation = _localRotation;
break;
case EffectRotationType.faceCamera:
// 暂时使用UiManager上面那个安全接口
var mainCamera = UIManager.Instance().GetWorldCamera();
if (mainCamera != null)
{
var rotation = Quaternion.LookRotation(mainCamera.transform.position - effect.position);
rotation = rotation * _localRotation;
effect.rotation = rotation;
}
break;
}
}
}
///
/// 载入中特效数据,用于存放动态加载的特效实例
///
public class CommonEffectReference : BoundEffectReferenceBase
{
private readonly float _endTime;
public CommonEffectReference(Transform effect, Tab_Effect data, int? effectHandle, float endTime) : base(effect,
data,
effectHandle)
{
_endTime = endTime;
}
public override bool Update()
{
if (Time.time < _endTime)
return base.Update();
return false;
}
}
///
/// 子弹数据记录
///
public class BulletEffectReference : BaseEffectReference
{
private readonly Quaternion _localRotation;
private readonly BulletSimulator _simulator;
// 用于在Target析构后,重新恢复Target
private readonly int _targetId;
public readonly Tab_Bullet bulletData;
// 抛物线子弹最大偏移位置
private Vector3 _maxPathOffset;
private Vector3 _targetOffset;
private Transform _targetTransform;
public BulletEffectReference(Transform effect, int targetId, Obj source, Tab_Bullet bulletData, Tab_Effect data,
int? effectHandle) : base(data, effect, effectHandle)
{
this.bulletData = bulletData;
_localRotation = data.Yaw > 0 ? Quaternion.Euler(0f, data.Yaw, 0f) : Quaternion.identity;
_targetId = targetId;
// 提前推送目标无法获得时的攻击位置
_simulator = new BulletSimulator
{
targetPoint = source.transform.position + source.transform.rotation * GetStartOffset() +
source.transform.forward * bulletData.MinDistance
};
}
public Vector3 GetStartOffset()
{
return new Vector3(bulletData.StartPosX, bulletData.StartPosY, bulletData.StartPosZ);
}
public override bool Update()
{
var valid = base.Update() && MoveBullet();
// 子弹消散特效
if (!valid)
if (bulletData.EndEffectId > 0 && bulletData.EndEffectId != bulletData.EffectId)
if (IndependentEffectManager.Instance != null)
IndependentEffectManager.Instance.ShowEffect(bulletData.EndEffectId, effect.gameObject.layer, GameDefine_Globe.OBJ_TYPE.OBJ,
effect.position, Quaternion.Euler(0f, effect.eulerAngles.y, 0f));
return valid;
}
public bool InitBullet(float delay)
{
ResetAttackPoint();
_simulator.duration = (_simulator.targetPoint - effect.position).RemoveY().magnitude / bulletData.Speed;
var result = delay < _simulator.duration;
if (result)
{
_simulator.startTime = Time.time - delay;
_maxPathOffset = 0.5f * new Vector3(bulletData.BulletDirectionX, bulletData.BulletDirectionY, 0f) *
_simulator.duration;
_simulator.logicPoint = effect.position;
var scale = data.Scale > 0 ? data.Scale * Vector3.one : Vector3.one;
effect.localScale = scale;
result = MoveBullet();
}
return result;
}
///
/// 移动子弹位置
///
/// 子弹尚未飞行到目标位置
private bool MoveBullet()
{
var result = true;
ResetAttackPoint();
// 计算子弹飞行时间百分比
var direction = _simulator.UpdateBullet().normalized;
if (_simulator.lastRatio >= 1f)
result = false;
var logicPoint = _simulator.logicPoint;
if (_maxPathOffset == Vector3.zero || direction == Vector3.zero)
{
effect.position = logicPoint;
}
else
{
var x = Vector3.Cross(Vector3.up, direction).normalized;
effect.position = logicPoint + (_maxPathOffset.x * x + _maxPathOffset.y * Vector3.up) *
(1f - Mathf.Abs(_simulator.lastRatio * 2 - 1f).ToSquare());
}
// 调节子弹面对方向
if (direction != Vector3.zero)
{
var rotationType = data.RotationType;
// 如果没有摄像机就统一回退到最基础方式
var cameraTrans = SceneLogic.CameraController != null && SceneLogic.CameraController.MainCamera != null
? SceneLogic.CameraController.MainCamera.transform
: null;
if (cameraTrans == null)
rotationType = EffectRotationType.byEffectType;
switch (rotationType)
{
case EffectRotationType.faceCamera:
// 忽略这个Null警告,这个位置不可能是Null
var y = cameraTrans.position - effect.position;
var x = Vector3.Cross(y, direction);
y = Vector3.Cross(direction, x);
if (y == Vector3.zero)
y = Vector3.up;
effect.rotation = Quaternion.LookRotation(direction, y) * _localRotation;
break;
case EffectRotationType.byEffectType:
effect.rotation = Quaternion.LookRotation(direction, Vector3.up) * _localRotation;
break;
}
}
return result;
}
///
/// 获得子弹攻击目标位置
///
private void ResetAttackPoint()
{
// 试图恢复目标绑定
if (_targetId != 0 && _targetTransform == null)
{
var targetObj = Singleton.Instance.FindObjCharacterInScene(_targetId);
if (targetObj != null)
if (targetObj.ObjEffectLogic == null)
{
_targetTransform = targetObj.transform;
_targetOffset = Vector3.zero;
}
else
{
_targetTransform = targetObj.ObjEffectLogic.GetBindPoint(bulletData.AttackPoint, out _targetOffset);
}
}
// 如果目标没有丢失,使用目标校正攻击位置
if (_targetTransform != null)
_simulator.targetPoint = _targetTransform.rotation * _targetOffset + _targetTransform.position;
}
}
///
/// 对位置飞行子弹引用记录
///
public class BulletToPosReference : BaseEffectReference
{
private readonly float _duration;
private readonly Vector3 _endPos;
private readonly Quaternion _localRotation;
private readonly Vector3 _offsetDir;
private readonly Vector3 _startPos;
private readonly float _startTime;
public readonly Tab_Bullet bulletData;
public BulletToPosReference(Transform effect, Vector3 startPos, Vector3 endPos, Tab_Bullet bulletData,
Tab_Effect data,
int? effectHandle) : base(data, effect, effectHandle)
{
this.bulletData = bulletData;
_startPos = startPos;
_endPos = endPos;
_startTime = Time.time;
_localRotation = data.Yaw > 0 ? Quaternion.Euler(0f, data.Yaw, 0f) : Quaternion.identity;
var delta = endPos - startPos;
delta.y = 0f;
if (delta == Vector3.zero)
{
_startTime = float.NegativeInfinity;
_duration = 0f;
effect.position = startPos;
}
else
{
_duration = Mathf.Min(60f, delta.magnitude / bulletData.Speed);
var rotation = Quaternion.LookRotation(delta, Vector3.up) * _localRotation;
_offsetDir = rotation * new Vector3(bulletData.BulletDirectionX, bulletData.BulletDirectionY, 0f);
MoveBullet();
}
}
public override bool Update()
{
var valid = base.Update() && MoveBullet();
// 子弹消散特效
if (!valid)
if (bulletData.EndEffectId > 0 && bulletData.EndEffectId != bulletData.EffectId)
if (IndependentEffectManager.Instance != null)
IndependentEffectManager.Instance.ShowEffect(bulletData.EndEffectId, effect.gameObject.layer, GameDefine_Globe.OBJ_TYPE.OBJ,
effect.position, Quaternion.Euler(0f, effect.eulerAngles.y, 0f));
return valid;
}
///
/// 移动子弹位置
///
/// 子弹尚未飞行到目标位置
private bool MoveBullet()
{
var result = Time.time < _startTime + _duration;
if (result)
{
var time = Time.time - _startTime;
var ratio = time / _duration;
var direction = (_endPos - _startPos).normalized;
if (_offsetDir == Vector3.zero)
{
effect.position = Vector3.Lerp(_startPos, _endPos, ratio);
}
else
{
var currentOffsetDir = _offsetDir * (1f - ratio * 2f);
effect.position = Vector3.Lerp(_startPos, _endPos, ratio) +
(_offsetDir + currentOffsetDir) * 0.5f * bulletData.Speed * time;
direction += currentOffsetDir;
}
var rotationType = data.RotationType;
// 如果没有摄像机就统一回退到最基础方式
var cameraTrans = SceneLogic.CameraController != null && SceneLogic.CameraController.MainCamera != null
? SceneLogic.CameraController.MainCamera.transform
: null;
if (cameraTrans == null)
rotationType = EffectRotationType.byEffectType;
switch (rotationType)
{
case EffectRotationType.faceCamera:
// 忽略这个Null警告,这个位置不可能是Null
var y = cameraTrans.position - effect.position;
var x = Vector3.Cross(y, direction);
y = Vector3.Cross(direction, x);
if (y == Vector3.zero)
y = Vector3.up;
effect.rotation = Quaternion.LookRotation(direction, y) * _localRotation;
break;
case EffectRotationType.byEffectType:
effect.rotation = Quaternion.LookRotation(direction, Vector3.up) * _localRotation;
break;
}
}
else
{
effect.position = _endPos;
// 特殊处理结束不消失的流程
if (bulletData.EffectId > 0 && bulletData.EffectId == bulletData.EndEffectId &&
_startTime + data.Duration > Time.time)
result = true;
}
return result;
}
}
///
/// 锁链特效引用记录
///
public class ChainEffectReference : BaseEffectReference
{
// 用于在Source或者Target析构后,重新恢复效果
private readonly LinkFxController _controller;
// 如果锁链拥有消散时间和飞行速度,需要做子弹效果
private BulletSimulator _bulletSimulator;
private ChainCustomData _chainData;
private float _endTime;
private Obj_Character _source;
private Obj_Character _target;
public ChainEffectReference(Transform effect, ChainCustomData chainData, Tab_Effect data, int? effectHandle) : base(
data, effect, effectHandle)
{
_chainData = chainData;
_controller = effect.GetComponent();
if (_controller == null)
LogModule.ErrorLog(string.Format("特效物体{0}上不存在锁链控制器", data.Path));
ResetEndTime();
}
public override bool Update()
{
var result = base.Update() && Time.time < _endTime;
if (result && _controller != null)
{
if (_source == null || _source.ServerID != _chainData.sourceId)
_source = ObjManager.Instance.FindObjCharacterInScene(_chainData.sourceId);
if (_target == null || _target.ServerID != _chainData.TargetId)
_target = ObjManager.Instance.FindObjCharacterInScene(_chainData.TargetId);
// 需要动态初始化子弹的情况
if (_bulletSimulator == null && _source != null && _target != null)
{
var sourcePoint = GetChainPoint(_source, data.ParentName);
var targetPoint = GetChainPoint(_target, EffectLogic.centerName);
_bulletSimulator = new BulletSimulator(sourcePoint,
targetPoint,
_chainData.speed > 0f ? Vector3.Distance(sourcePoint, targetPoint) / _chainData.speed : 0f);
}
if (_bulletSimulator != null)
{
// 如果目标存在,刷新目标位置
if (_target != null)
_bulletSimulator.targetPoint = GetChainPoint(_target, EffectLogic.centerName);
_bulletSimulator.UpdateBullet();
if (_source != null)
{
var sourcePoint = GetChainPoint(_source, data.ParentName);
_controller.sourcePoint = sourcePoint;
}
_controller.targetPoint = _bulletSimulator.logicPoint;
_controller.TryUpdateLink();
}
}
return result;
}
public void ResetParameters(ChainCustomData chainData)
{
// 检查,如果Src或者Target有改变,就重置锁链
// 否则仅仅修改参数
if (chainData.sourceId != _chainData.sourceId
|| chainData.TargetId != _chainData.TargetId)
{
_source = null;
_target = null;
_bulletSimulator = null;
ResetEndTime();
}
_chainData = chainData;
Update();
}
private void ResetEndTime()
{
_endTime = _chainData.duration > 0 ? Time.time + _chainData.duration : float.PositiveInfinity;
if (_controller != null)
_controller.ResetEndTime(_endTime);
}
///
/// 恢复锁链攻击目标
///
private Vector3 GetChainPoint(Obj_Character target, string bindName)
{
if (target.ObjEffectLogic != null)
return target.ObjEffectLogic.GetBindPointPosition(bindName);
return target.Position;
}
}
public class StampEffectReference : BoundEffectReferenceBase
{
private readonly MarkSfxController _controller;
public StampEffectReference(Transform effect, Tab_Effect data, int initCount, int? effectHandle) : base(effect,
data, effectHandle)
{
_controller = effect.GetComponent();
if (_controller == null)
LogModule.ErrorLog(string.Format("特效物体{0}上不存在印记控制器", data.Path));
else
_controller.TargetCount = initCount;
}
public void SetCurrentCount(int count)
{
_controller.TargetCount = count;
}
public void Detonate()
{
_controller.Detonate(true);
}
public override bool Update()
{
var result = base.Update();
if (result)
result = !_controller.IsDetonated;
return result;
}
}
public class WuChangLinkReference : BaseEffectReference
{
// 用于在Source或者Target析构后,重新恢复效果
private readonly WuChangLinkController _controller;
public readonly WuChangLinkData linkData;
private bool _isBreak;
public WuChangLinkReference(Transform effect, WuChangLinkData linkData, Tab_Effect data, int? effectHandle) : base(
data, effect, effectHandle)
{
this.linkData = linkData;
_controller = effect.GetComponent();
if (_controller == null)
LogModule.ErrorLog(string.Format("特效物体{0}上不存在锁链控制器", data.Path));
else
_controller.Init(data);
}
public override bool Update()
{
var result = base.Update();
if (result && _controller != null)
{
var sourceObj = ObjManager.Instance.FindObjInScene(linkData.sourceId);
var targetObj = ObjManager.Instance.FindObjInScene(linkData.targetId);
if (sourceObj != null && targetObj != null)
{
var sourcePos = sourceObj.ObjEffectLogic == null
? sourceObj.Position
: sourceObj.ObjEffectLogic.GetBindPointPosition(EffectLogic.centerName);
var targetPos = targetObj.ObjEffectLogic == null
? targetObj.Position
: targetObj.ObjEffectLogic.GetBindPointPosition(EffectLogic.centerName);
_controller.gameObject.SetActive(true);
result = _controller.SetLinkPosition(sourcePos, targetPos, _isBreak);
}
else
{
_controller.gameObject.SetActive(false);
result = false;
}
// 如果没有断开,则不回收;否则等待断开动画结束
result = result || !_isBreak;
}
return result;
}
public void BreakLink()
{
_isBreak = true;
}
}
public class WuChangAirportReference : BaseEffectReference
{
// 用于在Source或者Target析构后,重新恢复效果
private readonly WuChangGhostAuraController _controller;
public readonly WuChangAirportData airportData;
private bool _active;
public WuChangAirportReference(Transform effect, WuChangAirportData airportData, Tab_Effect data,
int? effectHandle) : base(
data, effect, effectHandle)
{
this.airportData = airportData;
_active = true;
_controller = effect.GetComponent();
if (_controller == null)
LogModule.ErrorLog(string.Format("特效物体{0}上不存在幽灵机场控制器", data.Path));
else
_controller.Init(airportData.count, data);
}
public override bool Update()
{
var result = base.Update();
if (result && _controller != null)
{
var sourceObj = ObjManager.Instance.FindObjInScene(airportData.sourceId);
_active = sourceObj != null;
_controller.gameObject.SetActive(_active);
if (_active)
_controller.ownerPos = sourceObj.ObjEffectLogic == null
? sourceObj.Position
: sourceObj.ObjEffectLogic.GetBindPointPosition(data.ParentName);
}
return result;
}
public void AttackTarget(Transform target, Vector3 offset, float impactTime)
{
if (_active)
_controller.AttackTarget(target, offset, impactTime);
}
}
///
/// 陨石轨迹模拟器
///
// 陨石实际为一个三段式的子弹,三阶段都是匀速移动流程
public class MeteorEffectReference : BaseEffectReference
{
private readonly MeteorEffectController _meteorController;
private readonly MeteorEffectData _meteorData;
private readonly BulletSimulator _simulator;
private readonly float _startTime;
private int _groundEffectCount;
private MeteorState _meteorState;
public MeteorEffectReference(Transform effect, MeteorEffectData meteorData, Tab_Effect data,
int? effectHandle) : base(data, effect, effectHandle)
{
_groundEffectCount = 0;
_meteorData = meteorData;
_startTime = Time.unscaledTime;
_meteorState = MeteorState.Wait;
var z = _meteorData.fallPosition - _meteorData.holdPosition;
var x = Vector3.Cross(Vector3.up, z);
var y = Vector3.Cross(z, x);
if (y == Vector3.zero)
y = Vector3.up;
effect.rotation = Quaternion.LookRotation(z, y);
_meteorController = effect.GetComponent();
var collider = effect.GetComponentInChildren();
if (collider != null)
collider.gameObject.layer = ActiveScene.obstacleLayer;
if (_meteorController != null)
Move();
}
public override bool Update()
{
// 只通过句柄删除
var valid = base.Update() && _meteorController != null;
if (valid)
Move();
return valid;
}
private void Move()
{
MeteorState nextState;
if (Time.unscaledTime < _startTime + _meteorData.holdTime)
nextState = MeteorState.Wait;
else if (Time.unscaledTime < _startTime + _meteorData.fallTime)
nextState = MeteorState.Fall;
else if (Time.unscaledTime < _startTime + _meteorData.rollTime)
nextState = MeteorState.Roll;
else
nextState = MeteorState.End;
if (_meteorState != nextState)
{
_meteorState = nextState;
//if (_meteorState == MeteorState.End)
//{
// var distance = Vector3.Distance(_meteorData.rollPosition, _meteorData.fallPosition);
// var pitch = distance * _distanceToPitch;
// effect.rotation = _startQuaternion * Quaternion.Euler(pitch, 0f, 0f);
//}
if (_meteorState == MeteorState.Roll)
_meteorController.SetOnGround(true);
}
switch (_meteorState)
{
case MeteorState.Wait:
{
effect.position = _meteorData.holdPosition;
}
break;
case MeteorState.Fall:
{
var ratio = (Time.unscaledTime - _startTime - _meteorData.holdTime) /
(_meteorData.fallTime - _meteorData.holdTime);
effect.position = Vector3.Lerp(_meteorData.holdPosition,
_meteorData.fallPosition + Vector3.up * _meteorController.radius, ratio);
}
break;
case MeteorState.Roll:
{
var ratio = (Time.unscaledTime - _startTime - _meteorData.fallTime) /
(_meteorData.rollTime - _meteorData.fallTime);
var effectPos = Vector3.Lerp(_meteorData.fallPosition, _meteorData.rollPosition, ratio);
var distance = Vector3.Distance(effect.position, _meteorData.fallPosition);
_meteorController.SetRotationByDistance(distance);
effect.position = effectPos + _meteorController.radius * Vector3.up;
CreateGroundEffect(distance);
}
break;
default:
effect.position = _meteorData.rollPosition + _meteorController.radius * Vector3.up;
break;
}
}
private void CreateGroundEffect(float distance)
{
if (distance > _groundEffectCount)
{
// 制造第一个地面特效
var groundEffectId = data.GetParamValuebyIndex(1);
if (groundEffectId >= 0)
{
var time = Time.unscaledDeltaTime - _startTime - _meteorData.fallTime;
var groundData = CommonUtility.TryGetTable(groundEffectId, a => TableManager.GetEffectByID(a, 0));
if (groundData != null && groundData.Duration > time)
IndependentEffectManager.Instance.ShowEffect(groundEffectId, effect.gameObject.layer, GameDefine_Globe.OBJ_TYPE.OBJ,
effect.position - _meteorController.radius * Vector3.up,
Quaternion.identity, groundData.Duration - time);
}
_groundEffectCount = Mathf.FloorToInt(distance) + 1;
}
}
private enum MeteorState
{
Wait, // 下落前
Fall, // 下落中
Roll, // 落地后
End // 停止滚动后
}
}
// 背后神像效果
public class AvatarEffectReference : CommonEffectReference
{
private readonly AnimationLogic _effectAnim;
private AnimationLogic _characterAnim;
public AvatarEffectReference(Transform effect, Tab_Effect data, int? effectHandle, float endTime) : base(effect,
data, effectHandle, endTime)
{
_effectAnim = effect.gameObject.EnsureComponent();
_effectAnim.InitAnimLogicData(effect.GetComponentInChildren(), null);
_effectAnim.enabled = true;
}
public void Init(Obj_Character objCharacter)
{
// 注:Obj本身必然会在特效执行前执行事件绑定,因此特效监听到的时机必然在Obj执行过模型初始化
objCharacter.ModelNode.onModelCreate += OnCharacterModelCreate;
objCharacter.ModelNode.onModelDestroy += OnCharacterModelRemove;
if (objCharacter.ModelNode.model != null)
OnCharacterModelCreate(objCharacter.ModelNode.model, null);
effect.SetParent(objCharacter.ObjTransform);
effect.localPosition = new Vector3(data.OffsetX, data.OffsetY, data.OffsetZ);
effect.localScale = Vector3.one * (data.Scale > 0 ? data.Scale : 1f);
effect.localRotation = Quaternion.identity;
}
private void OnCharacterModelCreate(ObjPartRoot partRoot, object args)
{
_characterAnim = partRoot.GetComponent();
if (_characterAnim != null)
_characterAnim.onPlayAnim += OnCharacterPlayAnim;
}
private void OnCharacterModelRemove(ObjPartRoot partRoot)
{
if (_characterAnim != null)
{
_characterAnim.onPlayAnim -= OnCharacterPlayAnim;
_characterAnim = null;
}
}
public override void EndEffect()
{
base.EndEffect();
if (_characterAnim != null)
_characterAnim.onPlayAnim -= OnCharacterPlayAnim;
}
private void OnCharacterPlayAnim(Tab_Animation animData, string transitionName)
{
if (_effectAnim != null)
_effectAnim.Play(animData);
}
}
///
/// 模拟子弹运动轨迹的模拟器
///
public class BulletSimulator
{
// 子弹预期命中目标时间
public float duration;
// 子弹在上一帧的行走时间记录
public float lastRatio;
// 子弹逻辑位置记录 - 抛物线子弹逻辑位置和显示位置不一致
public Vector3 logicPoint;
// 子弹飞行开始时间
public float startTime;
// 子弹目标位置
public Vector3 targetPoint;
public BulletSimulator()
{
}
public BulletSimulator(Vector3 startPoint, Vector3 targetPoint, float duration, float startTime = -1f)
{
this.startTime = startTime < 0f ? Time.time : startTime;
this.duration = duration;
logicPoint = startPoint;
this.targetPoint = targetPoint;
lastRatio = 0f;
}
public Vector3 UpdateBullet()
{
// 计算子弹飞行时间百分比
var ratio = duration > 0f ? (Time.time - startTime) / duration : 1f;
var lastPoint = logicPoint;
var direction = Vector3.zero;
if (ratio >= 1f)
{
logicPoint = targetPoint;
if (lastRatio < 1f)
direction = logicPoint - lastPoint;
}
else
{
var deltaRatio = (ratio - lastRatio) / (1f - lastRatio);
logicPoint = Vector3.Lerp(logicPoint, targetPoint, deltaRatio);
direction = logicPoint - lastPoint;
}
lastRatio = ratio;
return direction;
}
}
#endregion
///
/// 特效逻辑类型
///
public enum CommonEffectType
{
Bound, // 绑定在节点上面的类型
Position, // 按照位置特效播放的特效类型
Bullet, // 子弹类型
Chain, // 闪电链类型特效
Stamp, // 印记类型特效
WuChangLink, // 无常锁链特效
WuChangAirport, // 无常幽灵机场特效
Meteor, // 陨石类型特效
Avatar, // 背后神象效果
BulletToPos // 对位置攻击的子弹
}
///
/// 特效旋转策略类型
///
public static class EffectRotationType
{
public const int noRotation = 0;
public const int faceCamera = 1;
public const int byEffectType = 2;
}