伪全息老婆制作2(Live2D入门1)

本文主要介绍了在Unity中使用Live2D的开发过程。包括初始化Live2D、模型载入的两种方法、给模型赋贴图、设置矩阵、将相机设为正交相机、动画设置、动作切换、多动作播放及优先级设置等内容,并给出了相关代码实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

初始化live2D

模型载入(读取)[写在Start()里]

载入方法一:

载入方法二:

给模型赋贴图

设置矩阵

把相机设置成正交相机

动画(动作)设置

键入M切换动作代码:[写在Update()中]

两个动作同时播放

动作优先级设置

所有代码:


 

首先导入Live2D SDK2.1里面的一些包

Live2D需要初始化代码,只有Live2D初始化之后才可以使用里面的函数/API

写在:Live2DModel.cs

初始化live2D

首先要导入live2D命名空间:

using live2d;

如果想要使用Lived2D里面的API以及模型的创建,需要在开始使用Lived2D前至少调用一次,一次都没有调用或者连续调用都可能会导致执行程序不稳定,相当于是引入了一个大环境

Live2D.init();

全部运行结束后,可以释放Live2D占用的全部资源(没有初始化过就不能释放!!!!)

Live2D.dispose();

我们本程序中会一直使用Live2D的环境,所以不用释放,注释掉这一句

Live2DModelUnity:这个 类专门负责Unity加载和读取Live2D的模型

 Live2DModelUnity.loadModel(,)

在括号里打逗号,按动键盘的上下键,可以看API种有哪几种重载(官方推荐的一种是二进制的形式进行加载,一种是直接给他一个MOC文件的路径,从而直接读取moc文件)

模型载入(读取)[写在Start()里]

载入方法一:

我们可以把Epsilon文件夹放在Resources文件夹下,这样之后可以直接用.load方法加载

Live2DModelUnity.loadModel(Application.dataPath + "Resources/Epsilon/runtime/Epsilon.moc");

Application.dataPath默认的是Assets根目录(记得要写.moc!!!!!)

之后我们返回Unity创建一个空物体,命名为:Live2DModle,并将脚本挂载上去

显示加载失败

我们发现少了个反斜杠在这里,修改后:

Live2DModelUnity.loadModel(Application.dataPath + "/Resources/Epsilon/runtime/Epsilon.moc");

载入方法二:

把.moc文件复制一份,并在后面加上.bytes

这是个文本类型的文件,我们要用TextAsset来接收它:

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

最后,为了方便赋值,我们可以在类中定义一个共有类型变量,便于我们拖拽赋值

public TextAsset modelFile;

给模型赋贴图

我们得明确具体是给哪个模型贴图,模型文件加载完之后,实际上是有一个返回值的,其类型是Live2DModelUnity(鼠标放在函数上就可以看到)

,我们可以设置他的位置,贴图,甚至更新他的一些参数,所以模型变量我们要先取到

 1、定义出来

private Live2DModelUnity live2DModel;

 2、将之前读取的模型文件赋值给live2DModel[在Start()里]

live2DModel=Live2DModelUnity.loadModel(modelFile.bytes);

 3、加载贴图(只有通过路径建立关联)[在Start()里]

        Texture2D texture2D0 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_00");
        Texture2D texture2D1 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_01");
        Texture2D texture2D2 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_02");
        live2DModel.setTexture(0, texture2D0);
        live2DModel.setTexture(1, texture2D1);
        live2DModel.setTexture(2, texture2D2);

当然,这样用代码查找之后关联看起来会很繁琐,我们也可以定义共有变量数组textures

public Texture2D[] textures;

并编写代码,使其通过拖拽赋值:(for循环写了for之后按两下Tab Tab可自动帮忙补全)

        for (int i = 0; i < textures.Length; i++)
        {
            live2DModel.setTexture(i, textures[i]);
        }

 到这一步还不能正常看到图形,因为图形比例太大了,我们得设置矩阵的行列位置,矩阵使用上代码基本是固定的

设置矩阵

1、定义画布位置

private Matrix4x4 live2DCanvasPos;

2、给4*4矩阵初始化 

        //指定显示位置与尺寸(固定写法)
        float modelWidth = live2DModel.getCanvasWidth();
        live2DCanvasPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50, 50);

3、 把当前的矩阵设置给我们当前的模型[在Update()里完成]

live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos);

4、更新模型定点信息或参数信息 [在Update()里完成]

live2DModel.update();

5、绘图,绘图方法放在一个固定的生命周期函数中OnRenderObject()[Unity中固定的生命周期函数]

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

把相机设置成正交相机

我们可以通过相机的size值调整视图中小姐姐的大小

动画(动作)设置

动画不是自己去播放的,是动作管理者(MotionQueueManager)播放的

        //设置某些动画的属性
        //重复播放不淡入
        motions[0].setLoopFadeIn(false);
        //设置淡入淡出时间(以毫秒为单位计算)
        motions[0].setFadeOut(1000);
        motions[0].setFadeIn(1000);
        //动画是否循环播放
        motions[0].setLoop(true);

 1、定义出动作管理者

private MotionQueueManager motionQueueManager;

2、在Start()函数里初始化动作管理者

 motionQueueManager = new MotionQueueManager();

3、在Start()函数里,告诉动作管理者播放哪个动画

motionQueueManagerA.startMotion(motions[0]);

4、动作的播放对应于某一个模型,要将当前设定的参数告诉哪一个模型,在Update()中,每个动作都要更新到对应的模型中身上

motionQueueManager.updateParam(live2DModel);

注意:动作文件也不可以直接使用.mtn文件,得转换成.bytes文件,像之前一样,CtrlC+CtrlV创建副本,再将副本加后缀.bytes

 

键入M切换动作代码:[写在Update()中]

 添加一个动作索引:

public int motionIndex;
       if (Input.GetKeyDown(KeyCode.M))
        {
            motionIndex++;
            if (motionIndex>=motions.Length)
            {
                motionIndex = 0;
            }
            motionQueueManager.startMotion(motions[motionIndex]);
        }

两个动作同时播放

如果要两个动画同时播放时,尽量不要让两个参数同时改变(比如不要同时改变嘴的形状)

需要几个动作同时播放就需要几个动作管理者,如:

1、定义出动作管理者

private MotionQueueManager motionQueueManagerA;

2、在Start()函数里初始化动作管理者

 motionQueueManagerA = new MotionQueueManager();

3、告诉动作管理者播放哪个动画

motionQueueManagerA.startMotion(motions[5]);

4、与之前相同,在Update()中,每个动作都要更新到对应的模型中身上

motionQueueManagerA.updateParam(live2DModel);

动作优先级设置

l2DMotionManager继承自MotionQueueManager,其实内容和MotionQueueManager差不多,就是加了一些优先级设置的东西

优先级设置标准:

1、动作未进行时的状态,优先级为0

2、待机状态发生时,优先级为1

3、其他动作发生时,优先级为2

4、无视优先级,强制发生的动作,优先级为3

使用方法:

1、初始化[在Start()里完成]

l2DMotionManager = new L2DMotionManager();

 2、封装一个播放动作的优先级方法,如果正在播放的动画比新播放的动画优先级高或者优先级相同,则播放当前优先级的动画,反之播放新的动画

    private void StartMotion(int motionIndex,int priority)
    {
        //得到正在播放的动画的优先级
        if (l2DMotionManager.getCurrentPriority()>=priority)
        {
            return;
        }
        l2DMotionManager.startMotion(motions[motionIndex]);
    }

 3、判断待机动作(当前是否有动画正在播放)并调用我们封装的播放动画的函数[在Update()里完成]

        if (l2DMotionManager.isFinished())
        {
            StartMotion(0, 1);
        }
        else if (Input.GetKeyDown(KeyCode.M))
        {
            StartMotion(14, 2);
        }

4、我们要在每次动画播放完毕时,得更新一下动作数据

l2DMotionManager.updateParam(live2DModel);

现在小姐姐按下M键会改变动作,之后又会恢复成Idle动作!之前不会恢复!!!!

所有代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using live2d;
using live2d.framework;


public class Live2DModel : MonoBehaviour
{
    //模型显示
    public TextAsset modelFile;
    public Texture2D[] textures;
    private Live2DModelUnity live2DModel;
    private Matrix4x4 live2DCanvasPos;

    //动作播放
    public TextAsset[] motionFiles;
    private Live2DMotion[] motions;
    //动作不是自己播放的,是由一个动作管理控制播放的
    private MotionQueueManager motionQueueManager;

    private MotionQueueManager motionQueueManagerA;
    //private Live2DMotion live2DMontionIdle;
    //优先级设置标准:
    //1.动作未进行时的状态,优先级为0
    //2.待机状态发生时,优先级为1
    //3.其他动作发生时,优先级为2
    //4.无视优先级,强制发生的动作,优先级为3
    private L2DMotionManager l2DMotionManager;

    public int motionIndex;

    // Start is called before the first frame update
    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 texture2D0 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_00");
        //Texture2D texture2D1 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_01");
        //Texture2D texture2D2 = Resources.Load<Texture2D>("Epsilon/runtime/Epsilon.1024/texture_02");
        //live2DModel.setTexture(0, texture2D0);
        //live2DModel.setTexture(1, texture2D1);
        //live2DModel.setTexture(2, texture2D2);

        for (int i = 0; i < textures.Length; i++)
        {
            live2DModel.setTexture(i, textures[i]);
        }

        //指定显示位置与尺寸(固定写法)
        float modelWidth = live2DModel.getCanvasWidth();
        //创建正交视口,前四个参数代表正交视口的左右下上
        live2DCanvasPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50, 50);

        //播放动作
        //实例化动作对象
        //live2DMontionIdle = Live2DMotion.loadMotion(Application.dataPath + "");
        //TextAsset mtnFile = Resources.Load<TextAsset>("");
        //live2DMontionIdle = Live2DMotion.loadMotion(mtnFile.bytes);
        motions = new Live2DMotion[motionFiles.Length];
        for (int i = 0; i < motions.Length; i++)
        {
            motions[i] = Live2DMotion.loadMotion(motionFiles[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]);

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

        //动作的优先级使用
        l2DMotionManager = new L2DMotionManager();

    }


    // Update is called once per frame
    void Update()
    {
        //设置当前模型的位置
        live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos);

        //if (Input.GetKeyDown(KeyCode.M))
        //{
        //    motionIndex++;
        //    if (motionIndex >= motions.Length)
        //    {
        //        motionIndex = 0;
        //    }
        //    motionQueueManager.startMotion(motions[motionIndex]);
        //}

        //motionQueueManager.updateParam(live2DModel);
        //motionQueueManagerA.updateParam(live2DModel);

        //判断待机动作(当前是否有动画正在播放)
        if (l2DMotionManager.isFinished())
        {
            StartMotion(0, 1);
        }
        else if (Input.GetKeyDown(KeyCode.M))
        {
            StartMotion(14, 2);
        }
        l2DMotionManager.updateParam(live2DModel);

        live2DModel.update();
    }


    //生命周期函数
    private void OnRenderObject()
    {
        live2DModel.draw();
    }
   
    //封装一个播放动作的优先级方法
    private void StartMotion(int motionIndex,int priority)
    {
        //得到正在播放的动画的优先级
        if (l2DMotionManager.getCurrentPriority()>=priority)
        {
            return;
        }
        l2DMotionManager.startMotion(motions[motionIndex]);
    }
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值