fileFormatVersion: 2 guid: 949e01a86a7a65d4facebf6e5c01e0b9 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant:
using System; using System.Collections.Generic; using System.Linq; using UnityEditor.IMGUI.Controls; using UnityEngine; using UnityEngine.SceneManagement; using UnityObject = UnityEngine.Object;
namespace UnityEditor.TreeViewExamples {
class TransformTreeView : TreeView
{
public TransformTreeView (TreeViewState state)
: base (state)
{
Reload ();
}
/// <summary>
/// 创建 根节点, 分两种方式, 一下代码采用的第 2 种, 需要重写 BuildRows 函数
/// 1:创建根节点和所有的TreeViewItem
/// 2: 只创建根节点,这种需要重写一些函数, 好处是比较适合那些有很多TreeViewItem 显示的情况
/// </summary>
/// <returns></returns>
protected override TreeViewItem BuildRoot()
{
return new TreeViewItem {id = 0, depth = -1};
}
/// <summary>
/// 在Reload 函数之后调用,
/// 创建需要显示的 TreeViewItem
/// </summary>
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
{
var rows = GetRows () ?? new List<TreeViewItem> (200);
Scene scene = SceneManager.GetSceneAt (0);
// We use the GameObject instanceIDs as ids for items as we want to
// select the game objects and not the transform components.
rows.Clear ();
var gameObjectRoots = scene.GetRootGameObjects ();
foreach (var gameObject in gameObjectRoots)
{
var item = CreateTreeViewItemForGameObject (gameObject);
root.AddChild (item);
rows.Add (item);
if (gameObject.transform.childCount > 0)
{
if (IsExpanded (item.id))
{
AddChildrenRecursive (gameObject, item, rows);
}
else
{
//没展开, 就创建个占位的
item.children = CreateChildListForCollapsedParent ();
}
}
}
//根据子、父节点来设置该TreeViewItem和子物体的 depth 值
SetupDepthsFromParentsAndChildren (root);
return rows;
}
void AddChildrenRecursive (GameObject go, TreeViewItem item, IList<TreeViewItem> rows)
{
int childCount = go.transform.childCount;
item.children = new List<TreeViewItem> (childCount);
for (int i = 0; i < childCount; ++i)
{
var childTransform = go.transform.GetChild (i);
var childItem = CreateTreeViewItemForGameObject (childTransform.gameObject);
item.AddChild (childItem);
rows.Add (childItem);
//子节点处理
if (childTransform.childCount > 0)
{
if (IsExpanded (childItem.id))
{
AddChildrenRecursive (childTransform.gameObject, childItem, rows);
}
else
{
childItem.children = CreateChildListForCollapsedParent ();
}
}
}
}
static TreeViewItem CreateTreeViewItemForGameObject (GameObject gameObject)
{
// We can use the GameObject instanceID for TreeViewItem id, as it ensured to be unique among other items in the tree.
// To optimize reload time we could delay fetching the transform.name until it used for rendering (prevents allocating strings
// for items not rendered in large trees)
// We just set depth to -1 here and then call SetupDepthsFromParentsAndChildren at the end of BuildRootAndRows to set the depths.
return new TreeViewItem(gameObject.GetInstanceID(), -1, gameObject.name);
}
/// <summary>
/// 没有在BuildRoot 函数中创建所有的 TreeViewItem, 就需要重写这个函数
/// 用于显示被折叠的 TreeViewItem
/// </summary>
protected override IList<int> GetAncestors (int id)
{
// The backend needs to provide us with this info since the item with id
// may not be present in the rows
var transform = GetGameObject(id).transform;
List<int> ancestors = new List<int> ();
while (transform.parent != null)
{
ancestors.Add (transform.parent.gameObject.GetInstanceID ());
transform = transform.parent;
}
return ancestors;
}
/// <summary>
/// 没有在BuildRoot 函数中创建所有的 TreeViewItem, 就需要重写这个函数
/// 用于展开整个 子树
/// </summary>
protected override IList<int> GetDescendantsThatHaveChildren (int id)
{
Stack<Transform> stack = new Stack<Transform> ();
var start = GetGameObject(id).transform;
stack.Push (start);
var parents = new List<int> ();
while (stack.Count > 0)
{
Transform current = stack.Pop ();
parents.Add (current.gameObject.GetInstanceID ());
for (int i = 0; i < current.childCount; ++i)
{
if (current.childCount > 0)
stack.Push (current.GetChild (i));
}
}
return parents;
}
GameObject GetGameObject (int instanceID)
{
return (GameObject)EditorUtility.InstanceIDToObject(instanceID);
}
/// <summary>
/// Custom GUI
/// 自定义UI, 在这个函数调用前那个"三角形”已经绘制了
/// </summary>
/// <param name="args"></param>
protected override void RowGUI (RowGUIArgs args)
{
Event evt = Event.current;
extraSpaceBeforeIconAndLabel = 18f;
// GameObject isStatic toggle
var gameObject = GetGameObject(args.item.id);
if (gameObject == null)
return;
Rect toggleRect = args.rowRect;
toggleRect.x += GetContentIndent(args.item);
toggleRect.width = 16f;
// Ensure row is selected before using the toggle (usability)
// 通过一个Rect对象,来判断是否有点击了这个区域
if (evt.type == EventType.MouseDown && toggleRect.Contains(evt.mousePosition))
SelectionClick(args.item, false);
EditorGUI.BeginChangeCheck ();
bool isStatic = EditorGUI.Toggle(toggleRect, gameObject.isStatic);
if (EditorGUI.EndChangeCheck ())
gameObject.isStatic = isStatic;
// Text
// 绘制名字,注释掉一下代码就看不到名字了
base.RowGUI(args);
}
/// <summary>
/// Selection
/// 监听选择的改变
/// </summary>
/// <param name="selectedIds"> 选择到的 TreeViewItem 的 Id , 按 ctrl建或Shift建 一次选多个, selectIds 就会是对应的个数</param>
protected override void SelectionChanged (IList<int> selectedIds)
{
Selection.instanceIDs = selectedIds.ToArray();
}
/// <summary>
/// 处理 TreeViewItem 是否可以拖拽
/// 父类中默认返回false, 所以需要拖拽功能就必须重写
/// 如果返回为 true, 则需要 重写 SetupDragAndDrop 函数, 来实现拖拽
/// </summary>
/// <param name="args"> 选择要拖拽的TreeViewItem 的相关信息 </param>
/// <returns></returns>
protected override bool CanStartDrag (CanStartDragArgs args)
{
return true;
}
/// <summary>
/// 在 CanStartDrag 函数返回 true 的情况下, 该函数才有用
/// 实现拖拽功能
/// </summary>
protected override void SetupDragAndDrop (SetupDragAndDropArgs args)
{
//清除之前的数据
DragAndDrop.PrepareStartDrag ();
//调用 SortItemIDsInRowOrder函数,进行排序
//规则是 根据 TreeViewItem 在视图中的位置进行排序, 越高的排在越前面的位置
var sortedDraggedIDs = SortItemIDsInRowOrder (args.draggedItemIDs);
//获取要拖拽的 object
List<UnityObject> objList = new List<UnityObject> (sortedDraggedIDs.Count);
foreach (var id in sortedDraggedIDs)
{
UnityObject obj = EditorUtility.InstanceIDToObject (id);
if (obj != null)
objList.Add (obj);
}
//设置要拖拽的对象
DragAndDrop.objectReferences = objList.ToArray ();
string title = objList.Count > 1 ? "<Multiple>" : objList[0].name;
DragAndDrop.StartDrag (title); //开始拖拽
}
/// <summary>
/// 在 CanStartDrag 函数返回 true 的情况下, 该函数才有用
/// 在 TreeView 视图中进行拖拽时, 该函数会被持续调用,
/// 可以在函数中 接受 或 拒绝 该拖动
/// </summary>
protected override DragAndDropVisualMode HandleDragAndDrop (DragAndDropArgs args)
{
// First check if the dragged objects are GameObjects
var draggedObjects = DragAndDrop.objectReferences;
var transforms = new List<Transform> (draggedObjects.Length);
foreach (var obj in draggedObjects)
{
var go = obj as GameObject;
if (go == null)
{
return DragAndDropVisualMode.None;
}
transforms.Add (go.transform);
}
// Filter out any unnecessary transforms before the reparent operation
RemoveItemsThatAreDescendantsFromOtherItems (transforms);
// Reparent
if (args.performDrop)
{
switch (args.dragAndDropPosition)
{
case DragAndDropPosition.UponItem:
case DragAndDropPosition.BetweenItems:
Transform parent = args.parentItem != null ? GetGameObject (args.parentItem.id).transform : null;
if (!IsValidReparenting (parent, transforms))
return DragAndDropVisualMode.None;
foreach (var trans in transforms)
trans.SetParent (parent);
if (args.dragAndDropPosition == DragAndDropPosition.BetweenItems)
{
int insertIndex = args.insertAtIndex;
for (int i = transforms.Count - 1; i >= 0; i--)
{
var transform = transforms[i];
insertIndex = GetAdjustedInsertIndex (parent, transform, insertIndex);
transform.SetSiblingIndex (insertIndex);
}
}
break;
case DragAndDropPosition.OutsideItems:
foreach (var trans in transforms)
{
trans.SetParent (null); // make root when dragged to empty space in treeview
}
break;
default:
throw new ArgumentOutOfRangeException ();
}
Reload ();
SetSelection (transforms.Select (t => t.gameObject.GetInstanceID ()).ToList (), TreeViewSelectionOptions.RevealAndFrame);
}
return DragAndDropVisualMode.Move;
}
int GetAdjustedInsertIndex (Transform parent, Transform transformToInsert, int insertIndex)
{
if (transformToInsert.parent == parent && transformToInsert.GetSiblingIndex () < insertIndex)
return --insertIndex;
return insertIndex;
}
bool IsValidReparenting (Transform parent, List<Transform> transformsToMove)
{
if (parent == null)
return true;
foreach (var transformToMove in transformsToMove)
{
if (transformToMove == parent)
return false;
if (IsHoveredAChildOfDragged (parent, transformToMove))
return false;
}
return true;
}
bool IsHoveredAChildOfDragged (Transform hovered, Transform dragged)
{
Transform t = hovered.parent;
while (t)
{
if (t == dragged)
return true;
t = t.parent;
}
return false;
}
// Returns true if there is an ancestor of transform in the transforms list
static bool IsDescendantOf (Transform transform, List<Transform> transforms)
{
while (transform != null)
{
transform = transform.parent;
if (transforms.Contains (transform))
return true;
}
return false;
}
static void RemoveItemsThatAreDescendantsFromOtherItems (List<Transform> transforms)
{
transforms.RemoveAll (t => IsDescendantOf (t, transforms));
}
}
}