将看板娘放在网页上!

前言

因为有放上一个看板娘的需要,我从某鸽子画师那里借来了一个live2d的cmoc3文件。由于cmoc3文件不能直接在网页上放出来,并且像网络上的插件都或多或少有些瑕疵,不满足需要。当然,如果只是一般的需求,则网络上的插件基本能实现了,但由于本人强迫症,对于不显示表情、动作表现不全、没有随即加载动作、定位不准、模糊等瑕疵很在意,于是便有了这几天的折腾过程。
由于本篇并非一步到位法,而是属于自我折腾范畴,故仅限于闲的没事的来报错。
本篇折腾过程全部基于live2d2.0,如果想要寻找关于live2d3.0的教程可以移步至参考资料。


文件结构

在进行魔改之前,我们先认清一个放在网页上的一般模型的文件结构。
参阅Live2DViewerEX的例子(这是个跨平台动态壁纸解决方案,与我们需要用的不同):live2d配置例子
最基本的只含有moc文件和材质文件,贴图格式是png。moc文件相当于提供骨架和其它数据,而表现出来还要联系材质文件。同一人物,相同动作的换装也是依靠切换材质文件。当然,如果只是最基本的模型的话,没有动作的话,太显得单调了。因此将最基本的模型通过软件稍稍处理,就会得到下面的例子,含动作文件夹motions:

model.json //对模型各种文件的关系配置,即引用关系。
model.moc//3d骨架,格式是moc(live2d2.0)。
physics.json//物理效果,比如头发随动作的摆动。
pose.json//部件置换的姿势效果。
textures //文件夹,存放材质文件,往3d骨架上贴的,有时候一个模型加载需要几个材质文件。

a.png
b.png
...

motions //文件夹,存放mtn动作文件,这些动作由于json中的引用,会在不同的事件中表现出来。

jump.mtn
dance.mtn
...

expressions //文件夹,命名各异,存放表情文件,但是在部分插件表情文件会被忽略。

f00.exp.json
f01.exp.json
···

sound //文件夹,存放声音文件,可以在点击事件中引用。

normal_01.mp3
normal_02.mp3

出于多种原因,绝大部分制作师发布时只会发布导出后的模型文件(moc或moc3)。如果获得源文件(cmox或cmo3),就能通过软件自定义一些动作和表情,同时也能轻松地解决点击事件的区域问题,这一点接下来会说明。
当然,具体的命名方式要依情况而定。比如有些插件规定要将配置文件命名为index.json;又比如有些插件规定要将moc文件命名为name.model.moc,这个时候就要编辑配置文件,更改一下相关的路径。因此,接下来介绍模型设置model.json。
这是一个扒自OkYes个人博客的看板娘的例子。原因是他上次嘲笑我看板娘错位了。

{
    "version":"Sample 1.0.0",
    "model":"seifuku.moc",
    "textures":[
        "seifuku.1024/L2DMSR_U_00.png",
        "seifuku.1024/L2DMSR_U_01.png",
        "seifuku.1024/L2DMSR_U_02.png"
    ],
    "lip_sync": "false",
    "layout": {
        "center_x":0.0,
        "center_y":-0.05,
        "width":2.0
    },
    "hit_areas_custom":{
        "head_x":[-0.3, 1.12],
        "head_y":[0.26, 0.56],
        "body_x":[-0.41, 0.58],
        "body_y":[0.46, -1.12]
    },
    "motions":{
        "flick_head":[
            {
                "file":"seifuku.motions/motion1.mtn"
            },
            {
                "file":"seifuku.motions/motion10.mtn"
            },
            {
                "file":"seifuku.motions/motion11.mtn"
            },
            {
                "file":"seifuku.motions/motion12.mtn"
            },
            {
                "file":"seifuku.motions/motion2.mtn"
            },
            {
                "file":"seifuku.motions/motion4.mtn"
            },
            {
                "file":"seifuku.motions/motion5.mtn"
            },
            {
                "file":"seifuku.motions/motion6.mtn"
            },
            {
                "file":"seifuku.motions/motion7.mtn"
            },
            {
                "file":"seifuku.motions/motion8.mtn"
            }
        ],
        "tap_body":[
            {
                "file":"seifuku.motions/motion1.mtn"
            },
            {
                "file":"seifuku.motions/motion10.mtn"
            },
            {
                "file":"seifuku.motions/motion11.mtn"
            },
            {
                "file":"seifuku.motions/motion12.mtn"
            },
            {
                "file":"seifuku.motions/motion2.mtn"
            },
            {
                "file":"seifuku.motions/motion4.mtn"
            },
            {
                "file":"seifuku.motions/motion5.mtn"
            },
            {
                "file":"seifuku.motions/motion6.mtn"
            },
            {
                "file":"seifuku.motions/motion7.mtn"
            },
            {
                "file":"seifuku.motions/motion8.mtn"
            }
        ],
        "talk":[
            {
                "file":"talk/DK_NOZOMU_0041.mtn"
            },
            {
                "file":"talk/DK_NOZOMU_0061.mtn"
            },
            {
                "file":"talk/DK_NOZOMU_0067.mtn"
            }
        ],
        "idle":[
            {
                "file":"seifuku.motions/motion3.mtn"
            },
            {
                "file":"seifuku.motions/motion9.mtn"
            }
        ]
    },
    "expressions":[
        {"name":"f01.exp.json","file":"expressions/f01.exp.json"},
        {"name":"f02.exp.json","file":"expressions/f02.exp.json"},
        {"name":"f03.exp.json","file":"expressions/f03.exp.json"},
        {"name":"f04.exp.json","file":"expressions/f04.exp.json"},
        {"name":"f05.exp.json","file":"expressions/f05.exp.json"},
        {"name":"f06.exp.json","file":"expressions/f06.exp.json"},
        {"name":"f07.exp.json","file":"expressions/f07.exp.json"},
        {"name":"f08.exp.json","file":"expressions/f08.exp.json"}
    ],
    "physics":"physics.json",
    "pose":"seifuku.pose.json"
}

关键的参数有以下几个:

路径(model&textures&expressions&physics&pose):

依次为相应文件对应的路径。

激活自动对口型功能(lip_sync):

在这里用不上的东西,可以直接删去。

布局(layout):

layout的center_x字段和center_y字段用于偏移显示模型,日后若有显示错位可以修改此处的值。width定义缩放。

自定义HIT_AREA的方法(hit_areas_custom):

此处是Jad大佬用来解决问题而自定义的方法,如果你使用的插件是基于Jad大佬提供的js修改的版本,这个参数就能实现接下来的功能。但是接下来因我要从live2d-widget.js引用,这个参数将无意义。
用来设置头部和身体的点击区域,设置过后就能确定头部和身体在的位置,配置motions点击过后做出相应的动作和声音。头身体都是正方形区域,head_x是头左上角定点坐标,head_y是头部右下角定点坐标,身体和头相同。

怎么获取坐标?
在live2d.js中搜索DEBUG_MOUSE_LOG,将值改为1,这样控制台就会显示你点击的坐标值,这样就可以设置了。

触发区域名(hit_areas):

官方的web只定义了头部和身体的点击区域。其中id为制作模型时定义的区域id值,如下所示,可以自动地定义点击区域。
如果你没有源文件的话,就不知道id,只能通过上面的参数来手动设置区域。

"hit_areas": [
        {
            "name": "head",
            "id": "D_PSD.55"
        },
        {
            "name": "body",
            "id": "D_PSD.16"
        }
    ]

如何获取部件ID?
在拥有源文件的前提下,在Cubism 3 Editor中打开源文件,点击相应部件,在左下角窗口的属性中,即可查看部件ID。
【图】

动作(motion):

根据我接下来要进行接头霸王的而引用的js文件中显示:
可配置的只有这几个:"idle","sleepy","tap_body","flick_head","shake"。
依次对应为“待机时动作”、“闲置时动作”、“点击body时动作”、“点击head时动作”、“摇动时动作(限支持重力感应功能的设备使用)”。
尽管如此,有些动作,由于不同插件的定义问题,是不显示的。通常会在网页live2d上显示的的动作有"idle","tap_body","flick_head"。而我需要显示的也只是这几个动作,因此足够了。当然,由于版本问题,存在更多的动作,根据js内容可能会显示,比如其中的"talk"(对话)。
在关键词其后面的参数file是动作文件路径,依据实际情况来更改,是必须指明的。
除此之外,关键词后的参数还可以包含"pinch_in","pinch_out","sound",依次对应为“淡入”、“淡出”、“声音文件路径”。

将源文件转换成我们所需要的文件

在获得鸽子画师提供的源文件(cmo3)后,如果想要将之放在网页上,还需要一些必经的处理。
接下来会使用的工具有:Cubism 3 EditorLive2D Viewer

将模型格式由v3转v2

准备工作

cmo3是v3格式的模型文件,但是我们此次基于live2d2.0,因此选择将模型文件由v3转为v2。
我们进入Cubism 3 Editor,打开cmo3文件。因为模型材质图的分辨率高达8192px,因此我们只能使用专业版来操作。不过专业版有42天的试用时间,试用期间功能与正常版本无异,足够满足暂时的需求了。这里我们采用另外一个模型来演示。

如果42天后仍想白嫖,可移步至参考资料查看方法(不建议)。

【图】

删除和修改部件

如上图所示,如果要把天依放在网页上,有一些部件是不需要的,有两种方法来解决。
一是将目标区域的材质图修改为透明,那么在显示时自然就会消失;
二是删除目标区域部分的模型。为了减少模型文件大小和减轻运算量,推荐使用第二种方法。当然第一种方法并不是不可以,在无法接触到模型的源文件时,这也是唯一的方法。
在主界面内点击想要删除的部件,出现红框即代表此部件被选中,然后按下Del键删除。
或者在左下角的菜单中,选中三个背景,按下Del键删除,更加简单利索。如图所示。
【图】
如果想对显示结果中的部分区域做修改,可以选择导出材质图,用图像处理软件进行修改注意不要对材质图进行任何的移动或裁剪。之后再将材质图导入软件中即可(也可以在导出后的textures文件夹中直接修改)。
如果想进行更加精细的操作,可以在建模-纹理-编辑纹理中实现。如修改分辨率来降低文件大小(同时这样也避免了高分辨率需要专业版进行编辑的尴尬),如将无用纹理删除。如下面两张图演示。
【图】
【图】

更改尺寸

现在我们看到,天依处于画面右方,且多出来许多空白画面,会影响到看板娘的显示。因此我们需要更改尺寸和裁剪。
如果觉得天依太大了,可以在文件-模型尺寸中按比例缩小。这里我设置为50%。
为了裁剪画布多余的空白区域,如下图所示,在文件-画布设置中,
【图】
我们设置画布的大小。注意选择区域。这里因为缩小后天依处在靠画布正上方的位置,我们需要先将画布从正上方裁剪。我给定的数值裁剪效果会不好,之后再重复步骤,直到天依显示在画面正中央且没有过多多于画布即可。
【图】
想要看看此时模型的实际大小,可以点击下方的1:1按钮显示原始尺寸。当然,裁剪之后我们应该点击产生随机动作,来检查模型做动作时是否会超出画布。之后在从建模-参数-恢复默认设置中恢复默认姿态。
【图】

转换部件ID

由于v3格式与v2的部位ID不同,直接导出的话会出现不跟随鼠标动作等问题,需要把v3格式的部位ID转为v2格式,编辑器已经包含了这个功能:
在主窗口中依次点击建模-转换模型ID打开ID转换窗口。保持默认,点击OK即可。
【图】

解决小问题

再进行最后一步的导出工作前,我们来检查一下不同版本到底产生了哪些问题,并尽量改过来。以下是我遇到的问题和解决方案。

天依的闭眼和睁眼反了
从上方的演示图以及在开启随机动作后不难发现,天依的闭眼和睁眼动作反了过来。为了防止误操作,我们可以点击坐上方两个按钮,锁定绘画部件和变形器。之后,在左下角窗口参数中找到控制睁眼闭眼的参数。点击参数后面的数据,在接下来显示的列表中先选择参数,再点击反转即可。
【图】
鸽子画师的模型脸整没了
这里是不透明度和绘图顺序的问题。找出遮住脸的黑幕,原来是后发部分。如果设置完全透明的话,后发会消失。因此我们改变其绘图顺序,将之放在脸这一图层的下方即可。
【图】

导出模型

经过测试后,如果一切基本上没有问题了,则导出模型。在文件-导出嵌入式数据-导出moc文件(2.1用)导出。导出的文件应该包含textures文件夹、moc文件和model.json配置文件。
同时,为了接下来的工作需要,我们还通过文件-保存为2.1所用模型(cmox),另存一个2.1版本的源文件。
在导出模型时,可以根据需要选择分辨率。在网页上显示的话,我才用1024px就足够了。
【图】

增加动作

导出后的模型,理论上已经可以在插件中使用了,但是因为缺乏动作、表情、物理效果等文件,会呆呆地站在那里。在这一步中,我们为其加入动作。

如果已经有动作文件(can3)

在打开了模型的前提下打开动作文件。画布尺寸没有必要调。
在窗口左侧的场景中可以看到总共录了一个动作,名称为“= =”。
【图】
同时和上面所说一样,这里也出现了闭眼睁眼反了的毛病。我们展开下方的Live2D参数,找准控制睁闭眼的参数对应的几个关键帧,将参数移至睁眼。
【图】
整理点小问题后,接下来选择文件-导出嵌入式数据-导出动作数据,根据不同需要进行调整,注意勾选导出为2.1格式(mtn)。因为只有一个场景,所以我只导出了选中场景。为了应对在后期测试时发现动作有问题的情况,建议在文件-另存为中将修改后的动作另存为can3。
【图】

如果没有动作文件(can3)

没有动作文件的话,我们需要自己录制。在Cubism 3 Editor中,点击右下角的录制,便可自行设计动作。具体方法可以参照官网的说明。最后将动作导出为2.1格式(mtn)。

增加表情

因为兼容性问题,我们用2.1的办法来增加表情和物理效果。
打开Live2D Viewer,引入2.1格式的源文件。如图所示,已经给出了一些例子,另外基本的表情参数也能在官网上找到。在设置时自行调试就行。当然,局限于模型的精细度,一些参数没用上的话,例子也可能并不奏效。
在鼠标按右键保存后,需要更改格式为json。
【图】

增加物理效果

和上一步一样,软件已经给出了头发的物理效果模型。加入即可。之后可以在窗口右边观察更改后模型的情况。

将模型组装起来

根据之前对json的介绍,我们来依样画葫芦。在之前导出模型的操作中,我们已经得到一个json文件,现在用文本编辑器编辑。
将原来的内容:

{
    "type": "Live2D Model Setting",
    "name": "Model[洛天依]",
    "model": "Model[洛天依].moc",
    "textures": [
        "Model[洛天依].1024/texture_00.png"
    ]
}

考虑到尽量避免中文命名和相关的需求,进行相应地修改:

{
    "type": "Live2D Model Setting",
    "name": "luotianyi",
    "model": "model.moc",
    "textures": [
        "textures/texture_00.png"
    ],
    "layout": {
        "center_x":0.0,
        "center_y":-0.05,
        "width":2.0
    },
    "hit_areas":[
        {"name":"head","id":"D_PSD_01"},
        {"name":"body","id":"D_PSD_17"}
    ],
    "motions":{
        "idle":[
            {
                "file":"motions/abaaba.mtn"
            }
        ],
        "flick_head":[
            {
                "file":"motions/= =.mtn"
            }
        ]
    },
    "expressions":[
        {"name":"zhengyan.json","file":"expressions/zhengyan.json"},
        {"name":"biyan.json","file":"expressions/biyan.json"}
    ],
    "physics":"physics.json"
}

此时,模型的文件结构如下:

expressions

zhengyan.json
biyan.json

motions

= =.mtn
abaaba.mtn

textures

texture_00.png

model.json
model.moc
physics.json

进行模型的检查

此时可以在本地服务器上放上看板娘,来检查有无问题。这里选择直接使用保罗大佬的Pio插件来检查。
【图】
显示模型没有问题。
而这时,通过这个插件会出现一些麻烦。
其一,控制台报错:

l2d.js:1 Uncaught TypeError: Cannot read property '0' of undefined
    at o.r.hitTestSimpleCustom (l2d.js:1)
    at o.hitTestCustom (l2d.js:1)
    at o.tapEvent (l2d.js:1)
    at p (l2d.js:1)
    at g (l2d.js:1)

这个是因为我们没有写上hit_areas_custom属性,也因此导致了点击事件没有识别区域,故点击事件全部木大的效果。如果要使用这个插件,有关解决方法在此:看板娘插件文档
其二,表情不加载,需要修改l2d.js来解决。
其三,在缩放中定位出现偏差:即使填写了hit_areas_custom属性,但在拥有源文件的情况下如此这般一则定位可能不准,二则该插件在手机上干脆取消了点击事件。
其四,鸽子画师总是喜欢哔哔赖赖,说自己的模型十分高清,显示出来却一片模糊,岂可修。
如果你使用另一个版本的话,则可以解决第一个和第四个问题,但在解决第四个问题的同时会使得第三个问题更加严重。因此,由于特殊需要,我决定进行缝合,寻找万恶之源的衍生万恶之源

将看板娘放在网页上

引入脚本

无论是保罗大佬的插件还是FGHRSH的一条龙服务,都不能满足鸽子画师和我的需求,因此我找到了上文所说的万恶之源。
百度上有很多关于这个看板娘的教程,但都是告诉添加L2Dwidget.min.js这个脚本,然后使用。关于 L2Dwidget.min.js 这个脚本的源码怎么来的,却没有说明。以至于一开始我这个菜鸟直接下载万恶之源却发现用不了,以为是万恶之源遗漏了L2Dwidget.min.js文件,以至于去其演示页F12偷取,真是贻笑大方。
在此之前,应该准备些什么?在本地或云服务器上安装nodeJs以及cnpm,才能进行编译操作,这一部分的详细情况在参考资料中。
之后,将项目git下来(感谢@叉鸡临时给我补习我Linux的操作)。L2Dwidget.min.js脚本是需要编译生成的。

根据需求进行修改

刚Git下来的目录,是没有 lib和 node_modules目录的。
【图】
接下来我们安装依赖和编译项目,在当前目录打开命令窗口,输入如下命令:

cnpm install

这一步安装依赖和编译项目后,若成功,node_modules目录就生成了。之后,我们执行脚本,编译项目:

cnpm run build:dev

成功后,将生成lib目录,其中的L2Dwidget.min.js和L2Dwidget.0.min.js脚本成功生成。
不过在cnpm run build:dev 运行后,一直不退到输入界面,在成功后需要直接关闭。
当一切看起来完美无缺时,我们按照步骤在网页上插入以下部分:

  <script src="lib/L2Dwidget.min.js"></script>
  <script type="text/javascript">
    L2Dwidget.init();
  </script>

这个时候加载出的是默认配置,可以通过src/config/defaultConfig.js查看到:

  const defaultConfig = {
  model: {
    jsonPath: 'https://unpkg.com/live2d-widget-model-shizuku@latest/assets/shizuku.model.json',
    scale: 1,
  },
  display: {
    superSample: 2,
    width: 200,
    height: 400,
    position: 'right',
    hOffset: 0,
    vOffset: -20,
  },
  mobile: {
    show: true,
    scale: 0.8,
    motion: true,
  },
  name: {
    canvas: 'live2dcanvas',
    div: 'live2d-widget',
  },
  react: {
    opacity: 1,
  },
  dev: {
    border: false
  },
  dialog: {
    enable: false,
    script: null
  }
}

同时,可以定义的部分在src/index.js文件中也有注释。具体的解释请参考官方文档
在引入的时候,为了显示出我需要的模型,需要更改一下模型配置文件的路径:

L2Dwidget.init({
    "model": {
        "jsonPath": "www.example.com/name/model.json"
    }//省略了别的参数,省略部分将加载默认配置
});

当一切准备就绪后,在网页上显示出了人物模型,定位问题也完美的解决了,也通过定义超采样等级使模型清晰起来。但是遗憾的是,并没有满足需求。虽然能够切换表情了,但是冒出了一些更奇怪的问题。比如点击头部只会发生切换表情的事件,再比如多个动作时每次加载只会执行其中一个动作,除非刷新页面等。另外叉鸡还很不稀罕自带的对话框,始终想着FGHRSH大佬的对话框,又美观、功能又多,因此还要继续做修改。

随机的动作

修改cModel.js的第369行开始的if语句:

if (this.motions[name] == null)
{
    this.loadMotion(name, this.modelHomeDir + motionName, function(mtn) {
        motion = mtn;

        thisRef.setFadeInFadeOut(name, no, priority, motion);

    });
}
else
{
    motion = this.motions[name];


    thisRef.setFadeInFadeOut(name, no, priority, motion);
}

修改成:

if (this.motions[motionName] == null)
{
    this.loadMotion(motionName, this.modelHomeDir + motionName, function(mtn) {
        motion = mtn;

        thisRef.setFadeInFadeOut(name, no, priority, motion);

    });
}
else
{
    motion = this.motions[motionName];


    thisRef.setFadeInFadeOut(name, no, priority, motion);
}

切换表情

我的需求是点击头部的时候会切换表情,也会反应点击事件,因此修改cManager.js的第89行:

this.models[i].setRandomExpression();

改成:

this.models[i].setRandomExpression();
this.models[i].startRandomMotion(cDefine.MOTION_GROUP_FLICK_HEAD,
        cDefine.PRIORITY_NORMAL);

如果将思路拓展开来,其它的需求也能通过类似的方法满足。

编译

当我们将所要做的修改工作完成后,再次编译。

cnpm run build:dev

此后新的js文件将替换成原文件。在清除缓存后,这两个问题就能解决,基本满足了需求。可谓舍近求远、南辕北辙、绕道前行的大成功。

将对话框缝合

如果你不需要缝合对话框,到此处的配置便已经满足需求了,只需要在L2Dwidget.init();中修改配置,就能轻松地达成效果。

样式问题和亲爱的F12

简化并引用

引用高清涩图和路径问题

测试效果和评估完成度

参考资料

已有 4 条评论
标签:

  1. (ฅ´ω`ฅ)

  2. 需要破解版吗(手动狗头
    需要我可以分享出来(狗头

  3. 宁这模型咋离地5px呢(牛顿你咋活了OωO

      1. (╯‵□′)╯︵┴─┴这是css问题,图片仅供参考!