Files
JJBB/Assets/Project/Script/Scene/SceneLogic/BaseEffectLogic.cs
2024-08-23 15:49:34 +08:00

1886 lines
64 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <inheritdoc />
/// <summary>
/// 最基本的特效管理器
/// </summary>
public abstract class BaseEffectLogic : MonoBehaviour
{
/// <summary>
/// 最小加载容忍时间,加载时间超过该值会忽略特效
/// </summary>
public const float minLoadTolerance = 0.5f;
/// <summary>
/// 最小加载容忍时间比例,加载时间超过特效总时长比例会忽略特效
/// </summary>
public const float minLoadRatio = 0.3f;
private bool _enable;
public List<BaseEffectReference> ActiveList { get; private set; }
public List<DelayEffectReference> DelayList { get; private set; }
public bool IsInited { get; private set; }
/// <summary>
/// 获得特效后,如果是需要延迟出现的情况,特效将会延迟出现
/// </summary>
/// <param name="effect">特效物体</param>
/// <param name="loadData">加载数据</param>
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<ObjManager>.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<BaseEffectReference>();
DelayList = new List<DelayEffectReference>();
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<WarningCircle>().SetEffectData(data);
break;
case EffectLogic.EffectType.TYPE_RECTANGLE:
effect.GetComponent<WarningRect>().SetEffectData(data);
break;
case EffectLogic.EffectType.TYPE_SECTOR:
effect.GetComponent<WarningSector>().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());
}
}
/// <summary>
/// 清除全部播放中的特效
/// </summary>
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);
}
/// <summary>
/// 从延迟列表获得HandleId符合条件的物体
/// </summary>
// 特殊处理HandleId为0的情况 - 0视为不可用id
public DelayEffectReference GetDelayByHandle(int? handleId)
{
if (handleId == null)
return null;
return DelayList.Find(a => a.loadData.handle == handleId);
}
/// <summary>
/// 从激活列表获得HandleId符合条件的物体
/// </summary>
// 延迟回收的特效不允许获得
public BaseEffectReference GetReferenceByHandle(int? handleId)
{
return handleId == null ? null : ActiveList.Find(a => a.effectHandle == handleId && !a.DelayRecovery);
}
/// <summary>
/// 从激活列表获得EffectId符合条件的物体
/// </summary>
// 延迟回收的特效不允许获得
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; }
/// <summary>
/// 更新特效状态
/// </summary>
/// <returns>特效是否继续存在</returns>
public virtual bool Update()
{
return effect != null;
}
/// <summary>
/// 检查特效是否需要延迟回收
/// </summary>
public bool CheckDelayRecovery()
{
var cleaner = effect.GetComponent<IParticleCleaner>();
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;
}
/// <summary>
/// 检查特效是否延迟回收尚未结束
/// </summary>
public virtual bool UpdateForDelayRecovery()
{
return Time.time < DelayRecoveryTime;
}
/// <summary>
/// 特效播放结束时统一处理
/// </summary>
public virtual void EndEffect()
{
}
}
public delegate void PlayEffectDelegate(GameObject effectObj, object param);
#region
public class EffectPool : AutoLoadPool<Transform, EffectLoadData, EffectLoadTask>
{
public EffectPool(Transform root, UnityAction<Transform> 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);
}
}
/// <summary>
/// 按照特效句柄取消一个特效加载任务
/// </summary>
/// <param name="handleId">特效句柄</param>
// 注:主要用于特效在完成加载前,服务器要求停止播放的情况
public void CancelLoadByHandleId(int handleId)
{
//if (!Killed)
for (var i = loadTaskList.Count - 1; i >= 0; i--)
loadTaskList[i].CancelByHandle(handleId);
}
}
/// <summary>
/// 全局唯一的特效管理池统一处理所有特效GameObject的回收和再利用
/// </summary>
public class CentralEffectPool : EffectPool
{
public CentralEffectPool(Transform root) : base(root, EnsureParticleCleaner)
{
}
private static void EnsureParticleCleaner(Transform child)
{
// 如果没有自定义粒子回收器,就添加通用回收器
var cleaner = child.GetComponent<IParticleCleaner>();
if (cleaner == null)
child.gameObject.AddComponent<ParticleCleaner>();
}
/// <summary>
/// EffectLogic被销毁时通知池子取消加载注册
/// </summary>
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<string> _holdEffects = new List<string>();
public void SetMainPlayerPreload()
{
var preloadList = new HashSet<string>();
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<string> 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会清空PoolKill后也不会重用池因此不需要额外调用这个。
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<Transform, EffectLoadData, EffectLoadTask>
{
/// <summary>
/// 按照特效句柄Id取消一个加载需求
/// </summary>
/// <param name="handle">特效句柄Id</param>
/// <returns>是否加载任务已空</returns>
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<Transform, EffectLoadData>
{
public readonly Tab_Effect data;
/// <summary>
/// 特效播放延迟的修正数值
/// </summary>
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
/// <summary>
/// 标准绑定位置的特效基类
/// </summary>
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;
}
}
}
/// <summary>
/// 载入中特效数据,用于存放动态加载的特效实例
/// </summary>
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;
}
}
/// <summary>
/// 子弹数据记录
/// </summary>
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;
}
/// <summary>
/// 移动子弹位置
/// </summary>
/// <returns>子弹尚未飞行到目标位置</returns>
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;
}
/// <summary>
/// 获得子弹攻击目标位置
/// </summary>
private void ResetAttackPoint()
{
// 试图恢复目标绑定
if (_targetId != 0 && _targetTransform == null)
{
var targetObj = Singleton<ObjManager>.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;
}
}
/// <summary>
/// 对位置飞行子弹引用记录
/// </summary>
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;
}
/// <summary>
/// 移动子弹位置
/// </summary>
/// <returns>子弹尚未飞行到目标位置</returns>
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;
}
}
/// <summary>
/// 锁链特效引用记录
/// </summary>
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<LinkFxController>();
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);
}
/// <summary>
/// 恢复锁链攻击目标
/// </summary>
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<MarkSfxController>();
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<WuChangLinkController>();
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<WuChangGhostAuraController>();
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);
}
}
/// <summary>
/// 陨石轨迹模拟器
/// </summary>
// 陨石实际为一个三段式的子弹,三阶段都是匀速移动流程
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<MeteorEffectController>();
var collider = effect.GetComponentInChildren<Collider>();
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<AnimationLogic>();
_effectAnim.InitAnimLogicData(effect.GetComponentInChildren<Animator>(), 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<AnimationLogic>();
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);
}
}
/// <summary>
/// 模拟子弹运动轨迹的模拟器
/// </summary>
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
/// <summary>
/// 特效逻辑类型
/// </summary>
public enum CommonEffectType
{
Bound, // 绑定在节点上面的类型
Position, // 按照位置特效播放的特效类型
Bullet, // 子弹类型
Chain, // 闪电链类型特效
Stamp, // 印记类型特效
WuChangLink, // 无常锁链特效
WuChangAirport, // 无常幽灵机场特效
Meteor, // 陨石类型特效
Avatar, // 背后神象效果
BulletToPos // 对位置攻击的子弹
}
/// <summary>
/// 特效旋转策略类型
/// </summary>
public static class EffectRotationType
{
public const int noRotation = 0;
public const int faceCamera = 1;
public const int byEffectType = 2;
}