mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-04-10 06:28:58 +08:00
chore: Create toggle with submenu in "CreateToggleForSelection" (#1437)
* chore: refactor and Ignore GameObjects with submenu for CreateToggleForSelection * chore: create toggle with submenu in CreateToggleForSelection * chore: use simple suffixes * chore: update CHANGELOG * fix: update CHANGELOG --------- Co-authored-by: bd_ <bd_@nadena.dev>
This commit is contained in:
parent
98311f11f8
commit
fc9b2683c8
@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
に対してはWrite Defaultsを調整しないように変更。
|
に対してはWrite Defaultsを調整しないように変更。
|
||||||
- [#1429] Merge Armature は、特定の場合にPhysBoneに指定されたヒューマノイドボーンをマージできるようになりました。
|
- [#1429] Merge Armature は、特定の場合にPhysBoneに指定されたヒューマノイドボーンをマージできるようになりました。
|
||||||
- 具体的には、子ヒューマノイドボーンがある場合はPhysBoneから除外される必要があります。
|
- 具体的には、子ヒューマノイドボーンがある場合はPhysBoneから除外される必要があります。
|
||||||
|
- [#1437] Create Toggle for Selectionにおいて、複数選択時時に必要に応じてサブメニューを生成し、子としてトグルを生成するように変更されました。
|
||||||
- [#1499] `Object Toggle`で制御される`Audio Source`がアニメーションブロックされたときに常にアクティブにならないように、
|
- [#1499] `Object Toggle`で制御される`Audio Source`がアニメーションブロックされたときに常にアクティブにならないように、
|
||||||
アニメーションがブロックされたときにオーディオソースを無効にするように変更。
|
アニメーションがブロックされたときにオーディオソースを無効にするように変更。
|
||||||
- [#1489] `Merge Blend Tree` やリアクティブコンポーネントとMMDワールドの互換性の問題を修正。
|
- [#1489] `Merge Blend Tree` やリアクティブコンポーネントとMMDワールドの互換性の問題を修正。
|
||||||
|
@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
additive layers, or layers with only one state and no transitions.
|
additive layers, or layers with only one state and no transitions.
|
||||||
- [#1429] Merge Armature will now allow you to merge humanoid bones with PhysBones attached in certain cases.
|
- [#1429] Merge Armature will now allow you to merge humanoid bones with PhysBones attached in certain cases.
|
||||||
- Specifically, child humanoid bones (if there are any) must be excluded from all attached Physbones.
|
- Specifically, child humanoid bones (if there are any) must be excluded from all attached Physbones.
|
||||||
|
- [#1437] Create Toggle for Selection now creates submenus as necessary when multiple items are selected, and creates toggles as children.
|
||||||
- [#1499] When an audio source is controlled by an Object Toggle, disable the audio source when animations are blocked
|
- [#1499] When an audio source is controlled by an Object Toggle, disable the audio source when animations are blocked
|
||||||
to avoid it unintentionally being constantly active.
|
to avoid it unintentionally being constantly active.
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ Modular Avatarの主な変更点をこのファイルで記録しています。
|
|||||||
に対してはWrite Defaultsを調整しないように変更。
|
に対してはWrite Defaultsを調整しないように変更。
|
||||||
- [#1429] Merge Armature は、特定の場合にPhysBoneに指定されたヒューマノイドボーンをマージできるようになりました。
|
- [#1429] Merge Armature は、特定の場合にPhysBoneに指定されたヒューマノイドボーンをマージできるようになりました。
|
||||||
- 具体的には、子ヒューマノイドボーンがある場合はPhysBoneから除外される必要があります。
|
- 具体的には、子ヒューマノイドボーンがある場合はPhysBoneから除外される必要があります。
|
||||||
|
- [#1437] Create Toggle for Selectionにおいて、複数選択時時に必要に応じてサブメニューを生成し、子としてトグルを生成するように変更されました。
|
||||||
- [#1499] `Object Toggle`で制御される`Audio Source`がアニメーションブロックされたときに常にアクティブにならないように、
|
- [#1499] `Object Toggle`で制御される`Audio Source`がアニメーションブロックされたときに常にアクティブにならないように、
|
||||||
アニメーションがブロックされたときにオーディオソースを無効にするように変更。
|
アニメーションがブロックされたときにオーディオソースを無効にするように変更。
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
additive layers, or layers with only one state and no transitions.
|
additive layers, or layers with only one state and no transitions.
|
||||||
- [#1429] Merge Armature will now allow you to merge humanoid bones with PhysBones attached in certain cases.
|
- [#1429] Merge Armature will now allow you to merge humanoid bones with PhysBones attached in certain cases.
|
||||||
- Specifically, child humanoid bones (if there are any) must be excluded from all attached Physbones.
|
- Specifically, child humanoid bones (if there are any) must be excluded from all attached Physbones.
|
||||||
|
- [#1437] Create Toggle for Selection now creates submenus as necessary when multiple items are selected, and creates toggles as children.
|
||||||
- [#1499] When an audio source is controlled by an Object Toggle, disable the audio source when animations are blocked
|
- [#1499] When an audio source is controlled by an Object Toggle, disable the audio source when animations are blocked
|
||||||
to avoid it unintentionally being constantly active.
|
to avoid it unintentionally being constantly active.
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
using nadena.dev.modular_avatar.ui;
|
using nadena.dev.modular_avatar.ui;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -9,39 +12,170 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
internal static class ToggleCreatorShortcut
|
internal static class ToggleCreatorShortcut
|
||||||
{
|
{
|
||||||
[MenuItem(UnityMenuItems.GameObject_CreateToggleForSelection, false, UnityMenuItems.GameObject_CreateToggleForSelectionOrder)]
|
[MenuItem(UnityMenuItems.GameObject_CreateToggleForSelection, false, UnityMenuItems.GameObject_CreateToggleForSelectionOrder)]
|
||||||
private static void CreateToggleForSelection(MenuCommand command) => CreateToggleImpl(command, true);
|
private static void CreateToggleForSelection()
|
||||||
|
{
|
||||||
|
var forSelection = true;
|
||||||
|
|
||||||
|
var selections = Selection.objects.OfType<GameObject>();
|
||||||
|
// Ignore GameObjects with submenu in the context of CreateToggleForSelection.
|
||||||
|
selections = selections.Where(s => !TryGetChildrenSourceSubmenu(s, out var _));
|
||||||
|
if (selections.Count() == 0) return;
|
||||||
|
|
||||||
|
// Grouping according to parent
|
||||||
|
var groups = new Dictionary<GameObject, HashSet<GameObject>>();
|
||||||
|
foreach (var selected in selections)
|
||||||
|
{
|
||||||
|
var parent = selected.transform.parent?.gameObject;
|
||||||
|
if (parent == null) continue;
|
||||||
|
|
||||||
|
if (!groups.ContainsKey(parent))
|
||||||
|
{
|
||||||
|
groups[parent] = new();
|
||||||
|
}
|
||||||
|
groups[parent].Add(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var group in groups)
|
||||||
|
{
|
||||||
|
var parent = group.Key;
|
||||||
|
var targets = group.Value;
|
||||||
|
|
||||||
|
if (parent == null) continue;
|
||||||
|
if (targets == null || targets.Count() == 0) continue;
|
||||||
|
|
||||||
|
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(parent.transform);
|
||||||
|
if (avatarRoot == null) continue;
|
||||||
|
|
||||||
|
var subMenuName = parent.name + " Toggles";
|
||||||
|
|
||||||
|
// Try to find target submenu that should be the parent of toggles
|
||||||
|
ModularAvatarMenuItem targetSubMenu = null;
|
||||||
|
if (TryGetChildrenSourceSubmenu(parent, out var subMenu))
|
||||||
|
{
|
||||||
|
// If parent has subMenu, use it as target submenu.
|
||||||
|
targetSubMenu = subMenu;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If parent hasn't subMenu, get submenus at the same level
|
||||||
|
var subMenus = new List<ModularAvatarMenuItem>();
|
||||||
|
foreach (Transform sibling in parent.transform)
|
||||||
|
{
|
||||||
|
if (TryGetChildrenSourceSubmenu(sibling.gameObject, out var m)) { subMenus.Add(m); }
|
||||||
|
}
|
||||||
|
// Filter to submenus with the same name
|
||||||
|
subMenus = subMenus.Where(m => m.gameObject.name == subMenuName).ToList();
|
||||||
|
// If only one submenu as target is found, use it as target submenu.
|
||||||
|
if (subMenus.Count() == 1) targetSubMenu = subMenus.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetSubMenu != null) // If target SubMenu is found, add the toggles as children of it.
|
||||||
|
{
|
||||||
|
parent = targetSubMenu.gameObject;
|
||||||
|
CreateToggleImpl(targets, parent, forSelection, createInstaller:false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (targets.Count() > 1) // Create a submenu and add the toggles as children of it.
|
||||||
|
{
|
||||||
|
parent = CreateSubMenu(parent, subMenuName).gameObject;
|
||||||
|
CreateToggleImpl(targets, parent, forSelection, createInstaller:false);
|
||||||
|
}
|
||||||
|
else // Create a single toggle with installer.
|
||||||
|
{
|
||||||
|
var target = targets.First();
|
||||||
|
CreateToggleImpl(target, parent, forSelection, createInstaller:true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection.objects = null;
|
||||||
|
}
|
||||||
|
|
||||||
[MenuItem(UnityMenuItems.GameObject_CreateToggle, false, UnityMenuItems.GameObject_CreateToggleOrder)]
|
[MenuItem(UnityMenuItems.GameObject_CreateToggle, false, UnityMenuItems.GameObject_CreateToggleOrder)]
|
||||||
private static void CreateToggle(MenuCommand command) => CreateToggleImpl(command, false);
|
private static void CreateToggle()
|
||||||
|
|
||||||
private static void CreateToggleImpl(MenuCommand command, bool forSelection)
|
|
||||||
{
|
{
|
||||||
var selected = command.context as GameObject;
|
var selections = Selection.objects.OfType<GameObject>();
|
||||||
if (selected == null) return;
|
if (selections.Count() == 0) return;
|
||||||
|
|
||||||
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(selected.transform);
|
foreach (var selected in selections)
|
||||||
if (avatarRoot == null) return;
|
{
|
||||||
|
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(selected.transform);
|
||||||
|
if (avatarRoot == null) return;
|
||||||
|
|
||||||
bool createInstaller = true;
|
var parent = avatarRoot.gameObject;
|
||||||
Transform parent = avatarRoot;
|
var createInstaller = true;
|
||||||
|
|
||||||
|
if (TryGetChildrenSourceSubmenu(selected, out var _))
|
||||||
|
{
|
||||||
|
parent = selected;
|
||||||
|
createInstaller = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateToggleImpl(selected, parent, createInstaller:createInstaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection.objects = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetChildrenSourceSubmenu(GameObject target, out ModularAvatarMenuItem subMenu)
|
||||||
|
{
|
||||||
|
subMenu = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var selectedMenuItem = selected.GetComponent<ModularAvatarMenuItem>();
|
var mami = target.GetComponent<ModularAvatarMenuItem>();
|
||||||
if (selectedMenuItem?.Control?.type == VRCExpressionsMenu.Control.ControlType.SubMenu
|
if (mami?.Control?.type == VRCExpressionsMenu.Control.ControlType.SubMenu
|
||||||
&& selectedMenuItem.MenuSource == SubmenuSource.Children
|
&& mami.MenuSource == SubmenuSource.Children
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
parent = selected.transform;
|
subMenu = mami;
|
||||||
createInstaller = false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (MissingComponentException)
|
catch (MissingComponentException)
|
||||||
{
|
{
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModularAvatarMenuItem CreateSubMenu(GameObject parent, string submenuname)
|
||||||
|
{
|
||||||
|
var submenu = new GameObject(submenuname);
|
||||||
|
submenu.transform.SetParent(parent.transform);
|
||||||
|
|
||||||
var name = forSelection ? selected.name + " Toggle" : "New Toggle";
|
var mami = submenu.AddComponent<ModularAvatarMenuItem>();
|
||||||
|
mami.InitSettings();
|
||||||
|
mami.Control = new VRCExpressionsMenu.Control
|
||||||
|
{
|
||||||
|
type = VRCExpressionsMenu.Control.ControlType.SubMenu,
|
||||||
|
name = submenuname,
|
||||||
|
};
|
||||||
|
submenu.AddComponent<ModularAvatarMenuInstaller>();
|
||||||
|
|
||||||
|
Selection.activeGameObject = submenu;
|
||||||
|
EditorGUIUtility.PingObject(submenu);
|
||||||
|
|
||||||
|
Undo.RegisterCreatedObjectUndo(submenu, "Create SubMenu");
|
||||||
|
|
||||||
|
return mami;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateToggleImpl(IEnumerable<GameObject> selections, GameObject parent, bool forSelection = false, bool createInstaller = true)
|
||||||
|
{
|
||||||
|
foreach (var selected in selections)
|
||||||
|
{
|
||||||
|
CreateToggleImpl(selected, parent, forSelection, createInstaller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateToggleImpl(GameObject selected, GameObject parent, bool forSelection = false, bool createInstaller = true)
|
||||||
|
{
|
||||||
|
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(selected.transform);
|
||||||
|
if (avatarRoot == null) return;
|
||||||
|
|
||||||
|
var suffix = selected.activeSelf ? "OFF" : "ON";
|
||||||
|
var name = forSelection ? $"{selected.name} {suffix}" : "New Toggle";
|
||||||
|
|
||||||
var toggle = new GameObject(name);
|
var toggle = new GameObject(name);
|
||||||
|
|
||||||
@ -57,7 +191,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
toggle.transform.SetParent(parent, false);
|
toggle.transform.SetParent(parent.transform, false);
|
||||||
|
|
||||||
var mami = toggle.AddComponent<ModularAvatarMenuItem>();
|
var mami = toggle.AddComponent<ModularAvatarMenuItem>();
|
||||||
mami.InitSettings();
|
mami.InitSettings();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user