Live2D integration for PixiJS v5.
Only compatible with Cubism 2.1. Support of Cubism 4 is on the way!
npm install pixi-live2d-display
This library relies on Promise
, you may need a polyfill, e.g. es6-promise.
You'll also need to include the Cubism 2.1 core library, which is typically named live2d.min.js
. The official download has been dead since 9/4/2019, but its spirit still exists somewhere on the net...
import * as PIXI from 'pixi.js';
window.PIXI = PIXI;
import { Live2DModel } from 'pixi-live2d-display';
async function main() {
const model = await Live2DModel.fromModelSettingsFile('my-model.model.json');
const app = new PIXI.Application({ autoStart: true });
app.stage.addChild(model);
// transformation
model.position.set(100, 100);
model.scale.set(2, 2);
model.anchor.set(0.5, 0.5);
// motion
model.on('hit', hitAreas => {
if(hitAreas.includes('body')) {
model.motion('tapBody');
}
});
}
main();
It's possible to import only the necessary modules rather than a full build of PixiJS.
import { Application } from '@pixi/app';
import { Live2DModel } from 'pixi-live2d-display';
async function main() {
const model = await Live2DModel.fromModelSettingsFile('my-model.model.json');
const app = new Application();
app.stage.addChild(model);
}
main();
When including prebuilt file by script tag, all exported members are available in PIXI.live2d
namespace. For example import { Live2DModel } from 'pixi-live2d-display'
becomes PIXI.live2d.Live2DModel
.
<script src="pixi.min.js"></script>
<script src="pixi-live2d-display.browser.js"></script>
async function main() {
const model = await PIXI.live2d.Live2DModel.fromModelSettingsFile('my-model.model.json');
const app = new PIXI.Application({ autoStart: true });
app.stage.addChild(model);
}
main();
To make a Live2D model "live", it should be updated with delta time, which is the time elapsed from last frame to this frame, in milliseconds.
When a full build of PixiJS is imported, each model will be automatically updated using window.PIXI.Ticker.shared
.
import * as PIXI from 'pixi.js';
window.PIXI = PIXI;
Otherwise you need to register the Ticker
to Live2DModel
.
import { Application } from '@pixi/app';
import { Ticker, TickerPlugin } from '@pixi/ticker';
Application.registerPlugin(TickerPlugin);
Live2DModel.registerTicker(Ticker);
Or you may want to do it yourself:
import { Ticker } from '@pixi/ticker';
const model = await Live2DModel.fromModelSettingsFile('my-model.model.json', { autoUpdate: false });
Ticker.shared.add(()=> model.update(Ticker.shared.elapsedMS));
Or without Ticker
:
const model = await Live2DModel.fromModelSettingsFile('my-model.model.json', { autoUpdate: false });
let then = performance.now();
function tick(now) {
model.update(now - then);
then = now;
requestAnimationFrame(tick)
}
requestAnimationFrame(tick)
Interaction will be automatically set up for each model if Renderer
has an InteractionManager
plugin, which happens when a full build of PixiJS is imported:
import * as PIXI from 'pixi.js';
window.PIXI = PIXI;
Or when the plugin is manually registered using modules:
import { Renderer } from '@pixi/core';
import { InteractionManager } from '@pixi/interaction';
Renderer.registerPlugin('interaction', InteractionManager);
If you don't want the default behaviour, you can disable it by setting autoInteract
to false
, then play with corresponding interaction methods.
const model = await Live2DModel.fromModelSettingsFile('my-model.model.json', { autoInteract: false });
// focusing
canvasElement.addEventListener('mousemove', event => model.focus(event.clientX, event.clientY));
// tapping
canvasElement.addEventListener('mousedown', event => model.tap(event.clientX, event.clientY));
When any hit area (also called Collision Detection) is hit on tapping, a hit
event will be emitted with an array of hit hit areas.
model.on('hit', hitAreas => {
if(hitAreas.includes('body')) {
console.log('hit body')
}
});
Motions are managed by MotionManager
of each model.
import { Priority } from 'pixi-live2d-display'
// start a random motion in "tap_body" group
model.internal.motionManager.startRandomMotion('tapBody');
// start an explicit motion
model.internal.motionManager.startMotionByPriority('tapBody', 0, Priority.Normal);
// a shorthand of startRandomMotion()
model.motion('tapBody');
Expressions are managed by ExpressionManager
in MotionManager
.
model.internal.motionManager.expressionManager.setRandomExpression();
Sounds are managed by SoundManager
in MotionManager
.
model.internal.motionManager.soundManager.volume = 0.5;
When a motion with sound
defined starts, the sound will be played, and a sound
event will be emitted.
model.on('sound', (file, audio) => {
audio.addEventListener('ended', () => console.log('finished'));
});
The testing Live2D model, Shizuku, is distributed under Live2D's Free Material License.