| | |
| | | } |
| | | } |
| | | |
| | | private Bone followedBone; |
| | | |
| | | public bool isBindBone = false; |
| | | |
| | | public bool isRedCamp = true; |
| | | public EffectConfig effectConfig; |
| | | |
| | | public float speedRate = 1f; |
| | | public float speedRate = 1.5f; |
| | | |
| | | private float m_Alpha = 1f; |
| | | |
| | | public float scaleFactor = 1f;// 长期为1,只在需要跟模型一起缩放的时候更改 值为teamHero.ModelScale |
| | | |
| | | public float Alpha |
| | | { |
| | | get |
| | | { |
| | | return m_Alpha; |
| | | } |
| | | set |
| | | { |
| | | if (value == m_Alpha) |
| | | return; |
| | | |
| | | m_Alpha = value; |
| | | OnAlphaChanged(); |
| | | } |
| | | } |
| | | |
| | | private void OnAlphaChanged() |
| | | { |
| | | if (spineComp != null) |
| | | { |
| | | var skeleton = spineComp.Skeleton; |
| | | skeleton.A = Alpha; |
| | | spineComp.LateUpdate(); |
| | | } |
| | | |
| | | for (int i = 0; i < rendererList.Count; i++) |
| | | { |
| | | var renderer = rendererList[i]; |
| | | if (renderer != null && renderer.material != null && renderer.material.HasProperty("_Color")) |
| | | { |
| | | Color color = renderer.material.color; |
| | | color.a = Alpha; |
| | | renderer.material.color = color; |
| | | } |
| | | } |
| | | } |
| | | |
| | | [Header("播放完毕立即回收")] |
| | | public bool isReleaseImmediately = false; //界面特效一般不需要自我销毁,跟随界面或者父对象销毁就行 |
| | | |
| | | public Action<BattleEffectPlayer> onDestroy; |
| | | |
| | | [HideInInspector] public Canvas canvas = null; |
| | | |
| | | [HideInInspector] public GameObject effectTarget = null; |
| | | |
| | | protected EffectPenetrationBlocker blocker = null; |
| | | protected RendererAdjuster blocker = null; |
| | | |
| | | protected bool isInit = false; |
| | | |
| | |
| | | |
| | | public GameObjectPoolManager.GameObjectPool pool; |
| | | |
| | | public BattleField battleField; |
| | | |
| | | public Action onComplete; |
| | | |
| | | private bool isPlaying = false; |
| | | |
| | | private float timer = 0f; |
| | | |
| | | protected virtual void OnEnable() |
| | | { |
| | | if (spineComp != null) |
| | | { |
| | | //隐藏,会有静态显示问题 |
| | | spineComp.enabled = false; |
| | | } |
| | | // if (spineComp != null) |
| | | // { |
| | | // //隐藏,会有静态显示问题 |
| | | // spineComp.enabled = false; |
| | | // } |
| | | |
| | | ApplySortingOrder(); |
| | | } |
| | | |
| | | |
| | |
| | | } |
| | | // 有特效可能带spine又带unity特效的情况 |
| | | spineComp = gameObject.GetComponentInChildren<SkeletonAnimation>(true); |
| | | |
| | | |
| | | |
| | | if (effectConfig.effectPos != null && effectConfig.effectPos.Length >= 2) |
| | | { |
| | | rectTrans.anchoredPosition += new Vector2((isRedCamp ? 1f : -1f) * effectConfig.effectPos[0], effectConfig.effectPos[1]); |
| | | } |
| | | |
| | | if (effectConfig.effectScale > 0f) |
| | | { |
| | | rectTrans.localScale *= effectConfig.effectScale; |
| | | } |
| | | |
| | | |
| | | spineComp.loop = effectConfig.isLoop != 0; |
| | | } |
| | | |
| | | protected virtual void Clear() |
| | |
| | | onComplete?.Invoke(); |
| | | } |
| | | |
| | | public Func<bool> funcIsHeroFront; |
| | | |
| | | public void SetSortingOrder(Func<bool> _isHeroFrontCallback) |
| | | { |
| | | funcIsHeroFront = _isHeroFrontCallback; |
| | | |
| | | ApplySortingOrder(); |
| | | } |
| | | |
| | | |
| | | public void ApplySortingOrder() |
| | | { |
| | | if (null != blocker && effectConfig != null && funcIsHeroFront != null) |
| | | { |
| | | bool isEffectFront = effectConfig.frontBack == 1; |
| | | |
| | | bool isHeroFront = funcIsHeroFront(); |
| | | |
| | | int baseSortingOrder = isHeroFront ? BattleConst.SkillMaskOrder : BattleConst.BattleBackgroundOrder - BattleConst.BattleFrontHeroEffect - 1; |
| | | |
| | | // 区分是否在特效前面 还要再区分是否在UI前面 isBindBone |
| | | int dynamicOffset = isBindBone |
| | | ? BattleConst.BattleFrontHeroBehindUIEffect : (isEffectFront ? BattleConst.BattleFrontHeroEffect : BattleConst.BattleBehindHeroEffect); |
| | | |
| | | int finalSortingOrder = baseSortingOrder + dynamicOffset; |
| | | |
| | | |
| | | blocker.SetSortingOrder(finalSortingOrder); |
| | | } |
| | | } |
| | | |
| | | public void FollowBoneXY() |
| | | { |
| | | if (followedBone == null || !isBindBone) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | Vector2 vector2 = Vector2.zero; |
| | | |
| | | if (effectConfig.effectPos != null && effectConfig.effectPos.Length >= 2) |
| | | { |
| | | vector2 = new Vector2((isRedCamp ? 1f : -1f) * effectConfig.effectPos[0], effectConfig.effectPos[1]); |
| | | } |
| | | |
| | | if (spineComp != null) |
| | | { |
| | | spineComp.transform.localPosition = new Vector3(followedBone.WorldX + vector2.x, followedBone.WorldY + vector2.y, 0); |
| | | } |
| | | |
| | | if (effectTarget != null) |
| | | { |
| | | effectTarget.transform.localPosition = new Vector3(followedBone.WorldX + vector2.x, followedBone.WorldY + vector2.y, 0); |
| | | } |
| | | } |
| | | |
| | | public virtual void Play(bool showLog = true) |
| | | { |
| | | |
| | | |
| | | if (!isInit) |
| | | { |
| | | InitComponent(showLog); |
| | | isInit = true; |
| | | } |
| | | else |
| | | { |
| | | //避免重复创建 |
| | | if (!this.gameObject.activeSelf) |
| | | { |
| | | InitComponent(showLog); |
| | | isInit = true; |
| | | this.gameObject.SetActive(true); |
| | | } |
| | | else |
| | | { |
| | | //避免重复创建 |
| | | if (!this.gameObject.activeSelf) |
| | | { |
| | | this.gameObject.SetActive(true); |
| | | } |
| | | return; |
| | | } |
| | | return; |
| | | } |
| | | |
| | | if (EffectMgr.IsNotShowBySetting(effectId)) |
| | | { |
| | |
| | | this.gameObject.SetActive(true); |
| | | } |
| | | |
| | | if (effectConfig.autoDestroy != 0) |
| | | { |
| | | GameObject.Destroy(gameObject, effectConfig.destroyDelay); |
| | | } |
| | | |
| | | isPlaying = true; |
| | | timer = 0f; |
| | | |
| | | PlayEffect(); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | public void Run() |
| | | { |
| | | if (null == effectConfig) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (!isPlaying) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (effectConfig.autoDestroy != 0) |
| | | { |
| | | timer += Time.deltaTime * speedRate; |
| | | if (timer >= effectConfig.destroyDelay) |
| | | { |
| | | GameObject.DestroyImmediate(gameObject); |
| | | } |
| | | } |
| | | } |
| | | |
| | | protected virtual void PlayEffect() |
| | |
| | | } |
| | | |
| | | // 如果delay小于等于0 那会立刻执行 |
| | | this.DelayTime(effectConfig.delayPlay, () => |
| | | this.DelayTime(effectConfig.delayPlay / speedRate, () => |
| | | { |
| | | PlayEffectInternal(); |
| | | }); |
| | |
| | | { |
| | | PlayUnityEffect(); |
| | | } |
| | | |
| | | battleField?.soundManager.PlayEffectSound(effectConfig.audio); |
| | | |
| | | OnAlphaChanged(); |
| | | } |
| | | |
| | | protected void PlaySpineEffect() |
| | | { |
| | | // 这里是纯spine的逻辑 |
| | | |
| | | if (spineComp == null) |
| | | { |
| | | Debug.LogError("BattleEffectPlayer spineComp is null, effect id is " + effectId); |
| | | return; |
| | | } |
| | | |
| | | SkeletonDataAsset skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("UIEffect/" + effectConfig.packageName, effectConfig.fxName); |
| | | // ===== 简化:直接从缓存获取,缓存内部会自动加载 ===== |
| | | string directory = "UIEffect/" + effectConfig.packageName; |
| | | SkeletonDataAsset skeletonDataAsset = BattleResManager.Instance.GetSpineResource( |
| | | directory, |
| | | effectConfig.fxName, |
| | | battleField?.guid |
| | | ); |
| | | |
| | | if (skeletonDataAsset == null) |
| | | { |
| | | Debug.LogError($"BattleEffectPlayer: Failed to load effect spine {effectConfig.fxName}"); |
| | | return; |
| | | } |
| | | // ================================ |
| | | |
| | | spineComp.skeletonDataAsset = skeletonDataAsset; |
| | | spineComp.Initialize(true); |
| | | spineComp.timeScale = speedRate; |
| | | |
| | | spineAnimationState = spineComp.AnimationState; |
| | | if (scaleX != 0f && spineComp != null && spineComp.skeleton != null) |
| | | { |
| | | spineComp.skeleton.ScaleX = scaleX; |
| | | } |
| | | |
| | | spineComp.skeleton.A = Alpha; |
| | | |
| | | spineAnimationState = spineComp.state; |
| | | spineAnimationState.Complete -= OnSpineAnimationComplete; |
| | | spineAnimationState.Complete += OnSpineAnimationComplete; |
| | | |
| | | |
| | | if (null == canvas) |
| | | canvas = GetComponentInParent<Canvas>(); |
| | | |
| | | // 添加特效穿透阻挡器 |
| | | blocker = spineComp.AddMissingComponent<EffectPenetrationBlocker>(); |
| | | blocker = spineComp.AddMissingComponent<RendererAdjuster>(); |
| | | |
| | | blocker.onSortingChanged = OnSortingChanged; |
| | | |
| | | // 如果没有canvas的话 正常是因为不在BattleWin下面的节点 意思就是当前没有显示 等到切回战斗的时候再通过BattleField.UpdateCanvas来更新 |
| | | if (canvas != null) |
| | | { |
| | | blocker.SetParentCanvas(canvas); |
| | | } |
| | | ApplySortingOrder(); |
| | | |
| | | spineComp.enabled = true; |
| | | |
| | | spineComp.timeScale = speedRate; |
| | | |
| | | spineComp.LateUpdate(); |
| | | |
| | | |
| | | Spine.Animation animation = spineAnimationState.Data.SkeletonData.Animations.First(); |
| | | spineAnimationState.SetAnimation(0, animation, effectConfig.isLoop != 0); |
| | | |
| | | SoundPlayer.Instance.PlayUIAudio(effectConfig.audio); |
| | | // SoundPlayer.Instance.PlayUIAudio(effectConfig.audio); |
| | | |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // 从特效预制体池获取特效 |
| | | pool = GameObjectPoolManager.Instance.RequestPool(effectPrefab); |
| | | pool = GameObjectPoolManager.Instance.GetPool(effectPrefab); |
| | | effectTarget = pool.Request(); |
| | | // 设置父节点和位置 |
| | | effectTarget.transform.SetParent(transform); |
| | |
| | | |
| | | OnUnityAnimationComplete(); |
| | | |
| | | if (null == canvas) |
| | | canvas = GetComponentInParent<Canvas>(); |
| | | |
| | | // 添加特效穿透阻挡器 |
| | | blocker = effectTarget.AddMissingComponent<EffectPenetrationBlocker>(); |
| | | blocker = effectTarget.AddMissingComponent<RendererAdjuster>(); |
| | | |
| | | blocker.onSortingChanged = OnSortingChanged; |
| | | |
| | | // 如果没有canvas的话 正常是因为不在BattleWin下面的节点 意思就是当前没有显示 等到切回战斗的时候再通过BattleField.UpdateCanvas来更新 |
| | | if (canvas != null) |
| | | { |
| | | blocker.SetParentCanvas(canvas); |
| | | } |
| | | |
| | | SoundPlayer.Instance.PlayUIAudio(effectConfig.audio); |
| | | // SoundPlayer.Instance.PlayUIAudio(effectConfig.audio); |
| | | |
| | | } |
| | | |
| | |
| | | |
| | | public string sortingLayer; |
| | | public int sortingOrder; |
| | | public RectTransform rectTrans; |
| | | |
| | | protected void OnSortingChanged(string _sortingLayer, int _sortingOrder) |
| | | { |
| | | if (null == spineComp) |
| | | return; |
| | | |
| | | sortingLayer = _sortingLayer; |
| | | sortingOrder = _sortingOrder; |
| | | // 处理排序变化 |
| | |
| | | |
| | | |
| | | // 创建后的特效会自动隐藏 需要手动调用Play才能播放 |
| | | public static BattleEffectPlayer Create(int effectId, Transform parent) |
| | | public static BattleEffectPlayer Create(int effectId, BattleField _battleField, Transform parent, bool isRedCamp, float _scaleRatio) |
| | | { |
| | | // 直接创建特效播放器,不使用对象池 |
| | | BattleEffectPlayer battleEffectPlayer = null; |
| | | |
| | | GameObject newGo = new GameObject("BattleEffectPlayer_" + effectId); |
| | | newGo.transform.SetParent(parent, false); |
| | | newGo.AddMissingComponent<RectTransform>(); |
| | | battleEffectPlayer = newGo.AddComponent<BattleEffectPlayer>(); |
| | | battleEffectPlayer.battleField = _battleField; |
| | | battleEffectPlayer.rectTrans = newGo.AddMissingComponent<RectTransform>(); |
| | | |
| | | battleEffectPlayer.effectId = effectId; |
| | | |
| | | battleEffectPlayer.scaleFactor = _scaleRatio; |
| | | |
| | | // 设置阵营 |
| | | battleEffectPlayer.isRedCamp = isRedCamp; |
| | | |
| | | battleEffectPlayer.SetActive(true); |
| | | return battleEffectPlayer; |
| | | } |
| | |
| | | |
| | | public void Pause() |
| | | { |
| | | if (effectTarget == null) return; |
| | | // if (effectTarget == null) return; |
| | | |
| | | // Spine动画 |
| | | // var spineGraphics = effectTarget.GetComponentsInChildren<SkeletonGraphic>(true); |
| | |
| | | |
| | | public void Resume() |
| | | { |
| | | if (effectTarget == null) return; |
| | | // if (effectTarget == null) return; |
| | | |
| | | if (spineComp != null) |
| | | { |
| | |
| | | return true; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置遮罩(支持RectMask2D、Mask、SmoothMask等) |
| | | /// </summary> |
| | | public void SetMask(RectTransform maskArea = null) |
| | | public void SetSpeedRatio(float ratio) |
| | | { |
| | | if (effectTarget == null || blocker == null) |
| | | return; |
| | | |
| | | // 优先使用传入的maskArea |
| | | if (maskArea != null) |
| | | speedRate = ratio; |
| | | if (spineComp != null) |
| | | { |
| | | blocker.PerformMask(maskArea); |
| | | return; |
| | | spineComp.timeScale = speedRate; |
| | | } |
| | | |
| | | // Animator动画 |
| | | foreach (var animator in animatorList) |
| | | { |
| | | animator.speed = speedRate; |
| | | } |
| | | } |
| | | |
| | | public void BindBone(BattleObject battleObject, string v) |
| | | { |
| | | var skeletonAnim = battleObject?.GetSkeletonAnimation(); |
| | | if (skeletonAnim == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | Bone bone = skeletonAnim.skeleton.FindBone(v); |
| | | isBindBone = false; |
| | | followedBone = null; |
| | | if (null == bone) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | rectTrans.localScale *= scaleFactor; |
| | | |
| | | isBindBone = true; |
| | | followedBone = bone; |
| | | |
| | | ApplySortingOrder(); |
| | | |
| | | BoneFollower boneFollower = gameObject.AddMissingComponent<BoneFollower>(); |
| | | boneFollower.boneName = v; |
| | | boneFollower.skeletonRenderer = skeletonAnim; |
| | | |
| | | boneFollower.followBoneRotation = false; |
| | | boneFollower.followXYPosition = true; |
| | | boneFollower.followZPosition = false; |
| | | boneFollower.followLocalScale = false; |
| | | boneFollower.followParentWorldScale = false; |
| | | boneFollower.followSkeletonFlip = false; |
| | | |
| | | boneFollower.Initialize(); |
| | | boneFollower.LateUpdate(); |
| | | } |
| | | |
| | | private float scaleX = 0f; |
| | | |
| | | public void SetScaleX(float v) |
| | | { |
| | | if (spineComp != null && spineComp.skeleton != null) |
| | | { |
| | | spineComp.skeleton.ScaleX = v; |
| | | } |
| | | else |
| | | { |
| | | scaleX = v; |
| | | } |
| | | } |
| | | } |