From 7abfb021e37bb9431d5dab0fb9df454d03227da0 Mon Sep 17 00:00:00 2001 From: bd_ Date: Mon, 10 Mar 2025 19:11:45 -0700 Subject: [PATCH] feat: World Scale Object --- CHANGELOG-PRERELEASE-jp.md | 1 + CHANGELOG-PRERELEASE.md | 1 + CHANGELOG-jp.md | 1 + CHANGELOG.md | 1 + Editor/Inspector/WorldScaleObjectEditor.cs | 12 ++++ .../Inspector/WorldScaleObjectEditor.cs.meta | 3 + Editor/PluginDefinition/PluginDefinition.cs | 1 + Editor/WorldScaleObjectPass.cs | 46 +++++++++++++++ Editor/WorldScaleObjectPass.cs.meta | 3 + ...dena.dev.modular-avatar.core.editor.asmdef | 3 +- ...ena.dev.modular-avatar.core.editor.asmdef~ | 59 ------------------- Runtime/ModularAvatarWorldScaleObject.cs | 12 ++++ Runtime/ModularAvatarWorldScaleObject.cs.meta | 11 ++++ UnitTests~/WorldScaleObject.meta | 3 + .../WorldScaleObject/WorldScaleObjectTest.cs | 58 ++++++++++++++++++ .../WorldScaleObjectTest.cs.meta | 3 + docs~/docs/reference/world-scale-object.md | 17 ++++++ .../current/reference/world-scale.object.md | 14 +++++ 18 files changed, 189 insertions(+), 60 deletions(-) create mode 100644 Editor/Inspector/WorldScaleObjectEditor.cs create mode 100644 Editor/Inspector/WorldScaleObjectEditor.cs.meta create mode 100644 Editor/WorldScaleObjectPass.cs create mode 100644 Editor/WorldScaleObjectPass.cs.meta delete mode 100644 Editor/nadena.dev.modular-avatar.core.editor.asmdef~ create mode 100644 Runtime/ModularAvatarWorldScaleObject.cs create mode 100644 Runtime/ModularAvatarWorldScaleObject.cs.meta create mode 100644 UnitTests~/WorldScaleObject.meta create mode 100644 UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs create mode 100644 UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs.meta create mode 100644 docs~/docs/reference/world-scale-object.md create mode 100644 docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-scale.object.md diff --git a/CHANGELOG-PRERELEASE-jp.md b/CHANGELOG-PRERELEASE-jp.md index 4150c88e..15e1ea14 100644 --- a/CHANGELOG-PRERELEASE-jp.md +++ b/CHANGELOG-PRERELEASE-jp.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- [World Scale Object](https://m-a.nadena.dev/dev/ja/docs/reference/world-scale-object)を追加 ### Fixed diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index a01e411c..b50b3230 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Added [World Scale Object](https://m-a.nadena.dev/dev/docs/reference/world-scale-object) ### Fixed diff --git a/CHANGELOG-jp.md b/CHANGELOG-jp.md index cf49b41e..c27f5c80 100644 --- a/CHANGELOG-jp.md +++ b/CHANGELOG-jp.md @@ -10,6 +10,7 @@ Modular Avatarの主な変更点をこのファイルで記録しています。 ### Added - CHANGELOGファイルを追加 +- [World Scale Object](https://m-a.nadena.dev/ja/docs/reference/world-scale-object)を追加 ### Fixed - [#1460] パラメーターアセットをMA Parametersにインポートするとき、ローカルのみのパラメーターが間違ってアニメーターのみ扱いになる問題を修正 diff --git a/CHANGELOG.md b/CHANGELOG.md index 66fd48e0..4ebea092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added CHANGELOG files +- Added [World Scale Object](https://m-a.nadena.dev/docs/reference/world-scale-object) ### Fixed - [#1460] When importing parameter assets in MA Parameters, "local only" parameters were incorrectly treated as diff --git a/Editor/Inspector/WorldScaleObjectEditor.cs b/Editor/Inspector/WorldScaleObjectEditor.cs new file mode 100644 index 00000000..c878e376 --- /dev/null +++ b/Editor/Inspector/WorldScaleObjectEditor.cs @@ -0,0 +1,12 @@ +using UnityEditor; + +namespace nadena.dev.modular_avatar.core.editor +{ + [CustomEditor(typeof(ModularAvatarWorldScaleObject))] + internal class WorldScaleObjectEditor : MAEditorBase + { + protected override void OnInnerInspectorGUI() + { + } + } +} \ No newline at end of file diff --git a/Editor/Inspector/WorldScaleObjectEditor.cs.meta b/Editor/Inspector/WorldScaleObjectEditor.cs.meta new file mode 100644 index 00000000..71c01d8f --- /dev/null +++ b/Editor/Inspector/WorldScaleObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e9b8b83586074bd7a6441b4cd7539dc9 +timeCreated: 1741658287 \ No newline at end of file diff --git a/Editor/PluginDefinition/PluginDefinition.cs b/Editor/PluginDefinition/PluginDefinition.cs index 330f5df2..b951fe19 100644 --- a/Editor/PluginDefinition/PluginDefinition.cs +++ b/Editor/PluginDefinition/PluginDefinition.cs @@ -84,6 +84,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin seq.Run("World Fixed Object", ctx => new WorldFixedObjectProcessor().Process(ctx) ); + seq.Run(WorldScaleObjectPass.Instance); seq.Run(ReplaceObjectPluginPass.Instance); diff --git a/Editor/WorldScaleObjectPass.cs b/Editor/WorldScaleObjectPass.cs new file mode 100644 index 00000000..2632b73f --- /dev/null +++ b/Editor/WorldScaleObjectPass.cs @@ -0,0 +1,46 @@ +using nadena.dev.modular_avatar.editor.ErrorReporting; +using nadena.dev.ndmf; +using UnityEditor; +using UnityEngine; +#if VRC_SDK_VRCSDK3 +using VRC.Dynamics; +using VRC.SDK3.Dynamics.Constraint.Components; + +#else +using UnityEngine.Animations; +#endif + +namespace nadena.dev.modular_avatar.core.editor +{ + internal class WorldScaleObjectPass : Pass + { + protected override void Execute(ndmf.BuildContext context) + { + var fixedPrefab = + AssetDatabase.LoadAssetAtPath( + "Packages/nadena.dev.modular-avatar/Assets/FixedPrefab.prefab" + ); + var targets = context.AvatarRootTransform.GetComponentsInChildren(true); + + foreach (var target in targets) + { + BuildReport.ReportingObject(target, () => + { +#if MA_VRCSDK3_AVATARS + var c = target.gameObject.AddComponent(); + c.Sources.Add(new VRCConstraintSource(fixedPrefab.transform, 1)); + c.Locked = true; + c.IsActive = true; +#else + var c = target.gameObject.AddComponent(); + c.AddSource(new ConstraintSource() {sourceTransform = fixedPrefab.transform, weight = 1}); + c.locked = true; + c.constraintActive = true; +#endif + + Object.DestroyImmediate(target); + }); + } + } + } +} \ No newline at end of file diff --git a/Editor/WorldScaleObjectPass.cs.meta b/Editor/WorldScaleObjectPass.cs.meta new file mode 100644 index 00000000..b54b6c3d --- /dev/null +++ b/Editor/WorldScaleObjectPass.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 19a3b07a9eeb413887792469f344c34d +timeCreated: 1741657804 \ No newline at end of file diff --git a/Editor/nadena.dev.modular-avatar.core.editor.asmdef b/Editor/nadena.dev.modular-avatar.core.editor.asmdef index 6372b236..6d0af71a 100644 --- a/Editor/nadena.dev.modular-avatar.core.editor.asmdef +++ b/Editor/nadena.dev.modular-avatar.core.editor.asmdef @@ -29,7 +29,8 @@ "VRC.SDK3.Dynamics.Contact.Editor.dll", "VRC.SDK3.Dynamics.PhysBone.dll", "VRC.SDK3.Dynamics.PhysBone.Editor.dll", - "VRCCore-Editor.dll" + "VRCCore-Editor.dll", + "VRC.SDK3.Dynamics.Constraint.dll" ], "autoReferenced": false, "defineConstraints": [], diff --git a/Editor/nadena.dev.modular-avatar.core.editor.asmdef~ b/Editor/nadena.dev.modular-avatar.core.editor.asmdef~ deleted file mode 100644 index 6372b236..00000000 --- a/Editor/nadena.dev.modular-avatar.core.editor.asmdef~ +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "nadena.dev.modular-avatar.core.editor", - "rootNamespace": "", - "references": [ - "nadena.dev.modular-avatar.core", - "VRC.SDK3A", - "VRC.SDKBase", - "nadena.dev.ndmf", - "nadena.dev.ndmf.vrchat", - "nadena.dev.ndmf.runtime", - "VRC.SDK3A.Editor", - "Unity.Burst" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": true, - "precompiledReferences": [ - "Newtonsoft.Json.dll", - "System.Collections.Immutable.dll", - "VRCSDKBase.dll", - "VRCSDKBase-Editor.dll", - "VRCSDK3A.dll", - "VRCSDK3A-Editor.dll", - "VRC.Dynamics.dll", - "VRC.SDK3.Dynamics.Contact.dll", - "VRC.SDK3.Dynamics.Contact.Editor.dll", - "VRC.SDK3.Dynamics.PhysBone.dll", - "VRC.SDK3.Dynamics.PhysBone.Editor.dll", - "VRCCore-Editor.dll" - ], - "autoReferenced": false, - "defineConstraints": [], - "versionDefines": [ - { - "name": "com.anatawa12.avatar-optimizer", - "expression": "(,1.5.0-rc.8)", - "define": "LEGACY_AVATAR_OPTIMIZER" - }, - { - "name": "com.vrchat.avatars", - "expression": "", - "define": "MA_VRCSDK3_AVATARS" - }, - { - "name": "com.vrchat.avatars", - "expression": "3.5.2", - "define": "MA_VRCSDK3_AVATARS_3_5_2_OR_NEWER" - }, - { - "name": "com.vrchat.avatars", - "expression": "3.7.0-beta.2", - "define": "MA_VRCSDK3_AVATARS_3_7_0_OR_NEWER" - } - ], - "noEngineReferences": false -} \ No newline at end of file diff --git a/Runtime/ModularAvatarWorldScaleObject.cs b/Runtime/ModularAvatarWorldScaleObject.cs new file mode 100644 index 00000000..ace07e8e --- /dev/null +++ b/Runtime/ModularAvatarWorldScaleObject.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace nadena.dev.modular_avatar.core +{ + [AddComponentMenu("Modular Avatar/MA World Scale Object")] + [DisallowMultipleComponent] + [HelpURL("https://modular-avatar.nadena.dev/docs/reference/world-scale-object?lang=auto")] + public class ModularAvatarWorldScaleObject : AvatarTagComponent + { + // no configuration + } +} \ No newline at end of file diff --git a/Runtime/ModularAvatarWorldScaleObject.cs.meta b/Runtime/ModularAvatarWorldScaleObject.cs.meta new file mode 100644 index 00000000..ea8152be --- /dev/null +++ b/Runtime/ModularAvatarWorldScaleObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e113c01563a14226b5e863befe6fe769 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/WorldScaleObject.meta b/UnitTests~/WorldScaleObject.meta new file mode 100644 index 00000000..89d3214a --- /dev/null +++ b/UnitTests~/WorldScaleObject.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2ce1741d17804d6c94f296ef930afba8 +timeCreated: 1741658553 \ No newline at end of file diff --git a/UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs b/UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs new file mode 100644 index 00000000..8ef96a5a --- /dev/null +++ b/UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs @@ -0,0 +1,58 @@ +using modular_avatar_tests; +using nadena.dev.modular_avatar.core; +using nadena.dev.modular_avatar.core.editor; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.Animations; + +#if MA_VRCSDK3_AVATARS +using VRC.SDK3.Dynamics.Constraint.Components; +#endif + +namespace UnitTests.WorldScaleObject +{ + public class WorldScaleObjectTest : TestBase + { + [Test] + public void TestWSO() + { + var root = CreateRoot("root"); + var child = CreateChild(root, "child"); + var wso = child.AddComponent(); + + AvatarProcessor.ProcessAvatar(root); + + Assert.IsTrue(wso == null); + + #if MA_VRCSDK3_AVATARS + AssertVRCScaleConstraintPresent(child); + #else + AssertScaleConstraintPresent(child); + #endif + } + + #if MA_VRCSDK3_AVATARS + private void AssertVRCScaleConstraintPresent(GameObject child) + { + var scaleConstraint = child.GetComponent(); + Assert.IsNotNull(scaleConstraint); + Assert.AreEqual(1, scaleConstraint.Sources.Count); + Assert.AreEqual("FixedPrefab", scaleConstraint.Sources[0].SourceTransform.gameObject.name); + Assert.AreEqual(1, scaleConstraint.Sources[0].Weight); + Assert.AreEqual(true, scaleConstraint.Locked); + Assert.AreEqual(true, scaleConstraint.IsActive); + } + #endif + + private void AssertScaleConstraintPresent(GameObject child) + { + var scaleConstraint = child.GetComponent(); + Assert.IsNotNull(scaleConstraint); + Assert.AreEqual(1, scaleConstraint.sourceCount); + Assert.AreEqual("FixedPrefab", scaleConstraint.GetSource(0).sourceTransform.gameObject.name); + Assert.AreEqual(1, scaleConstraint.GetSource(0).weight); + Assert.AreEqual(true, scaleConstraint.locked); + Assert.AreEqual(true, scaleConstraint.constraintActive); + } + } +} \ No newline at end of file diff --git a/UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs.meta b/UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs.meta new file mode 100644 index 00000000..dba68e96 --- /dev/null +++ b/UnitTests~/WorldScaleObject/WorldScaleObjectTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 41ec840868664e28bd0546a3cc639692 +timeCreated: 1741658559 \ No newline at end of file diff --git a/docs~/docs/reference/world-scale-object.md b/docs~/docs/reference/world-scale-object.md new file mode 100644 index 00000000..236735ab --- /dev/null +++ b/docs~/docs/reference/world-scale-object.md @@ -0,0 +1,17 @@ +# World Scale Object + +This component can be used to force a game object to have the same scale as the world, regardless of the current avatar +scale. It will attach a (VRC) Scale Constraint to the game object, and set the constraint to scale to 1,1,1 scale relative +to the world. + +## When should I use it? + +When you want to have a game object scale with the world, rather than the avatar. This can be useful in certain complex +constraint gimmicks. + +## Setting up World Scale Object + +Simply attach the `World Scale Object` component to a GameObject. There are no configuration options to set. + +Note that `World Scale Object` currently is not previewed in the Unity Editor, but will work correctly in-game or in +play mode. \ No newline at end of file diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-scale.object.md b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-scale.object.md new file mode 100644 index 00000000..cc695562 --- /dev/null +++ b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/world-scale.object.md @@ -0,0 +1,14 @@ +# World Scale Object + +このコンポーネントは、現在のアバタースケールに関係なく、ゲームオブジェクトをワールドと同じスケールにするために使用できます。 +ゲームオブジェクトにScaleConstraintまたはVRCScaleConstraintをアタッチし、1,1,1スケールに設定して、世界に対してスケールを固定します。 + +## いつ使うべきか? + +アバターではなく、ワールドと一緒にスケールするゲームオブジェクトが必要な場合に使用します。特定の複雑なコンストレイントギミックなどで便利です。 + +## World Scale Objectの設定 + +単純に`World Scale Object`コンポーネントをゲームオブジェクトにアタッチするだけです。詳細設定はありません。 + +なお、`World Scale Object`は現在Unityエディターでプレビューされませんが、ゲーム内またはプレイモードで正常に動作します。 \ No newline at end of file