diff --git a/unity/Assets/Resources/Resources_moved/gui/panel/UI_LevelGiftPanel.prefab b/unity/Assets/Resources/Resources_moved/gui/panel/UI_LevelGiftPanel.prefab
index c7ab8ebc..e20954ef 100644
--- a/unity/Assets/Resources/Resources_moved/gui/panel/UI_LevelGiftPanel.prefab
+++ b/unity/Assets/Resources/Resources_moved/gui/panel/UI_LevelGiftPanel.prefab
@@ -1020,7 +1020,7 @@ GameObject:
- component: {fileID: 2933118048976270207}
- component: {fileID: 2429120442925024079}
- component: {fileID: 8817440891751262561}
- - component: {fileID: 4706064949595997982}
+ - component: {fileID: 6126304919991226096}
m_Layer: 5
m_HasEditorInfo: 1
m_Name: UI_LevelGiftPanel
@@ -1094,7 +1094,7 @@ MonoBehaviour:
tf_item: {fileID: 736732509760957031}
prefab_item: {fileID: 6078732611595661672, guid: 06738302f7ce6a548a9f6a4406c392d0,
type: 3}
---- !u!114 &4706064949595997982
+--- !u!114 &6126304919991226096
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -1106,7 +1106,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a2deb478a85eef4883e8465d48c426f, type: 3}
m_Name:
m_EditorClassIdentifier:
- isKeepShowWhenCovered: 1
+ isKeepShowWhenCovered: 0
--- !u!1 &6984570939639282589
GameObject:
m_ObjectHideFlags: 0
diff --git a/unity/Assets/Resources/Resources_moved/gui/panel/UI_MythHeroPanel.prefab b/unity/Assets/Resources/Resources_moved/gui/panel/UI_MythHeroPanel.prefab
index 34e4e8eb..bc6d8229 100644
--- a/unity/Assets/Resources/Resources_moved/gui/panel/UI_MythHeroPanel.prefab
+++ b/unity/Assets/Resources/Resources_moved/gui/panel/UI_MythHeroPanel.prefab
@@ -14065,7 +14065,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a2deb478a85eef4883e8465d48c426f, type: 3}
m_Name:
m_EditorClassIdentifier:
- isKeepShowWhenCovered: 1
+ isKeepShowWhenCovered: 0
--- !u!1 &6273192759332336783
GameObject:
m_ObjectHideFlags: 0
diff --git a/unity/Assets/Resources/Resources_moved/gui/panel/UI_PassPanel.prefab b/unity/Assets/Resources/Resources_moved/gui/panel/UI_PassPanel.prefab
index d98867ad..78966c76 100644
--- a/unity/Assets/Resources/Resources_moved/gui/panel/UI_PassPanel.prefab
+++ b/unity/Assets/Resources/Resources_moved/gui/panel/UI_PassPanel.prefab
@@ -1462,7 +1462,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a2deb478a85eef4883e8465d48c426f, type: 3}
m_Name:
m_EditorClassIdentifier:
- isKeepShowWhenCovered: 1
+ isKeepShowWhenCovered: 0
--- !u!1 &6984570939639282589
GameObject:
m_ObjectHideFlags: 0
diff --git a/unity/Assets/Resources/Resources_moved/gui/panel/UI_PetPanel.prefab b/unity/Assets/Resources/Resources_moved/gui/panel/UI_PetPanel.prefab
index ed816a9a..472d7d2f 100644
--- a/unity/Assets/Resources/Resources_moved/gui/panel/UI_PetPanel.prefab
+++ b/unity/Assets/Resources/Resources_moved/gui/panel/UI_PetPanel.prefab
@@ -4364,7 +4364,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a2deb478a85eef4883e8465d48c426f, type: 3}
m_Name:
m_EditorClassIdentifier:
- isKeepShowWhenCovered: 1
+ isKeepShowWhenCovered: 0
--- !u!1 &8696742643833394590
GameObject:
m_ObjectHideFlags: 0
diff --git a/unity/Assets/Resources/Resources_moved/gui/panel/UI_RaidSelectPanel.prefab b/unity/Assets/Resources/Resources_moved/gui/panel/UI_RaidSelectPanel.prefab
index 8367f81c..093e8953 100644
--- a/unity/Assets/Resources/Resources_moved/gui/panel/UI_RaidSelectPanel.prefab
+++ b/unity/Assets/Resources/Resources_moved/gui/panel/UI_RaidSelectPanel.prefab
@@ -6583,7 +6583,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a2deb478a85eef4883e8465d48c426f, type: 3}
m_Name:
m_EditorClassIdentifier:
- isKeepShowWhenCovered: 1
+ isKeepShowWhenCovered: 0
--- !u!1 &9182812132619542671
GameObject:
m_ObjectHideFlags: 0
diff --git a/unity/Assets/Resources/Resources_moved/gui/panel/UI_ShopPanel.prefab b/unity/Assets/Resources/Resources_moved/gui/panel/UI_ShopPanel.prefab
index a6fdadf7..f5be125e 100644
--- a/unity/Assets/Resources/Resources_moved/gui/panel/UI_ShopPanel.prefab
+++ b/unity/Assets/Resources/Resources_moved/gui/panel/UI_ShopPanel.prefab
@@ -3946,7 +3946,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a2deb478a85eef4883e8465d48c426f, type: 3}
m_Name:
m_EditorClassIdentifier:
- isKeepShowWhenCovered: 1
+ isKeepShowWhenCovered: 0
--- !u!1 &6483444692411636741
GameObject:
m_ObjectHideFlags: 0
diff --git a/unity/Assets/Resources/Resources_moved/gui/panel/UI_SkillPanel.prefab b/unity/Assets/Resources/Resources_moved/gui/panel/UI_SkillPanel.prefab
index 6269aa6a..84c6c01d 100644
--- a/unity/Assets/Resources/Resources_moved/gui/panel/UI_SkillPanel.prefab
+++ b/unity/Assets/Resources/Resources_moved/gui/panel/UI_SkillPanel.prefab
@@ -167,7 +167,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a2deb478a85eef4883e8465d48c426f, type: 3}
m_Name:
m_EditorClassIdentifier:
- isKeepShowWhenCovered: 1
+ isKeepShowWhenCovered: 0
--- !u!1 &1018902619444209224
GameObject:
m_ObjectHideFlags: 0
diff --git a/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectBase.cs b/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectBase.cs
index c3ceb2fd..210257d0 100644
--- a/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectBase.cs
+++ b/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectBase.cs
@@ -196,8 +196,11 @@ namespace Coffee.UIEffects
Profiler.BeginSample("(UIE)[UIEffect] GetModifiedMaterial");
var samplingScaleId = (uint)(Mathf.InverseLerp(0.01f, 100, actualSamplingScale) * uint.MaxValue);
- var rootId = (uint)(transitionRoot != null ? transitionRoot.GetHashCode() : 0);
- var hash = new Hash128((uint)baseMaterial.GetHashCode(), effectId, samplingScaleId, rootId);
+ // 使用特效配置哈希作为材质共享 key,相同配置的特效共享同一材质,从而可以合批。
+ // root 仅在特效需要视图矩阵(transition/gradation/detail/shiny)时才影响材质。
+ var configId = context.configId;
+ var rootId = context.useViewMatrix && transitionRoot ? (uint)transitionRoot.GetInstanceID() : 0u;
+ var hash = new Hash128((uint)baseMaterial.GetInstanceID(), configId, samplingScaleId, rootId);
if (!MaterialRepository.Valid(hash, _material))
{
Profiler.BeginSample("(UIE)[UIEffect] GetModifiedMaterial > Get or create material");
diff --git a/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectContext.cs b/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectContext.cs
index c315f4cb..48b6ae4b 100644
--- a/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectContext.cs
+++ b/unity/my_libs/unityLib/UIEffect-main/Packages/src/Runtime/UIEffectContext.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Reflection;
using Coffee.UIEffectInternal;
using UnityEngine;
using UnityEngine.Profiling;
@@ -263,6 +264,126 @@ namespace Coffee.UIEffects
|| m_DetailFilter != DetailFilter.None
|| m_EdgeMode == EdgeMode.Shiny;
+ ///
+ /// 特效配置的稳定指纹。自动遍历所有序列化配置字段生成,因此插件新增属性时无需手动维护字段清单。
+ /// 相同配置会得到相同指纹,从而可以共享同一个材质并参与合批。
+ ///
+ public uint configId
+ {
+ get
+ {
+ unchecked
+ {
+ var h = (uint)2166136261;
+ var fields = s_ConfigFields;
+ for (var i = 0; i < fields.Length; i++)
+ {
+ var f = fields[i];
+ h = Mix(h, (uint)f.Name.GetHashCode());
+ h = Mix(h, HashValue(f.GetValue(this)));
+ }
+
+ // Avalanche 混淆(MurmurHash3 终混),让相近配置散列到不同指纹。
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+ return h;
+ }
+ }
+ }
+
+ private static uint Mix(uint h, uint v)
+ {
+ h ^= v;
+ h *= 16777619;
+ return h;
+ }
+
+ ///
+ /// 对任意配置值做内容哈希。值类型会逐字段展开,嵌套结构体(Color、Vector2/3/4、MinMax01 等)
+ /// 无需单独写类型处理即可贡献全部数据。只有 UnityEngine.Object 和 Gradient 需要显式处理,
+ /// 因为它们的默认哈希基于引用而非内容。
+ ///
+ private static uint HashValue(object value)
+ {
+ if (value == null) return 0u;
+
+ if (value is UnityEngine.Object obj)
+ {
+ return obj ? (uint)obj.GetInstanceID() : 0u;
+ }
+
+ if (value is Gradient gradient)
+ {
+ return GradientHash(gradient);
+ }
+
+ var type = value.GetType();
+ if (type.IsPrimitive || type.IsEnum)
+ {
+ return unchecked((uint)value.GetHashCode());
+ }
+
+ if (type.IsValueType)
+ {
+ return HashFields(value, type);
+ }
+
+ return unchecked((uint)value.GetHashCode());
+ }
+
+ private static uint HashFields(object boxed, Type type)
+ {
+ unchecked
+ {
+ if (!s_FieldCache.TryGetValue(type, out var fields))
+ {
+ fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ s_FieldCache[type] = fields;
+ }
+
+ var h = (uint)2166136261;
+ for (var i = 0; i < fields.Length; i++)
+ {
+ h = Mix(h, HashValue(fields[i].GetValue(boxed)));
+ }
+
+ return h;
+ }
+ }
+
+ private static uint GradientHash(Gradient gradient)
+ {
+ unchecked
+ {
+ var h = (uint)2166136261;
+
+ var colorKeys = gradient.colorKeys;
+ for (var i = 0; i < colorKeys.Length; i++)
+ {
+ h = Mix(h, HashValue(colorKeys[i].color));
+ h = Mix(h, HashValue(colorKeys[i].time));
+ }
+
+ var alphaKeys = gradient.alphaKeys;
+ for (var i = 0; i < alphaKeys.Length; i++)
+ {
+ h = Mix(h, HashValue(alphaKeys[i].alpha));
+ h = Mix(h, HashValue(alphaKeys[i].time));
+ }
+
+ return h;
+ }
+ }
+
+ private static readonly FieldInfo[] s_ConfigFields = typeof(UIEffectContext)
+ .GetFields(BindingFlags.Public | BindingFlags.Instance);
+
+ private static readonly Dictionary s_FieldCache =
+ new Dictionary();
+
private Texture2D _gradationRampTex;
private Texture2D _transitionRampTex;
private bool _isGradientDirty = true;