Skip to content

Instantly share code, notes, and snippets.

@kxn4t
Last active September 23, 2024 11:51
Show Gist options
  • Save kxn4t/bd7acc37c17215e933f668370e89dbea to your computer and use it in GitHub Desktop.
Save kxn4t/bd7acc37c17215e933f668370e89dbea to your computer and use it in GitHub Desktop.
複数のアニメーションファイル間で足りないBlendShape操作を補完し、表情が破綻しないように修正できるEditor拡張
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.IO;
namespace kxn4t.gist
{
internal class MissingBlendShapeInserterConfirmationWindow : EditorWindow
{
private static List<AnimationClip> animationClips = new List<AnimationClip>();
private static string skinnedMeshRendererPath = "Body";
private static bool overwriteExistingFiles = true;
private static string newFilePath = "";
private static bool useReferenceGameObject = false;
private static GameObject referenceGameObject;
// スクロール位置を管理
private Vector2 scrollPos;
// 各アニメーションクリップに対する欠落しているBlendShapeのリスト
private Dictionary<AnimationClip, List<string>> missingBlendShapesPerClip = new Dictionary<AnimationClip, List<string>>();
// 各アニメーションクリップに対する適用フラグ
private Dictionary<AnimationClip, bool> clipApplyFlags = new Dictionary<AnimationClip, bool>();
// BlendShape名とその値の辞書
private Dictionary<string, float> blendShapeValues = new Dictionary<string, float>();
// 変更点が計算済みかどうかのフラグ
private bool changesCalculated = false;
public static void SetData(
List<AnimationClip> clips,
string rendererPath,
bool overwriteFiles,
string filePath,
bool useRefGameObject,
GameObject refGameObject)
{
animationClips = new List<AnimationClip>(clips);
skinnedMeshRendererPath = rendererPath;
overwriteExistingFiles = overwriteFiles;
newFilePath = filePath;
useReferenceGameObject = useRefGameObject;
referenceGameObject = refGameObject;
}
public static void ShowWindow()
{
var window = GetWindow<MissingBlendShapeInserterConfirmationWindow>("変更内容の確認");
window.minSize = new Vector2(600, 400);
window.CalculateMissingBlendShapes(); // 変更点の計算を開始
}
private void OnGUI()
{
if (!changesCalculated)
{
// 変更点がまだ計算中の場合
EditorGUILayout.LabelField("変更点を計算中...", EditorStyles.boldLabel);
return;
}
if (missingBlendShapesPerClip.Count == 0)
{
// 変更が必要なBlendShapeがない場合
EditorGUILayout.LabelField("すべてのアニメーションクリップに必要なBlendShapeが含まれています。", EditorStyles.boldLabel);
return;
}
DrawConfirmationHeader(); // ヘッダー部分の描画
DrawMissingBlendShapesList(); // 欠落しているBlendShapeのリストを描画
DrawApplyButton(); // 適用ボタンの描画
}
// 確認ヘッダーの描画
private void DrawConfirmationHeader()
{
EditorGUILayout.LabelField("変更内容の確認", EditorStyles.boldLabel);
// オプションに応じたメッセージを表示
string message = useReferenceGameObject
? "以下のアニメーションファイルにBlendShape操作を指定したSkinnedMeshの値で追加します。"
: "以下のアニメーションファイルにBlendShape操作を0で追加します。";
EditorGUILayout.LabelField(message);
}
// 欠落しているBlendShapeのリストを描画
private void DrawMissingBlendShapesList()
{
// スクロールビューの開始
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
foreach (var kvp in missingBlendShapesPerClip)
{
var clip = kvp.Key;
var missingBlendShapes = kvp.Value;
missingBlendShapes.Sort(); // BlendShape名を昇順にソート
EditorGUILayout.BeginVertical("box");
// チェックボックス付きのアニメーションクリップ名を表示
clipApplyFlags[clip] = EditorGUILayout.ToggleLeft($"アニメーション: {clip.name}", clipApplyFlags[clip], EditorStyles.boldLabel);
if (clipApplyFlags[clip])
DrawBlendShapeDetails(missingBlendShapes); // 詳細を描画
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
EditorGUILayout.EndScrollView(); // スクロールビューの終了
}
// BlendShapeの詳細を描画
private void DrawBlendShapeDetails(List<string> blendShapes)
{
foreach (var blendShape in blendShapes)
{
// BlendShapeの値を取得
float value = blendShapeValues.ContainsKey(blendShape) ? blendShapeValues[blendShape] : 0f;
// BlendShape名と値を表示
EditorGUILayout.LabelField($" - {blendShape} : {value}");
}
}
// 適用ボタンの描画
private void DrawApplyButton()
{
if (GUILayout.Button("変更を適用"))
ApplyChanges();
}
// 変更点の計算
private void CalculateMissingBlendShapes()
{
// 初期化
missingBlendShapesPerClip.Clear();
clipApplyFlags.Clear();
blendShapeValues.Clear();
changesCalculated = false;
// すべてのBlendShape名を取得
HashSet<string> allBlendShapeNames = GetAllBlendShapeNames();
// 各アニメーションクリップについて欠落しているBlendShapeを調べる
foreach (var clip in animationClips)
{
if (clip == null)
continue;
// アニメーションクリップであることを確認
if (!(clip is AnimationClip))
{
ShowInvalidClipError(clip);
continue;
}
// 欠落しているBlendShapeを取得
List<string> missingBlendShapes = GetMissingBlendShapes(clip, allBlendShapeNames);
if (missingBlendShapes.Count > 0)
{
// 欠落しているBlendShapeがある場合はリストに追加
missingBlendShapesPerClip[clip] = missingBlendShapes;
clipApplyFlags[clip] = true; // デフォルトで適用するように設定
}
}
// 参照GameObjectからBlendShapeの値を取得
if (useReferenceGameObject)
{
if (!RetrieveBlendShapeValues(allBlendShapeNames))
return; // 取得に失敗した場合は処理を中断
}
changesCalculated = true; // 変更点の計算が完了
}
// 無効なクリップのエラー表示
private void ShowInvalidClipError(AnimationClip clip)
{
EditorUtility.DisplayDialog("エラー", $"指定されたオブジェクトはAnimationClipではありません。オブジェクト名: {clip.name}", "OK");
}
// BlendShapeの値を取得
private bool RetrieveBlendShapeValues(HashSet<string> blendShapeNames)
{
// 参照GameObjectからSkinnedMeshRendererを取得
SkinnedMeshRenderer smr = GetSkinnedMeshRendererFromGameObject(referenceGameObject, skinnedMeshRendererPath);
if (smr == null)
{
ShowSMRNotFoundError();
Close(); // ウィンドウを閉じる
return false;
}
if (smr.sharedMesh == null)
{
ShowSharedMeshNullError();
Close(); // ウィンドウを閉じる
return false;
}
// 各BlendShapeの値を取得
foreach (var blendShapeName in blendShapeNames)
{
string name = blendShapeName.Substring("blendShape.".Length);
int index = smr.sharedMesh.GetBlendShapeIndex(name);
float value = index != -1 ? smr.GetBlendShapeWeight(index) : 0f;
blendShapeValues[blendShapeName] = value;
}
return true;
}
// SkinnedMeshRendererが見つからないエラー表示
private void ShowSMRNotFoundError()
{
EditorUtility.DisplayDialog("エラー", $"指定されたGameObjectにSkinnedMeshRendererが見つかりません。パス: {skinnedMeshRendererPath}", "OK");
}
// sharedMeshがnullのエラー表示
private void ShowSharedMeshNullError()
{
EditorUtility.DisplayDialog("エラー", $"指定されたSkinnedMeshRendererのsharedMeshがありません。パス: {skinnedMeshRendererPath}", "OK");
}
// GameObjectからSkinnedMeshRendererを取得
private SkinnedMeshRenderer GetSkinnedMeshRendererFromGameObject(GameObject go, string path)
{
// 指定したパスで子オブジェクトを検索
Transform child = go.transform.Find(path);
if (child != null)
return child.GetComponent<SkinnedMeshRenderer>();
// 自身の名前がパスと一致する場合
if (go.name == path)
return go.GetComponent<SkinnedMeshRenderer>();
return null; // 見つからなかった場合
}
// すべてのBlendShape名を取得
private HashSet<string> GetAllBlendShapeNames()
{
var blendShapeNames = new HashSet<string>();
// 各アニメーションクリップからBlendShape名を収集
foreach (var clip in animationClips)
{
if (clip == null)
continue;
var bindings = AnimationUtility.GetCurveBindings(clip);
foreach (var binding in bindings)
{
if (IsTargetBlendShape(binding))
blendShapeNames.Add(binding.propertyName);
}
}
return blendShapeNames;
}
// クリップから欠落しているBlendShapeを取得
private List<string> GetMissingBlendShapes(AnimationClip clip, HashSet<string> allBlendShapeNames)
{
var clipBlendShapes = new HashSet<string>();
var bindings = AnimationUtility.GetCurveBindings(clip);
// クリップが持っているBlendShape名を収集
foreach (var binding in bindings)
{
if (IsTargetBlendShape(binding))
clipBlendShapes.Add(binding.propertyName);
}
var missingBlendShapes = new List<string>();
// すべてのBlendShape名と比較して欠落しているものを抽出
foreach (var blendShape in allBlendShapeNames)
{
if (!clipBlendShapes.Contains(blendShape))
missingBlendShapes.Add(blendShape);
}
return missingBlendShapes;
}
// 対象のBlendShapeかどうかを判定
private bool IsTargetBlendShape(EditorCurveBinding binding)
{
return binding.type == typeof(SkinnedMeshRenderer) &&
binding.path.Equals(skinnedMeshRendererPath) &&
binding.propertyName.StartsWith("blendShape.") &&
!binding.isPPtrCurve;
}
// 変更の適用
private void ApplyChanges()
{
bool anyApplied = false; // 変更が適用されたかどうかのフラグ
foreach (var kvp in missingBlendShapesPerClip)
{
var clip = kvp.Key;
if (!clipApplyFlags[clip])
continue; // 適用しない場合はスキップ
// ターゲットクリップを準備
AnimationClip targetClip = PrepareTargetClip(clip);
if (targetClip == null)
continue; // 準備に失敗した場合はスキップ
// BlendShapeをクリップに追加
AddBlendShapesToClip(targetClip, kvp.Value);
anyApplied = true; // 変更が適用された
}
if (anyApplied)
{
// アセットデータベースを更新
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.DisplayDialog("完了", "変更が適用されました。", "OK");
}
else
{
EditorUtility.DisplayDialog("情報", "適用するアニメーションクリップが選択されていません。", "OK");
}
Close(); // ウィンドウを閉じる
}
// ターゲットクリップの準備
private AnimationClip PrepareTargetClip(AnimationClip clip)
{
if (overwriteExistingFiles)
{
// 上書き保存の場合
if (IsClipReadOnly(clip))
{
ShowReadOnlyError(clip);
return null;
}
return clip; // 既存のクリップを使用
}
else
{
// 新しいファイルに保存する場合
return CreateNewClip(clip);
}
}
// クリップが読み取り専用かどうかを確認
private bool IsClipReadOnly(AnimationClip clip)
{
string assetPath = AssetDatabase.GetAssetPath(clip);
string fullPath = Path.GetFullPath(assetPath);
return (File.GetAttributes(fullPath) & FileAttributes.ReadOnly) != 0;
}
// 読み取り専用エラーの表示
private void ShowReadOnlyError(AnimationClip clip)
{
string assetPath = AssetDatabase.GetAssetPath(clip);
EditorUtility.DisplayDialog("エラー", $"ファイルが読み取り専用のため、上書きできません。ファイルパス: {assetPath}", "OK");
}
// 新しいクリップの作成
private AnimationClip CreateNewClip(AnimationClip originalClip)
{
// クリップをコピー
AnimationClip newClip = Instantiate(originalClip);
string assetPath = GetNewAssetPath(originalClip);
if (string.IsNullOrEmpty(assetPath))
return null;
// 新しいアセットとして保存
AssetDatabase.CreateAsset(newClip, assetPath);
// 保存が成功したか確認
if (AssetDatabase.LoadAssetAtPath<AnimationClip>(assetPath) == null)
{
EditorUtility.DisplayDialog("エラー", $"アセットの作成に失敗しました。パス: {assetPath}", "OK");
return null;
}
return newClip;
}
// 新しいアセットパスを取得
private string GetNewAssetPath(AnimationClip originalClip)
{
string originalPath = AssetDatabase.GetAssetPath(originalClip);
string fileName = Path.GetFileNameWithoutExtension(originalPath);
string newFileName = fileName + "_modified.anim";
string directory = string.IsNullOrEmpty(newFilePath) ? Path.GetDirectoryName(originalPath) : newFilePath;
// 保存先のディレクトリが存在するか確認
if (!Directory.Exists(directory))
{
EditorUtility.DisplayDialog("エラー", $"保存先フォルダが存在しません。パス: {directory}", "OK");
return null;
}
// ユニークなアセットパスを生成
return AssetDatabase.GenerateUniqueAssetPath(Path.Combine(directory, newFileName));
}
// BlendShapeをクリップに追加
private void AddBlendShapesToClip(AnimationClip clip, List<string> blendShapes)
{
foreach (var blendShape in blendShapes)
{
// BlendShapeの値を取得
float value = blendShapeValues.ContainsKey(blendShape) ? blendShapeValues[blendShape] : 0f;
// カーブバインディングを設定
var binding = new EditorCurveBinding
{
type = typeof(SkinnedMeshRenderer),
path = skinnedMeshRendererPath,
propertyName = blendShape
};
// アニメーションカーブを作成
var curve = new AnimationCurve(new Keyframe(0f, value));
try
{
// カーブをクリップに設定
AnimationUtility.SetEditorCurve(clip, binding, curve);
}
catch (System.Exception ex)
{
EditorUtility.DisplayDialog("エラー", $"カーブの設定に失敗しました。BlendShape: {blendShape}, エラー: {ex.Message}", "OK");
}
}
}
}
}
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
namespace kxn4t.gist
{
internal class MissingBlendShapeInserterWindow : EditorWindow
{
// ユーザーが指定するアニメーションクリップのリスト
public List<AnimationClip> animationClips = new List<AnimationClip>();
// スクロール位置を管理
private Vector2 scrollPos;
private Vector2 mainScrollPos;
// 保存オプション関連
public bool overwriteExistingFiles = true;
public string newFilePath = "";
// SkinnedMeshRendererのパス(名前)
public string skinnedMeshRendererPath = "Body";
// BlendShapeの値を取得するためのオプションと参照GameObject
public bool useReferenceGameObject = false;
public GameObject referenceGameObject;
[MenuItem("Tools/kxn4t gists/Missing BlendShape Inserter")]
public static void ShowWindow()
{
var window = GetWindow<MissingBlendShapeInserterWindow>("Missing BlendShape Inserter");
window.minSize = new Vector2(600, 400);
}
private void OnGUI()
{
DrawHeader(); // ヘッダー部分の描画
// ウィンドウをスクロール可能にする
mainScrollPos = EditorGUILayout.BeginScrollView(mainScrollPos);
DrawDragAndDropArea(); // ドラッグ&ドロップエリアの描画
DrawAnimationClipList(); // アニメーションクリップリストの描画
DrawSkinnedMeshRendererPathField(); // SkinnedMeshRendererパスフィールドの描画
DrawReferenceGameObjectOption(); // 参照GameObjectオプションの描画
DrawSaveOptions(); // 保存オプションの描画
EditorGUILayout.EndScrollView(); // スクロールビューの終了
DrawExecuteButton(); // 実行ボタンの描画
}
// ヘッダー部分の描画
private void DrawHeader()
{
EditorGUILayout.LabelField("複数のアニメーションファイル間で足りないBlendShape操作を補完し、表情が破綻しないように修正できます。", EditorStyles.wordWrappedLabel);
}
// ドラッグ&ドロップエリアの描画
private void DrawDragAndDropArea()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("アニメーションファイルをドラッグ&ドロップで指定", EditorStyles.boldLabel);
// ドラッグ&ドロップエリアを作成
Rect dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
GUI.Box(dropArea, "ここにアニメーションファイルをドラッグ&ドロップ");
// ドラッグ&ドロップの処理を行う
HandleDragAndDrop(dropArea);
}
// ドラッグ&ドロップの処理
private void HandleDragAndDrop(Rect dropArea)
{
Event evt = Event.current;
// マウスがドロップエリア内にあるか確認
if (!dropArea.Contains(evt.mousePosition))
return;
// イベントタイプによって処理を分岐
switch (evt.type)
{
case EventType.DragUpdated:
case EventType.DragPerform:
// ドラッグ&ドロップの見た目を変更
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (evt.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
AddAnimationClips(DragAndDrop.objectReferences);
evt.Use(); // イベントを使用済みにする
}
break;
}
}
// アニメーションクリップの追加
private void AddAnimationClips(Object[] objects)
{
foreach (var obj in objects)
{
if (obj is AnimationClip clip)
{
if (!animationClips.Contains(clip))
animationClips.Add(clip);
// Noop
// else
// ShowDuplicateClipWarning(clip);
}
}
}
private void ShowDuplicateClipWarning(AnimationClip clip)
{
EditorUtility.DisplayDialog("注意", $"アニメーションクリップ '{clip.name}' は既にリストに追加されています。", "OK");
}
// アニメーションクリップリストの描画
private void DrawAnimationClipList()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("アニメーションリスト", EditorStyles.boldLabel);
// リストの高さを計算
int itemCount = animationClips.Count;
float itemHeight = EditorGUIUtility.singleLineHeight + 4f;
float maxListHeight = 200f;
float listHeight = Mathf.Min(itemCount * itemHeight, maxListHeight);
// スクロールビューの開始
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(listHeight + 10f));
// 各アニメーションクリップを描画
for (int i = 0; i < animationClips.Count; i++)
{
DrawAnimationClipItem(i);
}
EditorGUILayout.EndScrollView(); // スクロールビューの終了
}
// 個々のアニメーションクリップアイテムの描画
private void DrawAnimationClipItem(int index)
{
EditorGUILayout.BeginHorizontal(); // 水平レイアウトの開始
// アニメーションクリップのフィールドを描画
AnimationClip oldClip = animationClips[index];
AnimationClip newClip = (AnimationClip)EditorGUILayout.ObjectField(animationClips[index], typeof(AnimationClip), false);
// クリップが変更された場合の処理
if (newClip != oldClip)
UpdateAnimationClipAt(index, newClip);
// 削除ボタン
if (GUILayout.Button("-", GUILayout.Width(30)))
{
animationClips.RemoveAt(index);
return;
}
EditorGUILayout.EndHorizontal(); // 水平レイアウトの終了
// 直前に描画した領域を取得し、ドラッグ&ドロップ処理を設定
Rect lastRect = GUILayoutUtility.GetLastRect();
HandleDragAndDropOnItem(lastRect, index);
}
// アニメーションクリップの更新と重複チェック
private void UpdateAnimationClipAt(int index, AnimationClip newClip)
{
if (newClip != null && animationClips.Contains(newClip))
{
// 重複している場合は注意を表示
ShowDuplicateClipWarning(newClip);
}
else
{
// 重複していない場合はリストを更新
animationClips[index] = newClip;
}
}
// 個々のアイテムへのドラッグ&ドロップ処理
private void HandleDragAndDropOnItem(Rect dropArea, int index)
{
Event evt = Event.current;
// マウスがアイテム領域内にあるか確認
if (!dropArea.Contains(evt.mousePosition))
return;
// イベントタイプによって処理を分岐
switch (evt.type)
{
case EventType.DragUpdated:
case EventType.DragPerform:
// ドラッグ&ドロップの見た目を変更
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (evt.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
ReplaceAnimationClipAt(index, DragAndDrop.objectReferences);
evt.Use(); // イベントを使用済みにする
}
break;
}
}
// アニメーションクリップの置き換え
private void ReplaceAnimationClipAt(int index, Object[] objects)
{
foreach (var obj in objects)
{
if (obj is AnimationClip clip)
{
if (!animationClips.Contains(clip))
animationClips[index] = clip; // 新しいクリップで置き換え
else if (animationClips[index] != clip)
ShowDuplicateClipWarning(clip); // 重複注意
}
}
}
// SkinnedMeshRendererのパスフィールドの描画
private void DrawSkinnedMeshRendererPathField()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("SkinnedMeshRendererのパス(名前)を指定", EditorStyles.boldLabel);
// パスを入力するテキストフィールドを描画
skinnedMeshRendererPath = EditorGUILayout.TextField("パス(名前):", skinnedMeshRendererPath);
}
// BlendShapeの値取得オプションの描画
private void DrawReferenceGameObjectOption()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("BlendShapeの値の設定", EditorStyles.boldLabel);
// オプションのトグルを描画
useReferenceGameObject = EditorGUILayout.ToggleLeft("指定したGameObjectのBlendShapeの値を使用する", useReferenceGameObject);
// オプションが有効な場合はGameObjectのフィールドを表示
if (useReferenceGameObject)
referenceGameObject = (GameObject)EditorGUILayout.ObjectField("GameObject:", referenceGameObject, typeof(GameObject), true);
}
// 保存オプションの描画
private void DrawSaveOptions()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("保存オプション", EditorStyles.boldLabel);
// 上書き保存のオプションを描画
overwriteExistingFiles = EditorGUILayout.ToggleLeft("元のファイルを上書きする", overwriteExistingFiles);
// 上書きしない場合は新しい保存先を指定するフィールドを表示
if (!overwriteExistingFiles)
DrawNewFilePathField();
}
// 新しいファイルパスフィールドの描画
private void DrawNewFilePathField()
{
EditorGUILayout.BeginHorizontal();
// ラベルとテキストフィールドを水平に配置
EditorGUILayout.LabelField("新しいファイルの保存先:", GUILayout.Width(150));
newFilePath = EditorGUILayout.TextField(newFilePath);
// フォルダ選択ボタン
if (GUILayout.Button("選択", GUILayout.Width(50)))
SelectNewFilePath();
EditorGUILayout.EndHorizontal();
}
// 新しいファイルパスの選択ダイアログを表示
private void SelectNewFilePath()
{
string selectedPath = EditorUtility.OpenFolderPanel("保存先を選択", "", "");
if (string.IsNullOrEmpty(selectedPath))
return;
// 選択されたパスがプロジェクト内か確認
if (selectedPath.StartsWith(Application.dataPath))
newFilePath = "Assets" + selectedPath.Substring(Application.dataPath.Length);
else
EditorUtility.DisplayDialog("エラー", "保存先はプロジェクトのAssetsフォルダ内を指定してください。", "OK");
}
// 実行ボタンの描画
private void DrawExecuteButton()
{
EditorGUILayout.Space();
// アニメーションクリップが指定されている場合のみボタンを有効化
GUI.enabled = animationClips.Count > 0 && animationClips.Exists(clip => clip != null);
// 実行ボタン
if (GUILayout.Button("実行"))
CalculateMissingBlendShapes();
GUI.enabled = true; // GUIの状態を元に戻す
}
// 変更点の計算と確認ウィンドウの表示
private void CalculateMissingBlendShapes()
{
// 入力の検証
if (!ValidateInputs())
return;
// データを確認ウィンドウに渡す
MissingBlendShapeInserterConfirmationWindow.SetData(
animationClips,
skinnedMeshRendererPath,
overwriteExistingFiles,
newFilePath,
useReferenceGameObject,
referenceGameObject);
// 確認ウィンドウを表示
MissingBlendShapeInserterConfirmationWindow.ShowWindow();
}
// 入力の検証
private bool ValidateInputs()
{
// SkinnedMeshRendererのパスが指定されているか確認
if (string.IsNullOrEmpty(skinnedMeshRendererPath))
{
EditorUtility.DisplayDialog("エラー", "SkinnedMeshRendererのパス(名前)を指定してください。", "OK");
return false;
}
// 参照GameObjectが必要な場合の確認
if (useReferenceGameObject && referenceGameObject == null)
{
EditorUtility.DisplayDialog("エラー", "BlendShapeの値を取得するGameObjectを指定してください。", "OK");
return false;
}
// 新しいファイルパスが必要な場合の確認
if (!overwriteExistingFiles && string.IsNullOrEmpty(newFilePath))
{
EditorUtility.DisplayDialog("エラー", "新しいファイルの保存先を指定してください。", "OK");
return false;
}
return true;
}
}
}
@kxn4t
Copy link
Author

kxn4t commented Sep 23, 2024

面倒なのでCC0で

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment