将进酒
君不见,黄河之水天上来,奔流到海不复回。
君不见,高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。
古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。
主人何为言少钱,径须沽取对君酌。
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。
本文首发于Lohanry's Blog,转载请注明。
什么是Live2D
- Live2D是日本Cybernoids公司开发。
- Live2D现阶段有两个主要的版本是2.1的版本和3.0的版本。
- 国内2.1版本用的比较多(ps.此为自己的感觉,并没有数据支持,只是感觉国内讨论的比较多的就是2.1了)。
- 大家可以到官网去下载2.1版本,由于3.0版本的无法支持当前项目的Unity版本,所以暂时没有使用。
Unity中的Live2D
Live2D已经增加的了对Unity的支持,所以只需要将SDK下载下来后倒入就可以使用了,里面也有几个简单的例子可以直接跑。
本文讨论的是Unity中使用Live2D,对Live2D的制作不做任何讨论。
我们可以从Live2D导出的文件中看到如下标准格式:
- model.1024文件夹中放的是人物的模型贴图,由于多贴图导致DrawCall升高的,所以在游戏中尽量只使用一张贴图。
- motions文件夹是所有的动作文件.mtn
- expressions文件夹是所有表情动作.mtn
- model.moc是Live2D的模型文件
- physics是物理演算文件
- pose.json 可以用于动态的切换部件以实现特定需求
Live2D的Model.json文件
这里主要讲解下model.json这个文件
{
"version":"Sample 1.0.0",
"model":"model.moc",
"textures":[
"model.1024/texture_00.png"
],
"motions":{
"idle":[
{"file":"motions/idle_01.mtn"}
],
"":[
{"file":"motions/x1.mtn"}
]
},
"physics":"physics.json",
"hit_areas":[
{"name":"face","id":"D_REF.FACE"},
{"name":"body","id":"D_REF.BODY"},
{"name":"arm_l","id":"D_REF.ARM_L"}
]
}
事实上这个Json文件都可以不存在的,只要你手工调用方法加载上MOC文件和贴图文件,Live2D的基本就已经显示出来了。
但是我这边使用了model.json是因为方便资源的统一管理,方便可以看出来一个L2D模型中有多少资源包含。
在代码中虽然可以直接进行加载,但是我们更加希望有一份json作为引导文件告诉我们,现在需要加载哪些资源,然后引用哪些资源,以方便调用。
在游戏中中我们参考了SDK其中包含的Live2D的Demo,加载框架作为基础,然后修改,做出项目能用的框架。
此Model.json的key-value值参考的是Demo中的框架,大家闲得蛋疼可以搞一套属于自己的,但是也可以直接使用已经存在的。
Unity中Live2D的问题
在Unity中使用Live2D需要注意许多问题,我在使用中也踩到了很多坑,虽然现在是有给出了解决的方案,但是这些有可能都不是最终最好的解决方案,希望大家能帮我指出。
1.Live2D的RenderMode。
可能很多人一开始使用的时候都没有注意到这个参数的设置,默认是L2D_RENDER_DRAW_MESH_NOW。
这个参数有两个变量L2D_RENDER_DRAW_MESH_NOW和L2D_RENDER_DRAW_MESH。
主要区分为L2D_RENDER_DRAW_MESH_NOW是在OnRenderObject时候调用Draw()函数。
在OnRenderObject中调用Draw()会造成其Live2D一直处于最后渲染,而不会被其他物体而覆盖。
而L2D_RENDER_DRAW_MESH是在Update()中调用Draw()函数,可以进行半透明物体的渲染。
2.Live2D中图形层级的问题
在Unity中使用L2DSDK2.1时候,你会发现一个很痛苦的问题,他没办法改变图层。
本人在Ugui与Live2D结合中现在已经使用过这几个方案:
- 采用Unity的RT技术。
- 采用专门的一个摄像机进行拍摄,然后渲染到UI上。
- 实际使用时候需要进行矩阵的换算。
- 而且比较吃性能和内存,没有特别比必要就没有使用。
- 采用sortorder进行层级控制。
- 调整其他非Live2D的UI的sortorder。
- Ugui中可以使用sortorder来指定渲染层级。
- 但是由于项目一开始并没有考虑到会有live2D,所以Layer的定义都在同一个Deaflut中。
- 我猜测L2d在渲染时候是全部渲染到了order为0的层上了。
- 所以假设情况允许可以考虑将需要分层的下层物体的order分别设置为负数,需要在上层的物体的order设置为正数可以解决图层分层的问题。
- 使用L2D_RENDER_DRAW_MESH_NOW直接叠加。
- 如果两个Live2D模型需要叠加的话推荐使用L2D_RENDER_DRAW_MESH_NOW。
- 那有人会说那这样谁会渲染在谁上面?
- 经过测试这样的情况下,谁后实例化谁会最后进行渲染。
- 所以如果采用此方案进行最好自己创建一个Live2D生成队列进行管理。来维护渲染顺序。
- 使用L2D_RENDER_DRAW_MESH进行叠加。
- 假设遇到了情况是两个Live2D叠加而且需要使用L2D_RENDER_DRAW_MESH作为渲染模式时候。
- 两个Live2D的叠加顺序符合场景中的排序。
- 但是要注意的是在某些情况下会出现两个Live2D的部分部件相互穿透的问题。
- 这个问题的出现我猜测是在Live2D制作中图层的穿透问题。
- 但是在某些情况下并没有复现,而且美术最近比较忙,没有来得及详细测试找出原因。
- 使用RenderQueue。
- 如果你的方案是Unity+NGUI+Live2D,可以考虑使用RenderQueue来进行排序。
- 在Live2D自带的Shader中为3000,你可以根据层级来设置你自己的UI的RenderQueue,同样他不支持设置Live2D的RenderQueue。
- 同时即使采用UGUI也可以使用RnederQueue,比如叠加粒子时候。
Live2D动作管理
- 在Live2D中动作的文件均是以mtn后缀的文件
- 在model.json中可以使用motions这个字典来管理
- 通过key来管理多个不同种类的动作文件。
Live2D代码解析(以Demo中SampleApp1这个项目作为例子):
1.资源加载入口函数
LAppModel.LoadFromResource(String dir,String filename);
2.Model.Init函数(修改支持DrawMode切换)
public void Init(String modelJson,int DrawMode)
{
updating = true;
initialized = false;
modelSetting = new ModelSettingJson(modelJson);
if (LAppDefine.DEBUG_LOG) Debug.Log("Start to load model");
// Live2D Model
if (modelSetting.GetModelFile() != null)
{
loadModelData(modelHomeDir + modelSetting.GetModelFile());
//setRenderMode必须要在加载Moc文件后,加载贴图前。
GetLive2DModelUnity().setRenderMode(DrawMode);
var len = modelSetting.GetTextureNum();
for (int i = 0; i < len; i++)
{
loadTexture(i, modelHomeDir + modelSetting.GetTextureFile(i));
}
}
}
3.Model.Json 解析引导
//事实上所有的动作等资源均是类似管理,如果需要提高性能可以单独实现数组
//通过ModelSettingJson来解析model.json
modelSetting = new ModelSettingJson(modelJson);
//通过函数跟踪可以发现其实是直接返回json而已
len = modelSetting.GetTextureNum();
4.动作播放
游戏中动作被分为:
- 待机动作,
- 特殊待机动作,
- 触摸事件动作,
- 震动事件动作。
除了待机动作,所有动作均可以相互交换。
特殊待机动作,触摸动作,震动动作,被维护成一个字典,有唯一索引,进行调用。
//此方法已经是被我修改过的方法,原始方法可以在例子中找到,原始方法是支持以索引号去播放,我修改为可以根据name播放,大家可以根据代码自行修改一下比较简单。
LAppModel.StartMotion(string group,string motionName,int priority);
Live2D表情管理
- 游戏中没有太多的使用表情,所以也不展开讨论。
- 标签文件为json格式。
- 使用方法与动作类似 。
- 表情使用时候要注意覆盖的问题。
LAppModel.SetExpression(string name);
Live2D口型管理
在Live2D的动画播放时候有时候希望能使语音与嘴型对上可以有如下几个方法:
-
通过控制PARAM_MOUTH_OPEN_Y。
- 使用系统级API获得当前的设备的播放音量
- 量化到0-1之间去
- 然后设置PARAM_MOUTH_OPEN_Y来控制最新的张口。
-
使用单独mtn嘴型文件
- 单独做出关于嘴型参数的mtn动作
- 创建不同的MotionQueueManager来管理嘴型和一般动画
- 需要同时播放才可以对上。
Live2D的Pose文件
- 我们可以在游戏中使用Pose.json文件来实现某些特性
- 其实主体的方法就是切换部件来实现某些特性。
- 在需要的时候会读取Pose.json文件然后加载上需要的组件
LAppModel.loadPose(String path);
- 然后大家会发现自从切换上Pose文件后就不管样都无法恢复到一开始的样子了。
- 后来通过测试发现通过重新初始化来恢复。代码如下:
//非常简单,pose变量是L2DPose类型,是LAppModel上的变量。
//live2DModel就是当前的ALive2DModel。
//这样就可以恢复到加载pose文件之前的。
pose.initParam(live2DModel);
Live2D触摸事件的管理
- 在游戏中Live2D触摸事件在每个场景中是不一样的,为了提高整个组件的复用。
- 组件中的触摸事件是一个事件链
- 在不同场景中的控制器可以使用委托来事件触摸事件的反应,而且在做出反应后可以通过函数的返回值来确定下一级时候继续要处理整个事件,增加了这个的灵活性。
写在尾巴上的
- 事实上Live2D的更多功能还并没有完全的被探索使用出来,
- 本文由于本人自己的能力有限只是简单探索,作为抛砖引玉作用。
- 国内资料暂时还比较缺少,本人斗胆分享了出来,希望各位大牛不要耻笑。
最后做个小广告~
喜欢的可以转载下我的小文章
Blog:http://www.hailantown.com