GitBucket
Pull requests
Issues
Snippets
Sign in
syuji
/
PhysCopy.cs
Fork
0
Created at Sat Apr 08 19:22:03 JST 2023
Download ZIP
HTTP
Embed
Embed this snippet in your website.
HTTP
Clone with Git using the repository's web address.
Code
Revision
Forks
Syuji Igura
revised this
on 10 Apr 2023
388b172
PhysCopy.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using VRC.Dynamics; public class PhysCopy : EditorWindow { GameObject targetAvatar; GameObject baseAvatar; bool delFlg; bool delDFlg; bool backUpFlg; bool checkFlg; string alertMessage; Vector2 textScrollPos = Vector2.zero; [MenuItem("USUAJI_tool/PhysCopy")] static void Init() { PhysCopy window = (PhysCopy)EditorWindow.GetWindow(typeof(PhysCopy)); window.titleContent = new GUIContent("PhysCopy"); window.Show(); } void OnGUI() { using (var checkScope = new EditorGUI.ChangeCheckScope()) { baseAvatar = EditorGUILayout.ObjectField("コピー元アバター", baseAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.LabelField(" ↓ copy"); targetAvatar = EditorGUILayout.ObjectField("コピー先アバター", targetAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.Space(); delFlg = EditorGUILayout.ToggleLeft("コピー先PhysBone/Colliderを削除", delFlg); delDFlg = EditorGUILayout.ToggleLeft("コピー先DynamicBone/Colliderを削除", delDFlg); checkFlg = EditorGUILayout.ToggleLeft("AvatarDescripterの有無を気にしない", checkFlg); backUpFlg = EditorGUILayout.ToggleLeft("怖いので一応バックアップ", backUpFlg); EditorGUILayout.Space(); if (GUILayout.Button("コピー実行(ポチっとな)")) { CopyOperate(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("🔻エラーがあると、ここに出力(見づらかったら、メモ帳等にコピペしてね)"); textScrollPos = EditorGUILayout.BeginScrollView(textScrollPos, GUILayout.Width(350), GUILayout.Height(100)); { alertMessage = EditorGUILayout.TextArea(alertMessage, GUILayout.Width(350)); } } } string CheckAvatar(GameObject avatar, string name) { string alertText = ""; if (baseAvatar is null) { alertText += name + "がセットされておりません\n"; } else { if (!checkFlg && baseAvatar.GetComponent("VRCAvatarDescriptor") is null) { alertText += name + "にはVRCAvatarDescriptorが含まれておりません\n"; } } return alertText; } bool Check() { string alertText = ""; alertText += CheckAvatar(baseAvatar, "コピー元アバター"); alertText += CheckAvatar(targetAvatar, "コピー先アバター"); if (alertText.Length != 0) { EditorUtility.DisplayDialog("alert", alertText, "Oh no"); return false; } return true; } void CopyOperate() { if (!Check()) { return; } if (!EditorUtility.DisplayDialog("確認", "コピー元アバター: " + baseAvatar.name + "\n" + "コピー先アバター: " + targetAvatar.name + "\n" + "コピー先PhysBone/Collider: " + (delFlg ? "削除" : "残す") + "\n" + "コピー先DynamicBone/Collider: " + (delDFlg ? "削除" : "残す") + "\n" + "バックアップ: " + (backUpFlg ? "する" : "しない") + "\n" + "\nこちらの設定で実行してよろしいですか?", "いいぞ!", "ちょっと待て")) { return; } if (backUpFlg) { Instantiate(targetAvatar); } if (delFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { DestroyImmediate(c); } foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { DestroyImmediate(c); } } if (delDFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren<Component>()) { if (c.GetType().Name.Equals("DynamicBone") || c.GetType().Name.Equals("DynamicBoneCollider")) { DestroyImmediate(c); } } } string alertText = ""; alertText += CopyPBC(); if (alertText.Length != 0) { alertText = "以下のコライダーはコピー先オブジェクトが見つからず、コピーに失敗しました\n" + alertText + "\n"; } string alertText2 = ""; alertText2 += CopyPB(); if (alertText2.Length != 0) { alertText2 = "以下のボーンはコピーに失敗したか、参照コライダーの変更に失敗しました\n" + alertText2 + "\n"; } if (alertText.Length != 0 || alertText2.Length != 0) { EditorUtility.DisplayDialog("エラー箇所あり", "コピー中、エラーが発生した箇所がありました。詳しくは、エラーメッセージをご確認ください。", "ok"); alertMessage = alertText + alertText2; } else { EditorUtility.DisplayDialog("終了", "コピー完了しました。", "ok"); } } string GetPath(GameObject now) { string path = now.name; var current = now.transform.parent; while (current != null) { if (current.parent is null) { break; } path = current.name + "/" + path; current = current.parent; } return path; } string CopyPBC() { string alertText = ""; List<string> createdPaths = new List<string>(); // Create missing GameObjects before copying components foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf is null) { CreateEmptyGameObject(targetAvatar.transform, path, c.transform); createdPaths.Add(path); } } // Now copy the components foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else if (!createdPaths.Contains(path)) { alertText += " " + path + "\n"; } } // Update rootTransform references foreach (VRCPhysBoneColliderBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } } return alertText; } string CopyPB() { string alertText = ""; List<string> createdPaths = new List<string>(); // Create missing GameObjects before copying components foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf is null) { CreateEmptyGameObject(targetAvatar.transform, path, c.transform); createdPaths.Add(path); } } // Now copy the components foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else if (!createdPaths.Contains(path)) { alertText += " " + path + "\n"; } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } List<VRCPhysBoneColliderBase> cols = c.colliders; for (int i = 0; i < cols.Count; i++) { VRCPhysBoneColliderBase col = cols[i]; if (col != null) { string path = GetPath(col.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf != null) { Component[] tcols = tf.gameObject.GetComponents(typeof(VRCPhysBoneColliderBase)); foreach (VRCPhysBoneColliderBase tcol in tcols) { if (CeckColliderEqual(col, tcol)) { cols[i] = tcol; } } } else { alertText += " PhysBoneのCollider先を自アバに変更できませんでした: " + path + "\n"; } } } } return alertText; } //Objectに複数のコンポーネントがついてる時、一致を確認する bool CeckColliderEqual(VRCPhysBoneColliderBase col, VRCPhysBoneColliderBase tcol) { if (col.shapeType != tcol.shapeType) { return false; } if (col.radius != tcol.radius) { return false; } if (col.position.x != tcol.position.x || col.position.y != tcol.position.y || col.position.z != tcol.position.z) { return false; } if (col.rotation.x != tcol.rotation.x || col.rotation.y != tcol.rotation.y || col.rotation.z != tcol.rotation.z) { return false; } return true; } // Create empty GameObject function void CreateEmptyGameObject(Transform parent, string path, Transform referenceTransform) { string[] parts = path.Split('/'); Transform currentParent = parent; for (int i = 0; i < parts.Length; i++) { string part = parts[i]; Transform child = currentParent.Find(part); if (child == null) { GameObject newGameObject = new GameObject(part); newGameObject.transform.SetParent(currentParent); // Set the transform values to match the reference transform if (referenceTransform != null) { newGameObject.transform.localPosition = referenceTransform.localPosition; newGameObject.transform.localRotation = referenceTransform.localRotation; newGameObject.transform.localScale = referenceTransform.localScale; } currentParent = newGameObject.transform; } else { currentParent = child; } } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using VRC.Dynamics; public class PhysCopy : EditorWindow { GameObject targetAvatar; GameObject baseAvatar; bool delFlg; bool delDFlg; bool backUpFlg; bool checkFlg; string alertMessage; Vector2 textScrollPos = Vector2.zero; [MenuItem("USUAJI_tool/PhysCopy")] static void Init() { PhysCopy window = (PhysCopy)EditorWindow.GetWindow(typeof(PhysCopy)); window.titleContent = new GUIContent("PhysCopy"); window.Show(); } void OnGUI() { using (var checkScope = new EditorGUI.ChangeCheckScope()) { baseAvatar = EditorGUILayout.ObjectField("コピー元アバター", baseAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.LabelField(" ↓ copy"); targetAvatar = EditorGUILayout.ObjectField("コピー先アバター", targetAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.Space(); delFlg = EditorGUILayout.ToggleLeft("コピー先PhysBone/Colliderを削除", delFlg); delDFlg = EditorGUILayout.ToggleLeft("コピー先DynamicBone/Colliderを削除", delDFlg); checkFlg = EditorGUILayout.ToggleLeft("AvatarDescripterの有無を気にしない", checkFlg); backUpFlg = EditorGUILayout.ToggleLeft("怖いので一応バックアップ", backUpFlg); EditorGUILayout.Space(); if (GUILayout.Button("コピー実行(ポチっとな)")) { CopyOperate(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("🔻エラーがあると、ここに出力(見づらかったら、メモ帳等にコピペしてね)"); textScrollPos = EditorGUILayout.BeginScrollView(textScrollPos, GUILayout.Width(350), GUILayout.Height(100)); { alertMessage = EditorGUILayout.TextArea(alertMessage, GUILayout.Width(350)); } } } string CheckAvatar(GameObject avatar, string name) { string alertText = ""; if (baseAvatar is null) { alertText += name + "がセットされておりません\n"; } else { if (!checkFlg && baseAvatar.GetComponent("VRCAvatarDescriptor") is null) { alertText += name + "にはVRCAvatarDescriptorが含まれておりません\n"; } } return alertText; } bool Check() { string alertText = ""; alertText += CheckAvatar(baseAvatar, "コピー元アバター"); alertText += CheckAvatar(targetAvatar, "コピー先アバター"); if (alertText.Length != 0) { EditorUtility.DisplayDialog("alert", alertText, "Oh no"); return false; } return true; } void CopyOperate() { if (!Check()) { return; } if (!EditorUtility.DisplayDialog("確認", "コピー元アバター: " + baseAvatar.name + "\n" + "コピー先アバター: " + targetAvatar.name + "\n" + "コピー先PhysBone/Collider: " + (delFlg ? "削除" : "残す") + "\n" + "コピー先DynamicBone/Collider: " + (delDFlg ? "削除" : "残す") + "\n" + "バックアップ: " + (backUpFlg ? "する" : "しない") + "\n" + "\nこちらの設定で実行してよろしいですか?", "いいぞ!", "ちょっと待て")) { return; } if (backUpFlg) { Instantiate(targetAvatar); } if (delFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { DestroyImmediate(c); } foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { DestroyImmediate(c); } } if (delDFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren<Component>()) { if (c.GetType().Name.Equals("DynamicBone") || c.GetType().Name.Equals("DynamicBoneCollider")) { DestroyImmediate(c); } } } string alertText = ""; alertText += CopyPBC(); if (alertText.Length != 0) { alertText = "以下のコライダーはコピー先オブジェクトが見つからず、コピーに失敗しました\n" + alertText + "\n"; } string alertText2 = ""; alertText2 += CopyPB(); if (alertText2.Length != 0) { alertText2 = "以下のボーンはコピーに失敗したか、参照コライダーの変更に失敗しました\n" + alertText2 + "\n"; } if (alertText.Length != 0 || alertText2.Length != 0) { EditorUtility.DisplayDialog("エラー箇所あり", "コピー中、エラーが発生した箇所がありました。詳しくは、エラーメッセージをご確認ください。", "ok"); alertMessage = alertText + alertText2; } else { EditorUtility.DisplayDialog("終了", "コピー完了しました。", "ok"); } } string GetPath(GameObject now) { string path = now.name; var current = now.transform.parent; while (current != null) { if (current.parent is null) { break; } path = current.name + "/" + path; current = current.parent; } return path; } string CopyPBC() { string alertText = ""; //いったんコピー foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path, c.transform); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneColliderBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { Transform rtf = c.rootTransform; //rootTransformが未設定の場合は触らない if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } } return alertText; } string CopyPB() { string alertText = ""; foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path, c.transform); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } List<VRCPhysBoneColliderBase> cols = c.colliders; for (int i = 0; i < cols.Count; i++) { VRCPhysBoneColliderBase col = cols[i]; if (col != null) { string path = GetPath(col.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf != null) { Component[] tcols = tf.gameObject.GetComponents(typeof(VRCPhysBoneColliderBase)); foreach (VRCPhysBoneColliderBase tcol in tcols) { if (CeckColliderEqual(col, tcol)) { cols[i] = tcol; } } } else { alertText += " PhysBoneのCollider先を自アバに変更できませんでした: " + path + "\n"; } } } } return alertText; } //Objectに複数のコンポーネントがついてる時、一致を確認する bool CeckColliderEqual(VRCPhysBoneColliderBase col, VRCPhysBoneColliderBase tcol) { if (col.shapeType != tcol.shapeType) { return false; } if (col.radius != tcol.radius) { return false; } if (col.position.x != tcol.position.x || col.position.y != tcol.position.y || col.position.z != tcol.position.z) { return false; } if (col.rotation.x != tcol.rotation.x || col.rotation.y != tcol.rotation.y || col.rotation.z != tcol.rotation.z) { return false; } return true; } // Create empty GameObject function void CreateEmptyGameObject(Transform parent, string path, Transform referenceTransform) { string[] parts = path.Split('/'); Transform currentParent = parent; for (int i = 0; i < parts.Length; i++) { string part = parts[i]; Transform child = currentParent.Find(part); if (child == null) { GameObject newGameObject = new GameObject(part); newGameObject.transform.SetParent(currentParent); // Set the transform values to match the reference transform if (referenceTransform != null) { newGameObject.transform.localPosition = referenceTransform.localPosition; newGameObject.transform.localRotation = referenceTransform.localRotation; newGameObject.transform.localScale = referenceTransform.localScale; } currentParent = newGameObject.transform; } else { currentParent = child; } } } }
Syuji Igura
revised this
on 9 Apr 2023
06bb617
PhysCopy.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using VRC.Dynamics; public class PhysCopy : EditorWindow { GameObject targetAvatar; GameObject baseAvatar; bool delFlg; bool delDFlg; bool backUpFlg; bool checkFlg; string alertMessage; Vector2 textScrollPos = Vector2.zero; [MenuItem("USUAJI_tool/PhysCopy")] static void Init() { PhysCopy window = (PhysCopy)EditorWindow.GetWindow(typeof(PhysCopy)); window.titleContent = new GUIContent("PhysCopy"); window.Show(); } void OnGUI() { using (var checkScope = new EditorGUI.ChangeCheckScope()) { baseAvatar = EditorGUILayout.ObjectField("コピー元アバター", baseAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.LabelField(" ↓ copy"); targetAvatar = EditorGUILayout.ObjectField("コピー先アバター", targetAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.Space(); delFlg = EditorGUILayout.ToggleLeft("コピー先PhysBone/Colliderを削除", delFlg); delDFlg = EditorGUILayout.ToggleLeft("コピー先DynamicBone/Colliderを削除", delDFlg); checkFlg = EditorGUILayout.ToggleLeft("AvatarDescripterの有無を気にしない", checkFlg); backUpFlg = EditorGUILayout.ToggleLeft("怖いので一応バックアップ", backUpFlg); EditorGUILayout.Space(); if (GUILayout.Button("コピー実行(ポチっとな)")) { CopyOperate(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("🔻エラーがあると、ここに出力(見づらかったら、メモ帳等にコピペしてね)"); textScrollPos = EditorGUILayout.BeginScrollView(textScrollPos, GUILayout.Width(350), GUILayout.Height(100)); { alertMessage = EditorGUILayout.TextArea(alertMessage, GUILayout.Width(350)); } } } string CheckAvatar(GameObject avatar, string name) { string alertText = ""; if (baseAvatar is null) { alertText += name + "がセットされておりません\n"; } else { if (!checkFlg && baseAvatar.GetComponent("VRCAvatarDescriptor") is null) { alertText += name + "にはVRCAvatarDescriptorが含まれておりません\n"; } } return alertText; } bool Check() { string alertText = ""; alertText += CheckAvatar(baseAvatar, "コピー元アバター"); alertText += CheckAvatar(targetAvatar, "コピー先アバター"); if (alertText.Length != 0) { EditorUtility.DisplayDialog("alert", alertText, "Oh no"); return false; } return true; } void CopyOperate() { if (!Check()) { return; } if (!EditorUtility.DisplayDialog("確認", "コピー元アバター: " + baseAvatar.name + "\n" + "コピー先アバター: " + targetAvatar.name + "\n" + "コピー先PhysBone/Collider: " + (delFlg ? "削除" : "残す") + "\n" + "コピー先DynamicBone/Collider: " + (delDFlg ? "削除" : "残す") + "\n" + "バックアップ: " + (backUpFlg ? "する" : "しない") + "\n" + "\nこちらの設定で実行してよろしいですか?", "いいぞ!", "ちょっと待て")) { return; } if (backUpFlg) { Instantiate(targetAvatar); } if (delFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { DestroyImmediate(c); } foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { DestroyImmediate(c); } } if (delDFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren<Component>()) { if (c.GetType().Name.Equals("DynamicBone") || c.GetType().Name.Equals("DynamicBoneCollider")) { DestroyImmediate(c); } } } string alertText = ""; alertText += CopyPBC(); if (alertText.Length != 0) { alertText = "以下のコライダーはコピー先オブジェクトが見つからず、コピーに失敗しました\n" + alertText + "\n"; } string alertText2 = ""; alertText2 += CopyPB(); if (alertText2.Length != 0) { alertText2 = "以下のボーンはコピーに失敗したか、参照コライダーの変更に失敗しました\n" + alertText2 + "\n"; } if (alertText.Length != 0 || alertText2.Length != 0) { EditorUtility.DisplayDialog("エラー箇所あり", "コピー中、エラーが発生した箇所がありました。詳しくは、エラーメッセージをご確認ください。", "ok"); alertMessage = alertText + alertText2; } else { EditorUtility.DisplayDialog("終了", "コピー完了しました。", "ok"); } } string GetPath(GameObject now) { string path = now.name; var current = now.transform.parent; while (current != null) { if (current.parent is null) { break; } path = current.name + "/" + path; current = current.parent; } return path; } string CopyPBC() { string alertText = ""; //いったんコピー foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path, c.transform); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneColliderBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { Transform rtf = c.rootTransform; //rootTransformが未設定の場合は触らない if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } } return alertText; } string CopyPB() { string alertText = ""; foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path, c.transform); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } List<VRCPhysBoneColliderBase> cols = c.colliders; for (int i = 0; i < cols.Count; i++) { VRCPhysBoneColliderBase col = cols[i]; if (col != null) { string path = GetPath(col.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf != null) { Component[] tcols = tf.gameObject.GetComponents(typeof(VRCPhysBoneColliderBase)); foreach (VRCPhysBoneColliderBase tcol in tcols) { if (CeckColliderEqual(col, tcol)) { cols[i] = tcol; } } } else { alertText += " PhysBoneのCollider先を自アバに変更できませんでした: " + path + "\n"; } } } } return alertText; } //Objectに複数のコンポーネントがついてる時、一致を確認する bool CeckColliderEqual(VRCPhysBoneColliderBase col, VRCPhysBoneColliderBase tcol) { if (col.shapeType != tcol.shapeType) { return false; } if (col.radius != tcol.radius) { return false; } if (col.position.x != tcol.position.x || col.position.y != tcol.position.y || col.position.z != tcol.position.z) { return false; } if (col.rotation.x != tcol.rotation.x || col.rotation.y != tcol.rotation.y || col.rotation.z != tcol.rotation.z) { return false; } return true; } // Create empty GameObject function void CreateEmptyGameObject(Transform parent, string path, Transform referenceTransform) { string[] parts = path.Split('/'); Transform currentParent = parent; for (int i = 0; i < parts.Length; i++) { string part = parts[i]; Transform child = currentParent.Find(part); if (child == null) { GameObject newGameObject = new GameObject(part); newGameObject.transform.SetParent(currentParent); // Set the transform values to match the reference transform if (referenceTransform != null) { newGameObject.transform.localPosition = referenceTransform.localPosition; newGameObject.transform.localRotation = referenceTransform.localRotation; newGameObject.transform.localScale = referenceTransform.localScale; } currentParent = newGameObject.transform; } else { currentParent = child; } } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using VRC.Dynamics; public class PhysCopy : EditorWindow { GameObject targetAvatar; GameObject baseAvatar; bool delFlg; bool delDFlg; bool backUpFlg; bool checkFlg; string alertMessage; Vector2 textScrollPos = Vector2.zero; [MenuItem("USUAJI_tool/PhysCopy")] static void Init() { PhysCopy window = (PhysCopy)EditorWindow.GetWindow(typeof(PhysCopy)); window.titleContent = new GUIContent("PhysCopy"); window.Show(); } void OnGUI() { using (var checkScope = new EditorGUI.ChangeCheckScope()) { baseAvatar = EditorGUILayout.ObjectField("コピー元アバター", baseAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.LabelField(" ↓ copy"); targetAvatar = EditorGUILayout.ObjectField("コピー先アバター", targetAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.Space(); delFlg = EditorGUILayout.ToggleLeft("コピー先PhysBone/Colliderを削除", delFlg); delDFlg = EditorGUILayout.ToggleLeft("コピー先DynamicBone/Colliderを削除", delDFlg); checkFlg = EditorGUILayout.ToggleLeft("AvatarDescripterの有無を気にしない", checkFlg); backUpFlg = EditorGUILayout.ToggleLeft("怖いので一応バックアップ", backUpFlg); EditorGUILayout.Space(); if (GUILayout.Button("コピー実行(ポチっとな)")) { CopyOperate(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("🔻エラーがあると、ここに出力(見づらかったら、メモ帳等にコピペしてね)"); textScrollPos = EditorGUILayout.BeginScrollView(textScrollPos, GUILayout.Width(350), GUILayout.Height(100)); { alertMessage = EditorGUILayout.TextArea(alertMessage, GUILayout.Width(350)); } } } string CheckAvatar(GameObject avatar,string name) { string alertText = ""; if (baseAvatar is null) { alertText += name + "がセットされておりません\n"; } else { if (!checkFlg && baseAvatar.GetComponent("VRCAvatarDescriptor") is null) { alertText += name + "にはVRCAvatarDescriptorが含まれておりません\n"; } } return alertText; } bool Check() { string alertText = ""; alertText += CheckAvatar(baseAvatar,"コピー元アバター"); alertText += CheckAvatar(targetAvatar, "コピー先アバター"); if (alertText.Length != 0) { EditorUtility.DisplayDialog("alert", alertText, "Oh no"); return false; } return true; } void CopyOperate() { if (!Check()) { return; } if (!EditorUtility.DisplayDialog("確認", "コピー元アバター: " + baseAvatar.name + "\n" + "コピー先アバター: " + targetAvatar.name + "\n" + "コピー先PhysBone/Collider: " + (delFlg ? "削除" : "残す") + "\n" + "コピー先DynamicBone/Collider: " + (delDFlg ? "削除" : "残す") + "\n" + "バックアップ: " + (backUpFlg ? "する" : "しない") + "\n" + "\nこちらの設定で実行してよろしいですか?", "いいぞ!", "ちょっと待て")) { return; } if (backUpFlg) { Instantiate(targetAvatar); } if (delFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { DestroyImmediate(c); } foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { DestroyImmediate(c); } } if (delDFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren<Component>()) { if (c.GetType().Name.Equals("DynamicBone") || c.GetType().Name.Equals("DynamicBoneCollider")) { DestroyImmediate(c); } } } string alertText = ""; alertText += CopyPBC(); if (alertText.Length != 0) { alertText = "以下のコライダーはコピー先オブジェクトが見つからず、コピーに失敗しました\n"+ alertText +"\n"; } string alertText2 = ""; alertText2 += CopyPB(); if (alertText2.Length != 0) { alertText2 = "以下のボーンはコピーに失敗したか、参照コライダーの変更に失敗しました\n" + alertText2 + "\n"; } if (alertText.Length != 0 || alertText2.Length != 0) { EditorUtility.DisplayDialog("エラー箇所あり", "コピー中、エラーが発生した箇所がありました。詳しくは、エラーメッセージをご確認ください。", "ok"); alertMessage = alertText + alertText2; } else { EditorUtility.DisplayDialog("終了", "コピー完了しました。", "ok"); } } string GetPath(GameObject now) { string path = now.name; var current = now.transform.parent; while (current != null) { if (current.parent is null) { break; } path = current.name + "/" + path; current = current.parent; } return path; } string CopyPBC() { string alertText = ""; //いったんコピー foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path +"\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneColliderBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { Transform rtf = c.rootTransform; //rootTransformが未設定の場合は触らない if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } } return alertText; } string CopyPB() { string alertText = ""; foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } List<VRCPhysBoneColliderBase> cols = c.colliders; for (int i = 0; i< cols.Count; i++) { VRCPhysBoneColliderBase col = cols[i]; if (col != null) { string path = GetPath(col.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf != null) { Component[] tcols = tf.gameObject.GetComponents(typeof(VRCPhysBoneColliderBase)); foreach (VRCPhysBoneColliderBase tcol in tcols) { if (CeckColliderEqual(col, tcol)) { cols[i] = tcol; } } } else { alertText += " PhysBoneのCollider先を自アバに変更できませんでした: " + path + "\n"; } } } } return alertText; } //Objectに複数のコンポーネントがついてる時、一致を確認する bool CeckColliderEqual(VRCPhysBoneColliderBase col, VRCPhysBoneColliderBase tcol) { if (col.shapeType != tcol.shapeType) { return false; } if( col.radius != tcol.radius) { return false; } if(col.position.x != tcol.position.x || col.position.y != tcol.position.y || col.position.z != tcol.position.z) { return false; } if (col.rotation.x != tcol.rotation.x || col.rotation.y != tcol.rotation.y || col.rotation.z != tcol.rotation.z) { return false; } return true; } // Create empty GameObject function void CreateEmptyGameObject(Transform parent, string path) { string[] parts = path.Split('/'); Transform currentParent = parent; for (int i = 0; i < parts.Length; i++) { string part = parts[i]; Transform child = currentParent.Find(part); if (child == null) { GameObject newGameObject = new GameObject(part); newGameObject.transform.SetParent(currentParent); currentParent = newGameObject.transform; } else { currentParent = child; } } } }
Syuji Igura
revised this
on 9 Apr 2023
62a3329
PhysCopy.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using VRC.Dynamics; public class PhysCopy : EditorWindow { GameObject targetAvatar; GameObject baseAvatar; bool delFlg; bool delDFlg; bool backUpFlg; bool checkFlg; string alertMessage; Vector2 textScrollPos = Vector2.zero; [MenuItem("USUAJI_tool/PhysCopy")] static void Init() { PhysCopy window = (PhysCopy)EditorWindow.GetWindow(typeof(PhysCopy)); window.titleContent = new GUIContent("PhysCopy"); window.Show(); } void OnGUI() { using (var checkScope = new EditorGUI.ChangeCheckScope()) { baseAvatar = EditorGUILayout.ObjectField("コピー元アバター", baseAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.LabelField(" ↓ copy"); targetAvatar = EditorGUILayout.ObjectField("コピー先アバター", targetAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.Space(); delFlg = EditorGUILayout.ToggleLeft("コピー先PhysBone/Colliderを削除", delFlg); delDFlg = EditorGUILayout.ToggleLeft("コピー先DynamicBone/Colliderを削除", delDFlg); checkFlg = EditorGUILayout.ToggleLeft("AvatarDescripterの有無を気にしない", checkFlg); backUpFlg = EditorGUILayout.ToggleLeft("怖いので一応バックアップ", backUpFlg); EditorGUILayout.Space(); if (GUILayout.Button("コピー実行(ポチっとな)")) { CopyOperate(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("🔻エラーがあると、ここに出力(見づらかったら、メモ帳等にコピペしてね)"); textScrollPos = EditorGUILayout.BeginScrollView(textScrollPos, GUILayout.Width(350), GUILayout.Height(100)); { alertMessage = EditorGUILayout.TextArea(alertMessage, GUILayout.Width(350)); } } } string CheckAvatar(GameObject avatar,string name) { string alertText = ""; if (baseAvatar is null) { alertText += name + "がセットされておりません\n"; } else { if (!checkFlg && baseAvatar.GetComponent("VRCAvatarDescriptor") is null) { alertText += name + "にはVRCAvatarDescriptorが含まれておりません\n"; } } return alertText; } bool Check() { string alertText = ""; alertText += CheckAvatar(baseAvatar,"コピー元アバター"); alertText += CheckAvatar(targetAvatar, "コピー先アバター"); if (alertText.Length != 0) { EditorUtility.DisplayDialog("alert", alertText, "Oh no"); return false; } return true; } void CopyOperate() { if (!Check()) { return; } if (!EditorUtility.DisplayDialog("確認", "コピー元アバター: " + baseAvatar.name + "\n" + "コピー先アバター: " + targetAvatar.name + "\n" + "コピー先PhysBone/Collider: " + (delFlg ? "削除" : "残す") + "\n" + "コピー先DynamicBone/Collider: " + (delDFlg ? "削除" : "残す") + "\n" + "バックアップ: " + (backUpFlg ? "する" : "しない") + "\n" + "\nこちらの設定で実行してよろしいですか?", "いいぞ!", "ちょっと待て")) { return; } if (backUpFlg) { Instantiate(targetAvatar); } if (delFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { DestroyImmediate(c); } foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { DestroyImmediate(c); } } if (delDFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren<Component>()) { if (c.GetType().Name.Equals("DynamicBone") || c.GetType().Name.Equals("DynamicBoneCollider")) { DestroyImmediate(c); } } } string alertText = ""; alertText += CopyPBC(); if (alertText.Length != 0) { alertText = "以下のコライダーはコピー先オブジェクトが見つからず、コピーに失敗しました\n"+ alertText +"\n"; } string alertText2 = ""; alertText2 += CopyPB(); if (alertText2.Length != 0) { alertText2 = "以下のボーンはコピーに失敗したか、参照コライダーの変更に失敗しました\n" + alertText2 + "\n"; } if (alertText.Length != 0 || alertText2.Length != 0) { EditorUtility.DisplayDialog("エラー箇所あり", "コピー中、エラーが発生した箇所がありました。詳しくは、エラーメッセージをご確認ください。", "ok"); alertMessage = alertText + alertText2; } else { EditorUtility.DisplayDialog("終了", "コピー完了しました。", "ok"); } } string GetPath(GameObject now) { string path = now.name; var current = now.transform.parent; while (current != null) { if (current.parent is null) { break; } path = current.name + "/" + path; current = current.parent; } return path; } string CopyPBC() { string alertText = ""; //いったんコピー foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path +"\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneColliderBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { Transform rtf = c.rootTransform; //rootTransformが未設定の場合は触らない if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } } return alertText; } string CopyPB() { string alertText = ""; foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; // Create empty game object if not found CreateEmptyGameObject(targetAvatar.transform, path); } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } List<VRCPhysBoneColliderBase> cols = c.colliders; for (int i = 0; i< cols.Count; i++) { VRCPhysBoneColliderBase col = cols[i]; if (col != null) { string path = GetPath(col.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf != null) { Component[] tcols = tf.gameObject.GetComponents(typeof(VRCPhysBoneColliderBase)); foreach (VRCPhysBoneColliderBase tcol in tcols) { if (CeckColliderEqual(col, tcol)) { cols[i] = tcol; } } } else { alertText += " PhysBoneのCollider先を自アバに変更できませんでした: " + path + "\n"; } } } } return alertText; } //Objectに複数のコンポーネントがついてる時、一致を確認する bool CeckColliderEqual(VRCPhysBoneColliderBase col, VRCPhysBoneColliderBase tcol) { if (col.shapeType != tcol.shapeType) { return false; } if( col.radius != tcol.radius) { return false; } if(col.position.x != tcol.position.x || col.position.y != tcol.position.y || col.position.z != tcol.position.z) { return false; } if (col.rotation.x != tcol.rotation.x || col.rotation.y != tcol.rotation.y || col.rotation.z != tcol.rotation.z) { return false; } return true; } // Create empty GameObject function void CreateEmptyGameObject(Transform parent, string path) { string[] parts = path.Split('/'); Transform currentParent = parent; for (int i = 0; i < parts.Length; i++) { string part = parts[i]; Transform child = currentParent.Find(part); if (child == null) { GameObject newGameObject = new GameObject(part); newGameObject.transform.SetParent(currentParent); currentParent = newGameObject.transform; } else { currentParent = child; } } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using VRC.Dynamics; public class PhysCopy : EditorWindow { GameObject targetAvatar; GameObject baseAvatar; bool delFlg; bool delDFlg; bool backUpFlg; bool checkFlg; string alertMessage; Vector2 textScrollPos = Vector2.zero; [MenuItem("USUAJI_tool/PhysCopy")] static void Init() { PhysCopy window = (PhysCopy)EditorWindow.GetWindow(typeof(PhysCopy)); window.titleContent = new GUIContent("PhysCopy"); window.Show(); } void OnGUI() { using (var checkScope = new EditorGUI.ChangeCheckScope()) { baseAvatar = EditorGUILayout.ObjectField("コピー元アバター", baseAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.LabelField(" ↓ copy"); targetAvatar = EditorGUILayout.ObjectField("コピー先アバター", targetAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.Space(); delFlg = EditorGUILayout.ToggleLeft("コピー先PhysBone/Colliderを削除", delFlg); delDFlg = EditorGUILayout.ToggleLeft("コピー先DynamicBone/Colliderを削除", delDFlg); checkFlg = EditorGUILayout.ToggleLeft("AvatarDescripterの有無を気にしない", checkFlg); backUpFlg = EditorGUILayout.ToggleLeft("怖いので一応バックアップ", backUpFlg); EditorGUILayout.Space(); if (GUILayout.Button("コピー実行(ポチっとな)")) { CopyOperate(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("🔻エラーがあると、ここに出力(見づらかったら、メモ帳等にコピペしてね)"); textScrollPos = EditorGUILayout.BeginScrollView(textScrollPos, GUILayout.Width(350), GUILayout.Height(100)); { alertMessage = EditorGUILayout.TextArea(alertMessage, GUILayout.Width(350)); } } } string CheckAvatar(GameObject avatar,string name) { string alertText = ""; if (baseAvatar is null) { alertText += name + "がセットされておりません\n"; } else { if (!checkFlg && baseAvatar.GetComponent("VRCAvatarDescriptor") is null) { alertText += name + "にはVRCAvatarDescriptorが含まれておりません\n"; } } return alertText; } bool Check() { string alertText = ""; alertText += CheckAvatar(baseAvatar,"コピー元アバター"); alertText += CheckAvatar(targetAvatar, "コピー先アバター"); if (alertText.Length != 0) { EditorUtility.DisplayDialog("alert", alertText, "Oh no"); return false; } return true; } void CopyOperate() { if (!Check()) { return; } if (!EditorUtility.DisplayDialog("確認", "コピー元アバター: " + baseAvatar.name + "\n" + "コピー先アバター: " + targetAvatar.name + "\n" + "コピー先PhysBone/Collider: " + (delFlg ? "削除" : "残す") + "\n" + "コピー先DynamicBone/Collider: " + (delDFlg ? "削除" : "残す") + "\n" + "バックアップ: " + (backUpFlg ? "する" : "しない") + "\n" + "\nこちらの設定で実行してよろしいですか?", "いいぞ!", "ちょっと待て")) { return; } if (backUpFlg) { Instantiate(targetAvatar); } if (delFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { DestroyImmediate(c); } foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { DestroyImmediate(c); } } if (delDFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren<Component>()) { if (c.GetType().Name.Equals("DynamicBone") || c.GetType().Name.Equals("DynamicBoneCollider")) { DestroyImmediate(c); } } } string alertText = ""; alertText += CopyPBC(); if (alertText.Length != 0) { alertText = "以下のコライダーはコピー先オブジェクトが見つからず、コピーに失敗しました\n"+ alertText +"\n"; } string alertText2 = ""; alertText2 += CopyPB(); if (alertText2.Length != 0) { alertText2 = "以下のボーンはコピーに失敗したか、参照コライダーの変更に失敗しました\n" + alertText2 + "\n"; } if (alertText.Length != 0 || alertText2.Length != 0) { EditorUtility.DisplayDialog("エラー箇所あり", "コピー中、エラーが発生した箇所がありました。詳しくは、エラーメッセージをご確認ください。", "ok"); alertMessage = alertText + alertText2; } else { EditorUtility.DisplayDialog("終了", "コピー完了しました。", "ok"); } } string GetPath(GameObject now) { string path = now.name; var current = now.transform.parent; while (current != null) { if (current.parent is null) { break; } path = current.name + "/" + path; current = current.parent; } return path; } string CopyPBC() { string alertText = ""; //いったんコピー foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path +"\n"; } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneColliderBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { Transform rtf = c.rootTransform; //rootTransformが未設定の場合は触らない if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } } return alertText; } string CopyPB() { string alertText = ""; foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } List<VRCPhysBoneColliderBase> cols = c.colliders; for (int i = 0; i< cols.Count; i++) { VRCPhysBoneColliderBase col = cols[i]; if (col != null) { string path = GetPath(col.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf != null) { Component[] tcols = tf.gameObject.GetComponents(typeof(VRCPhysBoneColliderBase)); foreach (VRCPhysBoneColliderBase tcol in tcols) { if (CeckColliderEqual(col, tcol)) { cols[i] = tcol; } } } else { alertText += " PhysBoneのCollider先を自アバに変更できませんでした: " + path + "\n"; } } } } return alertText; } //Objectに複数のコンポーネントがついてる時、一致を確認する bool CeckColliderEqual(VRCPhysBoneColliderBase col, VRCPhysBoneColliderBase tcol) { if (col.shapeType != tcol.shapeType) { return false; } if( col.radius != tcol.radius) { return false; } if(col.position.x != tcol.position.x || col.position.y != tcol.position.y || col.position.z != tcol.position.z) { return false; } if (col.rotation.x != tcol.rotation.x || col.rotation.y != tcol.rotation.y || col.rotation.z != tcol.rotation.z) { return false; } return true; } // Create empty GameObject function void CreateEmptyGameObject(Transform parent, string path) { string[] parts = path.Split('/'); Transform currentParent = parent; for (int i = 0; i < parts.Length; i++) { string part = parts[i]; Transform child = currentParent.Find(part); if (child == null) { GameObject newGameObject = new GameObject(part); newGameObject.transform.SetParent(currentParent); currentParent = newGameObject.transform; } else { currentParent = child; } } } }
Syuji Igura
revised this
on 8 Apr 2023
7c56e1e
PhysCopy.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using VRC.Dynamics; public class PhysCopy : EditorWindow { GameObject targetAvatar; GameObject baseAvatar; bool delFlg; bool delDFlg; bool backUpFlg; bool checkFlg; string alertMessage; Vector2 textScrollPos = Vector2.zero; [MenuItem("USUAJI_tool/PhysCopy")] static void Init() { PhysCopy window = (PhysCopy)EditorWindow.GetWindow(typeof(PhysCopy)); window.titleContent = new GUIContent("PhysCopy"); window.Show(); } void OnGUI() { using (var checkScope = new EditorGUI.ChangeCheckScope()) { baseAvatar = EditorGUILayout.ObjectField("コピー元アバター", baseAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.LabelField(" ↓ copy"); targetAvatar = EditorGUILayout.ObjectField("コピー先アバター", targetAvatar, typeof(GameObject), true) as GameObject; EditorGUILayout.Space(); delFlg = EditorGUILayout.ToggleLeft("コピー先PhysBone/Colliderを削除", delFlg); delDFlg = EditorGUILayout.ToggleLeft("コピー先DynamicBone/Colliderを削除", delDFlg); checkFlg = EditorGUILayout.ToggleLeft("AvatarDescripterの有無を気にしない", checkFlg); backUpFlg = EditorGUILayout.ToggleLeft("怖いので一応バックアップ", backUpFlg); EditorGUILayout.Space(); if (GUILayout.Button("コピー実行(ポチっとな)")) { CopyOperate(); } EditorGUILayout.Space(); EditorGUILayout.LabelField("🔻エラーがあると、ここに出力(見づらかったら、メモ帳等にコピペしてね)"); textScrollPos = EditorGUILayout.BeginScrollView(textScrollPos, GUILayout.Width(350), GUILayout.Height(100)); { alertMessage = EditorGUILayout.TextArea(alertMessage, GUILayout.Width(350)); } } } string CheckAvatar(GameObject avatar,string name) { string alertText = ""; if (baseAvatar is null) { alertText += name + "がセットされておりません\n"; } else { if (!checkFlg && baseAvatar.GetComponent("VRCAvatarDescriptor") is null) { alertText += name + "にはVRCAvatarDescriptorが含まれておりません\n"; } } return alertText; } bool Check() { string alertText = ""; alertText += CheckAvatar(baseAvatar,"コピー元アバター"); alertText += CheckAvatar(targetAvatar, "コピー先アバター"); if (alertText.Length != 0) { EditorUtility.DisplayDialog("alert", alertText, "Oh no"); return false; } return true; } void CopyOperate() { if (!Check()) { return; } if (!EditorUtility.DisplayDialog("確認", "コピー元アバター: " + baseAvatar.name + "\n" + "コピー先アバター: " + targetAvatar.name + "\n" + "コピー先PhysBone/Collider: " + (delFlg ? "削除" : "残す") + "\n" + "コピー先DynamicBone/Collider: " + (delDFlg ? "削除" : "残す") + "\n" + "バックアップ: " + (backUpFlg ? "する" : "しない") + "\n" + "\nこちらの設定で実行してよろしいですか?", "いいぞ!", "ちょっと待て")) { return; } if (backUpFlg) { Instantiate(targetAvatar); } if (delFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { DestroyImmediate(c); } foreach (Component c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { DestroyImmediate(c); } } if (delDFlg) { foreach (Component c in targetAvatar.GetComponentsInChildren<Component>()) { if (c.GetType().Name.Equals("DynamicBone") || c.GetType().Name.Equals("DynamicBoneCollider")) { DestroyImmediate(c); } } } string alertText = ""; alertText += CopyPBC(); if (alertText.Length != 0) { alertText = "以下のコライダーはコピー先オブジェクトが見つからず、コピーに失敗しました\n"+ alertText +"\n"; } string alertText2 = ""; alertText2 += CopyPB(); if (alertText2.Length != 0) { alertText2 = "以下のボーンはコピーに失敗したか、参照コライダーの変更に失敗しました\n" + alertText2 + "\n"; } if (alertText.Length != 0 || alertText2.Length != 0) { EditorUtility.DisplayDialog("エラー箇所あり", "コピー中、エラーが発生した箇所がありました。詳しくは、エラーメッセージをご確認ください。", "ok"); alertMessage = alertText + alertText2; } else { EditorUtility.DisplayDialog("終了", "コピー完了しました。", "ok"); } } string GetPath(GameObject now) { string path = now.name; var current = now.transform.parent; while (current != null) { if (current.parent is null) { break; } path = current.name + "/" + path; current = current.parent; } return path; } string CopyPBC() { string alertText = ""; //いったんコピー foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path +"\n"; } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneColliderBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneColliderBase))) { Transform rtf = c.rootTransform; //rootTransformが未設定の場合は触らない if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } } return alertText; } string CopyPB() { string alertText = ""; foreach (Component c in baseAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { string path = GetPath(c.gameObject); Transform tf = targetAvatar.transform.Find(path); if (!(tf is null)) { UnityEditorInternal.ComponentUtility.CopyComponent(c); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(tf.gameObject); } else { alertText += " " + path + "\n"; } } //コピー元の参照先をtargetに直す foreach (VRCPhysBoneBase c in targetAvatar.GetComponentsInChildren(typeof(VRCPhysBoneBase))) { Transform rtf = c.rootTransform; if (rtf != null) { c.rootTransform = targetAvatar.transform.Find(GetPath(rtf.gameObject)); } List<VRCPhysBoneColliderBase> cols = c.colliders; for (int i = 0; i< cols.Count; i++) { VRCPhysBoneColliderBase col = cols[i]; if (col != null) { string path = GetPath(col.gameObject); Transform tf = targetAvatar.transform.Find(path); if (tf != null) { Component[] tcols = tf.gameObject.GetComponents(typeof(VRCPhysBoneColliderBase)); foreach (VRCPhysBoneColliderBase tcol in tcols) { if (CeckColliderEqual(col, tcol)) { cols[i] = tcol; } } } else { alertText += " PhysBoneのCollider先を自アバに変更できませんでした: " + path + "\n"; } } } } return alertText; } //Objectに複数のコンポーネントがついてる時、一致を確認する bool CeckColliderEqual(VRCPhysBoneColliderBase col, VRCPhysBoneColliderBase tcol) { if (col.shapeType != tcol.shapeType) { return false; } if( col.radius != tcol.radius) { return false; } if(col.position.x != tcol.position.x || col.position.y != tcol.position.y || col.position.z != tcol.position.z) { return false; } if (col.rotation.x != tcol.rotation.x || col.rotation.y != tcol.rotation.y || col.rotation.z != tcol.rotation.z) { return false; } return true; } // Create empty GameObject function void CreateEmptyGameObject(Transform parent, string path) { string[] parts = path.Split('/'); Transform currentParent = parent; for (int i = 0; i < parts.Length; i++) { string part = parts[i]; Transform child = currentParent.Find(part); if (child == null) { GameObject newGameObject = new GameObject(part); newGameObject.transform.SetParent(currentParent); currentParent = newGameObject.transform; } else { currentParent = child; } } } }