376 lines
11 KiB
C#
376 lines
11 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System;
|
|
using UnityEditor.Animations;
|
|
using UnityEditor.SceneManagement;
|
|
#if VRC_SDK_VRCSDK2
|
|
using VRCSDK2;
|
|
#endif
|
|
|
|
// ver 1.0
|
|
// Copyright (c) 2020 gatosyocora
|
|
|
|
namespace VRCDeveloperTool
|
|
{
|
|
public class VRCAvatarTester : EditorWindow
|
|
{
|
|
#if VRC_SDK_VRCSDK2
|
|
private VRC_AvatarDescriptor avatar;
|
|
#endif
|
|
[SerializeField]
|
|
private Animator animator;
|
|
[SerializeField]
|
|
private AnimatorOverrideController controller;
|
|
[SerializeField]
|
|
private RuntimeAnimatorController defaultController;
|
|
|
|
public enum PlayingType
|
|
{
|
|
NONE, OVERRIDE, EMOTE
|
|
};
|
|
private PlayingType playingType = PlayingType.NONE;
|
|
public enum PlayingHand
|
|
{
|
|
NONE, RIGHT, LEFT, BOTH
|
|
};
|
|
private PlayingHand playingHand = PlayingHand.NONE;
|
|
|
|
private static readonly string[] OVERRIDES = new string[]
|
|
{
|
|
"FIST", "HANDOPEN", "FINGERPOINT", "VICTORY", "ROCKNROLL", "HANDGUN", "THUMBSUP"
|
|
};
|
|
|
|
private static readonly string[] EMOTES = new string[]
|
|
{
|
|
"EMOTE1", "EMOTE2", "EMOTE3", "EMOTE4", "EMOTE5", "EMOTE6", "EMOTE7", "EMOTE8"
|
|
};
|
|
|
|
[SerializeField]
|
|
private GameObject poseConstraintObj;
|
|
private PoseConstraint poseConstraint;
|
|
|
|
[MenuItem("VRCDeveloperTool/VRCAvatarTester")]
|
|
public static void Open()
|
|
{
|
|
GetWindow<VRCAvatarTester>(nameof(VRCAvatarTester));
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
#if VRC_SDK_VRCSDK2
|
|
// 再生中
|
|
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
|
{
|
|
// 毎回取得しないとActiveへの変更がなぜか適用されない
|
|
poseConstraint = poseConstraintObj.GetComponent<PoseConstraint>();
|
|
|
|
animator.runtimeAnimatorController = controller;
|
|
if (playingType == PlayingType.OVERRIDE)
|
|
{
|
|
poseConstraint.Active = true;
|
|
animator.SetInteger($"Emote", 0);
|
|
|
|
switch (playingHand)
|
|
{
|
|
case PlayingHand.NONE:
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
|
|
break;
|
|
case PlayingHand.RIGHT:
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 1);
|
|
break;
|
|
case PlayingHand.LEFT:
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 1);
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
|
|
break;
|
|
case PlayingHand.BOTH:
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 1);
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (playingType == PlayingType.EMOTE)
|
|
{
|
|
poseConstraint.Active = false;
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
|
|
}
|
|
else
|
|
{
|
|
poseConstraint.Active = true;
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
|
|
}
|
|
}
|
|
// 未再生
|
|
else
|
|
{
|
|
if (animator != null && controller != null)
|
|
{
|
|
animator.runtimeAnimatorController = defaultController;
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
|
|
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
#if VRC_SDK_VRCSDK2
|
|
using (var check = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
avatar = EditorGUILayout.ObjectField("Avatar", avatar, typeof(VRC_AvatarDescriptor), true) as VRC_AvatarDescriptor;
|
|
|
|
if (check.changed)
|
|
{
|
|
if (avatar != null)
|
|
{
|
|
animator = avatar.gameObject.GetComponent<Animator>();
|
|
controller = avatar.CustomStandingAnims;
|
|
}
|
|
else
|
|
{
|
|
animator = null;
|
|
controller = null;
|
|
}
|
|
}
|
|
}
|
|
EditorGUILayout.Space();
|
|
|
|
EditorGUILayout.LabelField("Camera", EditorStyles.boldLabel);
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
if (GUILayout.Button("Scene View"))
|
|
{
|
|
EditorApplication.ExecuteMenuItem("Window/General/Scene");
|
|
}
|
|
if (GUILayout.Button("Game View"))
|
|
{
|
|
EditorApplication.ExecuteMenuItem("Window/General/Game");
|
|
}
|
|
}
|
|
/*
|
|
using (new EditorGUI.DisabledGroupScope(animator is null))
|
|
{
|
|
if (GUILayout.Button("Focus on Avatar"))
|
|
{
|
|
var sceneViewCamera = SceneView.lastActiveSceneView.camera;
|
|
sceneViewCamera.transform.position = animator.transform.position;
|
|
}
|
|
}
|
|
*/
|
|
|
|
GUILayout.Space(15);
|
|
|
|
EditorGUILayout.LabelField("Testing", EditorStyles.boldLabel);
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
using (new EditorGUI.DisabledGroupScope(EditorApplication.isPlayingOrWillChangePlaymode || avatar == null))
|
|
{
|
|
if (GUILayout.Button("Play"))
|
|
{
|
|
defaultController = animator.runtimeAnimatorController;
|
|
animator.runtimeAnimatorController = controller;
|
|
|
|
poseConstraintObj = CreatePoseConstrainterToRootIfNeeded();
|
|
poseConstraint = poseConstraintObj.GetComponent<PoseConstraint>();
|
|
poseConstraint.UpdateBoneInfo(animator);
|
|
|
|
EditorApplication.isPlaying = true;
|
|
}
|
|
}
|
|
using (new EditorGUI.DisabledGroupScope(!EditorApplication.isPlayingOrWillChangePlaymode))
|
|
{
|
|
if (GUILayout.Button("Stop"))
|
|
{
|
|
EditorApplication.isPlaying = false;
|
|
animator.runtimeAnimatorController = defaultController;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (avatar == null && !EditorApplication.isPlaying)
|
|
{
|
|
EditorGUILayout.HelpBox("Avatarを設定してください", MessageType.Error);
|
|
}
|
|
|
|
if (avatar != null && animator != null && controller != null)
|
|
{
|
|
EditorGUILayout.HelpBox("Playを選択するとテストが実行できます", MessageType.Info);
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
using (new EditorGUI.DisabledGroupScope(!EditorApplication.isPlayingOrWillChangePlaymode))
|
|
{
|
|
if (GUILayout.Button("Reset All"))
|
|
{
|
|
playingType = PlayingType.NONE;
|
|
playingHand = PlayingHand.NONE;
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
EditorGUILayout.LabelField("AnimationOverrides", EditorStyles.boldLabel);
|
|
using (new EditorGUI.IndentLevelScope())
|
|
{
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
EditorGUILayout.LabelField("NONE");
|
|
if (GUILayout.Button("Left"))
|
|
{
|
|
if (playingType == PlayingType.OVERRIDE &&
|
|
playingHand == PlayingHand.BOTH)
|
|
{
|
|
playingHand = PlayingHand.RIGHT;
|
|
}
|
|
else
|
|
{
|
|
playingType = PlayingType.NONE;
|
|
}
|
|
PlayOverride("Left", 0, animator);
|
|
}
|
|
if (GUILayout.Button("Right"))
|
|
{
|
|
if (playingType == PlayingType.OVERRIDE &&
|
|
playingHand == PlayingHand.BOTH)
|
|
{
|
|
playingHand = PlayingHand.LEFT;
|
|
}
|
|
else
|
|
{
|
|
playingType = PlayingType.NONE;
|
|
}
|
|
PlayOverride("Right", 0, animator);
|
|
}
|
|
}
|
|
for (int overrideNumber = 0; overrideNumber < OVERRIDES.Length; overrideNumber++)
|
|
{
|
|
AnimationClip overrideAnimation = null;
|
|
string overrideName = string.Empty;
|
|
|
|
if (controller != null)
|
|
{
|
|
overrideAnimation = controller[OVERRIDES[overrideNumber]];
|
|
|
|
if (overrideAnimation.name != OVERRIDES[overrideNumber])
|
|
{
|
|
overrideName = $"({overrideAnimation.name})";
|
|
}
|
|
}
|
|
|
|
// AnimationClipとOVERRIDES[overrideNumber]の名前が同じであれば未設定
|
|
using (new EditorGUI.DisabledGroupScope(controller == null || overrideAnimation.name == OVERRIDES[overrideNumber]))
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
EditorGUILayout.LabelField(OVERRIDES[overrideNumber], overrideName);
|
|
|
|
if (GUILayout.Button("Left"))
|
|
{
|
|
playingType = PlayingType.OVERRIDE;
|
|
if (playingHand == PlayingHand.RIGHT ||
|
|
playingHand == PlayingHand.BOTH)
|
|
{
|
|
playingHand = PlayingHand.BOTH;
|
|
}
|
|
else
|
|
{
|
|
playingHand = PlayingHand.LEFT;
|
|
}
|
|
PlayOverride("Left", overrideNumber, animator);
|
|
}
|
|
|
|
if (GUILayout.Button("Right"))
|
|
{
|
|
playingType = PlayingType.OVERRIDE;
|
|
if (playingHand == PlayingHand.LEFT ||
|
|
playingHand == PlayingHand.BOTH)
|
|
{
|
|
playingHand = PlayingHand.BOTH;
|
|
}
|
|
else
|
|
{
|
|
playingHand = PlayingHand.RIGHT;
|
|
}
|
|
PlayOverride("Right", overrideNumber, animator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
using (new EditorGUI.DisabledGroupScope(!EditorApplication.isPlayingOrWillChangePlaymode))
|
|
{
|
|
EditorGUILayout.LabelField("Emotes", EditorStyles.boldLabel);
|
|
using (new EditorGUI.IndentLevelScope())
|
|
{
|
|
for (int emoteNumber = 0; emoteNumber < EMOTES.Length; emoteNumber++)
|
|
{
|
|
AnimationClip emoteAnimation = null;
|
|
string buttonText = EMOTES[emoteNumber];
|
|
if (controller != null)
|
|
{
|
|
emoteAnimation = controller[EMOTES[emoteNumber]];
|
|
buttonText = emoteAnimation.name;
|
|
}
|
|
|
|
// 取得できるAnimationClipの名前が"EMOTE*"だったら設定されていない
|
|
using (new EditorGUI.DisabledGroupScope(emoteAnimation == null || emoteAnimation.name == EMOTES[emoteNumber]))
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
EditorGUILayout.LabelField(EMOTES[emoteNumber]);
|
|
|
|
if (GUILayout.Button(buttonText) && emoteAnimation != null)
|
|
{
|
|
if (animator.GetInteger($"Emote") != 0) return;
|
|
|
|
playingType = PlayingType.EMOTE;
|
|
playingHand = PlayingHand.NONE;
|
|
PlayEmote(emoteNumber + 1, animator, emoteAnimation);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
EditorGUILayout.HelpBox("使用するにはVRCSDK2がプロジェクトにインポートされている必要があります", MessageType.Error);
|
|
#endif
|
|
}
|
|
|
|
private void PlayOverride(string hand, int overrideNumber, Animator animator)
|
|
{
|
|
animator.SetInteger($"HandGesture{hand}", overrideNumber);
|
|
}
|
|
|
|
private async void PlayEmote(int emoteNumber, Animator animator, AnimationClip animationClip)
|
|
{
|
|
int waitMilliSecond = (int)(animationClip.length * 1000);
|
|
if (animationClip == null) waitMilliSecond = 1000;
|
|
animator.SetInteger($"Emote", emoteNumber);
|
|
// EmoteAnimationの実行が終わるまで待つ必要がある
|
|
await Task.Delay(waitMilliSecond);
|
|
animator.SetInteger($"Emote", 0);
|
|
}
|
|
|
|
private GameObject CreatePoseConstrainterToRootIfNeeded()
|
|
{
|
|
var obj = GameObject.Find("PoseConstrainter");
|
|
if (obj is null)
|
|
{
|
|
var prefab = Resources.Load("Tester/PoseConstrainter");
|
|
obj = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
|
|
}
|
|
return obj;
|
|
}
|
|
}
|
|
} |