読者です 読者をやめる 読者になる 読者になる

Onoty3D

Unityに関するメモとか

プロ生ちゃんの表情を保存する

Unity プロ生ちゃん スクリプト

もひとつBlendShapeネタ。

プロ生ちゃんの特定の表情を保存しておくのって結構大変です。
30個あるBlendShapeの各値をメモったり、キャプチャしたり…。
更に保存したものを復元するときも、そのメモから手で打ちなおしたりする必要があります。
値をXMLJSONなんかで吐き出すようなスクリプトを作ってもいいのですが、もう少し簡易に出来ないかな、と考えてみました。

例えば各値を整数化し、2桁の16進数に書き換えて、それを順につなげてひとつの文字列として出力してみたらどうでしょう。
前述のとおりプロ生ちゃんのBlendShapeは30個なので、60文字の文字列になります。
取り込むときはそれを2文字ずつ切り離して、10進数に戻して各BlendShapeにセットします。
本来各値はflort型なので、小数点以下が切捨てられるため、完全な値の保存・復元にはならないのですが、ブログにちょろっと貼ったりするのには便利かもしれません。

以前作ったFaceUpdateのスクリプトに機能を足してみました(最後にソースを載せてます)。

これをプロ生ちゃんに適用させると、前述の60文字の文字列データの入出力ができるようになります。

[出力]
表情を設定したら、インスペクタのFaceUpdate上で右クリックし、ExportBlendShapeValueを選びます。
選ぶと、BlendShapeValueの項目に今の表情を示す60文字の文字列が出力されます。
f:id:onoty3d:20150325193844p:plain

[入力]
インスペクタのFaceUpdateのBlendShapeValueの項目に任意の表情を示す60文字の文字列をセットし、右クリックでImportBlendShapeValueを選びます。
選ぶと、文字列に従った表情が再現されます。
f:id:onoty3d:20150325193851p:plain

どちらもシーンを実行中に利用可能です。
実用性があるかは謎…。

000000000030001600000000640c0000000000000000000000080b000000
f:id:onoty3d:20150325193921p:plain

00000000003000160000001c640c001522000000290000002f080b000000
f:id:onoty3d:20150325193950p:plain

0000640000640000001c0000640000002000000000003400000022000000
f:id:onoty3d:20150325194038p:plain

ソース
簡易にするため文字列の妥当性チェック等しておりませんので、お気をつけください。

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using UnityEngine;

namespace PronamaChan
{
    public class FaceUpdate : MonoBehaviour
    {
        public SkinnedMeshRenderer SkinnedMeshRenderer;

        public string BlendShapeValue;

        //各スライダーの現在値を保持するリスト
        private List<float> _sliderValues;

        //BlendShape名の表示スタイル
        private GUIStyle _labelTitleStyle;

        //スライダー値の表示スタイル
        private GUIStyle _labelValueStyle;

        private void Start()
        {
            //SkinnedMeshRendererの取得
            if (this.SkinnedMeshRenderer == null) this.SkinnedMeshRenderer = this.GetComponentInChildren<SkinnedMeshRenderer>();

            this._sliderValues = Enumerable.Repeat<float>(0f, this.SkinnedMeshRenderer.sharedMesh.blendShapeCount).ToList();

            this._labelTitleStyle = new GUIStyle { fixedWidth = 80f, alignment = TextAnchor.MiddleRight, normal = new GUIStyleState { textColor = Color.white } };
            this._labelValueStyle = new GUIStyle { fixedWidth = 20f, alignment = TextAnchor.MiddleRight, normal = new GUIStyleState { textColor = Color.white } };
        }

        private void OnGUI()
        {
            GUILayout.Box("", GUILayout.Width(220), GUILayout.Height(15 * (this.SkinnedMeshRenderer.sharedMesh.blendShapeCount + 1)));
            Rect screenRect = new Rect(10, 10, 190, 15 * (this.SkinnedMeshRenderer.sharedMesh.blendShapeCount + 1));
            GUILayout.BeginArea(screenRect);
            for (int index = 0; index < this.SkinnedMeshRenderer.sharedMesh.blendShapeCount; index++)
            {
                GUILayout.BeginHorizontal();

                //BlendShape名
                GUILayout.Label(this.SkinnedMeshRenderer.sharedMesh.GetBlendShapeName(index), this._labelTitleStyle);

                //スライダー
                this._sliderValues[index] = GUILayout.HorizontalSlider(this._sliderValues[index], 0f, 100f);

                //スライダーの値
                GUILayout.Label(((int)this._sliderValues[index]).ToString("#0"), this._labelValueStyle);

                GUILayout.EndHorizontal();

                //スライダーの値をBlendShapeに反映
                this.SkinnedMeshRenderer.SetBlendShapeWeight(index, this._sliderValues[index]);
            }
            GUILayout.EndArea();
        }

        private void Update()
        {
        }

        [ContextMenu("ExportBlendShapeValue")]
        private void ExportBlendShapeValue()
        {
            var sb = new StringBuilder(this.SkinnedMeshRenderer.sharedMesh.blendShapeCount);


            for (int index = 0; index < this.SkinnedMeshRenderer.sharedMesh.blendShapeCount; index++)
            {
                sb.Append(Convert.ToString((int)this._sliderValues[index], 16).PadLeft(2, '0'));
            }

            this.BlendShapeValue = sb.ToString();
        }

        [ContextMenu("ImportBlendShapeValue")]
        private void ImportBlendShapeValue()
        {
            for (int index = 0; index < this.SkinnedMeshRenderer.sharedMesh.blendShapeCount; index++)
            {
                this._sliderValues[index] = int.Parse(new string(new[] { this.BlendShapeValue[index * 2], this.BlendShapeValue[index * 2 + 1] }), NumberStyles.HexNumber);
            }
        }
    }
}