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