二次元日系游戏制作工具 - live2dSDK入门教程

24 篇文章 5 订阅

1.Live2d的sdk接入与素材模型的导入

导入该素材模型和Live2d的sdk

Live2D模型制作到显示的基本流程:

1.初始化模型。
    
    1.制作模型(读取moc文件)。
    
    2.与贴图建立关联。
    
    3.与绘图环境建立链接。(有些平台不需要)。

    4.制定显示位置与尺寸。

2.更新模型状态。

    1.更新顶点。

    2.绘图。

Live2d虚拟环境的初始化与模型读取的第一种方法

	void Start () {

        //初始化
        Live2D.init();

        //释放
        //Live2D.dispose();

        //读取模型
        Live2DModelUnity.loadModel(Application.dataPath + "/Resources/Epsilon/runtime/Epsilon.moc");
	}

以二进制形式去读取模型

现将素材的.moc文件添加后缀为.moc.bytes,然后在输入下面的代码

        TextAsset mocFile = Resources.Load<TextAsset>("Epsilon/runtime/Epsilon.moc");
        Live2DModelUnity.loadModel(mocFile.bytes);

模型与贴图建立关联(单张贴图)

        //与贴图建立关联
        Texture2D texture2D = Resources.Load<Texture2D>("");
        live2DModel.setTexture(0,texture2D);

多张贴图的设置方法

        //与多张贴图建立关联
        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, texture2D2);

创建负责渲染Live2dCanvas的4乘4矩阵与赋值

 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<TextAsset>("Epsilon/runtime/Epsilon.moc");

        live2DModel = Live2DModelUnity.loadModel(modelFile.bytes);

        //与贴图建立关联
        //Texture2D texture2D = Resources.Load<Texture2D>("");
        //live2DModel.setTexture(0,texture2D);

        //与多张贴图建立关联
        //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, 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<TextAsset>("");
        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<TextAsset>("Epsilon/runtime/Epsilon.moc");

        live2DModel = Live2DModelUnity.loadModel(modelFile.bytes);

        //与贴图建立关联
        //Texture2D texture2D = Resources.Load<Texture2D>("");
        //live2DModel.setTexture(0,texture2D);

        //与多张贴图建立关联
        //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, 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<TextAsset>("");
        //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);
        }

关于L2DTargetPoint获取坐标点的详细讲解

        //模型跟随鼠标转向与看向
        //得到的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);

Live2d中的表情系统

首先定义三个变量

    //表情
    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]);
        }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值