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;
}
}
}
}