大爷 a4d6c91d5f 修复Demo中的一些问题 %!s(int64=2) %!d(string=hai) anos
..
Animancer Tools 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Attributes 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
GUI 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Serialization 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Transition Preview Window 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Animancer Icon.png 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Animancer Icon.png.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Animancer Settings.asset a4d6c91d5f 修复Demo中的一些问题 %!s(int64=2) %!d(string=hai) anos
Animancer Settings.asset.meta a4d6c91d5f 修复Demo中的一些问题 %!s(int64=2) %!d(string=hai) anos
Animancer Tools.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimancerEditorUtilities.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimancerEditorUtilities.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimancerSettings.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimancerSettings.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimationBindings.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimationBindings.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimationGatherer.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AnimationGatherer.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AssemblyInfo.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
AssemblyInfo.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Attributes.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
CompactUnitConversionCache.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
CompactUnitConversionCache.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
ConversionCache.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
ConversionCache.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
GUI.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
OptionalWarning.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
OptionalWarning.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
ReadMe.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
ReadMe.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
ScriptingDefineSymbols.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
ScriptingDefineSymbols.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Serialization.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Strings.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Strings.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Transition Preview Window.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Validate.Value.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Validate.Value.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Validate.cs 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos
Validate.cs.meta 633b3da1a0 添加plugins插件 %!s(int64=2) %!d(string=hai) anos

ReadMe.cs

// Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //

#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.

#if UNITY_EDITOR

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace Animancer.Editor
{
/// [Editor-Only] A welcome screen for .
// [CreateAssetMenu(menuName = Strings.MenuPrefix + "Read Me", order = Strings.AssetMenuOrder)]
[HelpURL(Strings.DocsURLs.APIDocumentation + "." + nameof(Animancer.Editor) + "/" + nameof(ReadMe))]
public class ReadMe : ScriptableObject
{
/************************************************************************************************************************/
#region Fields and Properties
/************************************************************************************************************************/

/// The release ID of the current version.
///
/// [ 1] = v1.0: 2018-05-02.
/// [ 2] = v1.1: 2018-05-29.
/// [ 3] = v1.2: 2018-08-14.
/// [ 4] = v1.3: 2018-09-12.
/// [ 5] = v2.0: 2018-10-08.
/// [ 6] = v3.0: 2019-05-27.
/// [ 7] = v3.1: 2019-08-12.
/// [ 8] = v4.0: 2020-01-28.
/// [ 9] = v4.1: 2020-02-21.
/// [10] = v4.2: 2020-03-02.
/// [11] = v4.3: 2020-03-13.
/// [12] = v4.4: 2020-03-27.
/// [13] = v5.0: 2020-07-17.
/// [14] = v5.1: 2020-07-27.
/// [15] = v5.2: 2020-09-16.
/// [16] = v5.3: 2020-10-06.
/// [17] = v6.0: 2020-12-04.
/// [18] = v6.1: 2021-04-13.
/// [19] = v7.0: 2021-07-29.
/// [20] = v7.1: 2021-08-13.
/// [21] = v7.2: 2021-10-17.
/// [22] = v7.3: 2022-07-03.
///
protected virtual int ReleaseNumber => 22;

/// The display name of this product version.
protected virtual string VersionName => "v7.3";

/// The URL for the change log of this Animancer version.
protected virtual string ChangeLogURL => Strings.DocsURLs.ChangeLogPrefix + "v7-3";

/// The key used to save the release number.
protected virtual string ReleaseNumberPrefKey => nameof(Animancer) + "." + nameof(ReleaseNumber);

/// The name of this product.
protected virtual string ProductName => Strings.ProductName + " Pro";

/// The URL for the documentation.
protected virtual string DocumentationURL => Strings.DocsURLs.Documentation;

/// The URL for the example documentation.
protected virtual string ExampleURL => Strings.DocsURLs.Examples;

/// The URL for the Unity Forum thread.
protected virtual string ForumURL => Strings.DocsURLs.Forum;

/// The URL for the Github Issues page.
protected virtual string IssuesURL => Strings.DocsURLs.Issues;

/// The developer email address.
protected virtual string DeveloperEmail => Strings.DocsURLs.DeveloperEmail;

/************************************************************************************************************************/

///
/// The file name ends with the to detect if the user imported
/// this version without deleting a previous version.
///
/// When Unity's package importer sees an existing file with the same GUID as one in the package, it will
/// overwrite that file but not move or rename it if the name has changed. So it will leave the file there with
/// the old version name.
///

private bool HasCorrectName => name.EndsWith(VersionName);

/************************************************************************************************************************/

[SerializeField]
private DefaultAsset _ExamplesFolder;

/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Show On Startup
/************************************************************************************************************************/

[SerializeField]
private bool _DontShowOnStartup;

/// Should the system be prevented from automatically selecting this asset on startup?
public bool DontShowOnStartup => _DontShowOnStartup && HasCorrectName;

/************************************************************************************************************************/

/// Automatically selects a on startup.
[InitializeOnLoadMethod]
private static void ShowReadMe()
{
EditorApplication.delayCall += () =>
{
var asset = FindReadMe();
if (asset != null)// Delay the call again to ensure that the Project window actually shows the selection.
EditorApplication.delayCall += () =>
Selection.activeObject = asset;
};
}

/************************************************************************************************************************/

///
/// Finds the most recently modified asset with disabled.
///

private static ReadMe FindReadMe()
{
DateTime latestWriteTime = default;
ReadMe latestReadMe = null;
string latestGUID = null;

var guids = AssetDatabase.FindAssets($"t:{nameof(ReadMe)}");
for (int i = 0; i < guids.Length; i++)
{
var guid = guids[i];
if (SessionState.GetBool(guid, false))
continue;

var assetPath = AssetDatabase.GUIDToAssetPath(guid);
var asset = AssetDatabase.LoadAssetAtPath(assetPath);
if (asset != null && !asset.DontShowOnStartup)
{
var writeTime = File.GetLastWriteTimeUtc(assetPath);
if (latestWriteTime < writeTime)
{
latestWriteTime = writeTime;
latestReadMe = asset;
latestGUID = guid;
}
}
}

if (latestGUID != null)
SessionState.SetBool(latestGUID, true);

return latestReadMe;
}

/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Custom Editor
/************************************************************************************************************************/

/// [Editor-Only] A custom Inspector for .
[CustomEditor(typeof(ReadMe), editorForChildClasses: true)]
public class Editor : UnityEditor.Editor
{
/************************************************************************************************************************/

[NonSerialized] private ReadMe _Target;
[NonSerialized] private Texture2D _Icon;
[NonSerialized] private int _PreviousVersion;
[NonSerialized] private string _ExamplesDirectory;
[NonSerialized] private List _Examples;
[NonSerialized] private string _Title;
[NonSerialized] private string _EmailLink;
[NonSerialized] private SerializedProperty _DontShowOnStartupProperty;

/************************************************************************************************************************/

/// Don't use any margins.
public override bool UseDefaultMargins() => false;

/************************************************************************************************************************/

protected virtual void OnEnable()
{
_Target = (ReadMe)target;
_Icon = AssetPreview.GetMiniThumbnail(target);

var key = _Target.ReleaseNumberPrefKey;
_PreviousVersion = PlayerPrefs.GetInt(key, -1);
if (_PreviousVersion < 0)
_PreviousVersion = EditorPrefs.GetInt(key, -1);// Animancer v2.0 used EditorPrefs.

_Examples = ExampleGroup.Gather(_Target._ExamplesFolder, out _ExamplesDirectory);

_Title = $"{_Target.ProductName}\n{_Target.VersionName}";
_EmailLink = $"mailto:{_Target.DeveloperEmail}?subject={_Target.ProductName.Replace(" ", "%20")}";
_DontShowOnStartupProperty = serializedObject.FindProperty(nameof(_DontShowOnStartup));
}

/************************************************************************************************************************/

protected override void OnHeaderGUI()
{
GUILayout.BeginHorizontal(Styles.TitleArea);
{
using (ObjectPool.Disposable.AcquireContent(out var label, _Title, null, false))
{
var iconWidth = Styles.Title.CalcHeight(label, EditorGUIUtility.currentViewWidth);
GUILayout.Label(_Icon, GUILayout.Width(iconWidth), GUILayout.Height(iconWidth));
GUILayout.Label(label, Styles.Title);
}
}
GUILayout.EndHorizontal();
}

/************************************************************************************************************************/

public override void OnInspectorGUI()
{
serializedObject.Update();

DoSpace();

DoWarnings();

DoShowOnStartup();

DoSpace();

DoIntroductionBlock();

DoSpace();

DoExampleBlock();

DoSpace();

DoSupportBlock();

DoSpace();

DoShowOnStartup();

serializedObject.ApplyModifiedProperties();
}

/************************************************************************************************************************/

protected static void DoSpace() => GUILayout.Space(AnimancerGUI.LineHeight * 0.2f);

/************************************************************************************************************************/

private void DoShowOnStartup()
{
var area = AnimancerGUI.LayoutSingleLineRect();
area.xMin += AnimancerGUI.LineHeight * 0.2f;

using (ObjectPool.Disposable.AcquireContent(out var content, _DontShowOnStartupProperty, false))
{
var label = EditorGUI.BeginProperty(area, content, _DontShowOnStartupProperty);
EditorGUI.BeginChangeCheck();
var value = _DontShowOnStartupProperty.boolValue;
value = GUI.Toggle(area, value, label);
if (EditorGUI.EndChangeCheck())
{
_DontShowOnStartupProperty.boolValue = value;
if (value)
PlayerPrefs.SetInt(_Target.ReleaseNumberPrefKey, _Target.ReleaseNumber);
}
EditorGUI.EndProperty();
}
}

/************************************************************************************************************************/

private void DoWarnings()
{
MessageType messageType;

if (!_Target.HasCorrectName)
{
messageType = MessageType.Error;
}
else if (_PreviousVersion >= 0 && _PreviousVersion < _Target.ReleaseNumber)
{
messageType = MessageType.Warning;
}
else return;

// Upgraded from any older version.

DoSpace();

var directory = AssetDatabase.GetAssetPath(_Target);
if (string.IsNullOrEmpty(directory))
return;

directory = Path.GetDirectoryName(directory);

var productName = _Target.ProductName;

string versionWarning;
if (messageType == MessageType.Error)
{
versionWarning = $"You must fully delete any old version of {productName} before importing a new version." +
$"\n1. Check the Upgrade Guide in the Change Log." +
$"\n2. Click here to delete '{directory}'." +
$"\n3. Import {productName} again.";
}
else
{
versionWarning = $"You must fully delete any old version of {productName} before importing a new version." +
$"\n1. Ignore this message if you have already deleted the old version." +
$"\n2. Check the Upgrade Guide in the Change Log." +
$"\n3. Click here to delete '{directory}'." +
$"\n4. Import {productName} again.";
}

EditorGUILayout.HelpBox(versionWarning, messageType);
CheckDeleteDirectory(directory);

DoSpace();
}

/************************************************************************************************************************/

/// Asks if the user wants to delete the `directory` and does so if they confirm.
private void CheckDeleteDirectory(string directory)
{
if (!AnimancerGUI.TryUseClickEventInLastRect())
return;

var name = _Target.ProductName;

if (!AssetDatabase.IsValidFolder(directory))
{
Debug.Log($"{directory} doesn't exist." +
$" You must have moved {name} somewhere else so you will need to delete it manually.", this);
return;
}

if (!EditorUtility.DisplayDialog($"Delete {name}? ",
$"Would you like to delete {directory}?\n\nYou will then need to reimport {name} manually.",
"Delete", "Cancel"))
return;

AssetDatabase.DeleteAsset(directory);
}

/************************************************************************************************************************/

protected virtual void DoIntroductionBlock()
{
GUILayout.BeginVertical(Styles.Block);

DoHeadingLink("Documentation", null, _Target.DocumentationURL);

DoSpace();

DoHeadingLink("Change Log", null, _Target.ChangeLogURL);

GUILayout.EndVertical();
}

/************************************************************************************************************************/

protected virtual void DoExampleBlock()
{
GUILayout.BeginVertical(Styles.Block);

DoHeadingLink("Examples", null, _Target.ExampleURL);
if (_Target._ExamplesFolder != null)
{
EditorGUILayout.ObjectField(_ExamplesDirectory, _Target._ExamplesFolder, typeof(SceneAsset), false);

ExampleGroup.DoExampleGUI(_Examples);
}

DoExtraExamples();

GUILayout.EndVertical();
}

protected virtual void DoExtraExamples()
{
DoHeadingLink("Platformer Game Kit", null, "https://kybernetik.com.au/platformer", null, GUI.skin.label.fontSize);
}

/************************************************************************************************************************/

protected virtual void DoSupportBlock()
{
GUILayout.BeginVertical(Styles.Block);

DoHeadingLink("Forum",
"for general discussions, feedback, and news",
_Target.ForumURL);

DoSpace();

DoHeadingLink("Issues",
"for questions, suggestions, and bug reports",
_Target.IssuesURL);

DoSpace();

DoHeadingLink("Email",
"for anything private",
_EmailLink, _Target.DeveloperEmail);

GUILayout.EndVertical();
}

/************************************************************************************************************************/

protected void DoHeadingLink(string heading, string description, string url, string displayURL = null, int fontSize = 22)
{
// Heading.
var area = DoLinkButton(heading, url, url == null ? Styles.HeaderLabel : Styles.HeaderLink, fontSize);

// Description.

area.y += AnimancerGUI.StandardSpacing;

var urlHeight = Styles.URL.fontSize + Styles.URL.margin.vertical;
area.height -= urlHeight;

if (description != null)
GUI.Label(area, description, Styles.Description);

// URL.

area.y += area.height;
area.height = urlHeight;

if (displayURL == null)
displayURL = url;

if (displayURL != null)
{
using (ObjectPool.Disposable.AcquireContent(out var label,
displayURL, "Click to copy this link to the clipboard", false))
{
if (GUI.Button(area, label, Styles.URL))
{
GUIUtility.systemCopyBuffer = displayURL;
Debug.Log($"Copied '{displayURL}' to the clipboard.", this);
}

EditorGUIUtility.AddCursorRect(area, MouseCursor.Text);
}
}
}

/************************************************************************************************************************/

protected Rect DoLinkButton(string text, string url, GUIStyle style, int fontSize = 22)
{
using (ObjectPool.Disposable.AcquireContent(out var label, text, url, false))
{
style.fontSize = fontSize;

var size = style.CalcSize(label);
var area = GUILayoutUtility.GetRect(0, size.y);

var linkArea = AnimancerGUI.StealFromLeft(ref area, size.x);

if (url == null)
{
GUI.Label(linkArea, label, style);
}
else
{
if (GUI.Button(linkArea, label, style))
Application.OpenURL(url);

EditorGUIUtility.AddCursorRect(linkArea, MouseCursor.Link);

DrawLine(
new Vector2(linkArea.xMin, linkArea.yMax),
new Vector2(linkArea.xMax, linkArea.yMax),
style.normal.textColor);
}

return area;
}
}

/************************************************************************************************************************/

/// Draws a line between the `start` and `end` using the `color`.
public static void DrawLine(Vector2 start, Vector2 end, Color color)
{
var previousColor = Handles.color;
Handles.BeginGUI();
Handles.color = color;
Handles.DrawLine(start, end);
Handles.color = previousColor;
Handles.EndGUI();
}

/************************************************************************************************************************/

/// Various s used by the .
protected static class Styles
{
/************************************************************************************************************************/

public static readonly GUIStyle TitleArea = "In BigTitle";

public static readonly GUIStyle Title = new GUIStyle(GUI.skin.label)
{
fontSize = 26,
};

public static readonly GUIStyle Block = GUI.skin.box;

public static readonly GUIStyle HeaderLabel = new GUIStyle(GUI.skin.label)
{
stretchWidth = false,
};

public static readonly GUIStyle HeaderLink = new GUIStyle(HeaderLabel);

public static readonly GUIStyle Description = new GUIStyle(GUI.skin.label)
{
alignment = TextAnchor.LowerLeft,
};

public static readonly GUIStyle URL = new GUIStyle(GUI.skin.label)
{
fontSize = 9,
alignment = TextAnchor.LowerLeft,
};

/************************************************************************************************************************/

static Styles()
{
HeaderLink.normal.textColor = HeaderLink.hover.textColor =
new Color32(0x00, 0x78, 0xDA, 0xFF);

URL.normal.textColor = Color.Lerp(URL.normal.textColor, Color.grey, 0.8f);
}

/************************************************************************************************************************/
}

/************************************************************************************************************************/

/// A group of example scenes.
private class ExampleGroup
{
/************************************************************************************************************************/

/// The name of this group.
public readonly string Name;

/// The scenes in this group.
public readonly List Scenes = new List();

/// The folder paths of each of the .
public readonly List Directories = new List();

/// Indicates whether this group should show its contents in the GUI.
private bool _IsExpanded;

/************************************************************************************************************************/

public static List Gather(DefaultAsset rootDirectoryAsset, out string examplesDirectory)
{
if (rootDirectoryAsset == null)
{
examplesDirectory = null;
return null;
}

examplesDirectory = AssetDatabase.GetAssetPath(rootDirectoryAsset);
if (string.IsNullOrEmpty(examplesDirectory))
return null;

var directories = Directory.GetDirectories(examplesDirectory);
var examples = new List();

for (int i = 0; i < directories.Length; i++)
{
var group = Gather(examplesDirectory, directories[i]);

if (group != null)
examples.Add(group);
}

if (examples.Count == 0)
{
var group = Gather(examplesDirectory, examplesDirectory);
if (group != null)
examples.Add(group);
}

examplesDirectory = Path.GetDirectoryName(examplesDirectory);

return examples;
}

/************************************************************************************************************************/

public static ExampleGroup Gather(string rootDirectory, string directory)
{
var files = Directory.GetFiles(directory, "*.unity", SearchOption.AllDirectories);

List scenes = null;

for (int j = 0; j < files.Length; j++)
{
var scene = AssetDatabase.LoadAssetAtPath(files[j]);
if (scene != null)
{
if (scenes == null)
scenes = new List();
scenes.Add(scene);
}
}

if (scenes == null)
return null;

return new ExampleGroup(rootDirectory, directory, scenes);
}

/************************************************************************************************************************/

public ExampleGroup(string rootDirectory, string directory, List scenes)
{
var start = rootDirectory.Length + 1;
Name = start < directory.Length ?
directory.Substring(start, directory.Length - start) :
Path.GetFileName(directory);
Scenes = scenes;

start = directory.Length + 1;

for (int i = 0; i < scenes.Count; i++)
{
directory = AssetDatabase.GetAssetPath(scenes[i]);

directory = directory.Substring(start, directory.Length - start);
directory = Path.GetDirectoryName(directory);
Directories.Add(directory);
}
}

/************************************************************************************************************************/

public static void DoExampleGUI(List examples)
{
if (examples == null)
return;

for (int i = 0; i < examples.Count; i++)
examples[i].DoExampleGUI();
}

public void DoExampleGUI()
{
EditorGUI.indentLevel++;

using (ObjectPool.Disposable.AcquireContent(out var label, Name, null, false))
_IsExpanded = EditorGUILayout.Foldout(_IsExpanded, label, true);

if (_IsExpanded)
{
EditorGUI.indentLevel++;
for (int i = 0; i < Scenes.Count; i++)
EditorGUILayout.ObjectField(Directories[i], Scenes[i], typeof(SceneAsset), false);
EditorGUI.indentLevel--;
}

EditorGUI.indentLevel--;
}

/************************************************************************************************************************/
}

/************************************************************************************************************************/
}

/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}

#endif