Skip to content

Commit

Permalink
Merge pull request #56 from uezo/develop
Browse files Browse the repository at this point in the history
Add context menu to setup AnimatorController
  • Loading branch information
uezo authored Jun 17, 2020
2 parents 6072d49 + 2fd1082 commit 7f0ddba
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 20 deletions.
119 changes: 119 additions & 0 deletions ChatdollKit/Editor/FaceClipEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor.Animations;
using UnityEditor;
using Newtonsoft.Json;
using ChatdollKit.Model;
Expand Down Expand Up @@ -244,6 +245,124 @@ private static void Setup(MenuCommand menuCommand)
modelController.AudioSource = lipSyncObject.GetComponent<AudioSource>();
}

// Setup ModelController
[MenuItem("CONTEXT/ModelController/Setup Animator")]
private static void CreateAnimationControllerWithClips(MenuCommand menuCommand)
{
var animationClipFolderPath = EditorUtility.OpenFolderPanel("Select animation clip parent folder", Application.dataPath, string.Empty);

if (!string.IsNullOrEmpty(animationClipFolderPath))
{
// Get animation clips from folder
var animationClips = GetLayeredAnimationClips(animationClipFolderPath);

// Return when no clips found
if (animationClips.Count == 0)
{
Debug.LogWarning("No animation clips found");
return;
}

// Make path to create new animator controller
var animatorControllerPath = "Assets" + animationClipFolderPath.Replace(Application.dataPath, string.Empty);
animatorControllerPath = Path.Combine(animatorControllerPath, "ChatdollAnimatorController.controller");

if (AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(animatorControllerPath) != null)
{
// Confirm overwrite when exists
if (!EditorUtility.DisplayDialog("AnimatorController exists", $"AnimatorController already exists at {animatorControllerPath}. Are you sure to overwrite?", "OK", "Cancel"))
{
return;
}
}

// Create new animator controller
var animatorController = AnimatorController.CreateAnimatorControllerAtPath(animatorControllerPath);

foreach (var kv in animationClips)
{
// Select layer
var layerName = kv.Key;
var putOnBaseLayer = layerName == "Base layer" || EditorUtility.DisplayDialog("Select Layer", $"{kv.Value.Count} clips found in {layerName}. Put these clips on ...", "Base Layer", $"{layerName}");
if (!putOnBaseLayer)
{
animatorController.AddLayer(layerName);
}
var layer = putOnBaseLayer ? animatorController.layers[0] : animatorController.layers.Last();

// Create default state
if (!layer.stateMachine.states.Select(st => st.state.name).Contains("Default"))
{
var defaultState = layer.stateMachine.AddState("Default");
if (putOnBaseLayer && kv.Value.Count > 0)
{
defaultState.motion = kv.Value[0];
}

}

// Put animation clips on layer
foreach (var clip in kv.Value)
{
var state = layer.stateMachine.AddState(clip.name);
state.motion = clip;
}
}

// Set controller to animator
var modelController = menuCommand.context as ModelController;
var animator = modelController.gameObject.GetComponent<Animator>();
if (animator != null)
{
animator.runtimeAnimatorController = animatorController;
}
}
}

private static Dictionary<string, List<AnimationClip>> GetLayeredAnimationClips(string parentPath)
{
var animationClips = new Dictionary<string, List<AnimationClip>>();

var directories = Directory.GetDirectories(parentPath);
if (directories.Length > 0)
{
foreach (var d in directories)
{
var clips = GetAnimationClips(d);
if (clips.Count > 0)
{
animationClips[d.Split('/').Last()] = clips;
}
}
}
else
{
var clips = GetAnimationClips(parentPath);
if (clips.Count > 0)
{
animationClips["Base Layer"] = clips;
}
}

return animationClips;
}

private static List<AnimationClip> GetAnimationClips(string folderPath)
{
var clips = new List<AnimationClip>();
var files = Directory.GetFiles(folderPath, "*", SearchOption.TopDirectoryOnly);
foreach (var f in files)
{
var assetPath = "Assets" + f.Replace(Application.dataPath, string.Empty);
var a = AssetDatabase.LoadAssetAtPath<AnimationClip>(assetPath);
if (a != null)
{
clips.Add(a);
}
}
return clips;
}

private static VisemeTarget GetVisemeTarget(SkinnedMeshRenderer[] skinnedMeshRenderers, bool IsVRM = false)
{
var maxCount = 1;
Expand Down
37 changes: 27 additions & 10 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ Watch this 2 minutes video to learn how ChatdollKit works and the way to use qui

1. 🐟リソースの準備
- 3Dモデルをインポートしてシーンに追加
- 音声ファイルをリソースディレクトリに追加、AnimatorControllerを作成・設定して3Dモデルにセット
- 音声ファイルをリソースディレクトリに、アニメーションクリップをアニメーションディレクトリに配置

1. 🍣セットアップ
- インスペクターのコンテキストメニューから`Setup ModelController`を実行
- インスペクターのコンテキストメニューから`Setup ModelController``Setup Animator`を実行
- まばたき用のシェイプキーの名前を設定


Expand Down Expand Up @@ -53,14 +53,8 @@ Watch this 2 minutes video to learn how ChatdollKit works and the way to use qui

## Animations

Animator Controllerを作成してBase Layerに`Default`というステートを追加し、各モーションを配置してください。そして`Default`ステートにも任意のアイドル状態のアニメーションを追加しましょう。Base Layer以外にも、たとえば上半身のマスクを適用したレイヤーを追加し、モーションを追加しておくこともできます。その場合は必ずすべてのレイヤーに`Default`ステートを追加し、モーションには何も設定しない(=`None`のまま)ようにしてください。

<img src="https://uezo.blob.core.windows.net/github/chatdoll/04.png" width="640">

設定が完了したら、3Dモデルの`Animator`コンポーネントの`Controller`に設定しましょう。なおこの手順では[Anime Girls Idle Animations Free](https://assetstore.unity.com/packages/3d/animations/anime-girl-idle-animations-free-150406)というモーション集を利用しています。大変使い勝手が良いので気に入ったら有償版の購入をオススメします。

<img src="https://uezo.blob.core.windows.net/github/chatdoll/05_2.png" width="640">

`/Animations`ディレクトリを作成し、アニメーションクリップを配置してください。
なおこの手順では[Anime Girls Idle Animations Free](https://assetstore.unity.com/packages/3d/animations/anime-girl-idle-animations-free-150406)というモーション集を利用しています。大変使い勝手が良いので気に入ったら有償版の購入をオススメします。

# 🍣 セットアップ

Expand All @@ -78,6 +72,18 @@ Animator Controllerを作成してBase Layerに`Default`というステートを

手動で設定したい場合は [Appendix1. ModelControllerの手動設定](#Appendix%201.%20ModelControllerの手動設定) を参照してください。

## Animatorの設定

インスペクターのコンテキストメニューから`Setup Animator`を選択するとフォルダ選択ダイアログが表示されるので、アニメーションクリップが配置されたフォルダを選択してください。

<img src="https://uezo.blob.core.windows.net/github/chatdoll/setupanimator01.png" width="640">

サブフォルダが含まれる場合には、サブフォルダと同じ名前のレイヤーがAnimatorControllerに作成され、そのレイヤーにサブフォルダ内のアニメーションクリップが配置されます。(下図のCase1)

<img src="https://uezo.blob.core.windows.net/github/chatdoll/setupanimator02.png" width="640">

手動で設定したい場合は [Appendix2. Setup Animator manually](#Appendix%202.%20Animatorの手動設定)


## 動作確認

Expand Down Expand Up @@ -202,3 +208,14 @@ ChatdollKitの基本的な価値として、Unity初心者であっても簡単
- まばたきをするため、目を閉じる表現のためのシェイプキーの名前を`Blink Blend Shape Name`に設定

<img src="https://uezo.blob.core.windows.net/github/chatdoll/06_2.png" width="640">


# Appendix 2. Animatorの手動設定

Animator Controllerを作成してBase Layerに`Default`というステートを追加し、各モーションを配置してください。そして`Default`ステートにも任意のアイドル状態のアニメーションを追加しましょう。Base Layer以外にも、たとえば上半身のマスクを適用したレイヤーを追加し、モーションを追加しておくこともできます。その場合は必ずすべてのレイヤーに`Default`ステートを追加し、モーションには何も設定しない(=`None`のまま)ようにしてください。

<img src="https://uezo.blob.core.windows.net/github/chatdoll/04.png" width="640">

設定が完了したら、3Dモデルの`Animator`コンポーネントの`Controller`に設定しましょう。

<img src="https://uezo.blob.core.windows.net/github/chatdoll/05_2.png" width="640">
35 changes: 25 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ Watch this 2 minutes video to learn how ChatdollKit works and the way to use qui

1. 🐟Resource preparation
- Import 3D model and put it on the scene
- Put voice files to resource directory and configure AnimatorController and set it to the 3D model
- Put voice files to resource directory and animation clips to animations directory

1. 🍣Setup
- Run `Setup ModelController` in the context menu on inspector
- Run `Setup ModelController` and `Setup Animator` in the context menu on inspector
- Set the name of ShapeKey for blink


Expand Down Expand Up @@ -55,16 +55,9 @@ Create `/Resources/Voices` directory and put voices into there. If your don't ha

## Animations

Create Animator Controller and create `Default` state on the Base Layer, then put animations. Lastly set a motion you like to the `Default` state. You can create other layers and put animations at this time. Note that every layers should have the `Default` state and `None` should be set to their motion except for the Base Layer.

<img src="https://uezo.blob.core.windows.net/github/chatdoll/04.png" width="640">

After configuration set the Animator Controller as a `Controller` of Animator component of the 3D model.
Create `/Animations` folder and put animation clips.
In this README, I use [Anime Girls Idle Animations Free](https://assetstore.unity.com/packages/3d/animations/anime-girl-idle-animations-free-150406). I believe it is worth for you to purchase the pro edition.

<img src="https://uezo.blob.core.windows.net/github/chatdoll/05_2.png" width="640">


# 🍣 Setup

## Add ChatdollKit
Expand All @@ -81,6 +74,18 @@ Select `Setup ModelController` in the context menu of ModelController and set th

If you want to setup manually, go to [Appendix1. Setup ModelController manually](#Appendix%201.%20Setup%20ModelController%20manually)

## Setup Animator

Select `Setup Animator` in the context menu of ModelController and select the folder that contains animation clips.

<img src="https://uezo.blob.core.windows.net/github/chatdoll/setupanimator01.png" width="640">

If subfolders are included, layers with the same name as the subfolders are created in the AnimatorController, and clips in each subfolders are put on each layers. (Case1. in the picture below)

<img src="https://uezo.blob.core.windows.net/github/chatdoll/setupanimator02.png" width="640">

If you want to setup manually, go to [Appendix2. Setup Animator manually](#Appendix%202.%20Setup%20Animator%20manually)

## Run

Press Play button of Unity editor. You can see the model starts with idling animation and blinking.
Expand Down Expand Up @@ -201,3 +206,13 @@ After that, select root GameObject to which ModelController is attached.
- Set the shape key that close the eyes for blink to `Blink Blend Shape Name`.

<img src="https://uezo.blob.core.windows.net/github/chatdoll/06_2.png" width="640">

# Appendix 2. Setup Animator manually

Create Animator Controller and create `Default` state on the Base Layer, then put animations. Lastly set a motion you like to the `Default` state. You can create other layers and put animations at this time. Note that every layers should have the `Default` state and `None` should be set to their motion except for the Base Layer.

<img src="https://uezo.blob.core.windows.net/github/chatdoll/04.png" width="640">

After configuration set the Animator Controller as a `Controller` of Animator component of the 3D model.

<img src="https://uezo.blob.core.windows.net/github/chatdoll/05_2.png" width="640">

0 comments on commit 7f0ddba

Please sign in to comment.