#if UNITY_EDITOR using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; namespace GSpawn { public class ObjectGroup : ScriptableObject, IUIItemStateProvider { [SerializeField] private ObjectGroup _parentGroup; [SerializeField] private PluginGuid _guid = new PluginGuid(Guid.NewGuid()); [SerializeField] private string _objectGroupName = string.Empty; [NonSerialized] private GameObject _gameObject; // Note: Can't serialize game objects. Loosing ref between Unity sessions because object groups are assets. [SerializeField] private string _globalObjectIdString = string.Empty; // Our link to the game object [SerializeField] private bool _uiSelected = false; [NonSerialized] private CopyPasteMode _uiCopyPasteMode = CopyPasteMode.None; [NonSerialized] private List _gameObjectBuffer = new List(); public PluginGuid guid { get { return _guid; } } public ObjectGroup parentGroup { get { return _parentGroup; } } public string objectGroupName { get { return _objectGroupName; } } public GameObject gameObject { get { if (_gameObject == null) { _gameObject = GameObjectEx.globalIdStringToGameObject(_globalObjectIdString); // Note: A second check is necessary because unfortunately, the global ID // is SOMETIMES regenerated when deleting the group object from the // scene (e.g. delete and then Undo to restore). This is why object groups // must have unique names. if (_gameObject == null) _gameObject = GameObject.Find(_objectGroupName); if (_gameObject != null) { if (parentGroup != null) _gameObject.transform.parent = parentGroup.gameObject.transform; } } return _gameObject; } set { _gameObject = value; _objectGroupName = _gameObject.name; _globalObjectIdString = _gameObject.getGlobalIdSlowString(); if (parentGroup != null) _gameObject.transform.parent = parentGroup.gameObject.transform; EditorUtility.SetDirty(this); } } public bool uiSelected { get { return _uiSelected; } set { UndoEx.record(this); _uiSelected = value; } } public CopyPasteMode uiCopyPasteMode { get { return _uiCopyPasteMode; } set { _uiCopyPasteMode = value; } } public void syncName() { if (_gameObject.name != _objectGroupName) { UndoEx.record(this); _objectGroupName = _gameObject.name; this.name = _objectGroupName; } } [NonSerialized] List _childrenBuffer = new List(); public void getAllNonGroupChildren(List allChildren) { allChildren.Clear(); gameObject.getAllChildren(true, true, _childrenBuffer); int numChildren = _childrenBuffer.Count; for (int i = 0; i < numChildren; ++i) { var child = _childrenBuffer[i]; if (ObjectGroupDb.instance.isObjectGroup(child)) continue; allChildren.Add(child); } } public void destroyImmediateNonGroupChildren() { _gameObjectBuffer.Clear(); int numChildren = gameObject.transform.childCount; for (int i = 0; i < numChildren; ++i) { Transform child = gameObject.transform.GetChild(i); if (ObjectGroupDb.instance.isObjectGroup(child.gameObject)) continue; _gameObjectBuffer.Add(child.gameObject); } // Note: The objects which are about to be deleted could be selected etc ObjectEvents.onObjectsWillBeDestroyed(_gameObjectBuffer); foreach (var go in _gameObjectBuffer) UndoEx.destroyGameObjectImmediate(go); } public void setDirectObjectGroupChildIndex(ObjectGroup childGroup, int newIndex) { if (childGroup.parentGroup != this) return; Transform transform = gameObject.transform; int numChildren = transform.childCount; int currentGroupIndex = -1; for (int i = 0; i < numChildren; ++i) { var go = transform.GetChild(i).gameObject; if (ObjectGroupDb.instance.isObjectGroup(go)) ++currentGroupIndex; if (currentGroupIndex == newIndex) { UndoEx.registerChildrenOrderUndo(transform); childGroup.gameObject.transform.SetSiblingIndex(i); break; } } } public void setParentObjectGroup(ObjectGroup parentGroup) { if (parentGroup) { UndoEx.setTransformParent(gameObject.transform, parentGroup.gameObject.transform); UndoEx.record(this); _parentGroup = parentGroup; EditorUtility.SetDirty(this); } else { UndoEx.setTransformParent(gameObject.transform, null); UndoEx.record(this); _parentGroup = null; EditorUtility.SetDirty(this); } } public void addChildren(IEnumerable gameObjects) { Transform groupTransform = gameObject.transform; foreach (var gameObject in gameObjects) UndoEx.setTransformParent(gameObject.transform, groupTransform); } public ObjectGroup findParentGroup() { Transform currentParent = gameObject.transform.parent; while (currentParent != null) { var objectGroup = ObjectGroupDb.instance.findObjectGroup(currentParent.gameObject); if (objectGroup != null) return objectGroup; currentParent = currentParent.parent; } return null; } public bool isChildOf(ObjectGroup parentObjectGroup) { if (parentGroup == null) return false; if (parentGroup == parentObjectGroup) return true; Transform currentParent = gameObject.transform.parent; while (currentParent != null) { var parent = ObjectGroupDb.instance.findObjectGroup(currentParent.gameObject); if (parent == parentObjectGroup) return true; currentParent = currentParent.parent; } return false; } public void getDirectChildren(List children) { children.Clear(); Transform transform = gameObject.transform; int numChildren = transform.childCount; for (int childIndex = 0; childIndex < numChildren; ++childIndex) { GameObject childObject = transform.GetChild(childIndex).gameObject; ObjectGroup childObjectGroup = ObjectGroupDb.instance.findObjectGroup(childObject); if (childObjectGroup != null) children.Add(childObjectGroup); } } public void getAllChildGroups(List allChildren) { allChildren.Clear(); // Note: It can happen when calling 'ObjectGroupDb.instance.deleteObjectGroup(this)' // from 'onHierarchyChanged'. if (_gameObject == null) return; gameObject.getAllChildren(true, true, _gameObjectBuffer); foreach(var childObject in _gameObjectBuffer) { ObjectGroup group = ObjectGroupDb.instance.findObjectGroup(childObject); if (group != null) allChildren.Add(group); } } [NonSerialized] private List _nameBuffer = new List(); private void onHierarchyChanged() { if (GSpawn.active == null) return; if (gameObject == null) { UndoEx.saveEnabledState(); UndoEx.enabled = false; ObjectGroupDbUI.instance.onObjectGroupWillBeDeleted(this); ObjectGroupDb.instance.deleteObjectGroup(this); PluginPrefabManagerUI.instance.onPrefabObjectGroupLinksChanged(); UndoEx.restoreEnabledState(); } else if (gameObject.name != _objectGroupName) { // Note: No duplicate names allowed. ObjectGroupDb.instance.getObjectGroupNames_IgnoreGroup(_nameBuffer, this); if (_nameBuffer.Contains(gameObject.name)) gameObject.name = UniqueNameGen.generate(gameObject.name, _nameBuffer); syncName(); ObjectGroupDbUI.instance.onObjectGroupNeedsUIRefresh(this); } else { // Maybe its parent changed if (gameObject.transform.hasChanged) { gameObject.transform.hasChanged = false; ObjectGroupDbUI.instance.refresh(); } } } private void OnEnable() { EditorApplication.hierarchyChanged += onHierarchyChanged; } private void OnDisable() { EditorApplication.hierarchyChanged -= onHierarchyChanged; } } } #endif