mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
Initial commit
This commit is contained in:
commit
3c7cc0c973
8391 changed files with 704313 additions and 0 deletions
|
@ -0,0 +1,2 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("Unity.2D.SpriteShape.Editor")]
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b8de732c98460ec4ca63677f37beb1e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
public class GenericScriptablePath<T> : ScriptablePath
|
||||
{
|
||||
[SerializeField]
|
||||
private List<T> m_Data = new List<T>();
|
||||
|
||||
public T[] data
|
||||
{
|
||||
get { return m_Data.ToArray(); }
|
||||
set
|
||||
{
|
||||
if (value.Length != pointCount)
|
||||
throw new Exception("Custom data count does not match control point count");
|
||||
|
||||
m_Data.Clear();
|
||||
m_Data.AddRange(value);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
|
||||
m_Data.Clear();
|
||||
}
|
||||
|
||||
public override void AddPoint(ControlPoint controlPoint)
|
||||
{
|
||||
base.AddPoint(controlPoint);
|
||||
|
||||
m_Data.Add(Create());
|
||||
}
|
||||
|
||||
public override void InsertPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
base.InsertPoint(index, controlPoint);
|
||||
|
||||
m_Data.Insert(index, Create());
|
||||
}
|
||||
|
||||
public override void RemovePoint(int index)
|
||||
{
|
||||
base.RemovePoint(index);
|
||||
|
||||
Destroy(m_Data[index]);
|
||||
|
||||
m_Data.RemoveAt(index);
|
||||
}
|
||||
|
||||
public T GetData(int index)
|
||||
{
|
||||
return m_Data[index];
|
||||
}
|
||||
|
||||
public void SetData(int index, T data)
|
||||
{
|
||||
m_Data[index] = data;
|
||||
}
|
||||
|
||||
protected virtual T Create()
|
||||
{
|
||||
return Activator.CreateInstance<T>();
|
||||
}
|
||||
|
||||
protected virtual void Destroy(T data) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 91ab30a448166b74e99d488f537bfc67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
public class GenericScriptablePathInspector<U,T> : ScriptablePathInspector where U : ScriptableData<T>
|
||||
{
|
||||
private List<U> m_DataObjects = new List<U>();
|
||||
private List<U> m_SelectedDataObjects = new List<U>();
|
||||
private Editor m_CachedEditor = null;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
PrepareDataObjects();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyDataObjects();
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
|
||||
DoCustomDataInspector();
|
||||
}
|
||||
|
||||
protected void DoCustomDataInspector()
|
||||
{
|
||||
PrepareDataObjects();
|
||||
|
||||
if (m_SelectedDataObjects.Count > 0)
|
||||
{
|
||||
CreateCachedEditor(m_SelectedDataObjects.ToArray(), null, ref m_CachedEditor);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
m_CachedEditor.OnInspectorGUI();
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
SetDataObjects();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareDataObjects()
|
||||
{
|
||||
var elementCount = 0;
|
||||
|
||||
m_SelectedDataObjects.Clear();
|
||||
|
||||
foreach(var path in paths)
|
||||
elementCount += path.pointCount;
|
||||
|
||||
while (m_DataObjects.Count < elementCount)
|
||||
CreateDataObject();
|
||||
|
||||
var index = 0;
|
||||
foreach(var path in paths)
|
||||
{
|
||||
var genericPath = path as GenericScriptablePath<T>;
|
||||
var customDataArray = genericPath.data;
|
||||
var length = customDataArray.Length;
|
||||
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
var dataObject = m_DataObjects[index + i];
|
||||
dataObject.data = customDataArray[i];
|
||||
|
||||
if (path.selection.Contains(i))
|
||||
{
|
||||
dataObject.owner = path.owner;
|
||||
dataObject.index = i;
|
||||
m_SelectedDataObjects.Add(dataObject);
|
||||
}
|
||||
}
|
||||
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDataObjects()
|
||||
{
|
||||
var index = 0;
|
||||
foreach(var path in paths)
|
||||
{
|
||||
var genericPath = path as GenericScriptablePath<T>;
|
||||
var customDataArray = genericPath.data;
|
||||
var length = customDataArray.Length;
|
||||
|
||||
for (var i = 0; i < length; ++i)
|
||||
customDataArray[i] = m_DataObjects[index + i].data;
|
||||
|
||||
genericPath.data = customDataArray;
|
||||
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
|
||||
private U CreateDataObject()
|
||||
{
|
||||
var dataObject = ScriptableObject.CreateInstance<U>();
|
||||
m_DataObjects.Add(dataObject);
|
||||
return dataObject;
|
||||
}
|
||||
|
||||
private void DestroyDataObjects()
|
||||
{
|
||||
foreach (var customDataObject in m_DataObjects)
|
||||
DestroyImmediate(customDataObject);
|
||||
|
||||
m_DataObjects.Clear();
|
||||
m_SelectedDataObjects.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d8d7f8780c69f9742b827ffd638225fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
public abstract class PathComponentEditor<T> : Editor where T : ScriptablePath
|
||||
{
|
||||
private static class Contents
|
||||
{
|
||||
public static readonly GUIContent snappingLabel = new GUIContent("Snapping", "Snap points using the snap settings");
|
||||
}
|
||||
|
||||
private Editor m_CachedEditor = null;
|
||||
|
||||
// Returns true on Changed.
|
||||
internal bool DoEditButtonChecked<U>(GUIContent icon, string label) where U : PathEditorTool<T>
|
||||
{
|
||||
const float kButtonWidth = 33;
|
||||
const float kButtonHeight = 23;
|
||||
const float k_SpaceBetweenLabelAndButton = 5;
|
||||
var buttonStyle = new GUIStyle("EditModeSingleButton");
|
||||
|
||||
var rect = EditorGUILayout.GetControlRect(true, kButtonHeight, buttonStyle);
|
||||
var buttonRect = new Rect(rect.xMin + EditorGUIUtility.labelWidth, rect.yMin, kButtonWidth, kButtonHeight);
|
||||
|
||||
var labelContent = new GUIContent(label);
|
||||
var labelSize = GUI.skin.label.CalcSize(labelContent);
|
||||
|
||||
var labelRect = new Rect(
|
||||
buttonRect.xMax + k_SpaceBetweenLabelAndButton,
|
||||
rect.yMin + (rect.height - labelSize.y) * .5f,
|
||||
labelSize.x,
|
||||
rect.height);
|
||||
|
||||
bool hasChanged = false;
|
||||
using (new EditorGUI.DisabledGroupScope(!EditorToolManager.IsAvailable<U>()))
|
||||
{
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
var isActive = GUI.Toggle(buttonRect, EditorToolManager.IsActiveTool<U>(), icon, buttonStyle);
|
||||
|
||||
GUI.Label(labelRect, label);
|
||||
|
||||
if (check.changed)
|
||||
{
|
||||
if (isActive)
|
||||
EditorTools.EditorTools.SetActiveTool<U>();
|
||||
else
|
||||
EditorTools.EditorTools.RestorePreviousTool();
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
protected void DoEditButton<U>(GUIContent icon, string label) where U : PathEditorTool<T>
|
||||
{
|
||||
DoEditButtonChecked<U>(icon, label);
|
||||
}
|
||||
|
||||
protected void DoPathInspector<U>() where U : PathEditorTool<T>
|
||||
{
|
||||
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
|
||||
{
|
||||
var paths = EditorToolManager.GetEditorTool<U>().paths;
|
||||
|
||||
CreateCachedEditor(paths, null, ref m_CachedEditor);
|
||||
|
||||
if (m_CachedEditor == null) //Needed to avoid a nullref on exiting playmode
|
||||
return;
|
||||
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
m_CachedEditor.OnInspectorGUI();
|
||||
|
||||
if (check.changed)
|
||||
EditorToolManager.GetEditorTool<U>().SetShapes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void DoSnappingInspector<U>() where U : PathEditorTool<T>
|
||||
{
|
||||
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
|
||||
{
|
||||
var tool = EditorToolManager.GetEditorTool<U>();
|
||||
tool.enableSnapping = EditorGUILayout.Toggle(Contents.snappingLabel, tool.enableSnapping);
|
||||
}
|
||||
}
|
||||
|
||||
protected void DoOpenEndedInspector<U>(SerializedProperty isOpenEndedProperty) where U : PathEditorTool<T>
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
EditorGUILayout.PropertyField(isOpenEndedProperty);
|
||||
|
||||
if (check.changed)
|
||||
{
|
||||
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
|
||||
{
|
||||
var paths = EditorToolManager.GetEditorTool<U>().paths;
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
path.undoObject.RegisterUndo("Set Open Ended");
|
||||
path.isOpenEnded = isOpenEndedProperty.boolValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 79c3c0b6ebc836f438b76a34f04dd076
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,494 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor.U2D.Path.GUIFramework;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
public static class PathEditorToolContents
|
||||
{
|
||||
internal static readonly GUIContent shapeToolIcon = IconContent("ShapeTool", "Start editing the Shape in the Scene View.");
|
||||
internal static readonly GUIContent shapeToolPro = IconContent("ShapeToolPro", "Start editing the Shape in the Scene View.");
|
||||
|
||||
internal static GUIContent IconContent(string name, string tooltip = null)
|
||||
{
|
||||
return new GUIContent(AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.unity.2d.path/Editor/Handles/" + name + ".png"), tooltip);
|
||||
}
|
||||
|
||||
public static GUIContent icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
return shapeToolPro;
|
||||
|
||||
return shapeToolIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IDuringSceneGuiTool
|
||||
{
|
||||
void DuringSceneGui(SceneView sceneView);
|
||||
bool IsAvailable();
|
||||
}
|
||||
|
||||
[InitializeOnLoad]
|
||||
internal class EditorToolManager
|
||||
{
|
||||
private static List<IDuringSceneGuiTool> m_Tools = new List<IDuringSceneGuiTool>();
|
||||
|
||||
static EditorToolManager()
|
||||
{
|
||||
SceneView.duringSceneGui += DuringSceneGui;
|
||||
}
|
||||
|
||||
internal static void Add(IDuringSceneGuiTool tool)
|
||||
{
|
||||
if (!m_Tools.Contains(tool) && tool is EditorTool)
|
||||
m_Tools.Add(tool);
|
||||
}
|
||||
|
||||
internal static void Remove(IDuringSceneGuiTool tool)
|
||||
{
|
||||
if (m_Tools.Contains(tool))
|
||||
m_Tools.Remove(tool);
|
||||
}
|
||||
|
||||
internal static bool IsActiveTool<T>() where T : EditorTool
|
||||
{
|
||||
return EditorTools.EditorTools.activeToolType.Equals(typeof(T));
|
||||
}
|
||||
|
||||
internal static bool IsAvailable<T>() where T : EditorTool
|
||||
{
|
||||
var tool = GetEditorTool<T>();
|
||||
|
||||
if (tool != null)
|
||||
return tool.IsAvailable();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static T GetEditorTool<T>() where T : EditorTool
|
||||
{
|
||||
foreach(var tool in m_Tools)
|
||||
{
|
||||
if (tool.GetType().Equals(typeof(T)))
|
||||
return tool as T;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void DuringSceneGui(SceneView sceneView)
|
||||
{
|
||||
foreach (var tool in m_Tools)
|
||||
{
|
||||
if (tool.IsAvailable() && EditorTools.EditorTools.IsActiveTool(tool as EditorTool))
|
||||
tool.DuringSceneGui(sceneView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class PathEditorTool<T> : EditorTool, IDuringSceneGuiTool where T : ScriptablePath
|
||||
{
|
||||
private Dictionary<UnityObject, T> m_Paths = new Dictionary<UnityObject, T>();
|
||||
private IGUIState m_GUIState = new GUIState();
|
||||
private Dictionary<UnityObject, PathEditor> m_PathEditors = new Dictionary<UnityObject, PathEditor>();
|
||||
private Dictionary<UnityObject, SerializedObject> m_SerializedObjects = new Dictionary<UnityObject, SerializedObject>();
|
||||
private MultipleEditablePathController m_Controller = new MultipleEditablePathController();
|
||||
private PointRectSelector m_RectSelector = new PointRectSelector();
|
||||
private bool m_IsActive = false;
|
||||
|
||||
internal T[] paths
|
||||
{
|
||||
get { return m_Paths.Values.ToArray(); }
|
||||
}
|
||||
|
||||
public bool enableSnapping
|
||||
{
|
||||
get { return m_Controller.enableSnapping; }
|
||||
set { m_Controller.enableSnapping = value; }
|
||||
}
|
||||
|
||||
public override GUIContent toolbarIcon
|
||||
{
|
||||
get { return PathEditorToolContents.icon; }
|
||||
}
|
||||
|
||||
public override bool IsAvailable()
|
||||
{
|
||||
return targets.Count() > 0;
|
||||
}
|
||||
|
||||
public T GetPath(UnityObject targetObject)
|
||||
{
|
||||
var path = default(T);
|
||||
m_Paths.TryGetValue(targetObject, out path);
|
||||
return path;
|
||||
}
|
||||
|
||||
public void SetPath(UnityObject target)
|
||||
{
|
||||
var path = GetPath(target);
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
var undoName = Undo.GetCurrentGroupName();
|
||||
var serializedObject = GetSerializedObject(target);
|
||||
|
||||
serializedObject.UpdateIfRequiredOrScript();
|
||||
|
||||
SetShape(path, serializedObject);
|
||||
|
||||
Undo.SetCurrentGroupName(undoName);
|
||||
}
|
||||
|
||||
private void RepaintInspectors()
|
||||
{
|
||||
var editorWindows = Resources.FindObjectsOfTypeAll<EditorWindow>();
|
||||
|
||||
foreach (var editorWindow in editorWindows)
|
||||
{
|
||||
if (editorWindow.titleContent.text == "Inspector")
|
||||
editorWindow.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_IsActive = false;
|
||||
EditorToolManager.Add(this);
|
||||
|
||||
SetupRectSelector();
|
||||
HandleActivation();
|
||||
|
||||
EditorTools.EditorTools.activeToolChanged += HandleActivation;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
EditorToolManager.Remove(this);
|
||||
|
||||
EditorTools.EditorTools.activeToolChanged -= HandleActivation;
|
||||
UnregisterCallbacks();
|
||||
}
|
||||
|
||||
private void HandleActivation()
|
||||
{
|
||||
if (m_IsActive == false && EditorTools.EditorTools.IsActiveTool(this))
|
||||
Activate();
|
||||
else if (m_IsActive)
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
private void Activate()
|
||||
{
|
||||
m_IsActive = true;
|
||||
RegisterCallbacks();
|
||||
InitializeCache();
|
||||
OnActivate();
|
||||
}
|
||||
|
||||
private void Deactivate()
|
||||
{
|
||||
OnDeactivate();
|
||||
DestroyCache();
|
||||
UnregisterCallbacks();
|
||||
m_IsActive = false;
|
||||
}
|
||||
|
||||
private void RegisterCallbacks()
|
||||
{
|
||||
UnregisterCallbacks();
|
||||
Selection.selectionChanged += SelectionChanged;
|
||||
EditorApplication.playModeStateChanged += PlayModeStateChanged;
|
||||
Undo.undoRedoPerformed += UndoRedoPerformed;
|
||||
}
|
||||
|
||||
private void UnregisterCallbacks()
|
||||
{
|
||||
Selection.selectionChanged -= SelectionChanged;
|
||||
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
|
||||
Undo.undoRedoPerformed -= UndoRedoPerformed;
|
||||
}
|
||||
|
||||
private void DestroyCache()
|
||||
{
|
||||
foreach (var pair in m_Paths)
|
||||
{
|
||||
var path = pair.Value;
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
Undo.ClearUndo(path);
|
||||
UnityObject.DestroyImmediate(path);
|
||||
}
|
||||
}
|
||||
m_Paths.Clear();
|
||||
m_Controller.ClearPaths();
|
||||
m_PathEditors.Clear();
|
||||
m_SerializedObjects.Clear();
|
||||
}
|
||||
|
||||
private void UndoRedoPerformed()
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
|
||||
if (!path.modified)
|
||||
InitializePath(target);
|
||||
});
|
||||
}
|
||||
|
||||
private void SelectionChanged()
|
||||
{
|
||||
InitializeCache();
|
||||
}
|
||||
|
||||
private void PlayModeStateChanged(PlayModeStateChange stateChange)
|
||||
{
|
||||
if (stateChange == PlayModeStateChange.EnteredEditMode)
|
||||
EditorApplication.delayCall += () => { InitializeCache(); }; //HACK: At this point target is null. Let's wait to next frame to refresh.
|
||||
}
|
||||
|
||||
private void SetupRectSelector()
|
||||
{
|
||||
m_RectSelector.onSelectionBegin = BeginSelection;
|
||||
m_RectSelector.onSelectionChanged = UpdateSelection;
|
||||
m_RectSelector.onSelectionEnd = EndSelection;
|
||||
}
|
||||
|
||||
private void ForEachTarget(Action<UnityObject> action)
|
||||
{
|
||||
foreach(var target in targets)
|
||||
{
|
||||
if (target == null)
|
||||
continue;
|
||||
|
||||
action(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeCache()
|
||||
{
|
||||
m_Controller.ClearPaths();
|
||||
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetOrCreatePath(target);
|
||||
var pointCount = path.pointCount;
|
||||
|
||||
InitializePath(target);
|
||||
|
||||
if (pointCount != path.pointCount)
|
||||
path.selection.Clear();
|
||||
|
||||
CreatePathEditor(target);
|
||||
|
||||
m_Controller.AddPath(path);
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializePath(UnityObject target)
|
||||
{
|
||||
IShape shape = null;
|
||||
ControlPoint[] controlPoints = null;
|
||||
|
||||
try
|
||||
{
|
||||
shape = GetShape(target);
|
||||
controlPoints = shape.ToControlPoints();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.Message);
|
||||
}
|
||||
|
||||
var path = GetPath(target);
|
||||
path.Clear();
|
||||
|
||||
if (shape != null && controlPoints != null)
|
||||
{
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
path.shapeType = shape.type;
|
||||
path.isOpenEnded = shape.isOpenEnded;
|
||||
|
||||
foreach (var controlPoint in controlPoints)
|
||||
path.AddPoint(controlPoint);
|
||||
}
|
||||
|
||||
Initialize(path, GetSerializedObject(target));
|
||||
}
|
||||
|
||||
private T GetOrCreatePath(UnityObject targetObject)
|
||||
{
|
||||
var path = GetPath(targetObject);
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
path = ScriptableObject.CreateInstance<T>();
|
||||
path.owner = targetObject;
|
||||
m_Paths[targetObject] = path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private PathEditor GetPathEditor(UnityObject target)
|
||||
{
|
||||
PathEditor pathEditor;
|
||||
m_PathEditors.TryGetValue(target, out pathEditor);
|
||||
return pathEditor;
|
||||
}
|
||||
|
||||
private void CreatePathEditor(UnityObject target)
|
||||
{
|
||||
var pathEditor = new PathEditor();
|
||||
pathEditor.controller = m_Controller;
|
||||
pathEditor.drawerOverride = GetCustomDrawer(target);
|
||||
m_PathEditors[target] = pathEditor;
|
||||
}
|
||||
|
||||
private SerializedObject GetSerializedObject(UnityObject target)
|
||||
{
|
||||
var serializedObject = default(SerializedObject);
|
||||
|
||||
if (!m_SerializedObjects.TryGetValue(target, out serializedObject))
|
||||
{
|
||||
serializedObject = new SerializedObject(target);
|
||||
m_SerializedObjects[target] = serializedObject;
|
||||
}
|
||||
|
||||
return serializedObject;
|
||||
}
|
||||
|
||||
void IDuringSceneGuiTool.DuringSceneGui(SceneView sceneView)
|
||||
{
|
||||
if (m_GUIState.eventType == EventType.Layout)
|
||||
m_Controller.ClearClosestPath();
|
||||
|
||||
m_RectSelector.OnGUI();
|
||||
|
||||
bool changed = false;
|
||||
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
path.localToWorldMatrix = GetLocalToWorldMatrix(target);
|
||||
path.forward = GetForward(target);
|
||||
path.up = GetUp(target);
|
||||
path.right = GetRight(target);
|
||||
m_Controller.editablePath = path;
|
||||
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
var pathEditor = GetPathEditor(target);
|
||||
pathEditor.linearTangentIsZero = GetLinearTangentIsZero(target);
|
||||
pathEditor.OnGUI();
|
||||
OnCustomGUI(path);
|
||||
changed |= check.changed;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (changed)
|
||||
{
|
||||
SetShapes();
|
||||
RepaintInspectors();
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginSelection(ISelector<Vector3> selector, bool isAdditive)
|
||||
{
|
||||
m_Controller.RegisterUndo("Selection");
|
||||
|
||||
if (isAdditive)
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
path.selection.BeginSelection();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateSelection(selector);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSelection(ISelector<Vector3> selector)
|
||||
{
|
||||
var repaintInspectors = false;
|
||||
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
|
||||
repaintInspectors |= path.Select(selector);
|
||||
});
|
||||
|
||||
if (repaintInspectors)
|
||||
RepaintInspectors();
|
||||
}
|
||||
|
||||
private void EndSelection(ISelector<Vector3> selector)
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
path.selection.EndSelection(true);
|
||||
});
|
||||
}
|
||||
|
||||
internal void SetShapes()
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
SetPath(target);
|
||||
});
|
||||
}
|
||||
|
||||
private Transform GetTransform(UnityObject target)
|
||||
{
|
||||
return (target as Component).transform;
|
||||
}
|
||||
|
||||
private Matrix4x4 GetLocalToWorldMatrix(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).localToWorldMatrix;
|
||||
}
|
||||
|
||||
private Vector3 GetForward(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).forward;
|
||||
}
|
||||
|
||||
private Vector3 GetUp(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).up;
|
||||
}
|
||||
|
||||
private Vector3 GetRight(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).right;
|
||||
}
|
||||
|
||||
protected abstract IShape GetShape(UnityObject target);
|
||||
protected virtual void Initialize(T path, SerializedObject serializedObject) { }
|
||||
protected abstract void SetShape(T path, SerializedObject serializedObject);
|
||||
protected virtual void OnActivate() { }
|
||||
protected virtual void OnDeactivate() { }
|
||||
protected virtual void OnCustomGUI(T path) { }
|
||||
protected virtual bool GetLinearTangentIsZero(UnityObject target) { return false; }
|
||||
protected virtual IDrawer GetCustomDrawer(UnityObject target) { return null; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 320732be5a65ff84ea451c5bf9700876
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor.U2D.Path.GUIFramework;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
public static class PathEditorToolExtensions
|
||||
{
|
||||
public static void CycleTangentMode<T>(this PathEditorTool<T> pathEditorTool) where T : ScriptablePath
|
||||
{
|
||||
var first = true;
|
||||
var mixed = false;
|
||||
var tangentMode = TangentMode.Linear;
|
||||
var targets = pathEditorTool.targets;
|
||||
|
||||
foreach(var target in targets)
|
||||
{
|
||||
var path = pathEditorTool.GetPath(target);
|
||||
|
||||
if (path.selection.Count == 0)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
if (!path.selection.Contains(i))
|
||||
continue;
|
||||
|
||||
var point = path.GetPoint(i);
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
tangentMode = point.tangentMode;
|
||||
}
|
||||
else if (point.tangentMode != tangentMode)
|
||||
{
|
||||
mixed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mixed)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mixed)
|
||||
tangentMode = TangentMode.Linear;
|
||||
else
|
||||
tangentMode = GetNextTangentMode(tangentMode);
|
||||
|
||||
foreach(var target in targets)
|
||||
{
|
||||
var path = pathEditorTool.GetPath(target);
|
||||
|
||||
if (path.selection.Count == 0)
|
||||
continue;
|
||||
|
||||
path.undoObject.RegisterUndo("Cycle Tangent Mode");
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
if (!path.selection.Contains(i))
|
||||
continue;
|
||||
|
||||
path.SetTangentMode(i, tangentMode);
|
||||
}
|
||||
|
||||
pathEditorTool.SetPath(target);
|
||||
}
|
||||
}
|
||||
|
||||
public static void MirrorTangent<T>(this PathEditorTool<T> pathEditorTool) where T : ScriptablePath
|
||||
{
|
||||
var targets = pathEditorTool.targets;
|
||||
|
||||
foreach(var target in targets)
|
||||
{
|
||||
var path = pathEditorTool.GetPath(target);
|
||||
|
||||
if (path.selection.Count == 0)
|
||||
continue;
|
||||
|
||||
path.undoObject.RegisterUndo("Mirror Tangents");
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
if (!path.selection.Contains(i))
|
||||
continue;
|
||||
|
||||
path.MirrorTangent(i);
|
||||
}
|
||||
|
||||
pathEditorTool.SetPath(target);
|
||||
}
|
||||
}
|
||||
|
||||
private static TangentMode GetNextTangentMode(TangentMode tangentMode)
|
||||
{
|
||||
return (TangentMode)((((int)tangentMode) + 1) % Enum.GetValues(typeof(TangentMode)).Length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 94a1e3e609709aa4f9106a9aeea179f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
public class ScriptableData<T> : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
private T m_Data;
|
||||
public UnityObject owner { get; set; }
|
||||
public int index { get; set; }
|
||||
|
||||
public T data
|
||||
{
|
||||
get { return m_Data; }
|
||||
set { m_Data = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae511f1b2818bc54f87b84a327b16f71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
public class ScriptablePath : ScriptableObject, IEditablePath, IUndoObject
|
||||
{
|
||||
[SerializeField]
|
||||
private EditablePath m_EditablePath = new EditablePath();
|
||||
[SerializeField]
|
||||
private bool m_Modified = false;
|
||||
|
||||
internal bool modified
|
||||
{
|
||||
get { return m_Modified; }
|
||||
}
|
||||
|
||||
internal UnityObject owner { get; set; }
|
||||
|
||||
public ShapeType shapeType
|
||||
{
|
||||
get { return m_EditablePath.shapeType; }
|
||||
set { m_EditablePath.shapeType = value; }
|
||||
}
|
||||
|
||||
public IUndoObject undoObject
|
||||
{
|
||||
get { return this; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public ISelection<int> selection
|
||||
{
|
||||
get { return m_EditablePath.selection; }
|
||||
}
|
||||
|
||||
public Matrix4x4 localToWorldMatrix
|
||||
{
|
||||
get { return m_EditablePath.localToWorldMatrix; }
|
||||
set { m_EditablePath.localToWorldMatrix = value; }
|
||||
}
|
||||
|
||||
public Vector3 forward
|
||||
{
|
||||
get { return m_EditablePath.forward; }
|
||||
set { m_EditablePath.forward = value; }
|
||||
}
|
||||
|
||||
public Vector3 up
|
||||
{
|
||||
get { return m_EditablePath.up; }
|
||||
set { m_EditablePath.up = value; }
|
||||
}
|
||||
|
||||
public Vector3 right
|
||||
{
|
||||
get { return m_EditablePath.right; }
|
||||
set { m_EditablePath.right = value; }
|
||||
}
|
||||
|
||||
public bool isOpenEnded
|
||||
{
|
||||
get { return m_EditablePath.isOpenEnded; }
|
||||
set { m_EditablePath.isOpenEnded = value; }
|
||||
}
|
||||
|
||||
public int pointCount
|
||||
{
|
||||
get { return m_EditablePath.pointCount; }
|
||||
}
|
||||
|
||||
public bool Select(ISelector<Vector3> selector)
|
||||
{
|
||||
return m_EditablePath.Select(selector);
|
||||
}
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
m_EditablePath.Clear();
|
||||
}
|
||||
|
||||
public virtual ControlPoint GetPoint(int index)
|
||||
{
|
||||
return m_EditablePath.GetPoint(index);
|
||||
}
|
||||
|
||||
public virtual void SetPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
m_EditablePath.SetPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
public virtual void AddPoint(ControlPoint controlPoint)
|
||||
{
|
||||
m_EditablePath.AddPoint(controlPoint);
|
||||
}
|
||||
|
||||
public virtual void InsertPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
m_EditablePath.InsertPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
public virtual void RemovePoint(int index)
|
||||
{
|
||||
m_EditablePath.RemovePoint(index);
|
||||
}
|
||||
|
||||
void IUndoObject.RegisterUndo(string name)
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(this, name);
|
||||
m_Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c208d96210d896247a45095d5fa9bf09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,233 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.U2D.Path
|
||||
{
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(ScriptablePath), true)]
|
||||
public class ScriptablePathInspector : Editor
|
||||
{
|
||||
private static class Contents
|
||||
{
|
||||
public static readonly GUIContent linearIcon = IconContent("TangentLinear", "TangentLinearPro", "Linear");
|
||||
public static readonly GUIContent continuousIcon = IconContent("TangentContinuous", "TangentContinuousPro", "Continuous");
|
||||
public static readonly GUIContent brokenIcon = IconContent("TangentBroken", "TangentBrokenPro", "Broken");
|
||||
public static readonly GUIContent positionLabel = new GUIContent("Position", "Position of the Control Point");
|
||||
public static readonly GUIContent enableSnapLabel = new GUIContent("Snapping", "Snap points using the snap settings");
|
||||
public static readonly GUIContent tangentModeLabel = new GUIContent("Tangent Mode");
|
||||
public static readonly GUIContent pointLabel = new GUIContent("Point");
|
||||
|
||||
|
||||
private static GUIContent IconContent(string name, string tooltip = null)
|
||||
{
|
||||
return new GUIContent(AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.unity.2d.path/Editor/Handles/" + name + ".png"), tooltip);
|
||||
}
|
||||
|
||||
private static GUIContent IconContent(string personal, string pro, string tooltip)
|
||||
{
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
return IconContent(pro, tooltip);
|
||||
|
||||
return IconContent(personal, tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ScriptablePath> m_Paths = null;
|
||||
private bool m_Dragged = false;
|
||||
|
||||
protected List<ScriptablePath> paths
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Paths == null)
|
||||
m_Paths = targets.Select( t => t as ScriptablePath).ToList();
|
||||
|
||||
return m_Paths;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DoTangentModeInspector();
|
||||
DoPositionInspector();
|
||||
}
|
||||
|
||||
protected void DoTangentModeInspector()
|
||||
{
|
||||
if (!IsAnyShapeType(ShapeType.Spline))
|
||||
return;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel(Contents.tangentModeLabel);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(!IsAnyPointSelected()))
|
||||
{
|
||||
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Linear), Contents.linearIcon))
|
||||
SetMixedTangentMode(TangentMode.Linear);
|
||||
|
||||
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Continuous), Contents.continuousIcon))
|
||||
SetMixedTangentMode(TangentMode.Continuous);
|
||||
|
||||
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Broken), Contents.brokenIcon))
|
||||
SetMixedTangentMode(TangentMode.Broken);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
protected void DoPositionInspector()
|
||||
{
|
||||
var showMixedValue = EditorGUI.showMixedValue;
|
||||
var wideMode = EditorGUIUtility.wideMode;
|
||||
|
||||
var position = Vector3.zero;
|
||||
var isMixed = GetMixedPosition(out position);
|
||||
|
||||
EditorGUI.showMixedValue = isMixed;
|
||||
EditorGUIUtility.wideMode = true;
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(!IsAnyPointSelected()))
|
||||
{
|
||||
if (GUIUtility.hotControl == 0)
|
||||
m_Dragged = false;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var delta = EditorGUILayout.Vector2Field(Contents.positionLabel, position) - (Vector2)position;
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (m_Dragged == false)
|
||||
{
|
||||
foreach(var path in paths)
|
||||
path.undoObject.RegisterUndo("Point Position");
|
||||
|
||||
m_Dragged = true;
|
||||
}
|
||||
|
||||
SetMixedDeltaPosition(delta);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = showMixedValue;
|
||||
EditorGUIUtility.wideMode = wideMode;
|
||||
}
|
||||
|
||||
private bool DoToggle(bool value, GUIContent icon)
|
||||
{
|
||||
const float kButtonWidth = 33f;
|
||||
const float kButtonHeight = 23f;
|
||||
var buttonStyle = new GUIStyle("EditModeSingleButton");
|
||||
|
||||
var changed = false;
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
value = GUILayout.Toggle(value, icon, buttonStyle, GUILayout.Width(kButtonWidth), GUILayout.Height(kButtonHeight));
|
||||
changed = check.changed;
|
||||
}
|
||||
|
||||
return value && changed;
|
||||
}
|
||||
|
||||
private bool GetToggleStateFromTangentMode(TangentMode mode)
|
||||
{
|
||||
foreach(var path in paths)
|
||||
{
|
||||
var selection = path.selection;
|
||||
|
||||
foreach (var index in selection.elements)
|
||||
if (path.GetPoint(index).tangentMode != mode)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetMixedTangentMode(TangentMode tangentMode)
|
||||
{
|
||||
foreach(var path in paths)
|
||||
{
|
||||
path.undoObject.RegisterUndo("Tangent Mode");
|
||||
|
||||
foreach (var index in path.selection.elements)
|
||||
path.SetTangentMode(index, tangentMode);
|
||||
}
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
private bool GetMixedPosition(out Vector3 position)
|
||||
{
|
||||
var first = true;
|
||||
position = Vector3.zero;
|
||||
|
||||
foreach(var path in paths)
|
||||
{
|
||||
var selection = path.selection;
|
||||
var matrix = path.localToWorldMatrix;
|
||||
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
foreach (var index in selection.elements)
|
||||
{
|
||||
var controlPoint = path.GetPoint(index);
|
||||
|
||||
if (first)
|
||||
{
|
||||
position = controlPoint.position;
|
||||
first = false;
|
||||
}
|
||||
else if (position != controlPoint.position)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
path.localToWorldMatrix = matrix;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetMixedDeltaPosition(Vector3 delta)
|
||||
{
|
||||
foreach(var path in paths)
|
||||
{
|
||||
var selection = path.selection;
|
||||
var matrix = path.localToWorldMatrix;
|
||||
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
foreach (var index in selection.elements)
|
||||
{
|
||||
var controlPoint = path.GetPoint(index);
|
||||
controlPoint.position += delta;
|
||||
path.SetPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
path.localToWorldMatrix = matrix;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAnyShapeType(ShapeType shapeType)
|
||||
{
|
||||
foreach(var path in paths)
|
||||
if (path.shapeType == shapeType)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool IsAnyPointSelected()
|
||||
{
|
||||
foreach(var path in paths)
|
||||
if (path.selection.Count > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6c5abda88d831f347a177bb63ff287b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue