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.tsrun函数

 /**
     * 実行処理。
     */
    public run(): void
    {
        // メインループ
        let loop = () =>
        {
            // インスタンスの有無の確認
            if(s_instance == null)
            { return;}
            // 時間更新
            LAppPal.updateTime();
            
            // 画面の初期化
            //这里时初始化背景的,四个参数分别是 r g b a
            //原本的参数是0,0,0,1
            //我们把它改成0,0,0,0
            gl.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. 修改资源路径等文件

  1. 新建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]);
    }
}
  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 = 1
    constructor() {
        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;

//下方省略.................
  1. 打开**【Samples\TypeScript\Demo\src\lappdelegate.ts】,导入lappdefine.ts中的resourceConfig对象,定位到initialize()**修改内容如下:
import * as LAppDefine from './lappdefine';
/**
省略其他方法
*/

public initialize(): boolean {
    // 修改成我们自己配置的canvas
    canvas = <HTMLCanvasElement>document.getElementById(LAppDefine.resourcesConfig.getCanvasId());
  
    // glコンテキストを初期化
    // @ts-ignore
    gl = 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>&lt;canvas&gt;</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;
  }

  1. 打开【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);
  }
  1. 打开【Sample\TypeScript\Demo\src\lappview.ts】文件,initializeSprite()函数
//修改
const resourcesPath = LAppDefine.resourcesConfig.getResourcesPath();
  1. 打开【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);
  }

  1. 修改【Sample\TypeScript\Demo\src\main.ts】
import { LAppDelegate } from './lappdelegate';
import { resourcesConfig} from './lappdefine';
import { LAppLive2DManager} from './lapplive2dmanager'

function  start() {

   // create the application instance
   if (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}
  1. 打开【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();
  }
  1. 打开【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'],
    //省略。。。。。。。。。。。
  1. 打开【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)网页看板娘设置(三)

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
很多博客都有live2D的萌萌哒看板娘,手痒,所以整理了一套HTML5可以用的,注释换一下就可以用啦,最好是放到线上服务器上使用。 L2Dwidget.init({ "model": { //萌娘 jsonPath: "model/live2d-shizuku/assets/shizuku.model.json", //短发女生 //jsonPath: "model/live2d-epsilon2_1/assets/Epsilon2.1.model.json", //双马尾女仆 // jsonPath: "model/live2d-haru_1/assets/haru01.model.json", //双马尾学生装 // jsonPath: "model/live2d-haru_2/assets/haru02.model.json", //小可爱(男) // jsonPath: "model/live2d-haruto/assets/haruto.model.json", //小可爱(女) // jsonPath: "model/live2d-koharu/assets/koharu.model.json", //傲娇学生装女 // jsonPath: "model/live2d-hibiki/assets/hibiki.model.json", //红衣长发女生 // jsonPath: "model/live2d-izumi/assets/izumi.model.json", //黄发制服可爱女生 // jsonPath: "model/live2d-z16/assets/z16.model.json", //黄发女生 // jsonPath: "model/live2d-unitychan/assets/unitychan.model.json", //可爱白猫 // jsonPath: "model/live2d-tororo/assets/tororo.model.json", //可爱黑猫 // jsonPath: "model/live2d-hijiki/assets/hijiki.model.json", //初音未来 // jsonPath: "model/live2d-miku/assets/miku.model.json", //小帅哥 //jsonPath: "model/live2d-chitose/assets/chitose.model.json", //持剑绿衣剑士 // jsonPath: "model/live2d-gf/assets/Gantzert_Felixander.model.json", //科技感短发小女孩 // jsonPath: "model/live2d-ni-j/assets/ni-j.model.json", //狐妖 // jsonPath: "model/live2d-nico/assets/nico.model.json", //绿发女生 // jsonPath: "model/live2d-tsumiki/assets/tsumiki.model.json", "scale": 1 }, "display": { "superSample": 2, "position": "top", "width": 150, "height": 300, "hOffset": 0, "v

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值