NGUI5.6与Unity2018.3 PrefabMode冲突问题解决方案
情景:
Unity在升级到2018.3版本后 新增了Prefab Mode系统,可以通过Open Prefab直接打开新的Scene对预设进行编辑。
NGUI的UI控件需要依附于UIRoot节点作为其顶级父节点,UIRoot主要是控制UI控件的变化和缩放。
问题:
Unity的Prefab Mode强制在对Prefab节点进行删除/移动时 需要在Prefab Mode系统中才能进行,因此若是在NGUI5.6升级Unity到2018.3 就会出现 在Scene场景中无法删、移预设,但是进入Prefab Mode会有导致NGUI控件报错的问题。
解决方法:
1,简单的解决方法是 每次进行预设编辑时 使用Unpack 进行预设的解引用,这样不需要进入Prefab Mode也可以进行预设的删、移,而缺点就是 每次修改之后需要进行预设的覆盖,带来操作成本,隐患则是进行Unpack会导致引用丢失,需要每次都重新拖引用,提高了操作失误率。
2,对NGUI 5.6 源码修改,在进入Prefab Mode时为其创建UIRoot,在寻找解决方案的时候发现了NGUI2018已经有相应的解决方法,这里将需要修改的代码整理出来:
1,实现配置环境
UIRect.cs protected virtual void Awake () {
#if UNITY_2018_3_OR_NEWER
NGUITools.CheckForPrefabStage (gameObject);
#endif
mStarted = false;
mGo = gameObject;
mTrans = transform;
}
static public void CheckForPrefabStage (GameObject gameObject) {
#if UNITY_EDITOR && UNITY_2018_3_OR_NEWER
var prefabStage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetPrefabStage (gameObject);
if (prefabStage == null)
return;
var rootsInParents = gameObject.GetComponentsInParent<UIRoot> (true);
var panelsInParents = gameObject.GetComponentsInParent<UIPanel> (true);
bool missingRoot = rootsInParents.Length == 0;
bool missingPanel = panelsInParents.Length == 0;
if (!missingRoot && !missingPanel)
return;
// Since this function is called from Awake/OnEnable, utilities like PrefabStage.prefabContentsRoot
// or Scene.GetRootGameObjects () aren't available at this point
var instanceRoot = gameObject.transform;
while (instanceRoot.parent != null)
instanceRoot = instanceRoot.parent;
GameObject container = UnityEditor.EditorUtility.CreateGameObjectWithHideFlags ("UIRoot (Environment)", HideFlags.DontSave);
container.layer = instanceRoot.gameObject.layer;
if (missingRoot)
container.AddComponent<UIRoot> ();
if (missingPanel)
container.AddComponent<UIPanel> ();
UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene (container, prefabStage.scene);
instanceRoot.SetParent (container.transform, false);
#endif
}
2,实现在预设场景能够正确保存NGUI预设
NGUITools.cs static public void SetDirty (UnityEngine.Object obj)
{
#if UNITY_EDITOR
if (obj)
{
if (UnityEditor.AssetDatabase.Contains(obj))
{
UnityEditor.EditorUtility.SetDirty(obj);
}
else if (!Application.isPlaying)
{
if (obj is Component)
{
var component = (Component)obj;
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(component.gameObject.scene);
}
else if (obj is UnityEditor.EditorWindow || obj is ScriptableObject)
{
UnityEditor.EditorUtility.SetDirty(obj);
}
else
{
UnityEditor.EditorUtility.SetDirty(obj);
UnityEditor.SceneManagement.EditorSceneManager.MarkAllScenesDirty();
}
}
}
//if (obj)
//{
// //if (obj is Component) Debug.Log(NGUITools.GetHierarchy((obj as Component).gameObject), obj);
// //else if (obj is GameObject) Debug.Log(NGUITools.GetHierarchy(obj as GameObject), obj);
// //else Debug.Log("Hmm... " + obj.GetType(), obj);
// UnityEditor.EditorUtility.SetDirty(obj);
// }
#endif
}
3,实现渲染
UIDrawCall static UIDrawCall Create (string name, UIPanel pan, Material mat, Texture tex, Shader shader)
{
UIDrawCall dc = Create(name);
dc.gameObject.layer = pan.cachedGameObject.layer;
dc.baseMaterial = mat;
dc.mainTexture = tex;
dc.shader = shader;
dc.renderQueue = pan.startingRenderQueue;
dc.sortingOrder = pan.sortingOrder;
dc.manager = pan;
#if UNITY_EDITOR && UNITY_2018_3_OR_NEWER
// We need to perform this check here and not in Create (string) to get to manager reference
var prefabStage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage ();
if (prefabStage != null && dc.manager != null)
{
// If prefab stage exists and new daw call
var stage = UnityEditor.SceneManagement.StageUtility.GetStageHandle (dc.manager.gameObject);
if (stage == prefabStage.stageHandle)
UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene (dc.gameObject, prefabStage.scene);
}
#endif
return dc;
}
剩下的是其他的修改,代码太多放不下
链接:https://pan.baidu.com/s/1LTdX-4ZjzyYTqxFh8UyAKQ
提取码:7xdt