是一种使用一系列连续的2d图像让图像看起像3d风格的绘图渲染技术。
在MyChild中的主人公就使用了该技术来实现一系列动作。非常适合需要不同方法与主角互动的游戏。
游戏中主人公的玩耍,吃饭 ,睡觉一系列动作以及鼠标点击主角和跟随鼠标转动等互动操作。
Live2D.init(); //初始化(只在最初调用一次)
//Live2D.dispose(); //释放live2d占用的资源
1.直接加载模型
Live2DModelUnity.loadModel(Application.dataPath + "/Resources/Model/Epsilon/runtime/Epsilon.moc");//读取模型(加载moc文件后缀moc一定要)
2.通过二进制文件加载
将moc文件复制一份并将后缀上加上.bytes
TextAsset mocFile = R
esources.Load
Live2DModelUnity.loadModel(mocFile.bytes);
和加载模型同理加载贴图。
live2dModel.setTexture("贴图索引" , ”贴图资源“);
private Matrix4x4 live2DCanvasPos; //可理解为画布位置 指定显示位置和尺寸(使用正交矩阵与相关API显示图像,在由游戏物体的位置和摄像机的size调整图像到合适的位置) float modelWidth = live2dModel.getCanvasWidth(); //正交投影矩阵 live2DCanvasPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50, 50);
之后就是在update中设置模型显示位置
//设置模型显示位置 live2dModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos); live2dModel.update();//更新模型可以更新的任何信息
最后
private void OnRenderObject() { live2dModel.draw(); //显示模型 }
模型贴图加载之后,就是让它动起来。
可public出去直接拖上去,也可以代码加载。
将动作源文件加载成Live2DMotion类型的动作。
public TextAsset[] motionFiles; //动作源文件 private Live2DMotion[] motions; //加载源文件之后的动作 motions = new Live2DMotion[motionFiles.Length]; //通过加载源文件来加载动作 for (int i = 0; i < motions.Length; i++) { motions[i] = Live2DMotion.loadMotion(motionFiles[i].bytes); }
动作播放方式API:
motions[0].setLoopFadeIn(false);//重复播放是否淡入
motions[0].setFadeOut(1000);//设置淡出时间(毫秒)
motions[0].setFadeIn(1000); //设置淡入时间
motions[0].setLoop(true); //动画是否循环播放
动作播放需要动作管理者类
motionQueueManger = new MotionQueueManager();
motionQueueManger.startMotion(motions[0]);
然后在update中指定哪个模型显示该动作、动作的播放对应模型。
motionQueueManger.updateParam(live2dModel);
多个动作同时播放:
需要多个动作管理者MotionQueueManager来管理不同的动作。
motionQueueMangerA = new MotionQueueManager();
motionQueueMangerA.startMotion(motions[5]);
motionQueueMangerA.updateParam(live2dModel);
动作播放的优先级:
//优先级设置的标准: //1.动作未进行的状态,优先级为0;(无任何动作或者所有动作都执行完毕) //2.待机动作发生时,优先级为1; //3.其他动作发生时,优先级为2; //4.无视优先级,强制发生的动作,优先级为3;
private L2DMotionManager l2DMotionManager; //加入了优先级设置标准其它与MotionQueueManager无异private void Update() { //判断待机动作 if (l2DMotionManager.isFinished()) { StartMotion(0, 1); } else if (Input.GetKeyDown(KeyCode.M)) { StartMotion(14, 2); } l2DMotionManager.updateParam(live2dModel); } public void StartMotion(int motionIndex , int priority) { if (l2DMotionManager.getCurrentPriority() >= priority) { return; } l2DMotionManager.startMotion(motions[motionIndex]); }
通过改变模型的某个部位的参数来达到模型行为的改变
//设置参数 live2dModel.setParamFloat("PARAM_EYE_R_OPEN" , 0 , 1 ); //ID , value , priority live2dModel.addToParamFloat("PARAM_EYE_R_OPEN", -0.1f, 1);//数值的累加 live2dModel.multParamFloat("PARAM_EYE_R_OPEN", -0.1f, 1);//数值乘法 通过获取索引去设置参数 int paramAngleX; paramAngleX = live2dModel.getParamIndex("PARAM_EYE_R_OPEN"); live2dModel.setParamFloat(paramAngleX, 30); //参数的保存和回复 live2dModel.setParamFloat("PARAM_EYE_R_OPEN", 0, 1); //保存与回复的参数是整个模型的所有参数,并不只是之前同方法里设置的某几个参数 live2dModel.saveParam(); live2dModel.loadParam(); //设定模型某一部分的透明度 live2dModel.setPartsOpacity("PARTS_01_HAIR_SIDE_002", 0); //"PARTS_01_HAIR_SIDE_002"参数是part 不是param
这是param
这是part
使用参数设置也可以让模型动起来,所以我认为一般应该是使用这些参数来设置。
一般模型师会给你配置表中会有这些ID,以及数值设置。
//auto眨眼 private EyeBlinkMotion eyeBlinkMotion; eyeBlinkMotion = new EyeBlinkMotion(); //眨眼 eyeBlinkMotion.setParam(live2dModel);
//鼠标拖拽引起的动作变化 private L2DTargetPoint drag; //拖拽 drag = new L2DTargetPoint(); //模型跟随鼠标转向 Vector3 pos = Input.mousePosition;//屏幕坐标 if (Input.GetMouseButton(0)) { //设定屏幕坐标点 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) { //设置身体眼睛头部跟随鼠标 //如果想让眼睛一直看着你 在drag.getX()前加"-"号 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", 1 * -drag.getX()); live2dModel.setParamFloat("PARAM_EYE_BALL_X" , -drag.getY()); } //得到的live2d鼠标检测点的比例值是-1到1(对应一个live2d拖拽) //管理坐标系,或者叫影响度 //然后我们通过这个值去设置我们的参数,比如旋转30度*当前得到的值
头发的摆动
根据模型的不同头发的参数也不同。demo里的模型头发分为前左侧,前有侧,后左侧,后右侧。所以,我们需要每个部分都进行操作。
首先定义Live2D插件中关于头发的类型
private PhysicsHair physicsHairSide_L; //前左侧发 private PhysicsHair physicsHairSide_R; //前右侧发 private PhysicsHair physicsHairBack_L; //左后发 private PhysicsHair physicsHairBack_R; //左后发
在Start中设置好各种参数
private void HairMotion() { //头发的摇摆 physicsHairSide_L = new PhysicsHair(); physicsHairSide_R = new PhysicsHair(); physicsHairBack_L = new PhysicsHair(); physicsHairBack_R = new PhysicsHair(); //套用物理运算,让头发动起来 physicsHairSide_L.setup(0.2f, 0.5f, 0.14f); //长度(影响摇摆周期,越长越慢,反之) 空气阻力: 可设定0-1的值。预设值是0.5 重量 kg physicsHairSide_R.setup(0.2f, 0.5f, 0.14f); physicsHairBack_L.setup(0.7f, 0.5f, 0.14f); //长度(影响摇摆周期,越长越慢,反之) 空气阻力: 可设定0-1的值。预设值是0.5 重量 kg physicsHairBack_R.setup(0.7f, 0.5f, 0.14f); //设置输入参数 //设置哪一部分变动时进行哪一种运算 PhysicsHair.Src srcX_L = PhysicsHair.Src.SRC_TO_X; //横向摇摆 //PhysicsHair.Src srcX_L = PhysicsHair.Src.SRC_TO_G_ANGLE; //下垂 PhysicsHair.Src srcX_R = PhysicsHair.Src.SRC_TO_X; //效果和下垂差不多 PhysicsHair.Src srcZ = PhysicsHair.Src.SRC_TO_G_ANGLE; //第三个参数 , 当PARAM_ANGLE_X变动时,头发受到0.005倍的影响度 physicsHairSide_L.addSrcParam(srcX_L, "PARAM_ANGLE_X", 0.005f, 1); physicsHairSide_R.addSrcParam(srcX_R, "PARAM_ANGLE_X", 0.005f, 1); physicsHairBack_L.addSrcParam(srcX_L, "PARAM_ANGLE_X", 0.005f, 1); physicsHairBack_R.addSrcParam(srcX_R, "PARAM_ANGLE_X", 0.005f, 1); physicsHairBack_L.addSrcParam(srcZ, "PARAM_ANGLE_Z", 0.8f, 1); physicsHairBack_R.addSrcParam(srcZ, "PARAM_ANGLE_Z", 0.8f, 1); //设置输出表现 PhysicsHair.Target target = PhysicsHair.Target.TARGET_FROM_ANGLE;//表现形式 physicsHairSide_L.addTargetParam(target, "PARAM_HAIR_SIDE_L", 0.005f, 1); physicsHairSide_R.addTargetParam(target, "PARAM_HAIR_SIDE_R", 0.005f, 1); physicsHairBack_L.addTargetParam(target, "PARAM_HAIR_BACK_L", 0.005f, 1); physicsHairBack_R.addTargetParam(target, "PARAM_HAIR_BACK_R", 0.005f, 1); }
最后在update中更新头发动作就完成了
//更新头发动作 long time = UtSystem.getSystemTimeMSec(); //执行时间(一定要是long类型,具体是什么时间我忘记了) physicsHairSide_L.update(live2dModel, time); physicsHairSide_R.update(live2dModel, time); physicsHairBack_L.update(live2dModel, time); physicsHairBack_R.update(live2dModel, time);
表情系统
////// 加载表情动作 /// private void ExpressionMotion() { ExpressionMotionMgr = new MotionQueueManager(); expressions = new L2DExpressionMotion[expressionFiles.Length]; for (int i = 0; i < expressions.Length; i++) { expressions[i] = L2DExpressionMotion.loadJson(expressionFiles[i].bytes); } } ////// 播放表情 /// private void PlayExpression() { if (Input.GetKeyDown(KeyCode.M)) { motionIndex++; if (motionIndex >= expressions.Length) { motionIndex = 0; } ExpressionMotionMgr.startMotion(expressions[motionIndex]); } ExpressionMotionMgr.updateParam(live2dModel); }
将口型动作做成moc动作文件
1.播放声音时播放动作。
2.如果是Android/IOS项目,从他SDK中获取到声音,根据声音的波频来设置嘴长的大小。
3.X