chore: 외부 에셋 권한 및 줄바꿈 재기록 반영
- Assets/External 하위 샘플 및 서드파티 에셋 파일의 실행 비트 변경을 별도 커밋으로 분리 - PolygonGeneric, SidekickCharacters, Synty 도구 자산 전반의 줄바꿈 및 재직렬화 차이를 그대로 보존 - 프로젝트 고유 로직 변경과 분리해 이후 히스토리에서 외부 에셋 노이즈 범위를 식별하기 쉽게 정리
This commit is contained in:
422
Assets/External/Tools/SyntyPackageHelper/Editor/SyntyPackageHelper.cs
vendored
Normal file → Executable file
422
Assets/External/Tools/SyntyPackageHelper/Editor/SyntyPackageHelper.cs
vendored
Normal file → Executable file
@@ -1,212 +1,212 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
[InitializeOnLoad]
|
||||
public class SyntyPackageHelper
|
||||
{
|
||||
static AddAndRemoveRequest Request;
|
||||
|
||||
static SyntyPackageHelper()
|
||||
{
|
||||
EditorApplication.projectChanged += OnProjectChanged;
|
||||
}
|
||||
|
||||
//Non async way of quickly checking a package is added to the package manager
|
||||
public static bool IsPackageInstalled(string packageId)
|
||||
{
|
||||
if ( !File.Exists("Packages/manifest.json") )
|
||||
return false;
|
||||
|
||||
string jsonText = File.ReadAllText("Packages/manifest.json");
|
||||
return jsonText.Contains( packageId );
|
||||
}
|
||||
|
||||
private static SyntyPackageHelperConfig[] LoadSyntyPackageHelperConfigs()
|
||||
{
|
||||
List<SyntyPackageHelperConfig> configs = new List<SyntyPackageHelperConfig>();
|
||||
string[] assetGuids = AssetDatabase.FindAssets("t:SyntyPackageHelperConfig");
|
||||
|
||||
foreach (string guid in assetGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
SyntyPackageHelperConfig config = AssetDatabase.LoadAssetAtPath<SyntyPackageHelperConfig>(path);
|
||||
if (config != null)
|
||||
{
|
||||
configs.Add(config);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"Loaded {configs.Count} ExamplePackConfig assets");
|
||||
|
||||
return configs.ToArray();
|
||||
}
|
||||
|
||||
static void OnProjectChanged()
|
||||
{
|
||||
EditorApplication.projectChanged -= OnProjectChanged;
|
||||
|
||||
ProcessConfigs(LoadSyntyPackageHelperConfigs());
|
||||
}
|
||||
|
||||
[MenuItem("Synty/Package Helper/Install Packages")]
|
||||
public static void InstallShaderGraphPackage()
|
||||
{
|
||||
ProcessConfigs(LoadSyntyPackageHelperConfigs(), true);
|
||||
}
|
||||
|
||||
static List<string> packagesToInstall = new List<string>();
|
||||
static int requiredPackageCount = 0;
|
||||
public static void ProcessConfigs(SyntyPackageHelperConfig[] configs, bool forceInstall = false)
|
||||
{
|
||||
if(Request != null)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
"A package installation process is already underway. Please wait until it finished before trying again.",
|
||||
"OK");
|
||||
return;
|
||||
}
|
||||
|
||||
packagesToInstall.Clear();
|
||||
requiredPackageCount = 0;
|
||||
|
||||
foreach(var config in configs)
|
||||
{
|
||||
BuildInstallList(config, forceInstall);
|
||||
config.hasPromptedUser = true;
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
if(packagesToInstall.Count == 0)
|
||||
{
|
||||
if(requiredPackageCount == 0)
|
||||
{
|
||||
if(forceInstall)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
"All required packages installed.",
|
||||
"OK");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
"No packages to install. You skipped the installation of " + requiredPackageCount + " packages.",
|
||||
"OK");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
InstallPackages(packagesToInstall.ToArray());
|
||||
}
|
||||
|
||||
public static void BuildInstallList( SyntyPackageHelperConfig config, bool forceInstall = false )
|
||||
{
|
||||
if(!forceInstall && config.hasPromptedUser)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(var packageId in config.packageIds)
|
||||
{
|
||||
if(IsPackageInstalled(packageId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(packagesToInstall.Contains(packageId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
requiredPackageCount++;
|
||||
bool addPackage = PromptPackageInstall(config.assetPackDisplayName, packageId);
|
||||
|
||||
if(addPackage)
|
||||
{
|
||||
packagesToInstall.Add(packageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool PromptPackageInstall(string packName, string packageName)
|
||||
{
|
||||
return EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
packName + " requires the package " + packageName + " to function correctly. Do you want to install this package now?",
|
||||
"Install", "Skip");
|
||||
}
|
||||
|
||||
static string[] requestPackages = null;
|
||||
static void InstallPackages(string[] packagesToInstall)
|
||||
{
|
||||
if(Request != null)
|
||||
{
|
||||
Debug.LogError( "Trying to install a packages when when packages are already being installed." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a package to the project
|
||||
requestPackages = packagesToInstall;
|
||||
Request = Client.AddAndRemove(packagesToInstall);
|
||||
EditorApplication.update += Progress;
|
||||
Progress();
|
||||
}
|
||||
|
||||
static void Progress()
|
||||
{
|
||||
if(Request == null)
|
||||
{
|
||||
EditorApplication.update -= Progress;
|
||||
}
|
||||
|
||||
if(requestPackages != null)
|
||||
{
|
||||
int installed = 0;
|
||||
foreach(var package in requestPackages)
|
||||
{
|
||||
if(IsPackageInstalled(package))
|
||||
{
|
||||
installed++;
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("Synty Package Helper", "Installing packages... " + installed + "/" + requestPackages.Length + ".", (float)installed / (float)requestPackages.Length);
|
||||
}
|
||||
|
||||
if (Request.IsCompleted)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (Request.Status == StatusCode.Success)
|
||||
{
|
||||
string message = requestPackages.Length + " packages installed successfully!";
|
||||
int skipped = requiredPackageCount - requestPackages.Length;
|
||||
if(skipped > 0)
|
||||
{
|
||||
message = message + "\n" + (requiredPackageCount - requestPackages.Length) + " were skipped.";
|
||||
}
|
||||
EditorUtility.DisplayDialog("Synty Package Helper", message, "OK");
|
||||
}
|
||||
else if (Request.Status >= StatusCode.Failure)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Synty Package Helper", "There was an error installing the required packages.\n\nError:\n" + Request.Error.message, "OK");
|
||||
}
|
||||
|
||||
Request = null;
|
||||
EditorApplication.update -= Progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
[InitializeOnLoad]
|
||||
public class SyntyPackageHelper
|
||||
{
|
||||
static AddAndRemoveRequest Request;
|
||||
|
||||
static SyntyPackageHelper()
|
||||
{
|
||||
EditorApplication.projectChanged += OnProjectChanged;
|
||||
}
|
||||
|
||||
//Non async way of quickly checking a package is added to the package manager
|
||||
public static bool IsPackageInstalled(string packageId)
|
||||
{
|
||||
if ( !File.Exists("Packages/manifest.json") )
|
||||
return false;
|
||||
|
||||
string jsonText = File.ReadAllText("Packages/manifest.json");
|
||||
return jsonText.Contains( packageId );
|
||||
}
|
||||
|
||||
private static SyntyPackageHelperConfig[] LoadSyntyPackageHelperConfigs()
|
||||
{
|
||||
List<SyntyPackageHelperConfig> configs = new List<SyntyPackageHelperConfig>();
|
||||
string[] assetGuids = AssetDatabase.FindAssets("t:SyntyPackageHelperConfig");
|
||||
|
||||
foreach (string guid in assetGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
SyntyPackageHelperConfig config = AssetDatabase.LoadAssetAtPath<SyntyPackageHelperConfig>(path);
|
||||
if (config != null)
|
||||
{
|
||||
configs.Add(config);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"Loaded {configs.Count} ExamplePackConfig assets");
|
||||
|
||||
return configs.ToArray();
|
||||
}
|
||||
|
||||
static void OnProjectChanged()
|
||||
{
|
||||
EditorApplication.projectChanged -= OnProjectChanged;
|
||||
|
||||
ProcessConfigs(LoadSyntyPackageHelperConfigs());
|
||||
}
|
||||
|
||||
[MenuItem("Synty/Package Helper/Install Packages")]
|
||||
public static void InstallShaderGraphPackage()
|
||||
{
|
||||
ProcessConfigs(LoadSyntyPackageHelperConfigs(), true);
|
||||
}
|
||||
|
||||
static List<string> packagesToInstall = new List<string>();
|
||||
static int requiredPackageCount = 0;
|
||||
public static void ProcessConfigs(SyntyPackageHelperConfig[] configs, bool forceInstall = false)
|
||||
{
|
||||
if(Request != null)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
"A package installation process is already underway. Please wait until it finished before trying again.",
|
||||
"OK");
|
||||
return;
|
||||
}
|
||||
|
||||
packagesToInstall.Clear();
|
||||
requiredPackageCount = 0;
|
||||
|
||||
foreach(var config in configs)
|
||||
{
|
||||
BuildInstallList(config, forceInstall);
|
||||
config.hasPromptedUser = true;
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
if(packagesToInstall.Count == 0)
|
||||
{
|
||||
if(requiredPackageCount == 0)
|
||||
{
|
||||
if(forceInstall)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
"All required packages installed.",
|
||||
"OK");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
"No packages to install. You skipped the installation of " + requiredPackageCount + " packages.",
|
||||
"OK");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
InstallPackages(packagesToInstall.ToArray());
|
||||
}
|
||||
|
||||
public static void BuildInstallList( SyntyPackageHelperConfig config, bool forceInstall = false )
|
||||
{
|
||||
if(!forceInstall && config.hasPromptedUser)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(var packageId in config.packageIds)
|
||||
{
|
||||
if(IsPackageInstalled(packageId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(packagesToInstall.Contains(packageId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
requiredPackageCount++;
|
||||
bool addPackage = PromptPackageInstall(config.assetPackDisplayName, packageId);
|
||||
|
||||
if(addPackage)
|
||||
{
|
||||
packagesToInstall.Add(packageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool PromptPackageInstall(string packName, string packageName)
|
||||
{
|
||||
return EditorUtility.DisplayDialog(
|
||||
"Synty Package Helper",
|
||||
packName + " requires the package " + packageName + " to function correctly. Do you want to install this package now?",
|
||||
"Install", "Skip");
|
||||
}
|
||||
|
||||
static string[] requestPackages = null;
|
||||
static void InstallPackages(string[] packagesToInstall)
|
||||
{
|
||||
if(Request != null)
|
||||
{
|
||||
Debug.LogError( "Trying to install a packages when when packages are already being installed." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a package to the project
|
||||
requestPackages = packagesToInstall;
|
||||
Request = Client.AddAndRemove(packagesToInstall);
|
||||
EditorApplication.update += Progress;
|
||||
Progress();
|
||||
}
|
||||
|
||||
static void Progress()
|
||||
{
|
||||
if(Request == null)
|
||||
{
|
||||
EditorApplication.update -= Progress;
|
||||
}
|
||||
|
||||
if(requestPackages != null)
|
||||
{
|
||||
int installed = 0;
|
||||
foreach(var package in requestPackages)
|
||||
{
|
||||
if(IsPackageInstalled(package))
|
||||
{
|
||||
installed++;
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("Synty Package Helper", "Installing packages... " + installed + "/" + requestPackages.Length + ".", (float)installed / (float)requestPackages.Length);
|
||||
}
|
||||
|
||||
if (Request.IsCompleted)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (Request.Status == StatusCode.Success)
|
||||
{
|
||||
string message = requestPackages.Length + " packages installed successfully!";
|
||||
int skipped = requiredPackageCount - requestPackages.Length;
|
||||
if(skipped > 0)
|
||||
{
|
||||
message = message + "\n" + (requiredPackageCount - requestPackages.Length) + " were skipped.";
|
||||
}
|
||||
EditorUtility.DisplayDialog("Synty Package Helper", message, "OK");
|
||||
}
|
||||
else if (Request.Status >= StatusCode.Failure)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Synty Package Helper", "There was an error installing the required packages.\n\nError:\n" + Request.Error.message, "OK");
|
||||
}
|
||||
|
||||
Request = null;
|
||||
EditorApplication.update -= Progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
0
Assets/External/Tools/SyntyPackageHelper/Editor/SyntyPackageHelper.cs.meta
vendored
Normal file → Executable file
0
Assets/External/Tools/SyntyPackageHelper/Editor/SyntyPackageHelper.cs.meta
vendored
Normal file → Executable file
930
Assets/External/Tools/SyntyPropBoneTool/Editor/Utils/PropBoneToolEditorUtil.cs
vendored
Normal file → Executable file
930
Assets/External/Tools/SyntyPropBoneTool/Editor/Utils/PropBoneToolEditorUtil.cs
vendored
Normal file → Executable file
@@ -1,466 +1,466 @@
|
||||
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
|
||||
//
|
||||
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
|
||||
// available at: https://syntystore.com/pages/end-user-licence-agreement
|
||||
//
|
||||
// For additional details, see the LICENSE.MD file bundled with this software.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
using UnityEditor.SceneManagement;
|
||||
#else
|
||||
using UnityEditor.Experimental.SceneManagement;
|
||||
#endif
|
||||
|
||||
namespace Synty.Tools.SyntyPropBoneTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class to help configure many characters with prop bone binders at once.
|
||||
/// </summary>
|
||||
public static class PropBoneBinderEditorUtil
|
||||
{
|
||||
// Path where the prop bone binder tool stores the default config file.
|
||||
private const string CONFIG_ASSET_NAME_DEFAULT = "Animation_PropBoneBindingConfig_Default.asset";
|
||||
private const string CONFIG_ASSET_PATH_DEFAULT = FOLDER_PATH_DEFAULT + CONFIG_ASSET_NAME_DEFAULT;
|
||||
|
||||
// Folder path where the prop bone binder tool stores config files by default.
|
||||
private const string FOLDER_PATH_DEFAULT = "Assets/Synty/Tools/SyntyPropBoneTool/Configs/";
|
||||
|
||||
/// <summary>
|
||||
/// Generates a file name based on the targetRigName and the defined config folder path.
|
||||
/// </summary>
|
||||
/// <returns>A <c>string</c> that can be used as a file path for a config asset. This path is not gauranteed to be unique.</returns>
|
||||
private static string GenerateNewConfigFileName(string targetRigName)
|
||||
{
|
||||
PropBoneConfigAsset defaultAsset = GetDefaultConfigAsset();
|
||||
|
||||
string targetFileName = targetRigName + "_PropBoneBindingConfig.asset";
|
||||
string targetPath = defaultAsset.path.Replace(CONFIG_ASSET_NAME_DEFAULT, targetFileName);
|
||||
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class used to help manage config assets in the AssetDatabase.
|
||||
/// </summary>
|
||||
private class PropBoneConfigAsset
|
||||
{
|
||||
public bool savedInAssetDatabase; // is true when loaded from or saved to the AssetDatabase
|
||||
public PropBoneConfig config;
|
||||
public string path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given PropBneBinder is part of a prefab asset.
|
||||
/// </summary>
|
||||
/// <param name="binder">The PropBoneBinder to test if it is part of a prefab asset.</param>
|
||||
/// <returns>A <c>bool</c>. True when the binder is part of a prefab asset.</returns>
|
||||
public static bool IsPrefabAsset(PropBoneBinder binder)
|
||||
{
|
||||
return PrefabUtility.IsPartOfPrefabAsset(binder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attemps to automatically configure all given PropBoneBinders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to setup.</param>
|
||||
public static void AutomaticSetup(List<PropBoneBinder> binders)
|
||||
{
|
||||
SetupAnimatorReferences(binders);
|
||||
SetupPropBoneConfigs(binders);
|
||||
CreatePropBones(binders);
|
||||
BindPropBones(binders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attemps to reset all given PropBoneBinders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to reset.</param>
|
||||
public static void AutomaticReset(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
binders[i].Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find the first PropBoneConfigAsset with the given fileName.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The file name of the config to find.</param>
|
||||
/// <returns>The first found <c>PropBoneConfigAsset</c> with file name matching the given fileName or null if none is found.</returns>
|
||||
private static PropBoneConfigAsset FindFirstConfig(string fileName)
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:PropBoneConfig");
|
||||
for (int i = 0; i < guids.Length; ++i)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
if (path.Contains(fileName))
|
||||
{
|
||||
return LoadPropBoneConfig(path);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load the default PropBoneConfigAsset or if one is not found creates a new default PropBoneConfigAsset.
|
||||
/// </summary>
|
||||
/// <returns>A <c>PropBoneConfigAsset</c> that contains the default settings to use for PropBoneBingings.</returns>
|
||||
private static PropBoneConfigAsset GetDefaultConfigAsset()
|
||||
{
|
||||
PropBoneConfigAsset defaultConfigAsset = FindFirstConfig(CONFIG_ASSET_NAME_DEFAULT);
|
||||
|
||||
if (defaultConfigAsset == null)
|
||||
{
|
||||
PropBoneConfig defaultConfig = CreatePropBoneConfig(PropBoneDefinitionPresets.PolygonBoneDefinition);
|
||||
defaultConfigAsset = CreatePropBoneConfigAsset(defaultConfig, CONFIG_ASSET_PATH_DEFAULT);
|
||||
SavePropBoneAssetToProject(defaultConfigAsset);
|
||||
}
|
||||
|
||||
return defaultConfigAsset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load the default PropBoneConfig or if one is not found creates a new default PropBoneConfig.
|
||||
/// </summary>
|
||||
/// <returns>A <c>PropBoneConfig</c> that contains the default settings to use for PropBoneBingings.</returns>
|
||||
private static PropBoneConfig GetDefaultConfig()
|
||||
{
|
||||
return GetDefaultConfigAsset().config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find and load a config that matched the given sourceRig and targetRig.
|
||||
/// </summary>
|
||||
/// <param name="sourceRig">The source rig to match when finding the PropBoneConfig.</param>
|
||||
/// <param name="targetRig">The target rig to match when finding the PropBoneConfig.</param>
|
||||
/// <returns>A <c>PropBoneConfig</c> that matches the sourceRig and targetRig or returns the default PropBoneConfig if a match is not found.</returns>
|
||||
public static PropBoneConfig FindFirstMatchingConfig(GameObject sourceRig, GameObject targetRig)
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:PropBoneConfig");
|
||||
for (int i = 0; i < guids.Length; ++i)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
PropBoneConfig config = LoadPropBoneConfig(path).config;
|
||||
if (config != null)
|
||||
{
|
||||
if (config.targetRig == targetRig && config.sourceRig == sourceRig)
|
||||
{
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GetDefaultConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find matching PropBoneConfigs or creates a new ones and assigns them to all the given PropBoneBinders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to setup.</param>
|
||||
public static void SetupPropBoneConfigs(List<PropBoneBinder> binders)
|
||||
{
|
||||
PropBoneConfig defaultConfig = GetDefaultConfig();
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binders[i].propBoneConfig == null)
|
||||
{
|
||||
GameObject targetRig = null;
|
||||
if (binders[i].animator != null)
|
||||
{
|
||||
targetRig = binders[i].animator.gameObject;
|
||||
}
|
||||
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(targetRig))
|
||||
{
|
||||
string path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(targetRig);
|
||||
if (path != null)
|
||||
{
|
||||
targetRig = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
}
|
||||
}
|
||||
else if (PrefabStageUtility.GetCurrentPrefabStage() != null)
|
||||
{
|
||||
string path = PrefabStageUtility.GetCurrentPrefabStage().assetPath;
|
||||
if (path != null)
|
||||
{
|
||||
targetRig = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetRig == null)
|
||||
{
|
||||
Debug.LogError($"Cannot locate target model asset for binder: {binders[i].gameObject.name}. You will need to set up the prop bone config for this character manually and then run setup again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneConfig targetConfig = FindFirstMatchingConfig(defaultConfig.sourceRig, targetRig);
|
||||
|
||||
if (targetConfig.targetRig != targetRig)
|
||||
{
|
||||
PropBoneConfig newConfig = ClonePropBoneConfig(defaultConfig);
|
||||
newConfig.targetRig = targetRig;
|
||||
newConfig.CalculateOffsetValues();
|
||||
Debug.Log($"Set up {targetRig.name} as target rig for {binders[i].gameObject.name}", targetRig);
|
||||
PropBoneConfigAsset configAsset = CreatePropBoneConfigAsset(newConfig, GenerateNewConfigFileName(targetRig.name));
|
||||
SavePropBoneAssetToProject(configAsset);
|
||||
targetConfig = configAsset.config;
|
||||
}
|
||||
|
||||
binders[i].propBoneConfig = targetConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new PropBoneConfig files based on the ones assigned to the given binders or bases new configs on the default PropBoneConfig if none are assigned.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to create new bone configs for.</param>
|
||||
public static void CreateNewBoneConfigs(List<PropBoneBinder> binders)
|
||||
{
|
||||
PropBoneConfig defaultConfig = GetDefaultConfig();
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneConfig newConfigBase = binders[i].propBoneConfig != null ? binders[i].propBoneConfig : defaultConfig;
|
||||
|
||||
PropBoneConfig newConfig = ClonePropBoneConfig(newConfigBase);
|
||||
PropBoneConfigAsset configAsset = CreatePropBoneConfigAsset(newConfig, GenerateNewConfigFileName(binders[i].name));
|
||||
SavePropBoneAssetToProject(configAsset);
|
||||
binders[i].propBoneConfig = configAsset.config;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to assign the animator referenec on all the given binders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to setup the animator references of.</param>
|
||||
public static void SetupAnimatorReferences(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.SetupAnimatorReference();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to create all the prop bones for all the given binders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to create prop bones for.</param>
|
||||
public static void CreatePropBones(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.CreatePropBones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to clear all the prop bone bindings for all the given binders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to create prop bones bindings for.</param>
|
||||
public static void ClearPropBoneBindings(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.ClearPropBoneBindings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to destroy all the prop bones on all the given bindings.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to destroy prop bones.</param>
|
||||
public static void DestroyPropBones(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.DestroyPropBones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to bind all the prop bones on all the given bindings.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to bind prop bones.</param>
|
||||
public static void BindPropBones(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.BindPropBones();
|
||||
binder.UpdateBones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a PropBoneConfig file at the given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to load the PropBoneConfig.</param>
|
||||
/// <returns>A <c>PropBoneConfigAsset</c> found at the given path or null if no asset of type PropBoneConfigAsset exists at that path.</returns>
|
||||
private static PropBoneConfigAsset LoadPropBoneConfig(string path)
|
||||
{
|
||||
PropBoneConfig config = AssetDatabase.LoadAssetAtPath<PropBoneConfig>(path);
|
||||
if (config != null)
|
||||
{
|
||||
PropBoneConfigAsset configAsset = new PropBoneConfigAsset();
|
||||
configAsset.config = config;
|
||||
configAsset.savedInAssetDatabase = true;
|
||||
configAsset.path = path;
|
||||
return configAsset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PropBoneConfig based on the given source config.
|
||||
/// </summary>
|
||||
/// <param name="source">The PropBoneConfig to clone.</param>
|
||||
/// <returns>A new <c>PropBoneConfig</c> clones from the source PropBoneConfig.</returns>
|
||||
private static PropBoneConfig ClonePropBoneConfig(PropBoneConfig source)
|
||||
{
|
||||
PropBoneConfig newPropBoneConfig = ScriptableObject.CreateInstance<PropBoneConfig>();
|
||||
newPropBoneConfig.propBoneDefinitions = source.propBoneDefinitions;
|
||||
newPropBoneConfig.sourceRig = source.sourceRig;
|
||||
newPropBoneConfig.targetRig = source.targetRig;
|
||||
return newPropBoneConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PropBoneConfig containing the given PropBoneDefinitions.
|
||||
/// </summary>
|
||||
/// <param name="definitions">The prop bone definitions to be used by the new PropBoneConfig.</param>
|
||||
/// <returns>A <c>PropBoneConfig</c> with the given PropBoneDefinitions.</returns>
|
||||
private static PropBoneConfig CreatePropBoneConfig(PropBoneDefinition[] definitions)
|
||||
{
|
||||
PropBoneConfig newPropBoneConfig = ScriptableObject.CreateInstance<PropBoneConfig>();
|
||||
newPropBoneConfig.propBoneDefinitions = definitions;
|
||||
return newPropBoneConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PropBoneConfigAsset of the given PropBoneConfig and with the given path.
|
||||
/// </summary>
|
||||
/// <param name="config">The config of the config asset.</param>
|
||||
/// <param name="path">The desired path for the config asset. The path used ma.</param>
|
||||
/// <param name="ensureUniquePath">When true the path is altered as necessary to ensure it is unique.</param>
|
||||
/// <returns>A new <c>PropBoneConfigAsset</c> based on the given parameters.</returns>
|
||||
private static PropBoneConfigAsset CreatePropBoneConfigAsset(PropBoneConfig config, string path, bool ensureUniquePath = true)
|
||||
{
|
||||
PropBoneConfigAsset result = new PropBoneConfigAsset();
|
||||
result.savedInAssetDatabase = false;
|
||||
result.path = path;
|
||||
if (ensureUniquePath)
|
||||
{
|
||||
result.path = AssetDatabase.GenerateUniqueAssetPath(result.path);
|
||||
if (string.IsNullOrEmpty(result.path))
|
||||
{
|
||||
// AssetDatabase.GenerateUniqueAssetPath(result.path); returns an empty path if the folder structure in the file path does not exist.
|
||||
result.path = path;
|
||||
}
|
||||
}
|
||||
result.config = config;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the given PropBoneConfigAsset to the AssetDatabase.
|
||||
/// </summary>
|
||||
/// <param name="asset">The PropBoneConfigAsset to save.</param>
|
||||
private static void SavePropBoneAssetToProject(PropBoneConfigAsset asset)
|
||||
{
|
||||
EnsureFolderExists(asset.path);
|
||||
|
||||
AssetDatabase.CreateAsset(asset.config, asset.path);
|
||||
AssetDatabase.SaveAssets();
|
||||
asset.savedInAssetDatabase = true;
|
||||
Debug.Log($"Successfully created prop bone config asset at path: {asset.path}.", asset.config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the folder at the given path exists, if not then the folder and all parent folders in the hierarchy are created.
|
||||
/// </summary>
|
||||
/// <param name="path">The folder path.</param>
|
||||
private static void EnsureFolderExists(string path)
|
||||
{
|
||||
string[] split = path.Split('/');
|
||||
string parentPath = split[0];
|
||||
for (int i = 1; i < split.Length - 1; ++i)
|
||||
{
|
||||
string head = split[i];
|
||||
if (!AssetDatabase.IsValidFolder(parentPath + "/" + head))
|
||||
{
|
||||
AssetDatabase.CreateFolder(parentPath, head);
|
||||
}
|
||||
|
||||
parentPath += "/" + head;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
|
||||
//
|
||||
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
|
||||
// available at: https://syntystore.com/pages/end-user-licence-agreement
|
||||
//
|
||||
// For additional details, see the LICENSE.MD file bundled with this software.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
using UnityEditor.SceneManagement;
|
||||
#else
|
||||
using UnityEditor.Experimental.SceneManagement;
|
||||
#endif
|
||||
|
||||
namespace Synty.Tools.SyntyPropBoneTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class to help configure many characters with prop bone binders at once.
|
||||
/// </summary>
|
||||
public static class PropBoneBinderEditorUtil
|
||||
{
|
||||
// Path where the prop bone binder tool stores the default config file.
|
||||
private const string CONFIG_ASSET_NAME_DEFAULT = "Animation_PropBoneBindingConfig_Default.asset";
|
||||
private const string CONFIG_ASSET_PATH_DEFAULT = FOLDER_PATH_DEFAULT + CONFIG_ASSET_NAME_DEFAULT;
|
||||
|
||||
// Folder path where the prop bone binder tool stores config files by default.
|
||||
private const string FOLDER_PATH_DEFAULT = "Assets/Synty/Tools/SyntyPropBoneTool/Configs/";
|
||||
|
||||
/// <summary>
|
||||
/// Generates a file name based on the targetRigName and the defined config folder path.
|
||||
/// </summary>
|
||||
/// <returns>A <c>string</c> that can be used as a file path for a config asset. This path is not gauranteed to be unique.</returns>
|
||||
private static string GenerateNewConfigFileName(string targetRigName)
|
||||
{
|
||||
PropBoneConfigAsset defaultAsset = GetDefaultConfigAsset();
|
||||
|
||||
string targetFileName = targetRigName + "_PropBoneBindingConfig.asset";
|
||||
string targetPath = defaultAsset.path.Replace(CONFIG_ASSET_NAME_DEFAULT, targetFileName);
|
||||
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class used to help manage config assets in the AssetDatabase.
|
||||
/// </summary>
|
||||
private class PropBoneConfigAsset
|
||||
{
|
||||
public bool savedInAssetDatabase; // is true when loaded from or saved to the AssetDatabase
|
||||
public PropBoneConfig config;
|
||||
public string path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given PropBneBinder is part of a prefab asset.
|
||||
/// </summary>
|
||||
/// <param name="binder">The PropBoneBinder to test if it is part of a prefab asset.</param>
|
||||
/// <returns>A <c>bool</c>. True when the binder is part of a prefab asset.</returns>
|
||||
public static bool IsPrefabAsset(PropBoneBinder binder)
|
||||
{
|
||||
return PrefabUtility.IsPartOfPrefabAsset(binder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attemps to automatically configure all given PropBoneBinders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to setup.</param>
|
||||
public static void AutomaticSetup(List<PropBoneBinder> binders)
|
||||
{
|
||||
SetupAnimatorReferences(binders);
|
||||
SetupPropBoneConfigs(binders);
|
||||
CreatePropBones(binders);
|
||||
BindPropBones(binders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attemps to reset all given PropBoneBinders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to reset.</param>
|
||||
public static void AutomaticReset(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
binders[i].Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find the first PropBoneConfigAsset with the given fileName.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The file name of the config to find.</param>
|
||||
/// <returns>The first found <c>PropBoneConfigAsset</c> with file name matching the given fileName or null if none is found.</returns>
|
||||
private static PropBoneConfigAsset FindFirstConfig(string fileName)
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:PropBoneConfig");
|
||||
for (int i = 0; i < guids.Length; ++i)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
if (path.Contains(fileName))
|
||||
{
|
||||
return LoadPropBoneConfig(path);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load the default PropBoneConfigAsset or if one is not found creates a new default PropBoneConfigAsset.
|
||||
/// </summary>
|
||||
/// <returns>A <c>PropBoneConfigAsset</c> that contains the default settings to use for PropBoneBingings.</returns>
|
||||
private static PropBoneConfigAsset GetDefaultConfigAsset()
|
||||
{
|
||||
PropBoneConfigAsset defaultConfigAsset = FindFirstConfig(CONFIG_ASSET_NAME_DEFAULT);
|
||||
|
||||
if (defaultConfigAsset == null)
|
||||
{
|
||||
PropBoneConfig defaultConfig = CreatePropBoneConfig(PropBoneDefinitionPresets.PolygonBoneDefinition);
|
||||
defaultConfigAsset = CreatePropBoneConfigAsset(defaultConfig, CONFIG_ASSET_PATH_DEFAULT);
|
||||
SavePropBoneAssetToProject(defaultConfigAsset);
|
||||
}
|
||||
|
||||
return defaultConfigAsset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load the default PropBoneConfig or if one is not found creates a new default PropBoneConfig.
|
||||
/// </summary>
|
||||
/// <returns>A <c>PropBoneConfig</c> that contains the default settings to use for PropBoneBingings.</returns>
|
||||
private static PropBoneConfig GetDefaultConfig()
|
||||
{
|
||||
return GetDefaultConfigAsset().config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find and load a config that matched the given sourceRig and targetRig.
|
||||
/// </summary>
|
||||
/// <param name="sourceRig">The source rig to match when finding the PropBoneConfig.</param>
|
||||
/// <param name="targetRig">The target rig to match when finding the PropBoneConfig.</param>
|
||||
/// <returns>A <c>PropBoneConfig</c> that matches the sourceRig and targetRig or returns the default PropBoneConfig if a match is not found.</returns>
|
||||
public static PropBoneConfig FindFirstMatchingConfig(GameObject sourceRig, GameObject targetRig)
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:PropBoneConfig");
|
||||
for (int i = 0; i < guids.Length; ++i)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
PropBoneConfig config = LoadPropBoneConfig(path).config;
|
||||
if (config != null)
|
||||
{
|
||||
if (config.targetRig == targetRig && config.sourceRig == sourceRig)
|
||||
{
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GetDefaultConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find matching PropBoneConfigs or creates a new ones and assigns them to all the given PropBoneBinders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to setup.</param>
|
||||
public static void SetupPropBoneConfigs(List<PropBoneBinder> binders)
|
||||
{
|
||||
PropBoneConfig defaultConfig = GetDefaultConfig();
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binders[i].propBoneConfig == null)
|
||||
{
|
||||
GameObject targetRig = null;
|
||||
if (binders[i].animator != null)
|
||||
{
|
||||
targetRig = binders[i].animator.gameObject;
|
||||
}
|
||||
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(targetRig))
|
||||
{
|
||||
string path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(targetRig);
|
||||
if (path != null)
|
||||
{
|
||||
targetRig = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
}
|
||||
}
|
||||
else if (PrefabStageUtility.GetCurrentPrefabStage() != null)
|
||||
{
|
||||
string path = PrefabStageUtility.GetCurrentPrefabStage().assetPath;
|
||||
if (path != null)
|
||||
{
|
||||
targetRig = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetRig == null)
|
||||
{
|
||||
Debug.LogError($"Cannot locate target model asset for binder: {binders[i].gameObject.name}. You will need to set up the prop bone config for this character manually and then run setup again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneConfig targetConfig = FindFirstMatchingConfig(defaultConfig.sourceRig, targetRig);
|
||||
|
||||
if (targetConfig.targetRig != targetRig)
|
||||
{
|
||||
PropBoneConfig newConfig = ClonePropBoneConfig(defaultConfig);
|
||||
newConfig.targetRig = targetRig;
|
||||
newConfig.CalculateOffsetValues();
|
||||
Debug.Log($"Set up {targetRig.name} as target rig for {binders[i].gameObject.name}", targetRig);
|
||||
PropBoneConfigAsset configAsset = CreatePropBoneConfigAsset(newConfig, GenerateNewConfigFileName(targetRig.name));
|
||||
SavePropBoneAssetToProject(configAsset);
|
||||
targetConfig = configAsset.config;
|
||||
}
|
||||
|
||||
binders[i].propBoneConfig = targetConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new PropBoneConfig files based on the ones assigned to the given binders or bases new configs on the default PropBoneConfig if none are assigned.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to create new bone configs for.</param>
|
||||
public static void CreateNewBoneConfigs(List<PropBoneBinder> binders)
|
||||
{
|
||||
PropBoneConfig defaultConfig = GetDefaultConfig();
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneConfig newConfigBase = binders[i].propBoneConfig != null ? binders[i].propBoneConfig : defaultConfig;
|
||||
|
||||
PropBoneConfig newConfig = ClonePropBoneConfig(newConfigBase);
|
||||
PropBoneConfigAsset configAsset = CreatePropBoneConfigAsset(newConfig, GenerateNewConfigFileName(binders[i].name));
|
||||
SavePropBoneAssetToProject(configAsset);
|
||||
binders[i].propBoneConfig = configAsset.config;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to assign the animator referenec on all the given binders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to setup the animator references of.</param>
|
||||
public static void SetupAnimatorReferences(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.SetupAnimatorReference();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to create all the prop bones for all the given binders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to create prop bones for.</param>
|
||||
public static void CreatePropBones(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.CreatePropBones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to clear all the prop bone bindings for all the given binders.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to create prop bones bindings for.</param>
|
||||
public static void ClearPropBoneBindings(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.ClearPropBoneBindings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to destroy all the prop bones on all the given bindings.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to destroy prop bones.</param>
|
||||
public static void DestroyPropBones(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.DestroyPropBones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to bind all the prop bones on all the given bindings.
|
||||
/// </summary>
|
||||
/// <param name="binders">All the PropBoneBinder components to bind prop bones.</param>
|
||||
public static void BindPropBones(List<PropBoneBinder> binders)
|
||||
{
|
||||
for (int i = 0; i < binders.Count; ++i)
|
||||
{
|
||||
if (IsPrefabAsset(binders[i]))
|
||||
{
|
||||
Debug.LogWarning($"Cannot edit prefab asset {binders[i].gameObject.name}. Open the asset in prefab edit mode or create a scene instance and try again.", binders[i].gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
PropBoneBinder binder = binders[i];
|
||||
if (binder != null)
|
||||
{
|
||||
binder.BindPropBones();
|
||||
binder.UpdateBones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a PropBoneConfig file at the given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to load the PropBoneConfig.</param>
|
||||
/// <returns>A <c>PropBoneConfigAsset</c> found at the given path or null if no asset of type PropBoneConfigAsset exists at that path.</returns>
|
||||
private static PropBoneConfigAsset LoadPropBoneConfig(string path)
|
||||
{
|
||||
PropBoneConfig config = AssetDatabase.LoadAssetAtPath<PropBoneConfig>(path);
|
||||
if (config != null)
|
||||
{
|
||||
PropBoneConfigAsset configAsset = new PropBoneConfigAsset();
|
||||
configAsset.config = config;
|
||||
configAsset.savedInAssetDatabase = true;
|
||||
configAsset.path = path;
|
||||
return configAsset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PropBoneConfig based on the given source config.
|
||||
/// </summary>
|
||||
/// <param name="source">The PropBoneConfig to clone.</param>
|
||||
/// <returns>A new <c>PropBoneConfig</c> clones from the source PropBoneConfig.</returns>
|
||||
private static PropBoneConfig ClonePropBoneConfig(PropBoneConfig source)
|
||||
{
|
||||
PropBoneConfig newPropBoneConfig = ScriptableObject.CreateInstance<PropBoneConfig>();
|
||||
newPropBoneConfig.propBoneDefinitions = source.propBoneDefinitions;
|
||||
newPropBoneConfig.sourceRig = source.sourceRig;
|
||||
newPropBoneConfig.targetRig = source.targetRig;
|
||||
return newPropBoneConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PropBoneConfig containing the given PropBoneDefinitions.
|
||||
/// </summary>
|
||||
/// <param name="definitions">The prop bone definitions to be used by the new PropBoneConfig.</param>
|
||||
/// <returns>A <c>PropBoneConfig</c> with the given PropBoneDefinitions.</returns>
|
||||
private static PropBoneConfig CreatePropBoneConfig(PropBoneDefinition[] definitions)
|
||||
{
|
||||
PropBoneConfig newPropBoneConfig = ScriptableObject.CreateInstance<PropBoneConfig>();
|
||||
newPropBoneConfig.propBoneDefinitions = definitions;
|
||||
return newPropBoneConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PropBoneConfigAsset of the given PropBoneConfig and with the given path.
|
||||
/// </summary>
|
||||
/// <param name="config">The config of the config asset.</param>
|
||||
/// <param name="path">The desired path for the config asset. The path used ma.</param>
|
||||
/// <param name="ensureUniquePath">When true the path is altered as necessary to ensure it is unique.</param>
|
||||
/// <returns>A new <c>PropBoneConfigAsset</c> based on the given parameters.</returns>
|
||||
private static PropBoneConfigAsset CreatePropBoneConfigAsset(PropBoneConfig config, string path, bool ensureUniquePath = true)
|
||||
{
|
||||
PropBoneConfigAsset result = new PropBoneConfigAsset();
|
||||
result.savedInAssetDatabase = false;
|
||||
result.path = path;
|
||||
if (ensureUniquePath)
|
||||
{
|
||||
result.path = AssetDatabase.GenerateUniqueAssetPath(result.path);
|
||||
if (string.IsNullOrEmpty(result.path))
|
||||
{
|
||||
// AssetDatabase.GenerateUniqueAssetPath(result.path); returns an empty path if the folder structure in the file path does not exist.
|
||||
result.path = path;
|
||||
}
|
||||
}
|
||||
result.config = config;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the given PropBoneConfigAsset to the AssetDatabase.
|
||||
/// </summary>
|
||||
/// <param name="asset">The PropBoneConfigAsset to save.</param>
|
||||
private static void SavePropBoneAssetToProject(PropBoneConfigAsset asset)
|
||||
{
|
||||
EnsureFolderExists(asset.path);
|
||||
|
||||
AssetDatabase.CreateAsset(asset.config, asset.path);
|
||||
AssetDatabase.SaveAssets();
|
||||
asset.savedInAssetDatabase = true;
|
||||
Debug.Log($"Successfully created prop bone config asset at path: {asset.path}.", asset.config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the folder at the given path exists, if not then the folder and all parent folders in the hierarchy are created.
|
||||
/// </summary>
|
||||
/// <param name="path">The folder path.</param>
|
||||
private static void EnsureFolderExists(string path)
|
||||
{
|
||||
string[] split = path.Split('/');
|
||||
string parentPath = split[0];
|
||||
for (int i = 1; i < split.Length - 1; ++i)
|
||||
{
|
||||
string head = split[i];
|
||||
if (!AssetDatabase.IsValidFolder(parentPath + "/" + head))
|
||||
{
|
||||
AssetDatabase.CreateFolder(parentPath, head);
|
||||
}
|
||||
|
||||
parentPath += "/" + head;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
0
Assets/External/Tools/SyntyPropBoneTool/Editor/Utils/PropBoneToolEditorUtil.cs.meta
vendored
Normal file → Executable file
0
Assets/External/Tools/SyntyPropBoneTool/Editor/Utils/PropBoneToolEditorUtil.cs.meta
vendored
Normal file → Executable file
966
Assets/External/Tools/SyntyPropBoneTool/Runtime/PropBoneBinder.cs
vendored
Normal file → Executable file
966
Assets/External/Tools/SyntyPropBoneTool/Runtime/PropBoneBinder.cs
vendored
Normal file → Executable file
@@ -1,486 +1,486 @@
|
||||
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
|
||||
//
|
||||
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
|
||||
// available at: https://syntystore.com/pages/end-user-licence-agreement
|
||||
//
|
||||
// For additional details, see the LICENSE.MD file bundled with this software.
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Synty.Tools.SyntyPropBoneTool
|
||||
{
|
||||
/// <summary>
|
||||
/// The PropBoneBinder is responsible for creating, managing and updating the prop bones at runtime.
|
||||
/// Only one of these components is needed per character. It will manage all prop bones defined in the propBoneConfig.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
public class PropBoneBinder : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Reference to the animator for this character")]
|
||||
public Animator animator;
|
||||
|
||||
[Tooltip("Configures how the bones are set up on the rig.")]
|
||||
public PropBoneConfig propBoneConfig;
|
||||
|
||||
[Tooltip("Determins when this script will update the transforms. For best results run this script later than the animator.")]
|
||||
public UpdateType updateType = UpdateType.LateUpdate;
|
||||
public bool updateInEditMode = true;
|
||||
|
||||
[Tooltip("Rebinds all the bones on awake. Useful if your rigs change often, saves needing to rebind them at edit time.")]
|
||||
public bool rebindOnAwake = false;
|
||||
|
||||
[Space]
|
||||
[Tooltip("Bindings and offset values applied at runtime.")]
|
||||
[SerializeField]
|
||||
private List<PropBoneBinding> _propBoneBindings = new List<PropBoneBinding>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the PropBoneBinder is configured correctly.
|
||||
/// </summary>
|
||||
public bool IsConfigured => AreReferencesConfigured() && AreBindingsConfigured();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the reference variables are configured correctly.
|
||||
/// </summary>
|
||||
/// <returns>A <c>bool</c> that is true when the reference variables are correctly configured.</returns>
|
||||
public bool AreReferencesConfigured()
|
||||
{
|
||||
return animator != null && propBoneConfig != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the hierarchy is configured correctly according to the propBoneConfig.
|
||||
/// </summary>
|
||||
/// <returns>A <c>bool</c> that is true when the hierarchy is correctly configured.</returns>
|
||||
public bool IsPropBoneHierarchyConfigured()
|
||||
{
|
||||
for (int i = 0; i < propBoneConfig.propBoneDefinitions.Length; ++i)
|
||||
{
|
||||
Transform propBone = TransformUtil.SearchHierarchy(transform, propBoneConfig.propBoneDefinitions[i].boneName);
|
||||
Transform propBoneSocket = TransformUtil.SearchHierarchy(transform, propBoneConfig.propBoneDefinitions[i].socketName);
|
||||
if (propBone == null || propBoneSocket == null || propBone.parent == null || propBone.parent.name != propBoneConfig.propBoneDefinitions[i].parentBoneName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all the prop bone bindings are configured correctly according to the propBoneConfig.
|
||||
/// </summary>
|
||||
/// <returns>A <c>bool</c> that is true when all the prop bone bindings are correctly configured.</returns>
|
||||
public bool AreBindingsConfigured()
|
||||
{
|
||||
if (_propBoneBindings.Count != propBoneConfig.propBoneDefinitions.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _propBoneBindings.Count; ++i)
|
||||
{
|
||||
if (!_propBoneBindings[i].IsMatch(propBoneConfig.propBoneDefinitions[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebinds the PropBoneBindings on awake if rebindOnAwake is set to true.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (rebindOnAwake)
|
||||
{
|
||||
BindPropBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to create a PropBoneBinding in accordance to the giving PropBoneDefinition.
|
||||
/// </summary>
|
||||
/// <param name="boneDefinition">The definition of the prop bone to be created.</param>
|
||||
/// <returns>A <c>PropBoneBinding</c> that is the new binding or null if the binding failed to be created.</returns>
|
||||
private PropBoneBinding CreateBoneBinding(PropBoneDefinition boneDefinition)
|
||||
{
|
||||
Transform parent = TransformUtil.SearchHierarchy(transform, boneDefinition.parentBoneName);
|
||||
if (parent == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find parent bone {boneDefinition.parentBoneName}.", transform);
|
||||
return null;
|
||||
}
|
||||
|
||||
Transform bone = TransformUtil.SearchHierarchy(transform, boneDefinition.boneName);
|
||||
if (bone == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find bone {boneDefinition.boneName}.", transform);
|
||||
}
|
||||
else if (bone.parent.name != boneDefinition.parentBoneName)
|
||||
{
|
||||
Debug.LogError($"bone.parent {bone.parent.name} does not match {boneDefinition.parentBoneName} in hierarchy.", bone);
|
||||
return null;
|
||||
}
|
||||
|
||||
Transform socket = TransformUtil.SearchHierarchy(transform, boneDefinition.socketName);
|
||||
if (socket == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find socket {boneDefinition.socketName}.", transform);
|
||||
return null;
|
||||
}
|
||||
else if (socket.parent != bone)
|
||||
{
|
||||
Debug.LogError($"socket {socket.name} is not parented to bone {bone.name}.", socket);
|
||||
return null;
|
||||
}
|
||||
|
||||
PropBoneBinding binding = new PropBoneBinding()
|
||||
{
|
||||
bone = bone,
|
||||
socket = socket,
|
||||
rotationOffset = boneDefinition.rotationOffset,
|
||||
scale = boneDefinition.scale
|
||||
};
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones if updateType is set to 'Update'.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (updateType == UpdateType.Update)
|
||||
{
|
||||
UpdateBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones if updateType is set to 'FixedUpdate'.
|
||||
/// </summary>
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (updateType == UpdateType.FixedUpdate)
|
||||
{
|
||||
UpdateBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones if updateType is set to 'LateUpdate'.
|
||||
/// </summary>
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (updateType == UpdateType.LateUpdate)
|
||||
{
|
||||
UpdateBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones according to the prop bone bindings configuration.
|
||||
/// If updateType is set to 'Manual' call this yourself when it is time to update the bones.
|
||||
/// </summary>
|
||||
public void UpdateBones()
|
||||
{
|
||||
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
|
||||
//
|
||||
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
|
||||
// available at: https://syntystore.com/pages/end-user-licence-agreement
|
||||
//
|
||||
// For additional details, see the LICENSE.MD file bundled with this software.
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Synty.Tools.SyntyPropBoneTool
|
||||
{
|
||||
/// <summary>
|
||||
/// The PropBoneBinder is responsible for creating, managing and updating the prop bones at runtime.
|
||||
/// Only one of these components is needed per character. It will manage all prop bones defined in the propBoneConfig.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
public class PropBoneBinder : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Reference to the animator for this character")]
|
||||
public Animator animator;
|
||||
|
||||
[Tooltip("Configures how the bones are set up on the rig.")]
|
||||
public PropBoneConfig propBoneConfig;
|
||||
|
||||
[Tooltip("Determins when this script will update the transforms. For best results run this script later than the animator.")]
|
||||
public UpdateType updateType = UpdateType.LateUpdate;
|
||||
public bool updateInEditMode = true;
|
||||
|
||||
[Tooltip("Rebinds all the bones on awake. Useful if your rigs change often, saves needing to rebind them at edit time.")]
|
||||
public bool rebindOnAwake = false;
|
||||
|
||||
[Space]
|
||||
[Tooltip("Bindings and offset values applied at runtime.")]
|
||||
[SerializeField]
|
||||
private List<PropBoneBinding> _propBoneBindings = new List<PropBoneBinding>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the PropBoneBinder is configured correctly.
|
||||
/// </summary>
|
||||
public bool IsConfigured => AreReferencesConfigured() && AreBindingsConfigured();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the reference variables are configured correctly.
|
||||
/// </summary>
|
||||
/// <returns>A <c>bool</c> that is true when the reference variables are correctly configured.</returns>
|
||||
public bool AreReferencesConfigured()
|
||||
{
|
||||
return animator != null && propBoneConfig != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the hierarchy is configured correctly according to the propBoneConfig.
|
||||
/// </summary>
|
||||
/// <returns>A <c>bool</c> that is true when the hierarchy is correctly configured.</returns>
|
||||
public bool IsPropBoneHierarchyConfigured()
|
||||
{
|
||||
for (int i = 0; i < propBoneConfig.propBoneDefinitions.Length; ++i)
|
||||
{
|
||||
Transform propBone = TransformUtil.SearchHierarchy(transform, propBoneConfig.propBoneDefinitions[i].boneName);
|
||||
Transform propBoneSocket = TransformUtil.SearchHierarchy(transform, propBoneConfig.propBoneDefinitions[i].socketName);
|
||||
if (propBone == null || propBoneSocket == null || propBone.parent == null || propBone.parent.name != propBoneConfig.propBoneDefinitions[i].parentBoneName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all the prop bone bindings are configured correctly according to the propBoneConfig.
|
||||
/// </summary>
|
||||
/// <returns>A <c>bool</c> that is true when all the prop bone bindings are correctly configured.</returns>
|
||||
public bool AreBindingsConfigured()
|
||||
{
|
||||
if (_propBoneBindings.Count != propBoneConfig.propBoneDefinitions.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _propBoneBindings.Count; ++i)
|
||||
{
|
||||
if (!_propBoneBindings[i].IsMatch(propBoneConfig.propBoneDefinitions[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebinds the PropBoneBindings on awake if rebindOnAwake is set to true.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (rebindOnAwake)
|
||||
{
|
||||
BindPropBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to create a PropBoneBinding in accordance to the giving PropBoneDefinition.
|
||||
/// </summary>
|
||||
/// <param name="boneDefinition">The definition of the prop bone to be created.</param>
|
||||
/// <returns>A <c>PropBoneBinding</c> that is the new binding or null if the binding failed to be created.</returns>
|
||||
private PropBoneBinding CreateBoneBinding(PropBoneDefinition boneDefinition)
|
||||
{
|
||||
Transform parent = TransformUtil.SearchHierarchy(transform, boneDefinition.parentBoneName);
|
||||
if (parent == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find parent bone {boneDefinition.parentBoneName}.", transform);
|
||||
return null;
|
||||
}
|
||||
|
||||
Transform bone = TransformUtil.SearchHierarchy(transform, boneDefinition.boneName);
|
||||
if (bone == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find bone {boneDefinition.boneName}.", transform);
|
||||
}
|
||||
else if (bone.parent.name != boneDefinition.parentBoneName)
|
||||
{
|
||||
Debug.LogError($"bone.parent {bone.parent.name} does not match {boneDefinition.parentBoneName} in hierarchy.", bone);
|
||||
return null;
|
||||
}
|
||||
|
||||
Transform socket = TransformUtil.SearchHierarchy(transform, boneDefinition.socketName);
|
||||
if (socket == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find socket {boneDefinition.socketName}.", transform);
|
||||
return null;
|
||||
}
|
||||
else if (socket.parent != bone)
|
||||
{
|
||||
Debug.LogError($"socket {socket.name} is not parented to bone {bone.name}.", socket);
|
||||
return null;
|
||||
}
|
||||
|
||||
PropBoneBinding binding = new PropBoneBinding()
|
||||
{
|
||||
bone = bone,
|
||||
socket = socket,
|
||||
rotationOffset = boneDefinition.rotationOffset,
|
||||
scale = boneDefinition.scale
|
||||
};
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones if updateType is set to 'Update'.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (updateType == UpdateType.Update)
|
||||
{
|
||||
UpdateBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones if updateType is set to 'FixedUpdate'.
|
||||
/// </summary>
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (updateType == UpdateType.FixedUpdate)
|
||||
{
|
||||
UpdateBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones if updateType is set to 'LateUpdate'.
|
||||
/// </summary>
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (updateType == UpdateType.LateUpdate)
|
||||
{
|
||||
UpdateBones();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bones according to the prop bone bindings configuration.
|
||||
/// If updateType is set to 'Manual' call this yourself when it is time to update the bones.
|
||||
/// </summary>
|
||||
public void UpdateBones()
|
||||
{
|
||||
if (!Application.isPlaying && !updateInEditMode)
|
||||
{
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < _propBoneBindings.Count; ++index)
|
||||
{
|
||||
UpdateBone(_propBoneBindings[index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bone's position and rotation according to the prop bone binding configuration.
|
||||
/// </summary>
|
||||
/// <param name="boneInstance">The prop bone binding to update.</param>
|
||||
public void UpdateBone(PropBoneBinding boneInstance)
|
||||
{
|
||||
if (boneInstance.IsValid)
|
||||
{
|
||||
Quaternion offsetRotation = Quaternion.Euler(boneInstance.rotationOffset);
|
||||
Matrix4x4 localRotation = Matrix4x4.Rotate(offsetRotation);
|
||||
Matrix4x4 localScale = Matrix4x4.Scale(Vector3.one * boneInstance.scale);
|
||||
|
||||
Matrix4x4 localTransform = localScale * localRotation;
|
||||
|
||||
boneInstance.socket.SetPositionAndRotation(
|
||||
boneInstance.bone.parent.localToWorldMatrix.MultiplyPoint(localTransform.MultiplyPoint(boneInstance.bone.localPosition)),
|
||||
boneInstance.bone.parent.rotation * offsetRotation * boneInstance.bone.localRotation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find and set the animator reference.
|
||||
/// </summary>
|
||||
public void SetupAnimatorReference()
|
||||
{
|
||||
if (animator == null)
|
||||
{
|
||||
animator = GetComponent<Animator>();
|
||||
}
|
||||
if (animator == null)
|
||||
{
|
||||
Debug.LogError($"Animator reference is null. New bones may not bind correctly.", transform);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completely resets the prop bones and bindings including Destroying all bones instantiated by the PropBoneBinder.
|
||||
/// Any objects parented to those bones will not be destroyed and will be reparented up the hierarchy.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
animator = null;
|
||||
propBoneConfig = null;
|
||||
ClearPropBoneBindings();
|
||||
DestroyPropBones();
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to create all the PropBoneBindings in accordance to the propBondConfig
|
||||
/// </summary>
|
||||
public void BindPropBones()
|
||||
{
|
||||
if (animator == null)
|
||||
{
|
||||
Debug.LogError($"animator reference is null.", transform);
|
||||
}
|
||||
|
||||
_propBoneBindings.Clear();
|
||||
|
||||
if (propBoneConfig == null)
|
||||
{
|
||||
Debug.LogError($"Prop bone config is null.", transform);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < propBoneConfig.propBoneDefinitions.Length; ++i)
|
||||
{
|
||||
PropBoneBinding binding = CreateBoneBinding(propBoneConfig.propBoneDefinitions[i]);
|
||||
if (binding == null)
|
||||
{
|
||||
Debug.LogError($"Could not create binding for prop bone definition {propBoneConfig.propBoneDefinitions[i].ToString()}.", transform);
|
||||
continue;
|
||||
}
|
||||
|
||||
_propBoneBindings.Add(binding);
|
||||
if (binding.bone != null && binding.socket != null)
|
||||
{
|
||||
Debug.Log($"Successfully bound {binding.socket.name} to {binding.bone.name}", binding.socket);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all the current bindings
|
||||
/// </summary>
|
||||
public void ClearPropBoneBindings()
|
||||
{
|
||||
_propBoneBindings.Clear();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates new Game Objects where nessessary to create the prop bones.
|
||||
/// </summary>
|
||||
public void CreatePropBones()
|
||||
{
|
||||
if (propBoneConfig == null)
|
||||
{
|
||||
Debug.LogError($"Prop bone config is null.", transform);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < propBoneConfig.propBoneDefinitions.Length; ++i)
|
||||
{
|
||||
CreatePropBones(gameObject, propBoneConfig.propBoneDefinitions[i]);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates new Game Objects where nessessary to create the prop bones.
|
||||
/// </summary>
|
||||
/// <param name="editScope">The root game object to create the prop bone under.</param>
|
||||
/// <param name="boneDefinition">The definition of the prop bone to be created.</param>
|
||||
/// <param name="boneInstance">The prop bone binding to update.</param>
|
||||
private void CreatePropBones(GameObject editScope, PropBoneDefinition boneDefinition)
|
||||
{
|
||||
Transform parent = TransformUtil.SearchHierarchy(editScope.transform, boneDefinition.parentBoneName);
|
||||
if (parent == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find parent prop bone {boneDefinition.parentBoneName} in hierarchy.", transform);
|
||||
return;
|
||||
}
|
||||
|
||||
Transform bone = TransformUtil.SearchHierarchy(editScope.transform, boneDefinition.boneName);
|
||||
if (bone == null)
|
||||
{
|
||||
bone = CreatePropBone(boneDefinition.boneName, parent);
|
||||
if (bone == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (bone.parent.name != boneDefinition.parentBoneName)
|
||||
{
|
||||
Debug.LogError($"bone.parent {bone.parent.name} does not match {boneDefinition.parentBoneName} in hierarchy.", bone);
|
||||
return;
|
||||
}
|
||||
|
||||
Transform socket = TransformUtil.SearchHierarchy(editScope.transform, boneDefinition.socketName);
|
||||
if (socket == null)
|
||||
{
|
||||
socket = CreatePropBone(boneDefinition.socketName, bone);
|
||||
}
|
||||
else if (socket.parent != bone)
|
||||
{
|
||||
Debug.LogError($"socket {socket.name} is not parented to bone {bone.name}.", socket);
|
||||
return;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
EditorGUIUtility.PingObject(socket);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new Game Object called 'name' and parents it to 'parent'.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the new bone to be created.</param>
|
||||
/// <param name="parent">The transform to parent the new bone to.</param>
|
||||
private Transform CreatePropBone(string name, Transform parent)
|
||||
{
|
||||
// does not check if there is already a bone called this. we should check before calling create
|
||||
Transform boneInstance = new GameObject(name).transform;
|
||||
boneInstance.SetParent(parent);
|
||||
if (boneInstance.parent != parent)
|
||||
{
|
||||
Debug.LogError($"Something went wrong when creating prop bone {boneInstance.name}. You may need to enter prefab edit mode to set up this character.", gameObject);
|
||||
DestroyImmediate(boneInstance.gameObject);
|
||||
return null;
|
||||
}
|
||||
boneInstance.localPosition = Vector3.zero;
|
||||
boneInstance.localRotation = Quaternion.identity;
|
||||
boneInstance.localScale = Vector3.one;
|
||||
// mark that these have been created by the tool
|
||||
boneInstance.gameObject.AddComponent<PropBone>().WasSpawnedBySyntyTool = true;
|
||||
|
||||
Debug.Log($"Successfully created prop bone {boneInstance.name}.", boneInstance);
|
||||
|
||||
return boneInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all the prop bones that have been created by the PropBoneBinder and clears all bindings.
|
||||
/// </summary>
|
||||
public void DestroyPropBones()
|
||||
{
|
||||
DestroyPropBones(gameObject);
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all the prop bones that have been created by the PropBoneBinder and clears all bindings
|
||||
/// </summary>
|
||||
/// <param name="editScope">The root game objects to destroy prop bones from.</param>
|
||||
private void DestroyPropBones(GameObject editScope)
|
||||
{
|
||||
_propBoneBindings.Clear();
|
||||
PropBone[] bones = editScope.GetComponentsInChildren<PropBone>();
|
||||
for (int i = 0; i < bones.Length; ++i)
|
||||
{
|
||||
if (bones[i].WasSpawnedBySyntyTool)
|
||||
{
|
||||
// Let's not destroy peoples swords and stuff.
|
||||
for (int c = 0; c < bones[i].transform.childCount; ++c)
|
||||
{
|
||||
Transform child = bones[i].transform.GetChild(c);
|
||||
child.SetParent(bones[i].transform.parent);
|
||||
bool hasPropBone = child.GetComponent<PropBone>() != null;
|
||||
if (!hasPropBone)
|
||||
{
|
||||
Debug.Log($"Successfully reparented object {child.name}.", child);
|
||||
}
|
||||
}
|
||||
|
||||
string boneName = bones[i].gameObject.name;
|
||||
Transform parent = bones[i].transform.parent;
|
||||
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
{
|
||||
DestroyImmediate(bones[i].gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(bones[i].gameObject);
|
||||
}
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
Debug.Log($"Successfully destroyed bone {boneName} under {parent.name}.", parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Successfully destroyed bone {boneName}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject gameObject = bones[i].gameObject;
|
||||
// Remove the component but don't destroy the object.
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
{
|
||||
DestroyImmediate(bones[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(bones[i]);
|
||||
}
|
||||
|
||||
if (gameObject != null)
|
||||
{
|
||||
Debug.Log($"Successfully removed SyntyPropBone component from {gameObject.name}.", gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < _propBoneBindings.Count; ++index)
|
||||
{
|
||||
UpdateBone(_propBoneBindings[index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the prop bone's position and rotation according to the prop bone binding configuration.
|
||||
/// </summary>
|
||||
/// <param name="boneInstance">The prop bone binding to update.</param>
|
||||
public void UpdateBone(PropBoneBinding boneInstance)
|
||||
{
|
||||
if (boneInstance.IsValid)
|
||||
{
|
||||
Quaternion offsetRotation = Quaternion.Euler(boneInstance.rotationOffset);
|
||||
Matrix4x4 localRotation = Matrix4x4.Rotate(offsetRotation);
|
||||
Matrix4x4 localScale = Matrix4x4.Scale(Vector3.one * boneInstance.scale);
|
||||
|
||||
Matrix4x4 localTransform = localScale * localRotation;
|
||||
|
||||
boneInstance.socket.SetPositionAndRotation(
|
||||
boneInstance.bone.parent.localToWorldMatrix.MultiplyPoint(localTransform.MultiplyPoint(boneInstance.bone.localPosition)),
|
||||
boneInstance.bone.parent.rotation * offsetRotation * boneInstance.bone.localRotation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find and set the animator reference.
|
||||
/// </summary>
|
||||
public void SetupAnimatorReference()
|
||||
{
|
||||
if (animator == null)
|
||||
{
|
||||
animator = GetComponent<Animator>();
|
||||
}
|
||||
if (animator == null)
|
||||
{
|
||||
Debug.LogError($"Animator reference is null. New bones may not bind correctly.", transform);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completely resets the prop bones and bindings including Destroying all bones instantiated by the PropBoneBinder.
|
||||
/// Any objects parented to those bones will not be destroyed and will be reparented up the hierarchy.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
animator = null;
|
||||
propBoneConfig = null;
|
||||
ClearPropBoneBindings();
|
||||
DestroyPropBones();
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to create all the PropBoneBindings in accordance to the propBondConfig
|
||||
/// </summary>
|
||||
public void BindPropBones()
|
||||
{
|
||||
if (animator == null)
|
||||
{
|
||||
Debug.LogError($"animator reference is null.", transform);
|
||||
}
|
||||
|
||||
_propBoneBindings.Clear();
|
||||
|
||||
if (propBoneConfig == null)
|
||||
{
|
||||
Debug.LogError($"Prop bone config is null.", transform);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < propBoneConfig.propBoneDefinitions.Length; ++i)
|
||||
{
|
||||
PropBoneBinding binding = CreateBoneBinding(propBoneConfig.propBoneDefinitions[i]);
|
||||
if (binding == null)
|
||||
{
|
||||
Debug.LogError($"Could not create binding for prop bone definition {propBoneConfig.propBoneDefinitions[i].ToString()}.", transform);
|
||||
continue;
|
||||
}
|
||||
|
||||
_propBoneBindings.Add(binding);
|
||||
if (binding.bone != null && binding.socket != null)
|
||||
{
|
||||
Debug.Log($"Successfully bound {binding.socket.name} to {binding.bone.name}", binding.socket);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all the current bindings
|
||||
/// </summary>
|
||||
public void ClearPropBoneBindings()
|
||||
{
|
||||
_propBoneBindings.Clear();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates new Game Objects where nessessary to create the prop bones.
|
||||
/// </summary>
|
||||
public void CreatePropBones()
|
||||
{
|
||||
if (propBoneConfig == null)
|
||||
{
|
||||
Debug.LogError($"Prop bone config is null.", transform);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < propBoneConfig.propBoneDefinitions.Length; ++i)
|
||||
{
|
||||
CreatePropBones(gameObject, propBoneConfig.propBoneDefinitions[i]);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates new Game Objects where nessessary to create the prop bones.
|
||||
/// </summary>
|
||||
/// <param name="editScope">The root game object to create the prop bone under.</param>
|
||||
/// <param name="boneDefinition">The definition of the prop bone to be created.</param>
|
||||
/// <param name="boneInstance">The prop bone binding to update.</param>
|
||||
private void CreatePropBones(GameObject editScope, PropBoneDefinition boneDefinition)
|
||||
{
|
||||
Transform parent = TransformUtil.SearchHierarchy(editScope.transform, boneDefinition.parentBoneName);
|
||||
if (parent == null)
|
||||
{
|
||||
Debug.LogError($"Cannot find parent prop bone {boneDefinition.parentBoneName} in hierarchy.", transform);
|
||||
return;
|
||||
}
|
||||
|
||||
Transform bone = TransformUtil.SearchHierarchy(editScope.transform, boneDefinition.boneName);
|
||||
if (bone == null)
|
||||
{
|
||||
bone = CreatePropBone(boneDefinition.boneName, parent);
|
||||
if (bone == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (bone.parent.name != boneDefinition.parentBoneName)
|
||||
{
|
||||
Debug.LogError($"bone.parent {bone.parent.name} does not match {boneDefinition.parentBoneName} in hierarchy.", bone);
|
||||
return;
|
||||
}
|
||||
|
||||
Transform socket = TransformUtil.SearchHierarchy(editScope.transform, boneDefinition.socketName);
|
||||
if (socket == null)
|
||||
{
|
||||
socket = CreatePropBone(boneDefinition.socketName, bone);
|
||||
}
|
||||
else if (socket.parent != bone)
|
||||
{
|
||||
Debug.LogError($"socket {socket.name} is not parented to bone {bone.name}.", socket);
|
||||
return;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
EditorGUIUtility.PingObject(socket);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new Game Object called 'name' and parents it to 'parent'.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the new bone to be created.</param>
|
||||
/// <param name="parent">The transform to parent the new bone to.</param>
|
||||
private Transform CreatePropBone(string name, Transform parent)
|
||||
{
|
||||
// does not check if there is already a bone called this. we should check before calling create
|
||||
Transform boneInstance = new GameObject(name).transform;
|
||||
boneInstance.SetParent(parent);
|
||||
if (boneInstance.parent != parent)
|
||||
{
|
||||
Debug.LogError($"Something went wrong when creating prop bone {boneInstance.name}. You may need to enter prefab edit mode to set up this character.", gameObject);
|
||||
DestroyImmediate(boneInstance.gameObject);
|
||||
return null;
|
||||
}
|
||||
boneInstance.localPosition = Vector3.zero;
|
||||
boneInstance.localRotation = Quaternion.identity;
|
||||
boneInstance.localScale = Vector3.one;
|
||||
// mark that these have been created by the tool
|
||||
boneInstance.gameObject.AddComponent<PropBone>().WasSpawnedBySyntyTool = true;
|
||||
|
||||
Debug.Log($"Successfully created prop bone {boneInstance.name}.", boneInstance);
|
||||
|
||||
return boneInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all the prop bones that have been created by the PropBoneBinder and clears all bindings.
|
||||
/// </summary>
|
||||
public void DestroyPropBones()
|
||||
{
|
||||
DestroyPropBones(gameObject);
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(gameObject);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all the prop bones that have been created by the PropBoneBinder and clears all bindings
|
||||
/// </summary>
|
||||
/// <param name="editScope">The root game objects to destroy prop bones from.</param>
|
||||
private void DestroyPropBones(GameObject editScope)
|
||||
{
|
||||
_propBoneBindings.Clear();
|
||||
PropBone[] bones = editScope.GetComponentsInChildren<PropBone>();
|
||||
for (int i = 0; i < bones.Length; ++i)
|
||||
{
|
||||
if (bones[i].WasSpawnedBySyntyTool)
|
||||
{
|
||||
// Let's not destroy peoples swords and stuff.
|
||||
for (int c = 0; c < bones[i].transform.childCount; ++c)
|
||||
{
|
||||
Transform child = bones[i].transform.GetChild(c);
|
||||
child.SetParent(bones[i].transform.parent);
|
||||
bool hasPropBone = child.GetComponent<PropBone>() != null;
|
||||
if (!hasPropBone)
|
||||
{
|
||||
Debug.Log($"Successfully reparented object {child.name}.", child);
|
||||
}
|
||||
}
|
||||
|
||||
string boneName = bones[i].gameObject.name;
|
||||
Transform parent = bones[i].transform.parent;
|
||||
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
{
|
||||
DestroyImmediate(bones[i].gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(bones[i].gameObject);
|
||||
}
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
Debug.Log($"Successfully destroyed bone {boneName} under {parent.name}.", parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Successfully destroyed bone {boneName}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject gameObject = bones[i].gameObject;
|
||||
// Remove the component but don't destroy the object.
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
{
|
||||
DestroyImmediate(bones[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(bones[i]);
|
||||
}
|
||||
|
||||
if (gameObject != null)
|
||||
{
|
||||
Debug.Log($"Successfully removed SyntyPropBone component from {gameObject.name}.", gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
Assets/External/Tools/SyntyPropBoneTool/Runtime/PropBoneBinder.cs.meta
vendored
Normal file → Executable file
0
Assets/External/Tools/SyntyPropBoneTool/Runtime/PropBoneBinder.cs.meta
vendored
Normal file → Executable file
Reference in New Issue
Block a user