目次
1. BlenderでVroidのモデルを編集しよう
2. Oculus Quest 2の準備をしよう
3. UnityにVRMモデルを入れてみよう
4. VeryAnimationで遊んでみよう
5. DynamicBoneでいろいろ遊んでみよう
6. UnityのAnimationでいちゃいちゃしよう
デモとパッケージ
5. DynamicBoneでいろいろ遊んでみよう
まず、目標としては
- おっぱいにふれる
- 髪の毛を触る
- スカートをめくる
の3つをやっていきます。
###6で使用するColliderの設置
これらのColliderは後でいちゃいちゃするのに使います。なので、6が必要ない方は次のステップに進んでください。
まず、初めにsecondary にあるVRM Spring Boneのチェックマークを外し、すべて無効にします。
そして、AssetにSAColliderBuilderをimport します。
次にVRMモデルの一番の親のところ(Animatorがあるところ)にSA Bone Collider Builderを設置して、画像のようにパラメータを設定し、Processを押します。
ちょっと、時間がかかるので待ってください。
次に、SA Collider ClearをRootのオブジェクトにアタッチして、右クリックからClean All Childrenを実行してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SAColliderCleaner : MonoBehaviour
{
[ContextMenu("Clean All Children")]
void CleanAllChildren()
{
var allChildren = GetAll(gameObject.gameObject);
foreach (var obj in allChildren)
{
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb)
{
DestroyImmediate(obj);
}
}
}
// Start is called before the first frame update
private List<GameObject> GetAll(GameObject gameObject)
{
List<GameObject> allChildren = new List<GameObject>();
GetChildren(gameObject, ref allChildren);
return allChildren;
}
//子要素を取得してリストに追加
private void GetChildren(GameObject obj, ref List<GameObject> allChildren)
{
Transform children = obj.GetComponentInChildren<Transform>();
//子要素がいなければ終了
if (children.childCount == 0)
{
return;
}
foreach (Transform ob in children)
{
allChildren.Add(ob.gameObject);
GetChildren(ob.gameObject, ref allChildren);
}
}
}
次にJ_Bip_C_Hips, J_Bip_C_UpperChest, J_Bip_L_UpperArm, J_Bip_R_LowerArmのSABoneColliderから、Processを実行しColliderを設置します。
各Colliderの大きさを調節して、できるだけ写真のようにメッシュに沿うようにしてください。
胸に髪の毛が入らないようにするためのDynamicBoneColliderの設置
J_Bip_C_UpperChestに空のオブジェクトをつくり、名前をDBC_upperChestにしてください。
DynamicBoneColliderをDBC_upperChestにAddComponentして、Coll.J_Bip_C_UpperChestを参考にして、大きさを調節します。
サイドヘアーがめり込まないようにするためなので、写真のように少しずらします。
Oculus Hand にDynamicBoneColliderの設置
まず、OculusHand_LとOculusHand_RのそれぞれにSABoneColliderBulderをAddComponentします。
写真のようにパラメータを設定し、Processを実行します。
OculusHand_L, OculusHand_Rのそれぞれに以下のDynamicBoneColliderSetterをAddComponentします。
関数の中身について
- CopyColliderInChildren -> 通常のColliderに合わせて、DynamicBoneColliderを仕込むやつ
- RemoveAllColliders -> CopyColliderInChildrenで仕込んだ、DynamicBoneColliderの消去
- SetTargetColliders -> アタッチされたコンポーネント中に存在するDynamicBone中に(ここに登録したDynamicBoneColliderにより、DynamicBoneが動かされる)、Target以下の子要素のDynamicBoneColliderに登録するやつ
- RemoveTargetColliders -> SetTargetCollidersで登録したDynamicBoneColliderを消去するやつ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DynamicBoneColliderSetter : MonoBehaviour
{
// [SerializeField] public GameObject baseObject;
private CapsuleCollider fromCollider;
private GameObject baseObject;
[SerializeField] private GameObject target;
[ContextMenu("Copy DynamicBoneColliders in Chilren refered to Unity Colliders")]
public void CopyColliderInChildren()
{
var allChildren = GetAll(target);
foreach (var obj in allChildren)
{
fromCollider = obj.GetComponent<CapsuleCollider>();
if (fromCollider ){
obj.AddComponent<DynamicBoneCollider>();
DynamicBoneCollider toCollider = obj.GetComponent<DynamicBoneCollider>();
if (fromCollider.direction == 0)
{
toCollider.m_Direction = DynamicBoneColliderBase.Direction.X;
}
else if (fromCollider.direction == 1)
{
toCollider.m_Direction = DynamicBoneColliderBase.Direction.Y;
}
else
{
toCollider.m_Direction = DynamicBoneColliderBase.Direction.Z;
}
toCollider.m_Center.x = fromCollider.center.x;
toCollider.m_Center.y = fromCollider.center.y;
toCollider.m_Center.z = fromCollider.center.z;
toCollider.m_Height = fromCollider.height;
toCollider.m_Radius = fromCollider.radius;
}
}
Debug.Log("Set all DynamicBoneColliders referenced Unity Colliders.");
}
[ContextMenu("Remove Colliders in Children set by Copy DynamicBoneColliders")]
public void RemoveAllColliders()
{
var allChildren = GetAll(target);
foreach (var obj in allChildren)
{
var toCollider = obj.GetComponent<DynamicBoneCollider>();
if (toCollider)
{
DestroyImmediate(toCollider);
}
}
Debug.Log("Remove all DynamicBoneColliders Set by CopyColliderInChildren.");
}
[ContextMenu("Set DynamicBoneColliders as Colliders in DynamicBone attatched in this component.")]
public void SetTargetColliders()
{
var allTargetColliders = new List<DynamicBoneCollider>();
var allChildren = GetAll(target);
var db = GetComponent<DynamicBone>();
foreach (var obj in allChildren)
{
DynamicBoneCollider dbc = obj.GetComponent<DynamicBoneCollider>();
if (dbc)
{
allTargetColliders.Add(dbc);
}
}
foreach (var col in allTargetColliders)
{
db.m_Colliders.Add(col);
}
Debug.Log("Set DynamicBoneColliders as Colliders in DynamicBone Attached in this component.");
}
[ContextMenu("Remove Target Colliders set by SetTargetColliders")]
public void RemoveTargetColliders()
{
var db = GetComponent<DynamicBone>();
db.m_Colliders.Clear();
Debug.Log("Remove DynamicBoneColliders in DynamicBone Attached in this component.");
}
private List<GameObject> GetAll (GameObject gameObject)
{
List<GameObject> allChildren = new List<GameObject> ();
GetChildren (gameObject, ref allChildren);
return allChildren;
}
//子要素を取得してリストに追加
private void GetChildren (GameObject obj, ref List<GameObject> allChildren)
{
Transform children = obj.GetComponentInChildren<Transform> ();
//子要素がいなければ終了
if (children.childCount == 0) {
return;
}
foreach (Transform ob in children) {
allChildren.Add (ob.gameObject);
GetChildren (ob.gameObject, ref allChildren);
}
}
}
DynamicBoneColliderSetterのhandにはそれぞれOculusHand_L, OculusHand_RをDrag&Dropして、右クリックから、Copy Colliders in Chilrenを実行します。
これで、手の形に添ってDynamicBoneColliderとColliderが設置できました。
おっぱいに触ってみよう!
いよいよ、おっぱいを揺らしてみたいと思います。
まず、J_Sec_L_Bust1とJ_Sec_R_Bust1にDynamicBoneをAddComponentして、以下の写真のようにパラメータを設定します。
RootとRadiusに関してはご自身のモデルにより、適宜調整してください。
次に先ほどのDynamicBoneColliderSetterをJ_Bip_L_Bust1とJ_Bip_L_Bust2にAddComponentして、HandにTrackingSpaceを入れ、右クリックからSet Hand Collidersを実行します。これにより、TrakingSpaceの下にあるすべてのDynamicBoneに対して、胸がColliderとして設置されます。
両胸のDynamicBoneにOculusHandのDynamicBoneColliderを設定したら、おっぱいに触れるようになっているので、アプリをBuildして、ぜひ触ってみてください。
髪の毛に触ってみよう
次に髪の毛を触れるようにしていきたいと思います。
髪の毛のDynamicBoneはLongHairSupporterの力をお借りして設定していきます。
LongHairSupporterとありますが、短髪の場合でも、髪の毛を管理するのにとても便利なので使っていただけると、楽できます。
まず、写真のようにAvatar Major Ojbects とAvatar Hair Objects のHairs Parentを設定します。
つぎに各髪の毛についてHairTypeを設定します。
- キャラクターの髪の毛が長髪の場合
アホ毛・触角 -> Others
前髪 -> Bangs
横(耳より前)の短い髪 -> Bangs
横(耳より前)の長い髪 -> Side Hair
後ろ髪 -> Back Hair
- キャラクターの髪の毛が短髪の場合
アホ毛・触角 -> Others
そのほかの髪 -> Bangs
以上のように設定してください。
そうしましたら、LongHairSupporter > Option > Dynamic Bone Reset のチェックをつけて、一番下のDynamicBone&Collider生成を押してください。
まず、J_Bip_C_Head > DBC_head > DBC_head_actualが作成され、ここにDynamicBoneColliderができているので、このDynamic Bone Collider のRadiusを1.0にします。
つぎにLongHairSupporterのPrefabの子を使って、各髪の毛の編集をしていきます。
DynamicBoneModel_back, DynamicBoneModel_side, DynamicBoneModel_bangs, DynamicBoneModel_othersのDynamicBoneをそれぞれ以下の写真のように設定してください。
つぎに、Oculus Hand にDynamicBoneColliderの設置 で使用した、DynamicBoneColliderSetter.csをDynamicBoneModel_back, DynamicBoneModel_side, DynamicBoneModel_bangs, DynamicBoneModel_othersにそれぞれアタッチしてください。
DynamicBoneColliderSetterのHandにはTrackingSpaceをDrag&Dropして、右クリックから、SetHandCollidersを実行します。
すると、DynamicBoneに両手のColliderがすべて設置されるので、そうしましたらParameter Copy H のDynamicBone設定コピー&ペーストを実行します。
これをDynamicBoneModel_back, DynamicBoneModel_side, DynamicBoneModel_bangs, DynamicBoneModel_othersについて、行います。
以上で、髪の毛の設定については完了です。
以下に注意点を上げていきます。
- DynamicBoneModel_sideのColliderについて
DynamicBoneModel_sideのColliderには両手のColliderの他にも、DBC_UpperChestを用います。
もし、DynamicBone設定コピー&ペーストをした後に入れる場合は、必ずDynamicBoneのCollidersを0にしてから、CollidersにDBC_UpperChestをいれて、DynamicBone設定コピー&ペーストを押してください。
- DynamicBoneModelのパラメータ変更について
公式にあるとおりにDynamicBone設定コピー&ペースト、または停止後ペーストをおしていただければ大丈夫です。
ですが、かならずCollidersを0にしてから実行してください。髪の毛にCollidersが重複して入ります。
- Collidersの消し忘れをしそうな方
Assets > LonghairSupporter > Script > Editor > ParameterCopyHEditor.csのCopy()関数の一番下に以下のスクリプトを追加してください。
private void Copy()
{
foreach (LonghairSupporter.HairsGroup group in longhairSupporter.hairsGroup)
{
///
}
longhairSupporter.dynamicBoneModels[(int) parameterCopyH.hairType].m_Colliders.Clear(); //取得したDynamicBoneのColliderを初期化 <= この行!
}
- Colliderを重複して入れてしまった方
以下のDynamicBoneClearner.csをJ_Bip_C_Headに追加し、右クリックからClean Dynamic Boneを実行してください。その後、LonghairSuporterのDynamicBone&Collider生成から、やり直します。この時は調整したパラメータはそのままなので、Colliderの追加だけ新規に行ってください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DynamicBoneCleaner : MonoBehaviour
{
private DynamicBone db;
[ContextMenu("Clean Dynamic Bone")]
void RemoveDynamicBone()
{
var allChildren = GetAll(gameObject);
foreach (var obj in allChildren)
{
var db = obj.GetComponent<DynamicBone>();
if (db)
{
DestroyImmediate(db);
}
}
}
private List<GameObject> GetAll (GameObject gameObject)
{
List<GameObject> allChildren = new List<GameObject> ();
allChildren.Add(gameObject);
GetChildren (gameObject, ref allChildren);
return allChildren;
}
//子要素を取得してリストに追加
private void GetChildren (GameObject obj, ref List<GameObject> allChildren)
{
Transform children = obj.GetComponentInChildren<Transform> ();
//子要素がいなければ終了
if (children.childCount == 0) {
return;
}
foreach (Transform ob in children) {
allChildren.Add (ob.gameObject);
GetChildren (ob.gameObject, ref allChildren);
}
}
}
- 誤った髪の毛をHairTypeに設定してしまった
Side HairのところをBangにしてしまうなどのミスが発生してしまった方用です。
LonghairSupporterについては Hair Hung を行うために、髪の毛に新しくオブジェクトが追加されます。
これらがOculus Quest 2では変に実行され、その髪の毛がスーパーサイヤ人化(笑)することがあります。
そうなってしまった場合に、対処する方法です。
LonghairSupporterには追加したObjectを消す機能はないので、追加します。
Projectから Assets > LOnghairSupoperter > Script > Editor の LonghairSupporterEditor.csを以下の用に書き換えます。
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(LonghairSupporter))]//拡張するクラスを指定
public class LonghairSupporterEditor : Editor
{
private LonghairSupporter longhairSupporter;
/// <summary>
/// InspectorのGUIを更新
/// </summary>
public override void OnInspectorGUI()
{
//元のInspector部分を表示
base.OnInspectorGUI();
//targetを変換して対象を取得
longhairSupporter = target as LonghairSupporter;
if (GUILayout.Button("DynamicBone&Collider生成"))
{
DynamicBoneCreate();
}
if (GUILayout.Button("LonghairSupporter 初期化"))
{
DynamicBoneRemove();
}
}
public void DynamicBoneCreate()
{
///
}
public void DynamicBoneRemove()
{
var head = longhairSupporter.head;
var allChildren = GetAll(head.gameObject);
foreach (var obj in allChildren)
{
if (obj.transform.parent.gameObject.name.EndsWith("_branch2"))
{
obj.transform.parent = obj.transform.parent.gameObject.transform.parent.gameObject.transform.parent.gameObject.transform.parent.gameObject.transform;
}
}
for (int i = head.transform.childCount-1; i >=0 ; --i)
{
var child = head.transform.GetChild(i).gameObject;
if (child.name.EndsWith("_root") || child.name == "DBC_head")
{
DestroyImmediate(child);
}
}
Debug.Log("DynamicBone&Collider消去完了");
}
private void ParameterCheck()
{
///
}
private void ClearObjects()
{
///
}
private void DynamicBonePrepare()
{
///
}
private void SetDynamicBoneCollider()
{
///
}
private void SetHairHang()
{
///
}
private void AfterCheck()
{
///
}
private List<Transform> GetAffectedObjects(Transform root, List<Transform> exclusions)
{
///
}
private void Alert(string message, bool error = true)
{
///
}
private List<GameObject> GetAll (GameObject gameObject)
{
List<GameObject> allChildren = new List<GameObject> ();
allChildren.Add(gameObject);
GetChildren (gameObject, ref allChildren);
return allChildren;
}
//子要素を取得してリストに追加
private void GetChildren (GameObject obj, ref List<GameObject> allChildren)
{
Transform children = obj.GetComponentInChildren<Transform> ();
//子要素がいなければ終了
if (children.childCount == 0) {
return;
}
foreach (Transform ob in children) {
allChildren.Add (ob.gameObject);
GetChildren (ob.gameObject, ref allChildren);
}
}
すると、LonghairSupporterのInspectorにLonghairSupporter初期化というボタンが出てくるので、これをクリックします。
次に、DynamicBoneClearner.csをJ_Bip_C_Headに追加し、右クリックからClean Dynamic Boneを実行してください。
これで、LonghairSupporterによるDynamicBoneの設定前の状態に戻すことができます。
誤った髪の毛のHairTypeを設定しなおして、再度DynamicBone&Collider生成を行ってください。
スカートに触れてみよう!
最後にスカートに触れてみたいと思います。
まず、J_BipC_Hipsに空のオブジェクトをつくり、名前をJ_Bip_Skirtにします。
そのあとで、J_Bip_L_Upper_LegとJ_Bip_R_Lower_Legからオブジェクトを移動させて、写真のような構造にします。
そうしましたら、J_Bip_SkirtにDynamicBoneをAddComponentから追加して写真のようにパラメータを設定します。
つぎに、J_Bip_SkirtにDynamicBoneColliderSetter.csを追加して、HandにはTrackinigSpaceを設定し、右クリックから Set Hand Collidersを実行します。
これでスカートにColliderが設定され、スカートをめくれるようになりました((+_+))
ちょっとしたコツなのですが、キャラクターの手にDynamicBoneColliderを設定し、J_Bip_SkirtのCollidersに追加すると、リアル感が増します。