//============================================================================= // RPG Maker MZ - SmartAnimation // // Copyright 2024 Panzer-IV. All rights reserved. // This source code or any portion thereof must not be // reproduced or used without licensed in any manner whatsoever. //============================================================================= /*: * @target MZ * @plugindesc スマートアニメーション * @author 四号戦車 * @base PluginCommonBase * @base DebugSwitch * @base TextResource * @base PluginUtils * @orderAfter PluginCommonBase * @orderAfter DebugSwitch * @orderAfter TextResource * @orderAfter PluginUtils * * @help SmartAnimation.js * * Version: 0.0.33 * * スマートアニメーションを実装するユーティリティプラグインです。 * フレームに合わせて複数の値を変更できます。 * 他のプラグインで使用する前提です。 * */ (() => { "use strict"; //======================================================================== // プラグイン定義 //======================================================================== const pluginName = "SmartAnimation"; const script = document.currentScript; const param = PluginManagerEx.createParameter(script); //======================================================================== // 定数定義 (EasingType) //======================================================================== const EasingType = { LINEAR: 'linear', EASE_IN: 'ease_in', EASE_OUT: 'ease_out', EASE_INOUT: 'ease_inout', }; window.EasingType = Object.freeze(EasingType); //======================================================================== // クラス定義 (SmartAnimation) //======================================================================== class SmartAnimation { constructor(name, object, property, repeat, start, end, callback, easing) { this.name = name; this.object = object; this.startValue = start; this.endValue = end; this.targetProperty = property; this.state = 'ready'; this.repeat = repeat; if (!!easing) { this.easing = easing; } else { this.easing = EasingType.LINEAR; } if (!!callback && typeof callback === 'function') { this.listener = callback; } else { this.listener = null; } this.directionReverse = (end < start); this.currentFrame = 0; this.currentValue = start; this.throttle = 0; this.duration = 0; this.inSkip = false; } setDuration(duration) { this.duration = duration; let amount = this.endValue - this.startValue; if (this.directionReverse) { amount = this.startValue - this.endValue; } this.throttle = Math.abs(amount / duration); // Logger.d(`setDuration: throttle = ${this.throttle}, duration = ${duration}, amount = ${amount}`); this.reset(); return this; } setThrottle(throttle) { this.throttle = throttle; let amount = this.endValue - this.startValue; if (this.directionReverse) { amount = this.startValue - this.endValue; } this.duration = amount / Math.abs(throttle); // Logger.d(`setThrottle: throttle = ${throttle}, duration = ${this.duration}, amount = ${amount}`); this.reset(); return this; } getDuration() { return this.duration; } getThrottle() { return this.throttle; } getState() { return this.state; } start() { this._setState('running'); } setSkip(skip) { this.inSkip = skip; } stop() { if (this.state !== 'complete') { this._setState('stopped'); this._callbackListener(true); } } completeForce() { if (this.state !== 'complete') { this.currentFrame = this.duration; this.update(); } } // Game_Picture.prototype.applyEasing = function(current, target) { // const d = this._duration; // const wd = this._wholeDuration; // const lt = this.calcEasing((wd - d) / wd); // const t = this.calcEasing((wd - d + 1) / wd); // const start = (current - target * lt) / (1 - lt); // return start + (target - start) * t; // }; calcTarget() { if (this.easing === EasingType.LINEAR) { return (this.throttle * this.currentFrame); } else { const current = this.duration - this.currentFrame; const lt = this.calcEasing(this.currentFrame / this.duration); const t = this.calcEasing((this.currentFrame + 1) / this.duration); } } calcEasing(t) { const exponent = 2; switch (this.easing) { case EasingType.EASE_IN: return Math.pow(t, exponent); case EasingType.EASE_OUT: return 1 - Math.pow(1 - t, exponent); case EasingType.EASE_INOUT: if (t < 0.5) { return Math.pow((t * 2), exponent) / 2; } else { return (1 - Math.pow(1 - (t * 2 - 1), exponent)) / 2 + 0.5; } } // switch return t; } update() { if (this.state !== 'running') { return; } let target = 0; if (this.directionReverse) { const currentValue = this.startValue - (this.throttle * this.currentFrame); target = Math.max(currentValue, this.endValue); } else { const currentValue = this.startValue + (this.throttle * this.currentFrame); target = Math.min(currentValue, this.endValue); } if (!!this.object && !!this.targetProperty) { this.object[this.targetProperty] = target; } this.currentFrame++; if (this.currentFrame >= this.duration || this.inSkip) { if (!!this.listener) { this.listener(this.name); } if (!!this.object && !!this.targetProperty) { this.object[this.targetProperty] = this.endValue; } this._setState('complete'); if (this.repeat) { this.reset(); } } } reset() { this.currentFrame = 0; this.state = 'ready'; if (!!this.object && !!this.targetProperty && !!this.object[this.targetProperty]) { this.object[this.targetProperty] = this.startValue; } } inRunning() { return this.state === 'running'; } isComplete() { return this.state === 'complete'; } isRepeat() { return this.repeat; } _callbackListener(byAbort) { if (!!this.listener && typeof this.listener === 'function') { this.listener(byAbort); } } _setState(newState) { this.state = newState; } } window.SmartAnimation = SmartAnimation; //======================================================================== // クラス定義 (SmartAnimationWait) //======================================================================== class SmartAnimationWait extends SmartAnimation { constructor(name, duration, callback) { super(name, null, null, false, 0, 0, callback); this.setDuration(duration); } } window.SmartAnimationWait = SmartAnimationWait; //======================================================================== // クラス定義 (SmartAnimationSequencial) //======================================================================== class SmartAnimationSequencial extends SmartAnimation { constructor(name, repeat, callback, ...values) { super(name, null, null, repeat, 0, 0, callback, ...values); this.values = !!values ? values : []; this.targetFrame = values.reduce((accum, value) => { return accum + value.getDuration(); }, 0); this.currentIndex = 0; } update() { if (this.state !== 'running') { return; } const current = this.values[this.currentIndex]; if (!!current) { if (current.isComplete()) { this.currentIndex++; if (this.currentIndex >= this.values.length) { this._setState('complete'); this._callbackListener(false); if (this.repeat) { this.reset(); this.start(); } } else { this.currentValueFrame = 0; this.values[this.currentIndex].update(); } } else { current.update(); } } } reset() { super.reset(); this.values.forEach(value => value.reset()); this.currentIndex = 0; } start() { super.start(); this.values.forEach(value => value.start()); } stop() { super.stop(); this.values.forEach(value => value.stop()); } } window.SmartAnimationSequencial = SmartAnimationSequencial; //======================================================================== // クラス定義 (SmartAnimationParallel) //======================================================================== class SmartAnimationParallel extends SmartAnimation { constructor(name, repeat, callback, ...values) { super(name, null, null, repeat, 0, 0, callback, ...values); this.values = !!values ? values : []; this.targetFrame = values.reduce((accum, value) => { const duration = value.getDuration(); if (accum < duration) { return duration; } else { return accum; } }, 0); this.currentFrame = 0; } update() { if (this.state !== 'running') { return; } this.values .filter(value => value.isComplete() === false) .forEach(value => value.update()); this.currentFrame++; if (this.currentFrame >= this.targetFrame) { this._setState('complete'); this._callbackListener(false); if (this.repeat) { this.reset(); } } } reset() { super.reset(); this.values.forEach(value => value.reset()); } start() { super.start(); this.values.forEach(value => value.start()); } stop() { super.stop(); this.values.forEach(value => value.stop()); } } window.SmartAnimationParallel = SmartAnimationParallel; })();