28
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Live2DのUnity版でシンプル実装

Last updated at Posted at 2015-02-18

Live2DのSampleApp1プロジェクトは全部入りですが、複雑で難しいです。
最も簡単なSimpleプロジェクトをカスタムしてモーション・音声が再生できるシンプル実装してみました。

Live2Dで音声・モーション付きのシンプル実装

でき上がりは、UGUIボタン操作でモーション再生するかんじ
2015-02-18_21h27_36.png

InspectorはこんなかんじでスクリプトとAudio Sourceだけ。
PathにはResources配下のフォルダ名をセットしてます。
2015-02-18_21h27_25.png

フォルダ構成はResourcesの下にLive2Dモデルをいれておきます。
ここにあるmodel.jsonをInspectorにセットしています。model.jsonはLive2D Cubism Modelerから出力できます。
2015-02-18_21h29_43.png

ソースコード

モーションボタンが押されない限り、配列0番目のモーションがループ再生される仕様です。
配列0番目のモーションはmodel.json上で指定します(model.jsonもソースコードの下に書きました!)

2015/09/17追記
Live2D Unity SDK2.1からクリッピングマスク使用モデルの場合はlive2DModel.updateのタイミングが変更

SimpleModel.cs
using UnityEngine;
using System;
using System.Collections;
using System.Text.RegularExpressions;
using live2d;


[ExecuteInEditMode]
public class SimpleModel : MonoBehaviour 
{
    public string path;                       // モデルパス
    public TextAsset modelJson;                 // model.json
    private string modelpath;                  // モデルパス
    private TextAsset mocFile;                  // モデルファイル
    private Texture2D[] textures;               // テクスチャファイル
    private TextAsset[] mtnFiles;               // モーションファイル
    private int[] mtnFadeines;                 // フェードイン
    private int[] mtnFadeoutes;                // フェードアウト
    private AudioClip[] soundFiles;             // 音声ファイル
	private Live2DModelUnity live2DModel;
    private Live2DMotion motion;                // モーションクラス
    private MotionQueueManager motionManager;     // モーション管理クラス
    private Matrix4x4 live2DCanvasPos;
    private int motioncnt = 0;                 // モーション番号


    /// <summary>
    /// 初期処理
    /// </summary>
	void Start () 
	{
        // Live2D初期化
        Live2D.init();
        // model.jsonを読み込む
        Json_Read();
        // モーション管理クラスのインスタンス作成
        motionManager = new MotionQueueManager();
        // モーションのインスタンス作成
        motion = Live2DMotion.loadMotion(mtnFiles[motioncnt].bytes);
        // モーションの再生
        motionManager.startMotion(motion, false);

        // Live2Dモデルの表示位置計算
        float modelWidth = live2DModel.getCanvasWidth();
        live2DCanvasPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50.0f, 50.0f);
	}


    /// <summary>
    /// model.jsonを読み込む
    /// </summary>
    void Json_Read()
    {
        // model.jsonを読み込む
        char[] buf = modelJson.text.ToCharArray();
        Value json = Json.parseFromBytes(buf);

        modelpath = path + "/";

        // モデルを読み込む
        mocFile = new TextAsset();
        mocFile = (Resources.Load(modelpath + json.get("model").toString(), typeof(TextAsset)) as TextAsset);
        live2DModel = Live2DModelUnity.loadModel(mocFile.bytes);

        // テクスチャを読み込む
        int texture_num = json.get("textures").getVector(null).Count;
        textures = new Texture2D[texture_num];

        for (int i = 0; i < texture_num; i++)
        {
            // 不要な拡張子を削除
            string texturenm = Regex.Replace(modelpath + json.get("textures").get(i).toString(), ".png$", "");
            textures[i] = (Resources.Load(texturenm, typeof(Texture2D)) as Texture2D);
            live2DModel.setTexture(i, textures[i]);
        }

        // モーションとサウンドを読み込む(motions配下のタグを読み込む)
        Value mtnpath = json.get("motions").get("null");
        int mtn_num = mtnpath.getVector(null).Count;
        mtnFiles = new TextAsset[mtn_num];
        soundFiles = new AudioClip[mtn_num];
        mtnFadeines = new int[mtn_num];
        mtnFadeoutes = new int[mtn_num];

        for (int n = 0; n < mtn_num; n++)
        {
            mtnFiles[n] = (Resources.Load(modelpath + mtnpath.get(n).get("file").toString()) as TextAsset);
            // サウンドファイルがあれば入れる            
            if (mtnpath.get(n).getMap(null).ContainsKey("sound"))
            {
                // 不要な拡張子を削除
                string soundnm = Regex.Replace(Regex.Replace(modelpath + mtnpath.get(n).get("sound").toString(), ".mp3$", ""), ".wav$", "");
                soundFiles[n] = (Resources.Load(soundnm, typeof(AudioClip)) as AudioClip);
            }
            //フェードイン
            if (mtnpath.get(n).getMap(null).ContainsKey("fade_in"))
            {
                mtnFadeines[n] = int.Parse(mtnpath.get(n).get("fade_in").toString());
            }
            //フェードアウト
            if (mtnpath.get(n).getMap(null).ContainsKey("fade_out"))
            {
                mtnFadeoutes[n] = int.Parse(mtnpath.get(n).get("fade_out").toString());
            }
        }
    }

    /// <summary>
    /// 更新処理
    /// </summary>
    void Update()
    {
        // モーション再生が終了した場合
        if (motionManager != null && motionManager.isFinished())
        {
            motioncnt = 0;
            // モーションをロードする
            motion = Live2DMotion.loadMotion(mtnFiles[motioncnt].bytes);
            // フェードインの設定
            if (mtnFadeines[motioncnt] != null) motion.setFadeIn(mtnFadeines[motioncnt]);
            // フェードアウトの設定
            if (mtnFadeoutes[motioncnt] != null) motion.setFadeOut(mtnFadeoutes[motioncnt]);
            // モーション再生
            motionManager.startMotion(motion, false);
        }
    }


    /// <summary>
    /// カメラシーンにレンダリング時呼ばれる
    /// </summary>
    void OnRenderObject()
    {
        if (live2DModel == null) return;
        live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos);
        // アプリが終了していた場合
        if (!Application.isPlaying)
        {
            live2DModel.update();
            live2DModel.draw();
            return;
        }

        // 再生中のモーションからモデルパラメータを更新
        motionManager.updateParam(live2DModel);
        // 頂点の更新
        live2DModel.update();
        // モデルの描画
        live2DModel.draw();
    }


    /// <summary>
    /// モーションチェンジ
    /// </summary>
    /// <param name="filenm"></param>
    void Motion_change(string filenm) 
    {
        int cnt = 0;
        for (int m = 0; m < mtnFiles.Length; m++)
        {
            if (mtnFiles[m].name == filenm)
            {
                break;
            }
            cnt++;            
        }
        // モーションのロードをする
        motion = Live2DMotion.loadMotion(mtnFiles[cnt].bytes);
        // フェードインの設定
        if (mtnFadeines[cnt] != null) motion.setFadeIn(mtnFadeines[cnt]);
        // フェードアウトの設定
        if (mtnFadeoutes[cnt] != null) motion.setFadeOut(mtnFadeoutes[cnt]);
        // モーション再生
        motionManager.startMotion(motion, false);
        // 音声再生
        if (soundFiles[cnt] != null)
        {
            audio.clip = soundFiles[cnt];
            audio.Play();
        } 
    }

    // UGUIから呼び出す処理1
    public void Motion_idle1()
    {
        Motion_change("tetudau.mtn");
    }

    // UGUIから呼び出す処理2
    public void Motion_idle2()
    {
        Motion_change("nurupo.mtn");
    }
}

model.jsonはこんなかんじでmoitons-null配下の1行目をアイドルモーションとしてます

model.json
{
	"version":"Sample 1.0.0",
	"model":"model/model.moc",
	"textures":[
		"model/model.1024/texture_00.png"
	],
	"motions":{
		"null":[
			{"file":"motions/idle.mtn"}, 
			{"file":"motions/chotto.mtn","sound":"sounds/kei_voice_020_1.mp3","fade_in":100,"fade_out":100},
			{"file":"motions/nurupo.mtn","sound":"sounds/kei_voice_035_1.mp3","fade_in":100,"fade_out":100},
			{"file":"motions/otukare.mtn","sound":"sounds/kei_voice_017_2.mp3","fade_in":100,"fade_out":100},
			{"file":"motions/tetudau.mtn","sound":"sounds/kei_voice_023_2.mp3","fade_in":100,"fade_out":100}
		],
		"idle":[
			{"file":"motions/idle.mtn"}
		]
	},
	"physics":"embed://Sample Physics.json"
}
code

HierarchyウィンドウでCreateボタンでUI-Buttonして、
UGUIボタンからOnClick()イベントを呼べばモーションと音声再生できるシンプル実装の出来上がりです!
2015-02-18_21h34_14.png

あと以前MiniJSON使ったものをブログに書いたけど、Live2DのライブラリにはJson解析コードが含まれていて、外部のものを使う必要ありませんでした~。

参考URL:Live2DでJSON対応のUnityコード書いた

28
26
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?