[Unity插件]Live2D插件學習

Live2D

參考鏈接:SIKI學院

一、前言:

1/ 官網:www.live2d.com

2/ 下載

需求下載1:Cubism SDK for Unity下載地址:https://www.live2d.com/download/cubism-sdk/

需求下載2:Cubism下載地址:https://www.live2d.com/download/cubism/

Live2D Cubism:美術用的2D建模軟件,下載好後安裝,打開可以免費使用一段時間的專業版。

需求下載3:模型數據下載:https://www.live2d.com/download/sample-data/

3/ 靜態變動態原理

在圖上添加一些點,通過這些點來扭曲圖片,通過障眼法表現出動來動去的感覺。相當於在操作一些圖層。

4/ 軟件版本

2.x用的較多,3.x最新版。本例使用2.x版本。

二、操作

解壓下載好的sdk2.x Live2D_SDK_Unity_2.1.04_2_jp,得到如下目錄:

  • framework框架代碼
  • lib用到的庫
  • sample文件夾下爲案例功能,可以分別打開看下玩下
  • tool工具
  • ReadMe.txt

1.體驗

分別打開上sample文件夾下工程,體驗下。

2.嘗試

1/ 導入模型數據:

把上面下載好的**[需求下載3]模版數據(如“miku_sample.cmox”)拖入Cubism Editor[需求下載2]**中,這個文件Unity無法使用,需要使用Cubism導出moc文件。

加載好後,可以看到:

2/ 導出moc:

菜單File-Export For Runtime-Export as moc file(for 2.1)根據自己的sdk版本選擇導出2.x或者3.x。

注:若菜單是暗的不能點,則說明是免費版,沒有這個功能。

彈出菜單使用默認選項,然後導出一個moc文件,正是我們所需要的,放在原來的模型文件夾下,和.cmox文件同級。

3/ Unity中查看下載的模型的效果

  • 打開上sample文件夾下simple工程,打開Sample場景;

  • 把整個模型文件夾拖入unity項目,然後複製一份模型文件夾下最外層的.moc文件,並修改.moc爲.bytes;

  • 然後修改Sample場景下的Live2DModel物體上SimpleModel組件的值,mocFile拖入剛剛新的moc的bytes文件,TextureFiles拖入模型的貼圖。

  • 運行遊戲,發現新的模型已經正確顯示在場景中了。

3.使用

此處引用老師給的筆記:

Live2D模型製作到顯示的基本流程:

1.初始化模型。    	
	1.製作模型(讀取moc文件)。    	
	2.與貼圖建立關聯。    	
	3.與繪圖環境建立鏈接。(有些平臺不需要)。   
	4.制定顯示位置與尺寸。

2.更新模型狀態。    
	1.更新頂點。  
	2.繪圖。

1/ 創建新的工程,(本人版本Unity2017.4.29),拖入上sdk解壓得到的這幾個文件夾:
framework、lib、tool;

2/ 在[模型下載3]的鏈接裏下載你想要的模型,此處使用"Epsilon",拖到項目裏的Resources文件夾下,並新增Live2dModel.cs添加下列代碼。

3/ 使用Live2D前需要初始化:

using live2d;
//初始化
Live2D.init();

4/ 讀取模型的兩種方式:

//方式1:直接使用runtime文件夾下moc文件
Live2DModelUnity.loadModel(Application.dataPath+ "/Resources/Epsilon/runtime/Epsilon.moc");

//方式2:加載二進制文件並讀取:
//先複製上moc文件,並添加後綴.bytes,文件可放在Resources文件夾下然後加載:
TextAsset mocFile = Resources.Load<TextAsset>("Epsilon/runtime/Epsilon.moc");
Live2DModelUnity live2DModel=Live2DModelUnity.loadModel(mocFile.bytes);
//也可以直接public TextAsset mocFile這樣開放出來,然後直接拖入bytes文件到組件上。

5/ 與貼圖建立關聯(假設已經使用上條的方式2獲得了live2DModel變量)

//方式1:根據路徑
//Texture2D texture2D1 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_00");
//Texture2D texture2D2 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_01");
//Texture2D texture2D3 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_02");
//live2DModel.setTexture(0,texture2D1); //第一個參數爲貼圖順序索引
//live2DModel.setTexture(1, texture2D2);
//live2DModel.setTexture(2, texture2D3);

//方式2:拖入組件public Texture2D[] textures;
for (int i = 0; i < textures.Length; i++)
{
    live2DModel.setTexture(i, textures[i]);
}

6/ 指定顯示位置與尺寸(使用正交矩陣與相關API顯示圖像,再由遊戲物體的位置和攝像機的size調整圖像到合適的位置

  private Matrix4x4 live2DCanvasPos;
  //live2d自身的畫布
  float modelWidth = live2DModel.getCanvasWidth();
  //創建正交投影矩陣,參數爲正交視口的左,右,下,上,近視口距離,遠視口距離
  live2DCanvasPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50, 50);

7/ 更新模型狀態:更新頂點

void Update () {
    live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos);
    live2DModel.update();
}

8/ 更新模型狀態:繪圖

private void OnRenderObject()
{
    live2DModel.draw();
}

然後這個腳本掛在場景中的一個物體上,運行遊戲,可以看到小姐姐的靜態模型了。可以調整攝像機參數控制看到的小姐姐的大小。

下面看動作播放:

動作文件爲runtime/motions文件夾下的mtn文件。

9/ 播放動作:加載動作文件

//方式1:直接加載mtn文件:
//Live2DMotion.loadMotion(Application.dataPath+"路徑");

//方式2:加載bytes文件,加載或者直接拖進組件(同樣,把mtn文件複製一份加.bytes後綴)
//Live2DMotion.loadMotion(mtnFile.bytes);

public TextAsset[] motionFiles;
private Live2DMotion[] motions;

motions = new Live2DMotion[motionFiles.Length];
for (int i = 0; i < motions.Length; i++)
{
    motions[i] = Live2DMotion.loadMotion(motionFiles[i].bytes);
}

10/ 設置某一個動畫的一些屬性

motions[0].setLoopFadeIn(false);//重複播放不淡入。
motions[0].setFadeOut(1000); //設置淡入淡出時間,參數單位爲毫秒
motions[0].setFadeIn(1000);

//motions[0].setLoop(true); //動畫是否循環播放

11/ 播放動作

//進行完上述操作後,播放動作:
//動作管理
private MotionQueueManager motionQueueManager;
motionQueueManager = new MotionQueueManager();
motionQueueManager.startMotion(motions[0]);

在Update中播放動作:
void Update () {
    live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos);
	//使這個模型播放動作
    motionQueueManager.updateParam(live2DModel);

    live2DModel.update();
}

動作的bytes拖進去後,發現可以播放了。

12/ 同時播放多個動作(如臉部和身體動作拆開了,需要自由組合播放)

//有幾個動作同時播放,就需要幾個MotionQueueManager
//在上面的步驟11基礎上,再同時播放動作5:

////播放多個動作
//motions[5].setLoop(true);
//private MotionQueueManager motionQueueManagerA;//在函數外部加上這個

motionQueueManagerA = new MotionQueueManager();
motionQueueManagerA.startMotion(motions[5]);

void Update () {
    live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos);
    motionQueueManager.updateParam(live2DModel);

	motionQueueManagerA.updateParam(live2DModel);//加上這句

    live2DModel.update();
}

注意:如果需要多個動作同時播放,儘可能不要設置相同的參數。

13/ 動作的優先級

 //優先級
//L2DMotionManager繼承自MotionQueueManager
//優先級的設置標準:
//1.動作未進行的狀態,優先級爲0。
//2.待機動作發生時,優先級爲1。
//3.其他動作進行時,優先級爲2。
//4.無視優先級,強制發生的動作,優先級爲3。 

//上述代碼爲測試直接播放某個動作,下面開始把動作串起來
//初始默認播放待機動作,然後有事件過來播放其他動作時,根據優先級播放其他動作,結束了返回默認動作

private L2DMotionManager l2DMotionManager;//目前使用的動作管理器

//初始化  動作的優先級使用:
l2DMotionManager = new L2DMotionManager();

//根據動作優先級播放動作接口:
private void StartMotion(int motionIndex,int priority)
{
    if (l2DMotionManager.getCurrentPriority()>= priority)
    {
        return; 
    }
    l2DMotionManager.startMotion(motions[motionIndex]);
}

//然後在Update中判斷是否有正在播放的動畫,沒有則播放待機
void Update () {
	live2DModel.setMatrix(transform.localToWorldMatrix*live2DCanvasPos);

	判斷待機動作
    if (l2DMotionManager.isFinished())
    {
        StartMotion(0,1);
    }
    else if (Input.GetKeyDown(KeyCode.M))//測試高優先級打斷
    {
        StartMotion(14,2);
    }
    l2DMotionManager.updateParam(live2DModel);
}

14/ 設置參數

//如圖所示 中間欄下方可以通過拖動來改變參數從而改變模型的顯示效果,在Unity裏也可以通過代碼控制

//設置參數,paramID獲得方式見下圖(2.x版本需要模型源文件,不然不知道參數id,需要建模師給到配置表;3.x版本可以直接設置)
//在Update中設置。
//方式1:live2DModel.setParamFloat(string paramID, float value, float weight = 1影響度);//設置爲指定值

//如: live2DModel.setParamFloat("PARAM_ANGLE_X",1);

//方式2:live2DModel.addToParamFloat(string paramID, float value);//當前值累加

//方式3:live2DModel.multParamFloat(string paramID, float value);//當前值擴大倍數

//上幾個函數都有重載,參數id可以傳入索引int值,其中這個索引可以通過這個函數獲取
int paramAngleX = live2DModel.getParamIndex("PARAM_ANGLE_X");
live2DModel.setParamFloat(paramAngleX,30);
		
//參數的保存與恢復
//保存與恢復的參數是整個模型的所有參數,並不只是之前同方法裏設置的某幾個參數
//live2DModel.saveParam();
//live2DModel.loadParam();

15/ 設置模型某一部分的不透明度

//live2DModel.setPartsOpacity(string partId, 0); 
//partId獲得方式見下圖,要使用整個文件夾的id,不要選擇文件夾裏小部件。

16/ 自動眨眼

 private EyeBlinkMotion eyeBlinkMotion;
//初始化
 eyeBlinkMotion = new EyeBlinkMotion();
在Update中調用:
 eyeBlinkMotion.setParam(live2DModel);

17/ 模型跟隨鼠標轉向與看向

private L2DTargetPoint drag;
//初始化
drag = new L2DTargetPoint();

//在Update中:

//得到的Live2d鼠標檢測點的比例值是-1到1(對應一個live2d拖拽
//管理座標系,或者叫做影響度。)
//然後我們通過這個值去設置我們的參數,比如旋轉30度*當前得到的值
//就會按照這個值所帶來的影響度去影響我們的模型動作
//從而到達看向某一個點的位置
Vector3 pos = Input.mousePosition;//屏幕座標
if (Input.GetMouseButton(0))
{
    drag.Set(pos.x/Screen.width*2-1,pos.y/Screen.height*2-1);
}
else if (Input.GetMouseButtonUp(0))
{
    drag.Set(0, 0);
}

//參數及時更新,考慮加速度等自然因素,計算座標,進行逐幀更新。
drag.update();

 //模型轉向
if (drag.getX()!=0)
{
    live2DModel.setParamFloat("PARAM_ANGLE_X",30*drag.getX());
    live2DModel.setParamFloat("PARAM_ANGLE_Y", 30 * drag.getY());
    live2DModel.setParamFloat("PARAM_BODY_ANGLE_X", 10 * drag.getX());
    live2DModel.setParamFloat("PARAM_EYE_BALL_X",drag.getX()); 
	//這裏的第二個參數和下一行的第二個參數都取反,則模型身體扭動,但眼睛會盯着屏幕看。
    live2DModel.setParamFloat("PARAM_EYE_BALL_Y",drag.getY());
}

18/ 套用物理運算去設置頭髮的長度重量與受到的空氣阻力

 //物理運算的設定
private PhysicsHair physicsHairSideLeft;
private PhysicsHair physicsHairSideRight;
private PhysicsHair physicsHairBackLeft;
private PhysicsHair physicsHairBackRight;

 #region 左右兩側頭髮的搖擺
//左測旁邊的頭髮
physicsHairSideLeft = new PhysicsHair();
//套用物理運算 
physicsHairSideLeft.setup(0.2f, // 長度 : 單位是公尺 影響搖擺週期(快慢)
                0.5f, // 空氣阻力 : 可設定0~1的值、預設值是0.5 影響搖擺衰減的速度
                0.14f); // 質量 : 單位是kg 
//設置輸入參數
//設置哪一個部分變動時進行哪一種物理運算
PhysicsHair.Src srcXLeft = PhysicsHair.Src.SRC_TO_X;//橫向搖擺

//第三個參數,"PARAM_ANGLE_X"變動時頭髮受到0.005倍的影響度的輸入參數
physicsHairSideLeft.addSrcParam(srcXLeft, "PARAM_ANGLE_X",0.005f,1);

//設置輸出表現
PhysicsHair.Target target = PhysicsHair.Target.TARGET_FROM_ANGLE;//表現形式
 
physicsHairSideLeft.addTargetParam(target, "PARAM_HAIR_SIDE_L",0.005f,1);


//右側旁邊的頭髮
physicsHairSideRight = new PhysicsHair();
//套用物理運算 
physicsHairSideRight.setup(0.2f, // 長度 : 單位是公尺 影響搖擺週期(快慢)
                0.5f, // 空氣阻力 : 可設定0~1的值、預設值是0.5 影響搖擺衰減的速度
                0.14f); // 質量 : 單位是kg 
//設置輸入參數
//設置哪一個部分變動時進行哪一種物理運算
PhysicsHair.Src srcXRight = PhysicsHair.Src.SRC_TO_X;//橫向搖擺
//PhysicsHair.Src srcXRight = PhysicsHair.Src.SRC_TO_Y;

//第三個參數,"PARAM_ANGLE_X"變動時頭髮受到0.005倍的影響度的輸入參數
physicsHairSideRight.addSrcParam(srcXRight, "PARAM_ANGLE_X", 0.005f, 1);

//設置輸出表現
PhysicsHair.Target targetRight = PhysicsHair.Target.TARGET_FROM_ANGLE;//表現形式

physicsHairSideRight.addTargetParam(targetRight, "PARAM_HAIR_SIDE_R",0.005f,1);

#endregion

#region 左右後邊頭髮的搖擺
//左邊
physicsHairBackLeft = new PhysicsHair();
physicsHairBackLeft.setup(0.24f, 0.5f, 0.18f);

PhysicsHair.Src srcXBackLeft = PhysicsHair.Src.SRC_TO_X;
PhysicsHair.Src srcZBackLeft = PhysicsHair.Src.SRC_TO_G_ANGLE;

physicsHairBackLeft.addSrcParam(srcXBackLeft, "PARAM_ANGLE_X",0.005f,1);
physicsHairBackLeft.addSrcParam(srcZBackLeft, "PARAM_ANGLE_Z",0.8f,1);

PhysicsHair.Target targetBackLeft = PhysicsHair.Target.TARGET_FROM_ANGLE;

physicsHairBackLeft.addTargetParam(targetBackLeft, "PARAM_HAIR_BACK_L", 0.005f, 1);

//右邊
physicsHairBackRight = new PhysicsHair();
physicsHairBackRight.setup(0.24f, 0.5f, 0.18f);

PhysicsHair.Src srcXBackRight = PhysicsHair.Src.SRC_TO_X;
PhysicsHair.Src srcZBackRight = PhysicsHair.Src.SRC_TO_G_ANGLE;

physicsHairBackRight.addSrcParam(srcXBackRight, "PARAM_ANGLE_X", 0.005f, 1);
physicsHairBackRight.addSrcParam(srcZBackRight, "PARAM_ANGLE_Z", 0.8f, 1);

PhysicsHair.Target targetBackRight = PhysicsHair.Target.TARGET_FROM_ANGLE;

physicsHairBackRight.addTargetParam(targetBackRight, "PARAM_HAIR_BACK_R", 0.005f, 1);

#endregion

//在Update中:
long time = UtSystem.getUserTimeMSec();//執行時間
physicsHairSideLeft.update(live2DModel,time);
physicsHairSideRight.update(live2DModel,time);
physicsHairBackLeft.update(live2DModel, time);
physicsHairBackRight.update(live2DModel,time);

19/ 表情系統(特殊的動作)

public TextAsset[] expressionFiles;
public L2DExpressionMotion[] expressions;
private MotionQueueManager expresionMotionQueueManager;
public int motionIndex;

//初始化
expresionMotionQueueManager = new MotionQueueManager();
expressions = new L2DExpressionMotion[expressionFiles.Length];
for (int i = 0; i < expressions.Length; i++)
{
    expressions[i] = L2DExpressionMotion.loadJson(expressionFiles[i].bytes);
}

在Update中加入測試表情:
if (Input.GetKeyDown(KeyCode.M))
{
    motionIndex++;
    if (motionIndex >= expressions.Length)
    {
        motionIndex = 0;
    }
    expresionMotionQueueManager.startMotion(expressions[motionIndex]);
}
expresionMotionQueueManager.updateParam(live2DModel);

//可以看到使用方式和普通動作是一樣的,只是
//Live2DMotion  <->  L2DExpressionMotion,都繼承自AMotion

4.官方demo中的框架

官方sdk2.x中的demo:SampleApp1中使用的框架,可以按照這個來;這裏提取出需要的,打一個package,內容如下:

鏈接:https://pan.baidu.com/s/13MLVGX-CwmTjN9ENJ600ew

在導入sdk2.x的基礎上,再導入這個包。

使用:(此處使用haru模型資源,見共享鏈接裏)

新建個空物體,掛上MeshFilter組件,拖入Live2D_Canvas資源;
掛上LAppModelProxy組件,Path填寫模型json文件在Resources下相對地址,包含後綴名;
掛上MyGameController組件,運行。

可根據需求修改。(如關閉運行時日誌LAppDefine.DEBUG_LOG)

 /*
  *開始運動。
  *檢查您是否可以播放,如果不能這樣做,則無所事事。
  *如果您可以自動播放,請閱讀文件並進行播放。
  *如果它有聲音,它也會播放。
  *如果您有關於淡入和淡出的信息,請在此處設置。 如果沒有初始值。
  *group:動作名稱,如下圖紅框,見模型的json文件
  *no:索引,0開始
  */
lAppModelProxy.GetModel().StartMotion(string group, int no, int priority);
如:lAppModelProxy.GetModel().StartMotion("tap_body",0,2);

//播放表情
//name爲上圖json中的"expressions"裏的name,如“f01”
lAppModelProxy.GetModel().SetExpression(string name)

//顯隱
lAppModelProxy.SetVisible(true);

//換裝(需要多張圖的衣服部件的佈局一致)
 Live2DModelUnity live2DModelUnity = lAppModelProxy.GetModel().GetLive2DModelUnity();
 live2DModelUnity.setTexture(int textureNo, Texture2D texture);

5.3.x版本的更新

1/ 動作參數和部位的不透明度參數都可視化了,可以直接在unity面板上調整。

2/ Cubism導出3.x的模型數據時,放到unity裏面可以看到直接是一個可識別的預製文件,可以直接拖到場景使用。

3/ 動畫製作,直接用Animation。

6.網上的破解資源的處理

文件用記事本打開,可以打開的話,根據內容修改文件名和後綴,後綴一般.txt
圖和moc文件一般都是png和moc,不用修改後綴。

若文本內容爲:{“type”:“Live2D Expression”,表示這是個表情;
若文本內容爲:# Live2D Animator Motion Data,表示這是個動作文件;
若文本內容爲:{“version”:"…",“model”:…,“textures”:…,表示這是個模型json文件,根據裏面內容,修改外面各文件的名稱和相對路徑。

然後這個文件夾可以放在項目裏測試使用了。


所需要的資源及2.x版本sdk:
鏈接:SIKI學院
https://pan.baidu.com/s/1KHeuxvdZyYjQPocaveYjbw 提取碼:d5qn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章