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 runtime library, which is typically named live2d.min.js
. The official download has been gone 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');
}
});
}
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);
}
When including prebuilt files 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>
const app = new PIXI.Application({ autoStart: true });
PIXI.live2d.Live2DModel.fromModelSettingsFile('my-model.model.json').then(model => {
app.stage.addChild(model);
})
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 });
new Ticker().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 the names 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, note that it's camelCased into "tapBody" when using in the code
model.internal.motionManager.startRandomMotion('tapBody');
// start an explicit motion as normal priority
model.internal.motionManager.startMotionByPriority('tapBody', 0, Priority.Normal);
// a shorthand of startRandomMotion()
model.motion('tapBody');
When a motion starts, the sound will be played (if there is), and a motion
event will be emitted.
model.on('motion', (group, index, audio) => {
if(audio) {
audio.addEventListener('ended', () => console.log('finished'));
}
});
Expressions are managed by ExpressionManager
in MotionManager
.
model.internal.motionManager.expressionManager.setRandomExpression();
Sounds are managed by static SoundManager
.
import { SoundManager } from 'pixi-live2d-display';
SoundManager.volume = 0.5;
import { config } from 'pixi-live2d-display';
// log level
config.logLevel = config.LOG_LEVEL_WARNING;
// play sound for motions
config.sound = true;
// defer motion and corresponding sound until both are loaded
config.motionSync = true;
The testing Live2D model, Shizuku, is distributed under Live2D's Free Material License.