live2d(Web SDK 4.x)Web看板娘进阶
接上篇文章
Live2D(WebSDK 4.X)网页看板娘设置(一)
欢迎大家阅读本篇教程
该篇教程为进阶版的看板娘设置教程,阅读完后可以极大程度的自定义你的看板娘项目。
教程内容:
- 去掉canvas中的背景图案和切换模型按钮,并将背景设置为透明
- 新增配置类,对需要自定义的属性进行修改,同时在模型中新建mapper
- 设置模型放大倍率
- 修改鼠标交互方式
- 适配模型的控制属性,自定义模型加载的位置
- 模块化该项目
1.修改canvas绘制的内容
打开【Sample\TypeScript\Demo\src\lappview.ts】文件,定位至以下位置,将我们不需要的功能注释掉。
定位到initializeSprite()函数
/*** 执行图像初始化*/public initializeSprite(): void{let width: number = canvas.width;let height: number = canvas.height;let textureManager = LAppDelegate.getInstance().getTextureManager();const resourcesPath = LAppDefine.ResourcesPath;let imageName: string = "";//注释掉所有图片加载方法/** // 背景画像初期化imageName = LAppDefine.BackImageName;// 创建回调函数,因为它是异步的let initBackGroundTexture = (textureInfo: TextureInfo): void =>{let x: number = width * 0.5;let y: number = height * 0.5;let fwidth = textureInfo.width * 2.0;let fheight = height * 0.95;this._back = new LAppSprite(x, y, fwidth, fheight, textureInfo.id);};//将背景图片的注释掉textureManager.createTextureFromPngFile(resourcesPath + imageName, false, initBackGroundTexture);// 歯車画像初期化imageName = LAppDefine.GearImageName;let initGearTexture = (textureInfo: TextureInfo): void =>{let x = width - textureInfo.width * 0.5;let y = height - textureInfo.height * 0.5;let fwidth = textureInfo.width;let fheight = textureInfo.height;this._gear = new LAppSprite(x, y, fwidth, fheight, textureInfo.id);};textureManager.createTextureFromPngFile(resourcesPath + imageName, false, initGearTexture);
*/-------省略其他代码-------}
onTouchesEnded函数
public onTouchesEnded(pointX: number, pointY: number): void{// 触摸结束let live2DManager: LAppLive2DManager = LAppLive2DManager.getInstance();live2DManager.onDrag(0.0, 0.0);{let x: number = this._deviceToScreen.transformX(this._touchManager.getX()); // 論理座標変換した座標を取得。let y: number = this._deviceToScreen.transformY(this._touchManager.getY()); // 論理座標変化した座標を取得。if(LAppDefine.DebugTouchLogEnable){LAppPal.printLog("[APP]touchesEnded x: {0} y: {1}", x, y);}live2DManager.onTap(x, y);/*// 点击了齿轮,调用live2D管理器切换人物模型if(this._gear.isHit(pointX, pointY)){live2DManager.nextScene();}*/}}
完成后我们找到Sample\TypeScript\Demo\src\lappdelegate.ts的run函数
/*** 実行処理。*/public run(): void{// メインループlet loop = () =>{// インスタンスの有無の確認if(s_instance == null){ return;}// 時間更新LAppPal.updateTime();// 画面の初期化//这里时初始化背景的,四个参数分别是 r g b a//原本的参数是0,0,0,1//我们把它改成0,0,0,0gl.clearColor(0.0, 0.0, 0.0, 0.0);----------------------省略.....----------------------};loop();}
修改完背景后,我们修改模型在页面中的布局。这里我是参考的别人的布局方式:是帐篷啊
完成以上步骤后我们可以运行一下看看效果
2.了解自己准备使用的模型
为了使项目兼容更多的模型,我们需要先了解不同模型直接的差异
- 首先是版本问题,该项目只支持模型入口文件为 *.moc3.json的模型
- 其次是模型的控制参数,这个是我们最需要关注的,因为这关系到你能不能控制人物的交互行为
控制参数我们可以使用官方的Cubism Viewer打开文件,之后点击moc文件,来查看。这里我们主要关注和控制身体、面部、眼球相关的参数(官方标准值为:ParamAngleX、Y、Z,ParamBodyAngleX和ParamEyeBallX、Y)
有了以上的了解,我们可以进行下一步,接下来我们要做的就是将当前模型的非标准值与标准值进行映射
首先,我们打开进入到你准备的模型的根目录(moc3.json文件同级),在这里新建mapper.json文件,内容如下:
{"parameter": [// 标准值:模型中所对应的值,如果你的模型用的就是标准值,也需要这样操作{"ParamAngleX": "PARAM_ANGLE_X"},{"ParamAngleY": "PARAM_ANGLE_Y"},{"ParamAngleZ": "PARAM_ANGLE_Z"},{"ParamEyeBallX": "PARAM_EYE_BALL_X"},{"ParamBodyAngleX": "PARAM_BODY_ANGLE_X"},{"ParamEyeBallY": "PARAM_EYE_BALL_Y"}],//这个暂时不用,后续更新的话有可能会使用到"url": [],//如果你想调整模型的位置,修改这个值,分别对应X轴和Y轴"center":[2,1]
}
该文件是根据我自己在源码中修改的加载逻辑而额外加的,方便来回切换模型时使用,你根据你自己的情况来决定要不要进行这一步。
第一步和第二步准备工作做好后,我们开始修改源码中与模型有关的内容
3. 修改资源路径等文件
- 新建MocMapper.ts文件
/*** 将Moc文件中的id值和资源文件名称及对应的url映射到程序中*/
import {resourcesConfig } from './lappdefine'export class MocMapper {private parameterIdMAp: Map<string, string> = new Map();private resourcesPathToUrlMap = new Map();private jsonResources = null;public static mapper: MocMapper = null;/*** 根据url地址获取资源路径和url的映射对象* @param url 保存映射关系的json的url地址*/public static getInstance() {if (this.mapper == null) {this.mapper = new MocMapper();}return this.mapper;}/*** 将当前模型的id值保存到键值对中* @param defaultParameter 官方默认的id值* @param currentModParameter 当前模型中与官方默认值效果相同的id值* @note 一般只需要设置 ParamEyeBall[X,Y] ParamAngle[X,Y,Z]*/public setParameter(defaultParameter: string, currentModParameter: string) {this.parameterIdMAp.set(defaultParameter,currentModParameter)}/*** * @param defaultParameter 根据默认id值获取当前模型对应的id值* @returns */public getParemeter(defaultParameter: string): string {return this.parameterIdMAp.get(defaultParameter)}
/*** * @param resourcesPath moc3.json中的资源路径* @param url 服务器中该路径对应文件的访问地址*/public setPathToUrl(resourcesPath:string, url:string) {this.resourcesPathToUrlMap.set(resourcesPath, url);}
/*** 根据资源路径获取url* @param resourcesPath 资源路径* @returns */public getUrl(resourcesPath: string): string {return this.resourcesPathToUrlMap.get(resourcesPath)}public getJsonConfig() {return this.jsonResources;}
/*** 从指定url读取模型目录中的mapper.json【自定义的变量映射关系文件】文件* @param url mapper文件的url*/public async setMapperJson(url: string) {this.jsonResources = await fetch(url).then(async function (response) {const json = await response.json();return json;})let arrayOfparameters = this.getJsonConfig().parameter;let arrayOfUrl = this.getJsonConfig().url;//将id值存入map中for (let i = 0; i < arrayOfparameters.length; i++) {let item = arrayOfparameters[i]for (let key in item) {this.setParameter(key,item[key])}}//将资源路径和url映射关系存入map中for (let i = 0; i < arrayOfUrl.length;i++) {let item = arrayOfUrl[i]for (let key in item) {this.setParameter(key,item[key])}}//设置模型的中心位置let centerPointScal = this.getJsonConfig().center;resourcesConfig.setXscal(centerPointScal[0]);resourcesConfig.setYscal(centerPointScal[1]);}
}
- 打开【Samples\TypeScript\Demo\src\lappdefine.ts】文件
修改源文件内容如下:
import { LogLevel } from '@framework/live2dcubismframework';// Canvas width and height pixel values, or dynamic screen size ('auto').
export const CanvasSize: { width: number; height: number } | 'auto' = 'auto';// 画面,默认放大倍率
export const ViewScale = 2;export const ViewMaxScale = 4.0;
export const ViewMinScale = 0.4;
export const ViewLogicalLeft = -1.0;
export const ViewLogicalRight = 1.0;
export const ViewLogicalBottom = -1.0;
export const ViewLogicalTop = 1.0;
export const ViewLogicalMaxLeft = -2.0;
export const ViewLogicalMaxRight = 2.0;
export const ViewLogicalMaxBottom = -2.0;
export const ViewLogicalMaxTop = 2.0;//新建ResouConfig对象
class ResourceConfig {public resourcesPath: string;public modelNames: string[];public modelSize: number;public canvasId: string = 'live2d';public x_scal: number = 2;public y_scal:number = 1constructor() {this.resourcesPath = '../../Resources/';this.modelNames = ['Haru', 'Hiyori', 'Mark', 'Natori', 'Rice'];this.modelSize = this.modelNames.length;}public setResourcesPath(path:string) {this.resourcesPath = path;}public setCanvasId(canvasId:string) {this.canvasId = canvasId;}public setModelNames(models:string[]) {this.modelNames = models;this.setModelSize();}public setModelSize() {this.modelSize = this.modelNames.length;}public getResourcesPath() {return this.resourcesPath;}public getModelNames() {return this.modelNames;}public getModelSize() { return this.modelSize;}public getCanvasId() {return this.canvasId}public setXscal(scal:number) { this.x_scal = scal }public setYscal(scal:number) { this.y_scal = scal }public getXscal() { return this.x_scal }public getYscal() { return this.y_scal}}
//将该对象导出,方便在其他文件中使用
export const resourcesConfig = new ResourceConfig();// 相対パス
//export const ResourcesPath = '../../Resources/';// モデルの後ろにある背景の画像ファイル
export const BackImageName = '';// 歯車
export const GearImageName = '';// 終了ボタン
export const PowerImageName = '';// モデル定義---------------------------------------------
// モデルを配置したディレクトリ名の配列
// ディレクトリ名とmodel3.jsonの名前を一致させておくこと
//export const ModelDir: string[] = ['Haru', 'Hiyori', 'Mark', 'Natori', 'Rice'];
//export const ModelDirSize: number = ModelDir.length;//下方省略.................
- 打开**【Samples\TypeScript\Demo\src\lappdelegate.ts】,导入lappdefine.ts中的resourceConfig对象,定位到initialize()**修改内容如下:
import * as LAppDefine from './lappdefine';
/**
省略其他方法
*/public initialize(): boolean {// 修改成我们自己配置的canvascanvas = <HTMLCanvasElement>document.getElementById(LAppDefine.resourcesConfig.getCanvasId());// glコンテキストを初期化// @ts-ignoregl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');if (!gl) {alert('Cannot initialize WebGL. This browser does not support.');gl = null;document.body.innerHTML ='This browser does not support the <code><canvas></code> element.';// gl初期化失敗return false;}// キャンバスを DOM に追加// document.body.appendChild(canvas);if (!frameBuffer) {frameBuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);}// 透過設定gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);// 注释掉原有的点击和触摸事件
/*const supportTouch: boolean = 'ontouchend' in document;if (supportTouch) {// タッチ関連コールバック関数登録document.ontouchstart = onTouchBegan;document.ontouchmove = onTouchMoved;document.ontouchend = onTouchEnded;//document.ontouchend = onTouchEnded;//canvas.ontouchcancel = onTouchCancel;} else {*/// マウス関連コールバック関数登録//只能在canvas的事件上调用on*Ended()方法,不然会影响人物的点击效果//onmousemove方法只能在onmousedown和onmouseup之间调用,不然没有效果//document.onmousemove = onMouseMoved;canvas.onmouseup = onClickEnded;document.addEventListener("mousemove", function (e) {if (!LAppDelegate.getInstance()._view) {LAppPal.printMessage("view notfound");return;}//之前我是使用documents对象来获取canvas,其实已经有全局变量了,这里也可以直接用let rect = canvas.getBoundingClientRect();let posX: number = e.clientX - rect.left;let posY: number = e.clientY - rect.top;LAppDelegate.getInstance()._view.onTouchesMoved(posX, posY);}, false);//在这里加上鼠标离开浏览器后,一切归位document.addEventListener("mouseout", function (e) {//鼠标离开document后,将其位置置为(0,0) let live2DManager: LAppLive2DManager = LAppLive2DManager.getInstance();live2DManager.onDrag(0.0, 0.0);}, false);// AppViewの初期化this._view.initialize();// Cubism SDKの初期化this.initializeCubism();return true;}
- 打开【Samples\TypeScript\Demo\src\lapplive2dmanager.ts】文件,添加或修改以下方法
//开头导入相关文件
import { MocMapper}from './MocMapper'
import * as LAppDefine from './lappdefine';
-----------------------------下边是对象内容----------------------------------------------public nextScene(): void {const no: number = (this._sceneIndex + 1) % LAppDefine.resourcesConfig.getModelSize();this.changeScene(no);}/*** 随机加载模型 */public randomScene(): void {//设置随机的模型选项let randomNum= Math.random()*(LAppDefine.resourcesConfig.getModelSize()+1)//取值范围[0,size+1)包含小数//转为整数后再次取余防止出现数组越界问题let indexOfModel = Math.floor(randomNum)%LAppDefine.resourcesConfig.getModelSize()this.changeScene(indexOfModel);}public async changeScene(index: number): Promise<void> {this._sceneIndex = index;if (LAppDefine.DebugLogEnable) {LAppPal.printMessage(`[APP]model index: ${this._sceneIndex}`);}// ModelDir[]に保持したディレクトリ名から// model3.jsonのパスを決定する。// ディレクトリ名とmodel3.jsonの名前を一致させておくこと。const model: string = LAppDefine.resourcesConfig.getModelNames()[index];const modelPath: string = LAppDefine.resourcesConfig.getResourcesPath() + model + '/';let modelJsonName: string = LAppDefine.resourcesConfig.getModelNames()[index];modelJsonName += '.model3.json';let mapperJsonOfModel = modelPath + 'mapper.json';let mapper = MocMapper.getInstance();await mapper.setMapperJson(mapperJsonOfModel)this.releaseAllModel();this._models.pushBack(new LAppModel());this._models.at(0).loadAssets(modelPath, modelJsonName);}
- 打开【Sample\TypeScript\Demo\src\lappview.ts】文件,initializeSprite()函数
//修改
const resourcesPath = LAppDefine.resourcesConfig.getResourcesPath();
- 打开【Framework\src\math\cubismmodelmatrix.ts】,导入resourcesConfig,然后找到构造方法
//在开头加上
import { resourcesConfig } from '../../../Samples/TypeScript/Demo/src/lappdefine';---------------------------------下面是构造方法内容----------------------------------------------
constructor(w?: number, h?: number) {super();this._width = w !== undefined ? w : 0.0;this._height = h !== undefined ? h : 0.0;//这个是官方随便给的一个数,调整后会影响模型的放大倍率this.setHeight(2.0);//设置模型中心位置this.setCenterPosition(w*resourcesConfig.getXscal()/2, h*resourcesConfig.getYscal()/2);}
- 修改【Sample\TypeScript\Demo\src\main.ts】
import { LAppDelegate } from './lappdelegate';
import { resourcesConfig} from './lappdefine';
import { LAppLive2DManager} from './lapplive2dmanager'function start() {// create the application instanceif (LAppDelegate.getInstance().initialize() == false) {return;}LAppDelegate.getInstance().run();
}
function stop() {LAppDelegate.releaseInstance();
}
/*** 根据index来控制切换模型,只能填整数* @param index 模型的次序* 从1开始。 小于0:随机加载* >0加载数组下标为index-1的模型* ==0 加载下一个模型*/
function changeScene(index: number) {let manager = LAppLive2DManager.getInstance();if (index < 0) {manager.randomScene();} else if (index > 0 ) {manager.changeScene(index-1)} else if (index ==0) {manager.nextScene();}
}
module.exports = { start , stop , changeScene , resourcesConfig}
- 打开【Samples\TypeScript\Demo\src\lappmodel.ts】,导入MocMapper,定位到构造方法
public constructor() {super();this._modelSetting = null;this._modelHomeDir = null;this._userTimeSeconds = 0.0;this._eyeBlinkIds = new csmVector<CubismIdHandle>();this._lipSyncIds = new csmVector<CubismIdHandle>();this._motions = new csmMap<string, ACubismMotion>();this._expressions = new csmMap<string, ACubismMotion>();this._hitArea = new csmVector<csmRect>();this._userArea = new csmVector<csmRect>();let mapper = MocMapper.mapper;if (mapper != null) {this._idParamAngleX = CubismFramework.getIdManager().getId(mapper.getParemeter("ParamAngleX"));this._idParamAngleY = CubismFramework.getIdManager().getId(mapper.getParemeter("ParamAngleY"));this._idParamAngleZ = CubismFramework.getIdManager().getId(mapper.getParemeter("ParamAngleZ"));this._idParamEyeBallX = CubismFramework.getIdManager().getId(mapper.getParemeter("ParamEyeBallX"));this._idParamEyeBallY = CubismFramework.getIdManager().getId(mapper.getParemeter("ParamEyeBallY"));this._idParamBodyAngleX = CubismFramework.getIdManager().getId(mapper.getParemeter("ParamBodyAngleX"));} else {this._idParamAngleX = CubismFramework.getIdManager().getId(CubismDefaultParameterId.ParamAngleX);this._idParamAngleY = CubismFramework.getIdManager().getId(CubismDefaultParameterId.ParamAngleY);this._idParamAngleZ = CubismFramework.getIdManager().getId(CubismDefaultParameterId.ParamAngleZ);this._idParamEyeBallX = CubismFramework.getIdManager().getId(CubismDefaultParameterId.ParamEyeBallX);this._idParamEyeBallY = CubismFramework.getIdManager().getId(CubismDefaultParameterId.ParamEyeBallY);this._idParamBodyAngleX = CubismFramework.getIdManager().getId(CubismDefaultParameterId.ParamBodyAngleX);}this._state = LoadStep.LoadAssets;this._expressionCount = 0;this._textureCount = 0;this._motionCount = 0;this._allMotionCount = 0;this._wavFileHandler = new LAppWavFileHandler();}
- 打开【Samples\TypeScript\Demo\webpack.config.js】,修改打包输出方式
module.exports = {mode: 'production',target: ['web', 'es5'],entry: './src/main.ts',output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.js',publicPath: '/dist/',library: 'live2dLoader',libraryTarget:'umd'},resolve: {extensions: ['.ts', '.js'],//省略。。。。。。。。。。。
- 打开【index.html】,添加js代码
<script type="text/javascript">var loader = live2dLoader //这两个是必须设置的loader.resourcesConfig.setResourcesPath("./live2d/models/")loader.resourcesConfig.setModelNames(['a','b'])loader.start();console.log(loader)</script>
以上修改完成后,打包运行即可。
结尾
修改完成后的项目我上传到GitHub:https://github.com/cqc233/live2dDemo
如果对于某个细节不是很明白可以参考我的旧版文章:
Live2D(Cubism3.x)网页看板娘设置(二)
Live2D(Cubism3.x)网页看板娘设置(三)
live2d(Web SDK 4.x)Web看板娘进阶相关推荐
- 用html5看板娘,记在nuxt.js中引入一个萌哒哒的看板娘(Live2d模型)
在vue中引入看板娘很简单,网上资源也很多 那么在nuxt中如何引入呢?这个问题我试了好多好多方法,像vue一样引入全局组件...都不行,最后找到了一个方法. 其实就是运用nuxt中的app.html ...
- live2d PHP,GitHub - hiccphp/live2d-widget: 把萌萌哒的看板娘抱回家 | Live2D widget for web platform...
Live2D Widget 特性 Feature 在网页中添加Live2D看板娘.与PJAX兼容,支持无刷新加载. Add Live2D widget to web page. Compatible ...
- html中看板娘添加,如何在自己开发的web中放入“看板娘”
什么是看板娘? 那就她了--> 就是她~ 看板娘是一种职业和习惯称呼,也是ACGN次文化中的萌属性之一.简而言之就是小店的女服务生,也有"吸引顾客,招揽生意,提高人气"等作用 ...
- L2Dwidget二次元看板娘的web用法
打工人的猎奇心: 发现某些blog网站左下方或者右下方出现的二次元卡通人物或萌萌阿猫,除了萌,还可以监听鼠标的行为,产生互动的现象.为探究竟,打工人开始了行动- 一.看板娘 hexo live ...
- 给网站加个漂亮可爱的看板娘live2d,自建api,简化无错版
给网站加个漂亮可爱的看板娘live2d,自建api,简化无错版 演示及相关下载 演示地址: 下载地址: 添加看板娘流程 1.解压文件到你的站点目录 如:https://www.ex.com/live2 ...
- Hexo添加Live2D看板娘最新教程
目录 前言 介绍 Live2D 看板娘 添加Live2D看板娘 准备工作 安装依赖 下载model文件 添加live2d看板娘到hexo 查看效果 发布 结束语 参考 前言 上次我们搭建了hexo博客 ...
- live2d 看板娘 简单添加看版娘到自己的网站
简单添加看版娘到自己的网站 关注公众号后台回复 "看板娘" 获取全部文件(接口,源代码). 我们先来看一下我们的网站会变成什么样吧! http://www.djyqxbc.vi ...
- Live2D看板娘详细实现
Live2D看板娘实现 国际惯例先上图: 所需资源: 链接:https://pan.baidu.com/s/1s7IJIqGnn-cNRAfoS-qG5w 提取码:dhf4 其中包含了看板娘所需的CS ...
- 网易技术干货 | 云信Web SDK测试实践
一.项目介绍 网易云信于2015年成立,为网易集团下属的内资公司,总部位于杭州.除资深老杭研外,团队核心90%来自硅谷.百度.腾讯.阿里.华为等大型企业/独角兽公司,平均行业经验10年以上,掌握业内领 ...
最新文章
- Python学习十大良好习惯
- 华硕无双新品首爆:H45标压处理器+全球首款2.8K 120Hz OLED屏
- caffe 使用小技巧
- joson返回数据库的时间格式在前台用js转换
- 鱼之死,越狱章鱼和雾霾黑客
- Spring AOP切面实现:参数传递
- 朋友去面试阿里蚂蚁金服测试岗位过程经历
- Gradle build daemon disappeared unexpectedly (it may have been killed or may hav
- webRTC(八):查看offer/answer 的 SDP
- MQ-3酒精模拟量 电压转换公式
- 2016MDCC移动开发者大会总结
- 鸿蒙对比ios流畅,鸿蒙OS 2.0对比iOS 14:苹果流畅度完败?
- 几种车载网络特征对比
- 解决IDEA项目一直Updating Index
- 近期基金有所上涨,你的基金回本了吗?如果回本了,你还会继续持仓吗?
- 磁盘IOPS和带宽(throughput)
- 6.oop-类和对象
- 无人巴士和无人出租车都能用的L4自动驾驶通用硬件方案
- 1、Centos7系统的初化始配置
- Linux——匿名管道、命名管道及进程池概念和实现原理