/** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ import { CubismMatrix44 } from '@framework/math/cubismmatrix44'; import { ACubismMotion } from '@framework/motion/acubismmotion'; import { csmVector } from '@framework/type/csmvector'; import * as LAppDefine from './lappdefine'; import { canvas } from './lappglmanager'; import { LAppModel } from './lappmodel'; import { LAppPal } from './lapppal'; export let s_instance: LAppLive2DManager = null; /** * サンプルアプリケーションにおいてCubismModelを管理するクラス * モデル生成と破棄、タップイベントの処理、モデル切り替えを行う。 */ export class LAppLive2DManager { /** * クラスのインスタンス(シングルトン)を返す。 * インスタンスが生成されていない場合は内部でインスタンスを生成する。 * * @return クラスのインスタンス */ public static getInstance(): LAppLive2DManager { if (s_instance == null) { s_instance = new LAppLive2DManager(); } return s_instance; } /** * クラスのインスタンス(シングルトン)を解放する。 */ public static releaseInstance(): void { if (s_instance != null) { s_instance = void 0; } s_instance = null; } /** * 現在のシーンで保持しているモデルを返す。 * * @param no モデルリストのインデックス値 * @return モデルのインスタンスを返す。インデックス値が範囲外の場合はNULLを返す。 */ public getModel(no: number): LAppModel { if (no < this._models.getSize()) { return this._models.at(no); } return null; } /** * 現在のシーンで保持しているすべてのモデルを解放する */ public releaseAllModel(): void { for (let i = 0; i < this._models.getSize(); i++) { this._models.at(i).release(); this._models.set(i, null); } this._models.clear(); } /** * 画面をドラッグした時の処理 * * @param x 画面のX座標 * @param y 画面のY座標 */ public onDrag(x: number, y: number): void { for (let i = 0; i < this._models.getSize(); i++) { const model: LAppModel = this.getModel(i); if (model) { model.setDragging(x, y); } } } /** * 画面をタップした時の処理 * * @param x 画面のX座標 * @param y 画面のY座標 */ public onTap(x: number, y: number): void { if (LAppDefine.DebugLogEnable) { LAppPal.printMessage( `[APP]tap point: {x: ${x.toFixed(2)} y: ${y.toFixed(2)}}` ); } for (let i = 0; i < this._models.getSize(); i++) { // if (this._models.at(i).hitTest(LAppDefine.HitAreaNameHead, x, y)) { // if (LAppDefine.DebugLogEnable) { // LAppPal.printMessage( // `[APP]hit area: [${LAppDefine.HitAreaNameHead}]` // ); // } // this._models.at(i).setRandomExpression(); // } else if (this._models.at(i).hitTest(LAppDefine.HitAreaNameBody, x, y)) { // if (LAppDefine.DebugLogEnable) { // LAppPal.printMessage( // `[APP]hit area: [${LAppDefine.HitAreaNameBody}]` // ); // } // this._models // .at(i) // .startRandomMotion( // LAppDefine.MotionGroupTapBody, // LAppDefine.PriorityNormal, // this._finishedMotion // ); // } // 点击画布后播放随机动作 this._models .at(i) .startRandomMotion( LAppDefine.MotionGroupTapBody, LAppDefine.PriorityNormal, this._finishedMotion ); } } /** * 画面を更新するときの処理 * モデルの更新処理及び描画処理を行う */ public onUpdate(): void { const { width, height } = canvas; const modelCount: number = this._models.getSize(); for (let i = 0; i < modelCount; ++i) { const projection: CubismMatrix44 = new CubismMatrix44(); const model: LAppModel = this.getModel(i); if (model.getModel()) { if (model.getModel().getCanvasWidth() > 1.0 && width < height) { // 横に長いモデルを縦長ウィンドウに表示する際モデルの横サイズでscaleを算出する model.getModelMatrix().setWidth(2.0); projection.scale(1.0, width / height); } else { projection.scale(height / width, 1.0); } // 设置自定义配置 projection.scale( model._scale * projection.getScaleX(), model._scale * projection.getScaleY() ); projection.translate(model._translateX, model._translateY); // 必要があればここで乗算 if (this._viewMatrix != null) { projection.multiplyByMatrix(this._viewMatrix); } } model.update(); model.draw(projection); // 参照渡しなのでprojectionは変質する。 } } /** * 次のシーンに切りかえる * サンプルアプリケーションではモデルセットの切り替えを行う。 */ public nextScene(): void { const no: number = (this._sceneIndex + 1) % LAppDefine.ModelDirSize; this.changeScene(no); } /** * シーンを切り替える * サンプルアプリケーションではモデルセットの切り替えを行う。 */ public changeScene(index: number): void { this._sceneIndex = index; if (LAppDefine.DebugLogEnable) { LAppPal.printMessage(`[APP]model index: ${this._sceneIndex}`); } // ModelDir[]に保持したディレクトリ名から // model3.jsonのパスを決定する。 // ディレクトリ名とmodel3.jsonの名前を一致させておくこと。 const model: string = LAppDefine.ModelDir[index]; const modelPath: string = LAppDefine.ResourcesPath + model + '/'; let modelJsonName: string = LAppDefine.ModelDir[index]; modelJsonName += '.model3.json'; this.releaseAllModel(); this._models.pushBack(new LAppModel()); this._models.at(0).loadAssets(modelPath, modelJsonName); } public setViewMatrix(m: CubismMatrix44) { for (let i = 0; i < 16; i++) { this._viewMatrix.getArray()[i] = m.getArray()[i]; } } /** * コンストラクタ */ constructor() { this._viewMatrix = new CubismMatrix44(); this._models = new csmVector(); this._sceneIndex = 0; // this.changeScene(this._sceneIndex); } _viewMatrix: CubismMatrix44; // モデル描画に用いるview行列 _models: csmVector; // モデルインスタンスのコンテナ _sceneIndex: number; // 表示するシーンのインデックス値 // モーション再生終了のコールバック関数 _finishedMotion = (self: ACubismMotion): void => { LAppPal.printMessage('Motion Finished:'); console.log(self); }; /** * 加载模型 * @param modelDir 模型目录 */ public loadModel(modelDir: string) { // 拼接模型路径和模型名称 const modelName = modelDir.substring( modelDir.lastIndexOf('/', modelDir.lastIndexOf('/') - 1) + 1, modelDir.length - 1 ); const modelJsonName: string = modelName + '.model3.json'; // 释放全部模型 this.releaseAllModel(); // 数组添加新的模型 this._models.pushBack(new LAppModel()); this._models.at(0).loadAssets(modelDir, modelJsonName); } /** * 随机表情 */ public randomExpression() { for (let i = 0; i < this._models.getSize(); i++) { this._models.at(i).setRandomExpression(); } } }