/******************************************************************************** * 文件名: EffectLogic.cs * 全路径: \Script\LogicCore\EffectLogic.cs * 创建人: 王迪 * 创建时间:2013-11-21 * * 功能说明:特效控制类 * 修改记录: *********************************************************************************/ using System; using System.Collections.Generic; using Games.GlobeDefine; using Games.LogicObj; using GCGame.Table; using Module.Log; using UnityEngine; public class EffectLogic : BaseEffectLogic { public enum EffectType { TYPE_NORMAL = 0, TYPE_CHANGEMODEL = 1, TYPE_CHANGMATPRO = 2, //材质属性 TYPE_BLIND = 3, //致盲 TYPE_CHANGEMODELMAT = 4, //改变模型材质 TYPE_CHANGESHADER = 5, //该变模型Shder TYPE_SCREENUIEFFECT = 6, //将一下UI当作特效显示 TYPE_CHAOS = 7, //混乱的效果逻辑 TYPE_ADDMAT = 8, // 增加材质 TYPE_CLIENTNPC = 9, //客户端NPC TYPE_CLONE = 10, //角色模型复制 TYPE_SCREEN = 11, //显示在屏幕的特效 TYPE_CIRCLE = 12, // 圆形指示器 TYPE_RECTANGLE = 13, // 矩形指示器 TYPE_SECTOR = 14, // 扇形指示器 TYPE_MATPROBYHEALTH = 15, // 颜色随生命值改变 TYPE_AVATAR = 16, // 背后幻象效果,跟随角色并且使用监听角色动画 TYPE_SCALE = 17 // 修改模型尺寸 } // 通用节点名称记录 public const string centerName = "CenterPoint"; public const string headName = "HeadPoint"; public const string leftHandName = "LHandPoint"; public const string rightHandName = "RHandPoint"; public const string leftFootName = "LFootPoint"; public const string rightFootName = "RFootPoint"; public const string leftWeaponName = "Weapon_L"; public const string rightWeaponName = "Weapon_R"; public const string namePointName = "NamePoint"; // 节点头顶位置 public const string normalPointName = "EffectPoint"; //通用点 private readonly Dictionary _effectBindPointCache = new Dictionary(); // 绑定点缓存 private readonly List _effectpointInfo = new List(); // 额外绑定点缓存 private Obj _effectObj; private ImpactEffectMgr _impactEffectMgr; private Tab_CharModel _modelData; public bool hideAllEffect { get; private set; } /// /// 获得绑定位置节点Transform /// public Transform GetBindPoint(string pointName) { Transform result; if (!_effectBindPointCache.TryGetValue(pointName, out result)) for (var i = _effectpointInfo.Count - 1; i >= 0; i--) // 原规则中自修复列表的功能 if (_effectpointInfo[i] == null) _effectpointInfo.RemoveAt(i); else if (string.Equals(_effectpointInfo[i].pointName, pointName, StringComparison.CurrentCultureIgnoreCase)) result = _effectpointInfo[i].transform; // 自修复逻辑,某些不存在或者挂点命名不规范的物体,使用控制器所在节点 if (result == null) result = _effectObj.effectPoint; return result; } public override Obj_Character GetOwnerCharacter() { var result = _effectObj as Obj_Character; if (result == null) LogModule.ErrorLog("EffectLogic拥有者不是一个角色,不应该使用角色独有的特效类型!"); return result; } /// /// 获得绑定位置节点Transform和相对偏移数值 /// public override Transform GetBindPoint(string pointName, out Vector3 offset) { offset = GetBindPointOffset(pointName); return GetBindPoint(pointName); } public bool IsHaveBindPoint(string pointName) { return GetBindPoint(pointName) != null; } public void SetHideAllEffect(bool isHideAll) { if (hideAllEffect != isHideAll) { hideAllEffect = isHideAll; if (hideAllEffect) { CleanEffect(); CleanImpacts(); } } } /// /// 角色模型析构时,移除特效绑定点 /// public void RemoveBindPoints() { _effectBindPointCache.Clear(); if (_effectObj != null) { var normalPoint = _effectObj.effectPoint; _effectBindPointCache.Add(normalPointName, normalPoint); for (var i = 0; i < ActiveList.Count; i++) if (ActiveList[i].effect != null) ActiveList[i].effect.SetParent(normalPoint, false); } } //上坐骑的时候修改一下默认特效节点位置 public void ResetNormalEffectPoint() { var move = false; var newParent = _effectObj.effectPoint; Transform oldParent; if (_effectBindPointCache.TryGetValue(normalPointName, out oldParent)) { if (oldParent != newParent) { move = true; _effectBindPointCache[normalPointName] = newParent; } } else { // 未初始化时,使用根节点作为实际位置 // 注:实际应该是使用ModelNode的模型节点作为根节点,但是由于早期特效都不继承模型缩放,继续使用根节点 oldParent = _effectObj.ObjTransform; if (oldParent != newParent) { move = true; _effectBindPointCache.Add(normalPointName, newParent); } } if (move) for (var i = 0; i < ActiveList.Count; i++) if (ActiveList[i].effect != null && ActiveList[i].effect.parent == oldParent) ActiveList[i].effect.SetParent(newParent, false); } /// /// 获得绑定位置的偏移数值 /// private Vector3 GetBindPointOffset(string pointName) { var result = Vector3.zero; if (_modelData == null) _modelData = TableManager.GetCharModelByID(_effectObj.ModelID); if (_modelData != null) if (pointName == headName) { if (_modelData.ModelType == (int) GameDefine_Globe.MODELTYPE.ANIMAL) result = new Vector3(0.0f, -0.8f, 0f); else if (_modelData.ModelType == (int) GameDefine_Globe.MODELTYPE.HUMAN || _modelData.ModelType == (int) GameDefine_Globe.MODELTYPE.HUMAN_FAT || _modelData.ModelType == (int) GameDefine_Globe.MODELTYPE.HUMAN_DYQ) result = new Vector3(-0.5f, 0f, 0f); else if (_modelData.ModelType == -1) result = new Vector3(0.0f, _modelData.HeadInfoHeight * 0.5f + 0.5f, 0f); } else if (pointName == centerName) { if (_modelData.ModelType == -1) result = new Vector3(0.0f, _modelData.HeadInfoHeight * 0.25f, 0f); } else if (pointName == leftHandName || pointName == rightHandName) { if (_modelData.ModelType == (int) GameDefine_Globe.MODELTYPE.HUMAN || _modelData.ModelType == (int) GameDefine_Globe.MODELTYPE.HUMAN_FAT || _modelData.ModelType == (int) GameDefine_Globe.MODELTYPE.HUMAN_DYQ) result = new Vector3(-0.03f, 0.035f, 0.01f); } else if (pointName == namePointName) { // 需要移除Transform缩放的影响 result = Vector3.up * _modelData.HeadInfoHeight / _effectObj.Scale.y; } return result; } /// /// 获得绑定节点当前的世界坐标位置 /// public Vector3 GetBindPointPosition(string pointName) { Vector3 offset; var bindPoint = GetBindPoint(pointName, out offset); // 无效节点校正到物体本身位置 if (bindPoint == null) bindPoint = gameObject.transform; return bindPoint.position + bindPoint.rotation * offset; } public void InitEffect(Obj effGameObj, GameObject modelRoot) { // 如果已经初始化过,只需要更新_effectObj if (!IsInited) { Init(); _impactEffectMgr = new ImpactEffectMgr(); } _effectObj = effGameObj; InitEffectPointInfo(modelRoot); } public void InitEffectPointInfo(GameObject modelRoot) { _effectBindPointCache.Clear(); _effectpointInfo.Clear(); // 绑定全部自定义节点 var points = _effectObj.GetComponentsInChildren(); for (var i = 0; i < points.Length; i++) if (points[i].pointName == namePointName) LogModule.ErrorLog(string.Format("{0}物体试图重新定义基础特效绑定点{1}!", modelRoot.transform.GetHierarchyName(), normalPointName)); else _effectpointInfo.Add(points[i]); // 绑定固定节点 if (_effectObj == null) { LogModule.ErrorLog(string.Format("物体{0}拥有EffectLogic,但是未绑定m_EffectObj!", gameObject.name)); } else { _effectBindPointCache.Add(normalPointName, _effectObj.effectPoint); var charModelInfo = TableManager.GetCharModelByID(_effectObj.ModelID); if (charModelInfo != null && charModelInfo.ModelType != -1) { var effectPointInfo = TableManager.GetEffectPointByID(charModelInfo.ModelType); if (effectPointInfo != null) { RegisterBindPoint(modelRoot, centerName, effectPointInfo.CenterPoint); RegisterBindPoint(modelRoot, headName, effectPointInfo.HeadPoint); RegisterBindPoint(modelRoot, leftHandName, effectPointInfo.LHandPoint); RegisterBindPoint(modelRoot, rightHandName, effectPointInfo.RHandPoint); RegisterBindPoint(modelRoot, leftFootName, effectPointInfo.LFootPoint); RegisterBindPoint(modelRoot, rightFootName, effectPointInfo.RFootPoint); RegisterBindPoint(modelRoot, leftWeaponName, effectPointInfo.WeaponL); RegisterBindPoint(modelRoot, rightWeaponName, effectPointInfo.WeaponR); // 头顶绑定点直接使用主要Transform _effectBindPointCache.Add(namePointName, _effectObj.transform); } for (var i = 0; i < ActiveList.Count; i++) { var effect = ActiveList[i].effect; var data = ActiveList[i].data; if ((data != null) & (effect != null)) { // 注:Offset位置当前Offset不变 var parent = GetBindPoint(data.ParentName); if (parent != effect.parent) { var offset = GetBindPointOffset(data.ParentName); // 旋转和缩放维持原有参数 effect.SetParent(parent, false); effect.localPosition = offset; } } } } } } private void RegisterBindPoint(GameObject modelRoot, string pointName, string path) { path = Obj.TrimModelRoot(path); var bindPoint = modelRoot.transform.Find(path); if (bindPoint) _effectBindPointCache.Add(pointName, bindPoint); } public void CleanImpacts() { if (_impactEffectMgr != null) _impactEffectMgr.ClearEffect(); } public void PlaySpecialEffect(Tab_Effect effectData, float overrideDuration = -1f) { if (effectData.Type == (int) EffectType.TYPE_SCREEN) { GlobalEffectMgr.PlayerNormalEffect(effectData.Path, Vector3.zero, overrideDuration > 0f ? overrideDuration : effectData.Duration); return; } if (_impactEffectMgr == null) return; _impactEffectMgr.StartImpactEffect(effectData, _effectObj, overrideDuration); } /// /// 检查普通特效是否可以被播放 /// private bool ValidEffectCondition(Tab_Effect effectInfo) { bool result; if (string.IsNullOrEmpty(effectInfo.Path) || _effectObj == null || GameManager.gameManager.effectPool == null) result = false; else result = ValidEffectByCount(effectInfo, _effectObj.ObjType); return result; } protected override void AfterCameraMovement(object args) { base.AfterCameraMovement(args); _impactEffectMgr.OnUpdate(); } /// /// 检查当前玩家状态是不是正常 /// private bool ValidPlayer() { return !hideAllEffect && _effectObj != null && _effectObj.IsVisibleChar(); } /// /// 开始播放一个特效 /// /// 特效id /// 特效播放回调 /// 额外参数 /// 延迟修正值,负数减少延迟,整数增加延迟 /// 特效句柄 public void PlayEffect(int effectId, PlayEffectDelegate delPlayEffect = null, object param = null, float delayModifier = 0f, int? handle = null) { if (effectId <= 0 || !PlayerPreferenceData.SystemRoleEffectEnable) return; var effectInfo = TableManager.GetEffectByID(effectId); if (effectInfo == null) { LogModule.ErrorLog(string.Format("无法找到id={0}的特效数据!", effectId)); if (null != delPlayEffect) delPlayEffect(null, param); return; } // 延迟跳过时间超过特效播放时间 if (effectInfo.Duration > 0 && -delayModifier > effectInfo.DelayTime + effectInfo.Duration) return; PlayEffect(effectInfo, delPlayEffect, param, delayModifier, handle); } /// /// 开始播放一个特效 /// /// 特效数据表 /// 特效播放回调 /// 额外参数 /// 延迟修正值,负数减少延迟,整数增加延迟 /// 特效句柄 public void PlayEffect(Tab_Effect effectInfo, PlayEffectDelegate delPlayEffect = null, object param = null, float delayModifier = 0f, int? handle = null) { if (!PlayerPreferenceData.SystemRoleEffectEnable) return; if (ValidPlayer() && (effectInfo.Profession < 0 || effectInfo.Profession == _effectObj.Profession)) { var effectType = (EffectType) effectInfo.Type; if (IsImpact(effectType)) { PlaySpecialEffect(effectInfo); } else if (effectType == EffectType.TYPE_AVATAR) { var loadData = new EffectLoadData(this, gameObject.layer, effectInfo, handle, delPlayEffect, param, new AvatarEffectData(), delayModifier); IndependentEffectManager.PullEffectItem(loadData); } else { if (!effectInfo.IsFellowOwner) { Vector3 offset; var bindPoint = GetBindPoint(effectInfo.ParentName, out offset); var rotation = bindPoint.rotation; var position = CommonUtility.RootOffsetPosition(bindPoint, offset + new Vector3(effectInfo.OffsetX, effectInfo.OffsetY, effectInfo.OffsetZ)); IndependentEffectManager.Instance.ShowEffect(effectInfo, gameObject.layer, _effectObj.ObjType, position, rotation, -1f, delPlayEffect, param, handle); } // 跟随主角的特效,仍然使用原来方式绑定到骨骼节点上 else if (ValidEffectCondition(effectInfo)) { var loadData = new EffectLoadData(this, gameObject.layer, effectInfo, handle, delPlayEffect, param, new BoundCustomData(), delayModifier); IndependentEffectManager.PullEffectItem(loadData); } } } } public void SetStampEffect(Tab_Effect effectInfo, int count) { if (count > 0) { var stampData = GetReferenceByEffectId(effectInfo.EffectID) as StampEffectReference; if (stampData != null) { stampData.SetCurrentCount(count); } else { // 防止稀有情况,第一层印记未加载完成,立刻来第二层的需求 IndependentEffectManager.CancelLoad(a => a.data.EffectID == effectInfo.EffectID); var loadData = new EffectLoadData(this, gameObject.layer, effectInfo, null, null, null, new StampCustomData(count)); IndependentEffectManager.PullEffectItem(loadData); } } else { // 仅仅移除当前加载 - 可能在上一个印记爆破期间叠加新的印记,因此仅仅在有激活中的印记时,才停止当前印记 IndependentEffectManager.CancelLoad(a => a.data.EffectID == effectInfo.EffectID); var stampData = GetReferenceByEffectId(effectInfo.EffectID) as StampEffectReference; if (stampData != null) StopEffect(effectInfo.EffectID); } } public void DetonateStamp(Tab_Effect effectInfo) { var stampData = GetReferenceByEffectId(effectInfo.EffectID) as StampEffectReference; if (stampData != null) stampData.Detonate(); else // 防止稀有情况,印记未加载完成就爆炸了 IndependentEffectManager.CancelLoad(a => a.data.EffectID == effectInfo.EffectID); } // public int GetEffectCountById(int effectId) // { // var count = 0; // if (ActiveList != null && DelayList != null) // { // for (var i = 0; i < ActiveList.Count; i++) // if (ActiveList[i].data.EffectID == effectId) // count++; // for (var i = 0; i < DelayList.Count; i++) // if (DelayList[i].loadData.data.EffectID == effectId) // count++; // } // // return count; // } ///// ///// 停止NPC所有特效 ///// //public void NpcStopEffect() //{ // if (_effectObj == null || _effectObj.ObjType != GameDefine_Globe.OBJ_TYPE.OBJ_NPC) // return; // _impactEffectMgr.ClearEffect(); // CleanEffect(); //} /// /// 停止其他玩家特效 /// public void Other_PlayerStopEffect() { if (_effectObj == null || _effectObj.ObjType != GameDefine_Globe.OBJ_TYPE.OBJ_OTHER_PLAYER) return; _impactEffectMgr.ClearEffect(); CleanEffect(); } public void StopEffect(int effectId, bool bStopAll = true) { if (effectId > 0) { var effectTable = TableManager.GetEffectByID(effectId); if (effectTable == null) LogModule.ErrorLog(string.Format("无法找到id={0}的特效数据!", effectId)); else StopEffect(effectTable, bStopAll); } } public void StopEffect(Tab_Effect effectTable, bool bStopAll = true) { if (IsImpact((EffectType) effectTable.Type)) { if ((EffectType) effectTable.Type == EffectType.TYPE_SCREEN) { GlobalEffectMgr.StopNormalEffect(effectTable.Path); return; } _impactEffectMgr.RemoveImpactEffect(effectTable, bStopAll); } else { var effectFound = false; // 试图从已经激活的任务中移除 for (var i = 0; i < ActiveList.Count; i++) if (ActiveList[i].data.EffectID == effectTable.EffectID) if (!ActiveList[i].DelayRecovery) { effectFound = !bStopAll; if (!ActiveList[i].CheckDelayRecovery()) RemoveEffectAt(i); break; } if (!effectFound) for (var i = 0; i < DelayList.Count; i++) if (DelayList[i].loadData.data.EffectID == effectTable.EffectID) { effectFound = !bStopAll; RemoveDelayAt(i); break; } if (!effectFound) IndependentEffectManager.CancelLoad(a => a.data.EffectID == effectTable.EffectID); } } private bool IsImpact(EffectType effectType) { return effectType != EffectType.TYPE_NORMAL && effectType != EffectType.TYPE_CIRCLE && effectType != EffectType.TYPE_RECTANGLE && effectType != EffectType.TYPE_SECTOR && effectType != EffectType.TYPE_AVATAR; } }