继续01篇的内容,在unity中加载入小姐姐后,此时她还只是一张图片,并不会动,这时就需要引入配套的动画。
实例化动作对象 加载mtn文件
//这是直接加载mtn文件的方法
live2DMotionIdle = Live2DMotion.loadMotion(Application.dataPath + "");
//这是加载二进制文件的方法
TextAsset mtnFile = Resources.Load("");
live2DMotionIdle = Live2DMotion.loadMotion(mtnFile.bytes);
定义一个TextAsset数组,用于存储动作的二进制文件,定义一个 Live2DMotion数组,用于存贮由二进制文件加载的动作
这个动作的二进制文件由此得来:在Epsilon/Epsilon/runtime/motions下面有许多mtn文件,CTRLcv复制粘贴得到副本,在副本后面加上.bytes变为二进制文件,再从unity的inspector视口拖入。
public TextAsset[] motionFiles;
//动作数组
private Live2DMotion[] motions;
//将读入的motion文件加载入对应的Live2DMotion数组中
motions = new Live2DMotion[motionFiles.Length];
for (int i = 0; i < motions.Length; i++)
{
motions[i] = Live2DMotion.loadMotion(motionFiles[i].bytes);
}
接下来可以单独设置每一个动画的属性
//重复播放时是否淡入,动作跨度大设置可以看起来更自然 参数为bool
motions[0].setLoopFadeIn(false);//重复播放不淡入
//设置淡入淡出时间
//淡出,不设置默认值也为1000毫秒。
motions[0].setFadeOut(1000);
//淡入
motions[0].setFadeIn(1000);
//设置动画是否循环播放
motions[0].setLoop(true);
播放动画还需要一个动画队列的管理器,定义一个motionQueueManager对象
//动作管理队列,管理动画的播放
private MotionQueueManager motionQueueManager;
使用startMotion设置播放的动画
motionQueueManager = new MotionQueueManager();
//播放动画
motionQueueManager.startMotion(motions[0]);
在update中更新状态,由于动画和模型是对应的,需要为管理器指定哪一个模型播放动画
motionQueueManager.updateParam(live2DModel);
动画是可以叠加的,比如角色动嘴没动手时,可以为手指定动作,动头没动身体时,可以为身体指定动作,当动作冲突时取后指定的动作。需要同时播放几个动作就需要几个动画管理器
//第二个动作管理,要同时播放几个动画就要有几个管理者
private MotionQueueManager motionQueueManagerA;
//Start函数中
motionQueueManager.startMotion(motions[0]);//第一个管理器
motions[5].setLoop(true);
motionQueueManagerA = new MotionQueueManager();//第二个管理器
motionQueueManagerA.startMotion(motions[5]);
//Update函数中
motionQueueManager.updateParam(live2DModel);
motionQueueManagerA.updateParam(live2DModel);//同时更新两个管理器状态
这样模型就同时做出了第一个和第5个动作
在unity的animator组件中可以设置动画的状态机,同理live2d也可以设置状态机,不过使用的是代码实现
注释掉前面motionQueueManager的相关语句,改用状态机控制动画。
动画优先级分为四个:
优先级的设置标准
//1.动作未进行的状态,优先级为0
//2.待机动作发生时,优先级为1
//3.其他动作进行时,优先级为2
//4.无视优先级,强制发生的动作
声明一下live2d框架的命名空间
using live2d.framework;
定义一个优先级管理器
private L2DMotionManager l2DMotionManager;
//Start 函数里
l2DMotionManager = new L2DMotionManager();
在update函数中
//优先级设置
//判断待机动作,isFinished判断动作完毕否
if (l2DMotionManager.isFinished())
{
StartMotion(0, 1);//0为idle动画,优先级是1
}
else if (Input.GetKeyDown(KeyCode.M))
{
StartMotion(14, 2);
}
//更新动作数据
l2DMotionManager.updateParam(live2DModel);
当按下m时,传入一个优先级为2的动画,就会将当前idle动画进行切换
//播放动画的方法封装
public void StartMotion(int motionIndex, int priority)
{
//getCurrentPriority()正在播放动画的优先级
//与要播放的动画的优先级对比,同优先级或者大于要播放的动画的优先级,不操作
if (l2DMotionManager.getCurrentPriority()>= priority)
{
return;
}
//要播放的动画优先级比现在的大
l2DMotionManager.startMotion(motions[motionIndex]);
}
这样就设置出了一个简易的状态机,当不操作时模型一直播放idle动画,按下m后播放14动画,播放完毕后又回到idle状态
完整代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using live2d;
using live2d.framework;
public class Live2dModel : MonoBehaviour {
public TextAsset modelFile;
private Live2DModelUnity live2DModel;
public Texture2D[] textures;
public TextAsset[] motionFiles;
//动作优先级管理
//优先级的设置标准
//1.动作未进行的状态,优先级为0
//2.待机动作发生时,优先级为1
//3.其他动作进行时,优先级为2
//4.无视优先级,强制发生的动作
private L2DMotionManager l2DMotionManager;
//动作数组
private Live2DMotion[] motions;
//动作管理队列,管理动画的播放
private MotionQueueManager motionQueueManager;
//第二个动作管理,要同时播放几个动画就要有几个管理者
private MotionQueueManager motionQueueManagerA;
//动作索引
public int motionIndex;
//live2d的画布
private Matrix4x4 live2dCanvasPos;
//动作对象
//private Live2DMotion live2DMotionIdle;
// Use this for initialization
void Start () {
//初始化环境 使用环境前一定要调用一次 连续调用/不调用会出错
//可以释放后再次初始化重新使用
Live2D.init();
//释放环境 没有初始化是不能释放的
//Live2D.dispose();
//1.读取模型
//Live2DModelUnity专门负责模型的加载
//加载moc文件一定要有.moc后缀
//加载moc↓
//Live2DModelUnity.loadModel(Application.dataPath + "/Resources/Epsilon/Epsilon/runtime/Epsilon.moc"); //二进制文件或者moc文件路径
//加载二进制文件 复制moc文件,在副本文件名后面加上.bytes后缀变为二进制文件
//使用textAsset接收二进制文件
//加载二进制 ↓
//TextAsset mocFile = Resources.Load("Epsilon/Epsilon/runtime/Epsilon.moc");
//Live2DModelUnity.loadModel(mocFile.bytes);
//方法返回一个类型为Live2DModelUnity的模型对象,对应于加载的这个模型
live2DModel = Live2DModelUnity.loadModel(modelFile.bytes);
//2.联系贴图
//只能通过路径读取
//Texture2D texture2D = Resources.Load("Epsilon/Epsilon/runtime/Epsilon.1024");
//live2DModel.setTexture(0, texture2D);
//设置贴图 前一个参数是贴图序号,由建模师决定
//设置多张贴图
//Texture2D texture2D1 = Resources.Load("Epsilon/Epsilon/runtime/Epsilon.1024/texture_00");
//Texture2D texture2D2 = Resources.Load("Epsilon/Epsilon/runtime/Epsilon.1024/texture_01");
//Texture2D texture2D3 = Resources.Load("Epsilon/Epsilon/runtime/Epsilon.1024/texture_02");
//live2DModel.setTexture(0, texture2D1);
//live2DModel.setTexture(1, texture2D2);
//live2DModel.setTexture(2, texture2D3);
for (int i = 0; i < textures.Length; i++)
{
live2DModel.setTexture(i, textures[i]);
}
//3与绘图环境建立链接(unity不需要)
//4指定显示位置与尺寸
//定义画布 Matrix4x4 live2dCanvasPos;
//创建正交的投影矩阵 参数是左右上下近视口远视口
float modelWidth = live2DModel.getCanvasWidth();
//+-50由官方提供
//将相机设置为正交相机
live2dCanvasPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, 50, -50);
//播放动作
//实例化动作对象 加载mtn文件
//live2DMotionIdle = Live2DMotion.loadMotion(Application.dataPath + "");
//TextAsset mtnFile = Resources.Load("");
//live2DMotionIdle = Live2DMotion.loadMotion(mtnFile.bytes);
//将读入的motion文件加载入对应的Live2DMotion数组中
motions = new Live2DMotion[motionFiles.Length];
for (int i = 0; i < motions.Length; i++)
{
motions[i] = Live2DMotion.loadMotion(motionFiles[i].bytes);
}
/*
//设置动画的属性
//重复播放时是否淡入,动作跨度大设置可以看起来更自然 参数为bool
motions[0].setLoopFadeIn(false);//重复播放不淡入
//设置淡入淡出时间
//淡出,不设置默认值也为1000毫秒。
motions[0].setFadeOut(1000);
//淡入
motions[0].setFadeIn(1000);
//设置动画是否循环播放
motions[0].setLoop(true);
motionQueueManager = new MotionQueueManager();
//播放动画
motionQueueManager.startMotion(motions[0]);
//动画的重叠
motions[5].setLoop(true);
motionQueueManagerA = new MotionQueueManager();
motionQueueManagerA.startMotion(motions[5]);*/
//动作的优先级使用
l2DMotionManager = new L2DMotionManager();
}
// Update is called once per frame
void Update () {
//设置矩阵 两个矩阵相乘
//localToWorldMatrix局部转世界
live2DModel.setMatrix(transform.localToWorldMatrix * live2dCanvasPos );
//更新顶点,贴图等
//按M更改动画
//if (Input.GetKeyDown(KeyCode.M))
//{
// motionIndex++;
// if(motionIndex >= motions.Length)
// {
// motionIndex = 0;
// }
// motionQueueManager.startMotion(motions[motionIndex]);
//}
////指定哪一个模型播放动画
//motionQueueManager.updateParam(live2DModel);
//motionQueueManagerA.updateParam(live2DModel);
//优先级设置
//判断待机动作,isFinished判断动作完毕否
if (l2DMotionManager.isFinished())
{
StartMotion(0, 1);//0为idle动画,优先级是1
}
else if (Input.GetKeyDown(KeyCode.M))
{
StartMotion(14, 2);
}
//更新动作数据
l2DMotionManager.updateParam(live2DModel);
live2DModel.update();
}
//绘图方法
private void OnRenderObject()
{
live2DModel.draw();
}
//播放动画的方法封装
public void StartMotion(int motionIndex, int priority)
{
//getCurrentPriority()正在播放动画的优先级
//与要播放的动画的优先级对比,同优先级或者大于要播放的动画的优先级,不操作
if (l2DMotionManager.getCurrentPriority()>= priority)
{
return;
}
//要播放的动画优先级比现在的大
l2DMotionManager.startMotion(motions[motionIndex]);
}
}