左下角小马2

live2d小马模型更新!

🌱起因

大概一两个月前偶然购买了一个live2d小马模型的使用权,虽然是别人的设子,但是这个模型有比较丰富的小配件和表情,展示效果应当比之前更生动。不过之前使用的“授之以鱼”版的加载脚本似乎并不支持表情切换,所以这次只好亲自动手,从源头开始寻找实现方法。

🍃方法

网上关于live2d网页展示的项目不少,但是大部分都是帮你部署好的模型,只需插入一行<script>就可以使用,还是属于授之以鱼。若是想部署自己的模型,可供查阅的信息就少了,例如我最开始是从这篇文章开始参考的。不过追根溯源,其实现在市面上大部分live2d预览软件,其开发都是基于Live2d Cubsim提供的SDK,因此在本篇教程中,我们将从它的Web SDK源代码入手,介绍如何在此基础上修改得到想要的功能。

🌿实现

我们的目标是在网页上加载live2d模型,并且支持表情切换。为此,我们分为如下步骤:

准备阶段

在开始前,我们需要满足如下前置条件:

  • live2d模型,需要至少包含.moc3, .model3.json, .physics3.json以及texture文件;
  • Live2d Cubsim Web SDK,下载链接见上文;
  • vscode或者其它IDE;
  • 对DOM模型交互过程有基本理解,能够大致读懂javascript和typescript代码以便进行修改;

表情文件生成

我们通过Live2D Cubism Viewer软件打开live2d模型,这里我使用的版本是4.2。通过软件的表情编辑器可以设计表情,当然这里设计的灵活程度取决于模型作者在制作模型时绑定的部件数量以及物理效果等因素。设计完成之后导出相应的.json文件:

live2d viewer界面

接下来将生成的表情文件放在expression目录下,然后打开pony.model3.json查看表情文件是否正确绑定。具体而言,我们需要在里面增加一个名称为Expressions的数组(如果之前不存在的话),在其中指明表情的名称和路径。例如以下是一个示例配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// pony.model3.json
{
"Version": 3,
"FileReferences": {
"Moc": "pony.moc3",
"Textures": [
"pony.2048/texture_00.png"
],
"Physics": "pony.physics3.json",
"DisplayInfo": "pony.cdi3.json",
"Expressions": [
{
"Name": "makeface",
"File": "expressions/makeface.exp3.json"
},
{
"Name": "happy",
"File": "expressions/happy.exp3.json"
}
]
},
}

我们也可以看看expression文件里面长什么样:

1
2
3
4
5
6
7
8
9
10
11
// happy.exp3.json
{
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamMouthForm",
"Value": 1,
"Blend": "Add"
}
]
}

可以看见其实是指明了表情涉及的部件、值以及混合方法。

WebSDK修改

我这里使用的SDK版本是CubismSdkForWeb-4-r.7,在vscode中打开此项目,注意需要提前安装好npm。

我们需要关注的部分目录结构如下:

1
2
3
4
5
6
Core/			#编译好的live2d模型解析相关的核心js代码
Framework/ #解析模型源文件,进行数学运算和物理渲染相关的ts代码
Samples/ #示例web项目
- Resources #示例live2d模型
- TypeScript #示例项目的源文件
README.md #英文的基本信息介绍

跟着Cubsim官网文档走可以学习如何在本机跑起来样例代码。这里我们直接看到Samples/TypeScript/src目录下的ts文件,这些是web展示相关的代码,也是我们需要修改的地方,目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
lappdefine.ts		 #定义了一些基本变量
lappdelegate.ts #webgl初始化及canvas回调函数绑定
lapplive2dmanager.ts #live2d交互的回调函数实现
lappmodel.ts #载入模型(包括动作表情和物理效果等)
lappal.ts #Cubism Platform Abstraction Layer
lappsprite.ts #画面sprite类
lapptexturemanager.ts#纹理加载
lappview.ts #视图管理,包括一些回调函数
lappwavfilehandler.ts#口型文件处理
main.ts #入口函数
touchmanager.ts #视线追踪

感觉理解所有代码还是比较困难的,幸运的是为了实现我们的目标,其实不需要理解太多代码逻辑,经过一番探索,归纳如下:

  1. 修改lappdefine.ts中的一些初始化常量(例如canvas大小),并且增加接口用于自定义模型存放的路径。注意为了能够对外面的js代码可见,一种简单的操作是把这个函数挂载到window对象下:

    1
    2
    3
    4
    5
    6
    7
    8
    // lappdefine.ts
    function initPath(resourcesPath: string, modelDir: string[]): void{
    ResourcesPath = resourcesPath;
    ModelDir = modelDir;
    ModelDirSize = ModelDir.length;
    }
    export const win:any = window;//window就是DOM中的window对象
    win.initPath = initPath;
  2. 修改lappdelegate.ts中回调函数的绑定。例如当需要全屏视线追踪光标的功能时,可以把onMouseMoved函数绑定到window对象下而非canvas对象

  3. 修改lapplive2dmanager.ts。首先把setRandomExpression()的调用位置改一下,原来的逻辑是判断点击位置是否是头部再决定是否触发表情切换,但我这里的模型在制作时并没有设置区域重叠检测,所以只好改成无条件触发;然后将其中的切换模型函数逻辑更改为让模型消失/显现,因为这里我只有一个模型,切换模型的功能没啥用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //lapplive2dmanager.ts
    /** 让模型消失或者显现 */
    public changeModelDisplay(): void {
    if (LAppDefine.DebugLogEnable) {
    LAppPal.printMessage(`[APP]change model display: ${this._sceneIndex}`);
    }
    if (this.display_model){
    this.display_model = false;
    this.releaseAllModel();
    } else {
    this.display_model = true;
    this._models.pushBack(new LAppModel());
    const index = this._sceneIndex;
    const model: string = LAppDefine.ModelDir[index];
    const modelPath: string = LAppDefine.ResourcesPath + model + '/';
    let modelJsonName: string = LAppDefine.ModelDir[index];
    modelJsonName += '.model3.json';
    this._models.at(0).loadAssets(modelPath, modelJsonName);
    }
    }
  4. lapptexturemanager.ts中加载纹理图片时,需要给img对象标记允许跨源访问,否则部署在cdn上的模型文件会因为违反跨源规则而无法在网页上显示:

    1
    2
    3
    // public createTextureFromPngFile
    const img = new Image();
    img.crossOrigin = "Anonymous"; // 跨源访问
  5. 构建打包:在vscode中通过ctrl+shift+b执行npm build任务,然后找到Samples\TypeScript\Demo\dist\bundle.js以及Core\live2dcubismcore.js两个文件,将它们复制到我们的网站目录下即可

整合至现有项目

上一小节中的代码实现了模型的加载与展示,但是我们还需要把导出的两个javascript文件整合到网页源码中。为了简洁起见,这次我们不再增加上次的对话功能:

  1. 增加css规则用来控制canvas的大小与透明度;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /*live2d.css*/
    canvas.live2d {
    position: fixed;
    left: 0px;
    bottom: 0px;
    opacity: 75%;
    }
    /*在宽度小于600px的设备上缩小canvas大小*/
    @media (max-width: 600px) {
    canvas.live2d {
    width: 200px;
    height: 200px;
    }
    }
  2. 由于live2d模型较大,加载持续时间较长,因此增加一个旋转的圈圈来提醒用户耐心等待。这里采用css实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /*loading.css*/
    .loading {
    width: 100px;
    height: 100px;
    border: 6px solid #000;
    border-top-color: transparent;
    border-radius: 100%;

    animation: circle infinite 0.75s linear;
    }
    @keyframes circle {
    0% {
    transform: rotate(0);
    }
    100% {
    transform: rotate(360deg);
    }
    }
  3. 添加js文件调用之前导出的接口进行初始化:

    1
    2
    3
    4
    // load.js
    var resourcesPath = 'xxx'; // 指定资源文件(模型)保存的路径
    var modelDir = ['pony']; // 指定需要加载的模型
    window.initPath(resourcesPath, modelDir); // 初始化模型
  4. 我使用的leedom主题过于简洁居然不支持css和js代码插入,这就给它加上相应的功能:

    1
    2
    3
    4
    5
    6
    7
    <!-- layout/_partial/header.ejs -->
    <% if (theme.inject.css.enable){ %>
    <%- css(theme.inject.css.path) %>
    <% } %>
    <% if (theme.inject.js.enable){ %>
    <%- js(theme.inject.js.path) %>
    <% } %>

    然后在主题的_config.xxx.yml配置文件下加入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 注入自定义代码
    inject:
    css:
    enable: true
    path:
    - /css/live2d.css
    - /css/loading.css
    js:
    enable: true
    path:
    - https://cdn路径/script/live2dcubismcore.js
    - https://cdn路径/script/bundle.js
    - /js/load.js

加载优化

之前把模型部署在网页项目的同一位置,但后来发现网页加载速度太慢了。通过性能调试器可以发现性能瓶颈在于模型文件的下载。文件大小是没法优化了,那就只能优化下载速度,因此通过gihub release部署到了cdn上,这样就把加载时间从一分钟缩短到十秒钟。为此还需要解决跨源访问的问题,具体参见上文。此外,既然live2d模型能放到cdn上,那干脆把大文件bundle.jslive2dcubismcore.js也放上去算了:

优化后moc文件的加载时间

🍂效果

当当当当~经过一番努力,我们终于在个人主页上部署了新的live2d小马!

交互方式:

  • 鼠标点击/触屏点击可触发表情切换;
  • 光标在canvas范围移动或者触屏在canvas范围滑动可触发视线追踪;
  • 闲置时动态物理效果;

image-20240122123010170


左下角小马2
https://www.hovering-clouds.cn/space/2024/01/22/左下角小马2/
作者
垂云
发布于
2024年1月22日
许可协议