/*: * @target MZ MV * @plugindesc ピクチャを連続でクロスフェード表示するプラグイン * @author Google Gemini * @version 2.0.1 * * @help SequentialPictureShow.js * * このプラグインは、指定したピクチャリストを連続で表示する機能を提供します。 * 決定キーを押すか画面をクリックすると、次のピクチャにクロスフェードで * 切り替わります。 * 最後のピクチャが表示された後、もう一度決定キーなどを押すと終了します。 * * このプラグインはマップ画面および戦闘画面の両方で安全に動作します。 * * ■ 設定方法 * プラグインパラメータ「Picture Lists」で、表示したいピクチャの * グループ(スライドショー)を登録します。 * * ■ 使用方法 * イベントコマンドの「プラグインコマンド」から使用します。 * * [RPGツクールMZの場合] * コマンド名: Show Pictures * * [RPGツクールMVの場合] * プラグインコマンドの入力欄に、以下のように入力します。 * SequentialPictureShow <リスト名> <フェード時間> * (例) SequentialPictureShow opening_slides 60 Decision1 * * ■ 変更履歴 * v2.0.1 - プラグインコマンドを連続実行すると2回目が機能しない不具合を修正。 * v2.0.0 - 戦闘中にフリーズする問題を解決するため、シーンを切り替えず * 現在のシーンに直接描画する方式に構造を全面変更。 * * @param pictureLists * @text Picture Lists * @desc 表示するピクチャのリストを管理します。 * @type struct[] * @default [] * * @command showPictures * @text Show Pictures * @desc 設定したピクチャリストを連続表示します。 * * @arg listName * @text List Name * @desc 表示するピクチャリストの名前。 * @type string * * @arg fadeDuration * @text Fade Duration * @desc クロスフェードにかかる時間(フレーム)。60 = 1秒。 * @type number * @default 30 * @min 1 * * @arg se * @text Sound Effect * @desc 各ピクチャ表示時に再生するSE。 * @type file * @dir audio/se/ * @default */ /*~struct~PictureList: * @param listName * @text List Name * @desc プラグインコマンドで識別するためのリスト名(半角英数)。 * @type string * * @param imageFiles * @text Image Files * @desc 表示するピクチャのリスト。上から順番に表示されます。 * @type file[] * @dir img/pictures/ * @default [] */ (() => { 'use strict'; const pluginName = 'SequentialPictureShow'; const params = PluginManager.parameters(pluginName); const pictureLists = JSON.parse(params.pictureLists || '[]').map(list => { const parsedList = JSON.parse(list); parsedList.imageFiles = JSON.parse(parsedList.imageFiles || '[]'); return parsedList; }); //----------------------------------------------------------------------------- // Game_System // // システム変数でスライドショーの状態を管理 //----------------------------------------------------------------------------- const _Game_System_initialize = Game_System.prototype.initialize; Game_System.prototype.initialize = function() { _Game_System_initialize.call(this); this.clearSequentialPictureShow(); }; Game_System.prototype.isSequentialPictureShowActive = function() { return this._spsActive || false; }; Game_System.prototype.setupSequentialPictureShow = function(listName, fadeDuration, seName) { const list = pictureLists.find(item => item.listName === listName); if (list && list.imageFiles.length > 0) { this._spsActive = true; this._spsPictures = list.imageFiles; this._spsFadeDuration = Number(fadeDuration) || 30; this._spsSe = seName ? { name: seName, volume: 90, pitch: 100, pan: 0 } : null; this._spsIndex = -1; this._spsIsFading = false; this._spsIsExiting = false; // ★追加:開始時に必ずリセット } else { console.error(`${pluginName}: Picture list "${listName}" not found or is empty.`); } }; Game_System.prototype.clearSequentialPictureShow = function() { this._spsActive = false; this._spsPictures = []; this._spsFadeDuration = 0; this._spsSe = null; this._spsIndex = -1; this._spsIsFading = false; this._spsIsExiting = false; // ★追加:終了時にリセット }; //----------------------------------------------------------------------------- // Plugin Commands //----------------------------------------------------------------------------- const launchShow = (listName, fadeDuration, seName, interpreter) => { $gameSystem.setupSequentialPictureShow(listName, fadeDuration, seName); if ($gameSystem.isSequentialPictureShowActive() && interpreter) { interpreter.setWaitMode("sps_wait"); } }; if (Utils.RPGMAKER_NAME === 'MZ') { PluginManager.registerCommand(pluginName, 'showPictures', function(args) { launchShow(args.listName, args.fadeDuration, args.se, this); }); } const _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function(command, args) { _Game_Interpreter_pluginCommand.call(this, command, args); if (command === pluginName) { launchShow(args[0], args[1], args[2], this); } }; const _Game_Interpreter_updateWaitMode = Game_Interpreter.prototype.updateWaitMode; Game_Interpreter.prototype.updateWaitMode = function() { if (this._waitMode === 'sps_wait') { return $gameSystem.isSequentialPictureShowActive(); } return _Game_Interpreter_updateWaitMode.call(this); }; //----------------------------------------------------------------------------- // Scene_Base // // 全てのシーンにスライドショー機能を追加 //----------------------------------------------------------------------------- const _Scene_Base_update = Scene_Base.prototype.update; Scene_Base.prototype.update = function() { _Scene_Base_update.call(this); if ($gameSystem.isSequentialPictureShowActive()) { this.updateSequentialPictureShow(); } }; Scene_Base.prototype.updateSequentialPictureShow = function() { if (!this._spsSpriteContainer) { this.createSpsSprites(); this.startSpsShow(); } this.handleSpsInput(); this.updateSpsFade(); }; Scene_Base.prototype.createSpsSprites = function() { this._spsSpriteContainer = new Sprite(); this._spsSpriteContainer.zIndex = 100; // ウィンドウなどより手前に this._spsSprite1 = new Sprite(); this._spsSprite2 = new Sprite(); this.centerSpsSprite(this._spsSprite1); this.centerSpsSprite(this._spsSprite2); this._spsSpriteContainer.addChild(this._spsSprite1); this._spsSpriteContainer.addChild(this._spsSprite2); this.addChild(this._spsSpriteContainer); }; Scene_Base.prototype.centerSpsSprite = function(sprite) { sprite.anchor.x = 0.5; sprite.anchor.y = 0.5; sprite.x = Graphics.width / 2; sprite.y = Graphics.height / 2; }; Scene_Base.prototype.terminateSequentialPictureShow = function() { if (this._spsSpriteContainer) { this.removeChild(this._spsSpriteContainer); this._spsSpriteContainer.destroy({ children: true }); this._spsSpriteContainer = null; this._spsSprite1 = null; this._spsSprite2 = null; this._spsActiveSprite = null; } $gameSystem.clearSequentialPictureShow(); }; Scene_Base.prototype.startSpsShow = function() { this.showNextSpsPicture(); }; Scene_Base.prototype.handleSpsInput = function() { const sys = $gameSystem; if (!sys._spsIsFading && (Input.isTriggered('ok') || TouchInput.isTriggered())) { if (sys._spsIndex >= sys._spsPictures.length - 1) { sys._spsIsExiting = true; sys._spsIsFading = true; } else { this.showNextSpsPicture(); } } }; Scene_Base.prototype.showNextSpsPicture = function() { const sys = $gameSystem; sys._spsIndex++; if (sys._spsSe) { AudioManager.playSe(sys._spsSe); } const oldSprite = this._spsActiveSprite; const newSprite = this._spsActiveSprite === this._spsSprite1 ? this._spsSprite2 : this._spsSprite1; newSprite.bitmap = ImageManager.loadPicture(sys._spsPictures[sys._spsIndex]); newSprite.opacity = 0; this._spsActiveSprite = newSprite; this._spsFadingOutSprite = oldSprite; sys._spsIsFading = true; }; Scene_Base.prototype.updateSpsFade = function() { const sys = $gameSystem; if (!sys._spsIsFading) return; const fadeStep = 255 / sys._spsFadeDuration; if (sys._spsIsExiting) { if (this._spsActiveSprite) { this._spsActiveSprite.opacity -= fadeStep; if (this._spsActiveSprite.opacity <= 0) { this.terminateSequentialPictureShow(); } } return; } if (this._spsFadingOutSprite) { this._spsFadingOutSprite.opacity -= fadeStep; } if (this._spsActiveSprite) { this._spsActiveSprite.opacity += fadeStep; } if (this._spsActiveSprite.opacity >= 255) { this._spsActiveSprite.opacity = 255; if (this._spsFadingOutSprite) { this._spsFadingOutSprite.opacity = 0; } sys._spsIsFading = false; } }; })();