123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- import * as RE from 'rogue-engine';
- import * as THREE from 'three';
- export default class Animator extends RE.Component {
- @RE.props.list.animation() animations: THREE.AnimationClip[] = [];
- private _curAnimations: THREE.AnimationClip[] = [];
- @RE.props.data()
- data: any[] = [];
- private animationsHaveChanged() {
- if (this._curAnimations.length !== this.animations.length) return true;
- for (let i = 0; i < this._curAnimations.length; i++) {
- if (this._curAnimations[i] !== this.animations[i]) return true;
- }
- return false;
- }
- private updateConfigs() {
- const newConfigs: any[] = [];
- for (let i = 0; i < this.animations.length; i++) {
- if (this.data[i]) {
- newConfigs[i] = this.data[i];
- continue;
- }
- newConfigs[i] = {
- actionName: i.toString(),
- playOnce: false,
- maxWeight: 1,
- duration: 1,
- speed: 1,
- };
- }
- this._curAnimations = this.animations.slice();
- this.data.splice(0);
- newConfigs.forEach(config => this.data.push(config));
- // this.activeAction && this.activeAction.reset();
- this._mixer = this._mixer = new THREE.AnimationMixer(this.object3d);
- this.animations.forEach((clip, i) => {
- const action = this._mixer.clipAction(clip);
- clip.name = this.data[i].actionName;
- this.data[i].playOnce && action.setLoop(THREE.LoopOnce, 0);
- this.actions[this.data[i].actionName] = {action, config: this.data[i]};
- });
- }
- private _selected = 0;
- @RE.props.select()
- get selected() {
- this.selectedOptions.splice(0, this.selectedOptions.length, ...this.animations.map((_, i) => i.toString() ));
- this.isReady && this.animationsHaveChanged() && this.updateConfigs();
- return this._selected;
- }
- editorUpdate: {stop: () => void} | undefined;
- set selected(value: number) {
- this._selected = value;
- this.updateAnimationConfigInputs();
- // this.activeAction && this.activeAction.reset();
- this.animationsHaveChanged() && this.updateConfigs();
- if (this.playLabel === "Stop" && !RE.Runtime.isRunning) {
- this.playAction();
- }
- }
- private updateAnimationConfigInputs() {
- this.data = this.data;
- const activeConfig = this.data[this._selected];
- this.actionName = activeConfig.actionName;
- this.playOnce = activeConfig.playOnce;
- this.duration = activeConfig.duration;
- this.speed = activeConfig.speed;
- }
- selectedOptions: string[] = this.animations.map((elem, i) => i.toString() );
- @RE.props.text()
- get actionName() {
- const activeConfig = this.data[this.selected];
- return activeConfig ? activeConfig.actionName : "";
- }
- set actionName(value: string) {
- if (this.selected < 0) return;
- const activeConfig = this.data[this.selected];
- if (!activeConfig) return;
- activeConfig.actionName = value;
- }
- @RE.props.checkbox()
- get playOnce() {
- const activeConfig = this.data[this.selected];
- return activeConfig ? activeConfig.playOnce : false;
- }
- set playOnce(value: boolean) {
- if (this.selected < 0) return;
- const activeConfig = this.data[this.selected];
- if (!activeConfig) return;
- activeConfig.playOnce = value;
- }
- @RE.props.num()
- get duration() {
- const activeConfig = this.data[this.selected];
- return activeConfig ? activeConfig.duration : 1;
- }
- set duration(value: number) {
- if (this.selected < 0) return;
- const activeConfig = this.data[this.selected];
- if (!activeConfig) return;
- activeConfig.duration = value;
- }
- @RE.props.num()
- get speed() {
- const activeConfig = this.data[this.selected];
- return activeConfig ? activeConfig.speed : 1;
- }
- set speed(value: number) {
- if (this.selected < 0) return;
- const activeConfig = this.data[this.selected];
- if (!activeConfig) return;
- activeConfig.speed = Number(value);
- }
- private stopped = false;
- private stopping = false;
- get isActive() {
- return !this.stopped && !this.stopping;
- }
- stop() {
- this.stopping = true;
- }
- resume() {
- this.stopped = false;
- this.stopping = false;
- }
- @RE.props.button()
- play() {
- if (RE.Runtime.isRunning) return;
- if (this.playLabel === "Play" && !this.editorUpdate) {
- this.mixer;
- this.animationsHaveChanged() && this.updateConfigs();
- this.playAction();
- this.editorUpdate = RE.onUpdate(sceneController => {
- if (sceneController === RE.Runtime) return;
- // this.animationsHaveChanged() && this.stopAction() && this.updateConfigs();
- this.mixer.update(sceneController.deltaTime * this.speed);
- });
- }
- else if (this.playLabel === "Stop") {
- this.playLabel = "Play";
- this.stopAction();
- this.editorUpdate?.stop();
- this.editorUpdate = undefined;
- }
- }
- private stopAction() {
- this.mixer.existingAction(this.animations[this.selected])?.reset();
- this.mixer.stopAllAction();
- }
- private playAction() {
- this.stopAction();
- const action = this.actions[this.actionName];
- if (!this.actions[this.actionName]) return;
- this.playLabel = "Stop";
- action.action.play();
- }
- playLabel = "Play";
- private _mixer: THREE.AnimationMixer;
- actions: {[name: string]: {action: THREE.AnimationAction, config}} = {};
- activeAction: THREE.AnimationAction;
- get mixer() {
- if (!this._mixer) {
- this._mixer = new THREE.AnimationMixer(this.object3d);
- this.animations.forEach((clip, i) => {
- const action = this._mixer.clipAction(clip);
- clip.name = this.data[i].actionName;
- this.data[i].playOnce && action.setLoop(THREE.LoopOnce, 0);
- this.actions[this.data[i].actionName] = {action, config: this.data[i]};
- });
- }
- return this._mixer;
- }
- awake() {
- this.editorUpdate?.stop();
- this.editorUpdate = undefined;
- }
- start() {
- this.mixer.existingAction(this.animations[this.selected])?.reset();
- this.mixer.stopAllAction();
- const configs = this.data;
- this.animations.forEach((clip, i) => {
- const clipConfig = configs[i];
- clipConfig.duration && (clip.duration = clipConfig.duration);
- const action = this.mixer.existingAction(clip)
- if (!action) return;
- action.play() as THREE.AnimationAction;
- clipConfig.playOnce && action.setLoop(THREE.LoopOnce, 0);
- this.setWeight(action, this.selected === i ? 1 : 0);
- });
- this.mixer.removeEventListener("finished", this.animationFinished);
- this.mixer.addEventListener("finished", this.animationFinished);
- this.activeAction = this.defaultAction;
- this.mix(Object.keys(this.actions)[0]);
- }
- update() {
- if (this.stopped) return;
- this.mixer.update(RE.Runtime.deltaTime * this.actions[this.activeAction.getClip().name].config.speed);
- }
- getAction(index: number | string) {
- return this.actions[index].action;
- }
- setWeight(action: THREE.AnimationAction, weight: number) {
- action.enabled = true;
- action.time = 0;
- action.setEffectiveTimeScale(1);
- action.setEffectiveWeight(weight);
- }
- getWeight(actionName: string) {
- return this.getAction(actionName).getEffectiveWeight();
- }
- get defaultAction() {
- return this.getAction(this.defaultActionName)
- }
- get defaultActionName() {
- return Object.keys(this.actions)[0];
- }
- private animationFinishedListeners: (() => void)[] = [];
- onAnimationFinished(cb: () => void) {
- this.animationFinishedListeners.push(cb);
- }
- private animationFinished = () => {
- if (this.stopping) {
- this.stopped = true;
- this.stopping = false;
- }
- this.animationFinishedListeners.forEach(listener => listener());
- if (this.activeAction.loop === THREE.LoopOnce && !this.activeAction.clampWhenFinished) {
- this.mix(this.defaultActionName, 0.001, false);
- }
- }
- mix(actionName: string, transitionTime: number = 0.1, warp = true, weight = 1) {
- const action = this.getAction(actionName);
- action.reset();
- this.setWeight(action, weight);
- action.crossFadeFrom(this.activeAction, transitionTime, warp);
- this.activeAction = action;
- }
- }
- RE.registerComponent(Animator);
|