导入该素材模型和Live2d的sdk
Live2D模型制作到显示的基本流程:
1.初始化模型。
1.制作模型(读取moc文件)。
2.与贴图建立关联。
3.与绘图环境建立链接。(有些平台不需要)。
4.制定显示位置与尺寸。
2.更新模型状态。
1.更新顶点。
2.绘图。
void Start () {
//初始化
Live2D.init();
//释放
//Live2D.dispose();
//读取模型
Live2DModelUnity.loadModel(Application.dataPath + "/Resources/Epsilon/runtime/Epsilon.moc");
}
现将素材的.moc文件添加后缀为.moc.bytes,然后在输入下面的代码
TextAsset mocFile = Resources.Load("Epsilon/runtime/Epsilon.moc");
Live2DModelUnity.loadModel(mocFile.bytes);
//与贴图建立关联
Texture2D texture2D = Resources.Load("");
live2DModel.setTexture(0,texture2D);
//与多张贴图建立关联
Texture2D texture2D1 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_00");
Texture2D texture2D2 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_01");
Texture2D texture2D3 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_02");
live2DModel.setTexture(0, texture2D1);
live2DModel.setTexture(1, texture2D2);
live2DModel.setTexture(2, texture2D2);
private Live2DModelUnity live2DModel;
//制定显示位置与尺寸
float modelWidth = live2DModel.getCanvasWidth();
live2DCanvansPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50, 50);
在Update添加更新顶点的代码以及添加OnRenderObject的方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using live2d;
public class Live2dModel : MonoBehaviour {
public TextAsset modelFile;
public Texture2D[] textures;
private Live2DModelUnity live2DModel;
private Matrix4x4 live2DCanvansPos;
void Start () {
//初始化
Live2D.init();
//释放
//Live2D.dispose();
//读取模型
//Live2DModelUnity.loadModel(Application.dataPath + "/Resources/Epsilon/runtime/Epsilon.moc");
//TextAsset mocFile = Resources.Load("Epsilon/runtime/Epsilon.moc");
live2DModel = Live2DModelUnity.loadModel(modelFile.bytes);
//与贴图建立关联
//Texture2D texture2D = Resources.Load("");
//live2DModel.setTexture(0,texture2D);
//与多张贴图建立关联
//Texture2D texture2D1 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_00");
//Texture2D texture2D2 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_01");
//Texture2D texture2D3 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_02");
//live2DModel.setTexture(0, texture2D1);
//live2DModel.setTexture(1, texture2D2);
//live2DModel.setTexture(2, texture2D2);
for (int i = 0; i < textures.Length; i++)
{
live2DModel.setTexture(i, textures[i]);
}
//制定显示位置与尺寸
float modelWidth = live2DModel.getCanvasWidth();
live2DCanvansPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50, 50);
}
void Update ()
{
live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvansPos);
//更新顶点
live2DModel.update();
}
private void OnRenderObject()
{
live2DModel.draw();
}
}
运行程序,结果如下:
两种实例化对象动作的方式,与模型的读取操作一样
//实例化动作对象
live2DMotionIdle = Live2DMotion.loadMotion(Application.dataPath + "");
TextAsset mtnFile = Resources.Load("");
live2DMotionIdle = Live2DMotion.loadMotion(mtnFile.bytes);
首先将动作属性进行导入
public TextAsset[] motionFile;
private Live2DMotion[] motions;
motions = new Live2DMotion[motionFile.Length];
for (int i = 0; i < motions.Length; i++)
{
motions[i] = Live2DMotion.loadMotion(motionFile[i].bytes);
}
对某一动作属性进行设置
//设置某一个动画的一些属性
//重复播放不淡入
motions[0].setLoopFadeIn(false);
//设置淡入淡出时间,参数单位为毫秒
motions[0].setFadeOut(1000);
motions[0].setFadeIn(1000);
//动画是否循环播放
motions[0].setLoop(true);
创建一个MotionQueueManager对motions进行管理
private MotionQueueManager motionQueueManager;
motionQueueManager = new MotionQueueManager();
motionQueueManager.startMotion(motions[0]);
按下M键可以改变人物的动作播放,将动作拖入Unity时也需要将动作的mtn文件修改为mtn.bytes。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using live2d;
public class Live2dModel : MonoBehaviour {
//模型
public TextAsset modelFile;
public Texture2D[] textures;
private Live2DModelUnity live2DModel;
private Matrix4x4 live2DCanvansPos;
//动作
public TextAsset[] motionFile;
private Live2DMotion[] motions;
private MotionQueueManager motionQueueManager;
public int motionIndex;
//private Live2DMotion live2DMotionIdle;
void Start () {
//初始化
Live2D.init();
//释放
//Live2D.dispose();
//读取模型
//Live2DModelUnity.loadModel(Application.dataPath + "/Resources/Epsilon/runtime/Epsilon.moc");
//TextAsset mocFile = Resources.Load("Epsilon/runtime/Epsilon.moc");
live2DModel = Live2DModelUnity.loadModel(modelFile.bytes);
//与贴图建立关联
//Texture2D texture2D = Resources.Load("");
//live2DModel.setTexture(0,texture2D);
//与多张贴图建立关联
//Texture2D texture2D1 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_00");
//Texture2D texture2D2 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_01");
//Texture2D texture2D3 = Resources.Load("Epsilon/runtime/Epsilon.1024/texture_02");
//live2DModel.setTexture(0, texture2D1);
//live2DModel.setTexture(1, texture2D2);
//live2DModel.setTexture(2, texture2D2);
for (int i = 0; i < textures.Length; i++)
{
live2DModel.setTexture(i, textures[i]);
}
//制定显示位置与尺寸
float modelWidth = live2DModel.getCanvasWidth();
live2DCanvansPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50, 50);
//播放动作
//实例化动作对象
//live2DMotionIdle = Live2DMotion.loadMotion(Application.dataPath + "");
//TextAsset mtnFile = Resources.Load("");
//live2DMotionIdle = Live2DMotion.loadMotion(mtnFile.bytes);
motions = new Live2DMotion[motionFile.Length];
for (int i = 0; i < motions.Length; i++)
{
motions[i] = Live2DMotion.loadMotion(motionFile[i].bytes);
}
//设置某一个动画的一些属性
//重复播放不淡入
motions[0].setLoopFadeIn(false);
//设置淡入淡出时间,参数单位为毫秒
motions[0].setFadeOut(1000);
motions[0].setFadeIn(1000);
//动画是否循环播放
motions[0].setLoop(true);
motionQueueManager = new MotionQueueManager();
motionQueueManager.startMotion(motions[0]);
}
void Update ()
{
live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvansPos);
if (Input.GetKeyDown(KeyCode.M))
{
motionIndex++;
if (motionIndex >= motions.Length)
{
motionIndex = 0;
}
motionQueueManager.startMotion(motions[motionIndex]);
}
//指定播放动作的模型
motionQueueManager.updateParam(live2DModel);
//更新顶点
live2DModel.update();
}
private void OnRenderObject()
{
live2DModel.draw();
}
}
多个动作同时播放只需要多创建一个MotionQueueManger来进行管理
private MotionQueueManager motionQueueManager;
private MotionQueueManager motionQueueManagerA;
motions[0].setLoop(true);
motionQueueManager = new MotionQueueManager();
motionQueueManager.startMotion(motions[0]);
motions[5].setLoop(true);
motionQueueManagerA = new MotionQueueManager();
motionQueueManagerA.startMotion(motions[5]);
motionQueueManager.updateParam(live2DModel);
motionQueueManagerA.updateParam(live2DModel);
使用L2DMotionManager来对行为优先级进行管理
private L2DMotionManager l2DMotionManager;
//优先级的设置标准“
//1.动作未进行的状态,优先级为0;
//2.待机动作发生时,优先级为1;
//3.其他动作进行时,优先级为2;
//4.无视优先级,强制发生动作,优先级为3
首先在Start里面进行L2DMotionManager的初始化
l2DMotionManager = new L2DMotionManager();
然后写一个通过优先级播放动作的方法StartMotion
private void StartMotion(int motionIndex,int priority)
{
if (l2DMotionManager.getCurrentPriority() >= priority)
{
return;
}
else
{
l2DMotionManager.startMotion(motions[motionIndex]);
}
}
在Update里面进行使用调用
//判断待机动作
if (l2DMotionManager.isFinished())
{
StartMotion(0, 1);
}
else if (Input.GetKeyDown(KeyCode.M))
{
StartMotion(14, 2);
}
l2DMotionManager.updateParam(live2DModel);
开始时处于Idle状态,按下M后会变成摇头不高兴,播放完自己又会变成Idle
首先根据Epsilon.cmox文件把需要设置参数的id取出来,如图所示,取到角度X的id为PARAM_ANGLE_X
然后在Update里面添加下面的代码进行参数设置,第一个是id,第二个是需要设置的值,第三个是权重。
//设置参数
live2DModel.setParamFloat("PARAM_ANGLE_X", 30, 1);
//设置参数
live2DModel.setParamFloat("PARAM_ANGLE_X", 30, 1);
//累加参数数值,一次增加10度
live2DModel.addToParamFloat("PARAM_ANGLE_X", 10);
//乘法设置参数
live2DModel.multParamFloat("PARAM_ANGLE_X", 10);
通过索引获取参数
//第二种设置参数的办法,通过索引去设置
int paramAngleX;
paramAngleX = live2DModel.getParamIndex("PARAM_ANGLE_X");
live2DModel.setParamFloat(paramAngleX, 30);
参数的保存和回复
////参数的保存与回复
live2DModel.setParamFloat("PARAM_ANGLE_X", 30);
////保存与回复的参数是整个模型的所有参数,并不只是之前同方法里设置的几个参数
live2DModel.saveParam();
live2DModel.loadParam();
不透明度的设置,需要设置的是模型的part部分
//设定模型某一部分的透明度
live2DModel.setPartsOpacity("PARTS_01_FACE_001", 0);
眨眼功能的实现,首先定义一个EyeBlinkMotion的类
private EyeBlinkMotion eyeBlinkMotion;
然后在Start方法里进行初始化
//眨眼
eyeBlinkMotion = new EyeBlinkMotion();
在Update方法里面进行调用
//眨眼
eyeBlinkMotion.setParam(live2DModel);
拖拽功能的实现,首先定义一个L2DTargetPoint的类
//鼠标拖拽引起的动作变化
private L2DTargetPoint drag;
然后在Start方法里进行初始化
//鼠标拖拽
drag = new L2DTargetPoint();
在Update方法里面进行调用
//模型跟随鼠标转向与看向
Vector3 pos = Input.mousePosition;
if (Input.GetMouseButton(0))
{
//把屏幕坐标转换成live2D检测的坐标
drag.Set(pos.x / Screen.width * 2 - 1, pos.y / Screen.height * 2 - 1);
}
//模型跟随鼠标转向与看向
//得到的live2d鼠标检测点的比例值是-1到1(对应一个live2d拖拽
//管理坐标系,或者叫做影响度)。
//然后通过这个值去设置我们的参数,比如旋转30度*当前得到的值
//就会按照这个值所带来的影响度去影响我们的模型动作
//从而到达看向某一个点的位置
在Update里面添加代码
Vector3 pos = Input.mousePosition;
if (Input.GetMouseButton(0))
{
//把屏幕坐标转换成live2D检测的坐标
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());
//眼睛只望向前方
live2DModel.setParamFloat("PARAM_EYE_BALL_X", -drag.getX());
live2DModel.setParamFloat("PARAM_EYE_BALL_Y", -drag.getY());
}
眼睛跟随鼠标移动
眼睛只望向前方
头发摇摆功能的实现,首先定义一个PhysicsHair的类
//物理运算的设定(本模型头发只有两侧和后侧)
private PhysicsHair physicsHairSide;
private PhysicsHair physicsHairBack;
然后在Start方法里进行初始化和设置
//头发的摇摆
physicsHairSide = new PhysicsHair();
physicsHairBack = new PhysicsHair();
//套用物理运算
//第一个参数是长度 单位是公尺 影响摇摆的快慢(周期)
//第二个参数是空气阻力
//第三参数是头发的重量
physicsHairSide.setup(0.2f, 0.5f, 0.14f);
//设置输入参数
//社会哪一个部分变动时进行哪一种物理运算
PhysicsHair.Src srcX = PhysicsHair.Src.SRC_TO_X;//横向摇摆
//第三个参数,"PARAM_ANGLE_X"变动时头发受到0.005倍影响度的输入参数
physicsHairSide.addSrcParam(srcX, "PARAM_ANGLE_X", 0.005f, 1);
因为该模型头发有两边,所以我们将之前设置的Hair都分成两个,为Left和Right。
//设置输出表现
PhysicsHair.Target targetLeft = PhysicsHair.Target.TARGET_FROM_ANGLE;//表现形式
PhysicsHair.Target targetRight = PhysicsHair.Target.TARGET_FROM_ANGLE;
physicsHairSideLeft.addTargetParam(targetLeft, "PARAM_HAIR_SIDE_L", 0.005f, 1);
physicsHairSideRight.addTargetParam(targetRight, "PARAM_HAIR_SIDE_R", 0.005f, 1);
在Update里面进行调用
//头发摇摆
long time = UtSystem.getUserTimeMSec();//获取执行时间
physicsHairSideLeft.update(live2DModel, time);
physicsHairSideRight.update(live2DModel, time);
在Start里面添加下面的代码
//左边的头发
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);
在Update里面添加下面的代码
physicsHairBackLeft.update(live2DModel, time);
physicsHairBackRight.update(live2DModel, time);
首先定义三个变量
//表情
public TextAsset[] expressionFile;
public L2DExpressionMotion[] expressions;
private MotionQueueManager expressionMotionQueueManager;
在Start里面进行初始化
//表情
expressionMotionQueueManager = new MotionQueueManager();
expressions = new L2DExpressionMotion[expressionFile.Length];
for (int i = 0; i < expressions.Length; i++)
{
expressions[i] = L2DExpressionMotion.loadJson(expressionFile[i].bytes);
}
Update里面添加播放表情的代码,与播放动作的代码相似
//播放表情
if (Input.GetKeyDown(KeyCode.M))
{
motionIndex++;
if (motionIndex >= expressions.Length)
{
motionIndex = 0;
}
expressionMotionQueueManager.startMotion(expressions[motionIndex]);
}