Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
| //============================================================================= | |
| // PerformanceRefine.js | |
| // ---------------------------------------------------------------------------- | |
| // Copyright (c) 2015-2016 Triacontane | |
| // This software is released under the MIT License. | |
| // http://opensource.org/licenses/mit-license.php | |
| // ---------------------------------------------------------------------------- | |
| // Version | |
| // 1.3.0 2017/08/20 最新のMW.jsで動作するよう修正 | |
| // 1.2.1 2017/03/11 コミュニティ版で動作しない問題を修正 | |
| // 1.2.0 2017/01/21 1フレーム中に実行したイベントの総数をログ出力する機能を追加 | |
| // 1.1.0 2017/01/09 ローカル環境以外でも動作するよう修正 | |
| // 実行中のイベントの更新時間を出力できる機能を追加 | |
| // 1.0.1 2016/10/05 セーブが失敗する不具合を修正 | |
| // 1.0.0 2016/10/02 初版 | |
| // 0.0.1 2016/09/19 作成途中 | |
| // ---------------------------------------------------------------------------- | |
| // [Blog] : http://triacontane.blogspot.jp/ | |
| // [Twitter]: https://twitter.com/triacontane/ | |
| // [GitHub] : https://github.com/triacontane/ | |
| //============================================================================= | |
| /*: | |
| * @plugindesc パフォーマンス計測プラグイン | |
| * @author トリアコンタン | |
| * | |
| * @param 更新ログ出力間隔 | |
| * @desc 更新ログの出力間隔です。この回数内の平均値をログとして出力します。0を指定すると出力されません。 | |
| * @default 300 | |
| * | |
| * @param その他ログ出力間隔 | |
| * @desc 更新ログ以外のログの出力間隔です。この回数内の平均値をログとして出力します。0を指定すると出力されません。 | |
| * @default 1 | |
| * | |
| * @param 更新ログ警告閾値 | |
| * @desc 更新ログの出力値がこの値を超えると警告ログ(左側に注意表示)になります。 | |
| * @default 1.0 | |
| * | |
| * @param その他ログ警告閾値 | |
| * @desc 更新ログ以外の出力値がこの値を超えると警告ログ(左側に注意表示)になります。 | |
| * @default 100.0 | |
| * | |
| * @param 表示閾値 | |
| * @desc 指定した値より小さいログは出力されません。 | |
| * @default 0.1 | |
| * | |
| * @param フレームで表示 | |
| * @desc ONの場合フレームで表示され、OFFの場合ミリ秒で表示されます。(ON/OFF) | |
| * @default OFF | |
| * | |
| * @param 警告ログのみ表示 | |
| * @desc ログの出力内容が「警告」扱いの場合のみ出力します。(ON/OFF) | |
| * @default OFF | |
| * | |
| * @param 小数部分桁数 | |
| * @desc 出力される数値の小数点以下の桁数です。 | |
| * @default 2 | |
| * | |
| * @param フレーム更新分割 | |
| * @desc 更新ログを「入力情報更新」「データ更新」「描画更新」の3つの処理ごとに分割して表示します。 | |
| * @default ON | |
| * | |
| * @param イベント更新出力 | |
| * @desc 実行中のイベントの処理時間を表示します。 | |
| * @default ON | |
| * | |
| * @param イベント細分出力 | |
| * @desc 実行中のイベントの処理時間を表示します。イベントの深さが1以上の場合も表示します。 | |
| * @default OFF | |
| * | |
| * @param メソッド名出力 | |
| * @desc ONにすると正確なメソッド名が、OFFだと処理の概要が日本語で出力されます。(ON/OFF) | |
| * @default OFF | |
| * | |
| * @param キャプチャ作成無効 | |
| * @desc キャプチャ作成を無効にします。キャプチャ作成はメニュー画面などを開いた際に使われます。(ON/OFF) | |
| * @default OFF | |
| * | |
| * @param 色調変更無効 | |
| * @desc ビットマップに対する色調変更を無効にします。色調変更はウィンドウカラーの変更で使われます。(ON/OFF) | |
| * @default OFF | |
| * | |
| * @param フラッシュ無効 | |
| * @desc スプライトに対するフラッシュを無効にします。フラッシュはアニメーション表示などで使われます。(ON/OFF) | |
| * @default OFF | |
| * | |
| * @param 色相変更無効 | |
| * @desc ビットマップに対する色相変更を無効にします。色相変更は敵キャラやアニメーション表示で使われます。(ON/OFF) | |
| * @default OFF | |
| * | |
| * @help 処理に掛かる時間を計測してログに出力します。 | |
| * 正確な結果を計測するため、このプラグインは一番下に配置してください。 | |
| * (このプラグインより下に配置されたプラグインの処理内容が | |
| * 計測時間から漏れる可能性があります) | |
| * ログが出力されるタイミングは主に2通りです。 | |
| * | |
| * 1.フレーム更新ログ | |
| * 1フレームに掛かる時間を計測します。出力内容は指定された間隔ごとの平均値です。 | |
| * また、ゲーム更新と描画の時間を分けて出力することも可能です。 | |
| * 平均値が指定された閾値を超える場合、警告ログになります。 | |
| * 頻繁に警告ログが出力されなければ、概ね快適なプレーであるといえます。 | |
| * | |
| * 2.その他ログ | |
| * 同期実行され、かつ時間の掛かる以下の処理の時間を計測します。 | |
| * ・シーン遷移時 | |
| * ・マップオブジェクト作成時 | |
| * ・キャプチャ取得時 | |
| * ・セーブ実行時 | |
| * | |
| * 結果が指定された閾値を超える場合、警告ログになります。 | |
| * 頻繁に警告ログが出力されなければ、概ね快適なプレーであるといえます。 | |
| * | |
| * 3.イベント処理時間ログ | |
| * 実行されているイベントごとの処理時間を計測できます。 | |
| * 特に並列実行について、処理が重くなっているイベントを特定することができます。 | |
| * | |
| * また、CTRLキーを押すと、そのフレーム中に実行したイベントコマンドの数を | |
| * ログ出力することができます。 | |
| * この値が2000を超えている場合は、組み方を見直すことを推奨します。 | |
| * | |
| * パフォーマンス改善の手掛かりにしてください。 | |
| * テストプレー時以外も動作しますが、製品版には付属しないことを推奨します。 | |
| * | |
| * このプラグインにはプラグインコマンドはありません。 | |
| * | |
| * 利用規約: | |
| * 作者に無断で改変、再配布が可能で、利用形態(商用、18禁利用等) | |
| * についても制限はありません。 | |
| * このプラグインはもうあなたのものです。 | |
| */ | |
| (function() { | |
| 'use strict'; | |
| const pluginName = 'PerformanceRefine'; | |
| const getParamOther = function(paramNames) { | |
| if (!Array.isArray(paramNames)) paramNames = [paramNames]; | |
| for (let i = 0; i < paramNames.length; i++) { | |
| const name = PluginManager.parameters(pluginName)[paramNames[i]]; | |
| if (name) return name; | |
| } | |
| return null; | |
| }; | |
| const getParamNumber = function(paramNames, min, max) { | |
| const value = getParamOther(paramNames); | |
| if (arguments.length < 2) min = -Infinity; | |
| if (arguments.length < 3) max = Infinity; | |
| return (parseInt(value, 10) || 0).clamp(min, max); | |
| }; | |
| const getParamBoolean = function(paramNames) { | |
| const value = getParamOther(paramNames); | |
| return (value || '').toUpperCase() === 'ON'; | |
| }; | |
| const getParamFloat = function(paramNames, min, max) { | |
| const value = getParamOther(paramNames); | |
| if (arguments.length < 2) min = -Infinity; | |
| if (arguments.length < 3) max = Infinity; | |
| return (parseFloat(value) || 0).clamp(min, max); | |
| }; | |
| const getClassName = function(object) { | |
| return object.constructor.toString().replace(/function\s+(.*)\s*\([\s\S]*/m, '$1'); | |
| }; | |
| //============================================================================= | |
| // パラメータの取得と整形 | |
| //============================================================================= | |
| const paramUpdateLogInterval = getParamNumber(['UpdateLogInterval', '更新ログ出力間隔']); | |
| const paramOtherLogInterval = getParamNumber(['OtherLogInterval', 'その他ログ出力間隔']); | |
| const paramUpdateLogWarnLimit = getParamFloat(['UpdateLogWarnLimit', '更新ログ警告閾値']); | |
| const paramOtherLogWarnLimit = getParamFloat(['OtherLogWarnLimit', 'その他ログ警告閾値']); | |
| const paramOutputFrame = getParamBoolean(['OutputFrame', 'フレームで表示']); | |
| const paramOutputOnlyWarn = getParamBoolean(['OutputOnlyWarn', '警告ログのみ表示']); | |
| const paramDecimalDigit = getParamNumber(['DecimalDigit', '小数部分桁数']); | |
| const paramUpdateLogSeparate = getParamBoolean(['UpdateLogSeparate', 'フレーム更新分割']); | |
| const paramOutputMethodName = getParamBoolean(['OutputMethodName', 'メソッド名出力']); | |
| const paramInvalidCapture = getParamBoolean(['InvalidCapture', 'キャプチャ作成無効']); | |
| const paramInvalidTint = getParamBoolean(['InvalidTint', '色調変更無効']); | |
| const paramInvalidFlash = getParamBoolean(['InvalidFlash', 'フラッシュ無効']); | |
| const paramInvalidHue = getParamBoolean(['InvalidHue', '色相変更無効']); | |
| const paramOutputEvent = getParamBoolean(['OutputEvent', 'イベント更新出力']); | |
| const paramOutputEventSub = getParamBoolean(['OutputEventSub', 'イベント細分出力']); | |
| const paramInfoLimit = getParamFloat(['InfoLimit', '表示閾値']); | |
| //============================================================================= | |
| // JsExtensions | |
| //============================================================================= | |
| Object.defineProperty(String.prototype, 'padSuffix', { | |
| value: function(length, c) { | |
| let s = this; | |
| while (s.length < length) { | |
| s = s + c; | |
| } | |
| return s; | |
| } | |
| }); | |
| //============================================================================= | |
| // 一部の画面効果を無効化してパフォーマンスの違いを確認します。 | |
| //============================================================================= | |
| if (paramInvalidFlash) { | |
| Sprite.prototype.setBlendColor = function(color) {}; | |
| } | |
| if (paramInvalidTint) { | |
| Bitmap.prototype.adjustTone = function(r, g, b) {}; | |
| } | |
| if (paramInvalidHue) { | |
| const _ImageManager_loadBitmap = ImageManager.loadBitmap; | |
| ImageManager.loadBitmap = function(folder, filename, hue, smooth) { | |
| arguments[2] = 0; | |
| _ImageManager_loadBitmap.apply(this, arguments); | |
| }; | |
| Bitmap.prototype.rotateHue = function(offset) {}; | |
| } | |
| if (paramInvalidCapture) { | |
| SceneManager.snapForBackground = function() {}; | |
| } | |
| //============================================================================= | |
| // SceneManager | |
| // 処理時間を計測して結果を出力します。 | |
| //============================================================================= | |
| const _SceneManager_initialize = SceneManager.initialize; | |
| SceneManager.initialize = function() { | |
| _SceneManager_initialize.apply(this, arguments); | |
| this.showDevTools(); | |
| }; | |
| SceneManager._averageTimes = {}; | |
| SceneManager.calculatePerformance = function(callBack, processName, interval, updateFlg) { | |
| if (!window.performance) { | |
| return callBack(); | |
| } | |
| const averageInfo = this._getAverageInfo(processName); | |
| const beforeTime = performance.now(); | |
| const result = callBack(); | |
| const processTime = performance.now() - beforeTime; | |
| const count = averageInfo.count; | |
| averageInfo.value = (averageInfo.value * count + processTime) / (count + 1); | |
| averageInfo.count++; | |
| if (interval && Graphics.frameCount % interval === interval - 1) { | |
| this._outputPerformance(processName, averageInfo, updateFlg); | |
| } | |
| return result; | |
| }; | |
| SceneManager._outputPerformance = function(processName, averageInfo, updateFlg) { | |
| const unit = Math.pow(10, paramDecimalDigit); | |
| const logValue = Math.round((averageInfo.value * (paramOutputFrame ? 60 / 1000 : 1)) * unit) / unit; | |
| if (logValue < paramInfoLimit) { | |
| return; | |
| } | |
| const logMethod = this._getPerformanceLogType(logValue, updateFlg); | |
| if (logMethod !== 'log' || !paramOutputOnlyWarn) { | |
| console[logMethod](this._getPerformanceLogValue(logValue) + processName); | |
| } | |
| averageInfo.value = 0; | |
| averageInfo.count = 0; | |
| }; | |
| SceneManager._getPerformanceLogValue = function(value) { | |
| return (value + (paramOutputFrame ? 'Frames' : 'MS')).padSuffix(10 + paramDecimalDigit, ' '); | |
| }; | |
| SceneManager._getPerformanceLogType = function(frame, updateFlg) { | |
| return frame > (updateFlg ? paramUpdateLogWarnLimit : paramOtherLogWarnLimit) ? 'warn' : 'log'; | |
| }; | |
| SceneManager._getAverageInfo = function(processName) { | |
| if (!this._averageTimes.hasOwnProperty(processName)) { | |
| this._averageTimes[processName] = {count: 0, value: 0}; | |
| } | |
| return this._averageTimes[processName]; | |
| }; | |
| if (paramUpdateLogSeparate) { | |
| const _SceneManager_updateInputData = SceneManager.updateInputData; | |
| SceneManager.updateInputData = function() { | |
| const methodName = paramOutputMethodName ? 'SceneManager.updateInputData()' : 'インプット更新'; | |
| this.calculatePerformance(_SceneManager_updateInputData.bind(this), methodName, | |
| paramUpdateLogInterval, true); | |
| }; | |
| const _SceneManager_updateScene = SceneManager.updateScene; | |
| SceneManager.updateScene = function() { | |
| const methodName = paramOutputMethodName ? 'SceneManager.updateScene()' : 'ゲーム状態更新'; | |
| this.calculatePerformance(_SceneManager_updateScene.bind(this), methodName, | |
| paramUpdateLogInterval, true); | |
| }; | |
| const _SceneManager_renderScene = SceneManager.renderScene; | |
| SceneManager.renderScene = function() { | |
| const methodName = paramOutputMethodName ? 'SceneManager.renderScene()' : '画面描画処理'; | |
| this.calculatePerformance(_SceneManager_renderScene.bind(this), methodName, | |
| paramUpdateLogInterval, true); | |
| }; | |
| } else { | |
| const _SceneManager_updateMain = SceneManager.updateMain; | |
| SceneManager.updateMain = function() { | |
| const methodName = paramOutputMethodName ? 'SceneManager.updateMain()' : 'フレーム更新全体'; | |
| this.calculatePerformance(_SceneManager_updateMain.bind(this), | |
| methodName, paramUpdateLogInterval, true); | |
| }; | |
| } | |
| if (paramOutputEvent) { | |
| //============================================================================= | |
| // Game_Interpreter | |
| // 計測対象のメソッドです。 | |
| //============================================================================= | |
| const _Game_Interpreter_update = Game_Interpreter.prototype.update; | |
| Game_Interpreter.prototype.update = function() { | |
| if (this.isOutputEvent()) { | |
| const methodName = (paramOutputMethodName ? getClassName(this) + '.update()' : 'イベント更新') + this.getProcessNumber(); | |
| SceneManager.calculatePerformance(_Game_Interpreter_update.bind(this), | |
| methodName, paramUpdateLogInterval, true); | |
| } else { | |
| _Game_Interpreter_update.apply(this, arguments); | |
| } | |
| }; | |
| const _Game_Interpreter_setupReservedCommonEvent = Game_Interpreter.prototype.setupReservedCommonEvent; | |
| Game_Interpreter.prototype.setupReservedCommonEvent = function() { | |
| if ($gameTemp.isCommonEventReserved()) { | |
| this.setCommonEventId($gameTemp._commonEventId); | |
| } | |
| return _Game_Interpreter_setupReservedCommonEvent.apply(this, arguments); | |
| }; | |
| const _Game_Interpreter_command117 = Game_Interpreter.prototype.command117; | |
| Game_Interpreter.prototype.command117 = function() { | |
| const result = _Game_Interpreter_command117.apply(this, arguments); | |
| if (this._childInterpreter) { | |
| this._childInterpreter.setCommonEventId(this._params[0]); | |
| } | |
| return result; | |
| }; | |
| Game_Interpreter.prototype.getProcessNumber = function() { | |
| if (this._commonEventId) { | |
| return ` Common Event ID:${this._commonEventId}`; | |
| } else { | |
| return ` Map ID:${this._mapId} Event ID:${this._eventId}`; | |
| } | |
| }; | |
| Game_Interpreter.prototype.isOutputEvent = function() { | |
| return this.isRunning() && (paramOutputEventSub || this._depth === 0); | |
| }; | |
| Game_Interpreter.prototype.setCommonEventId = function(id) { | |
| this._commonEventId = id; | |
| }; | |
| const _Game_CommonEvent_refresh = Game_CommonEvent.prototype.refresh; | |
| Game_CommonEvent.prototype.refresh = function() { | |
| _Game_CommonEvent_refresh.apply(this, arguments); | |
| if (this._interpreter) { | |
| this._interpreter.setCommonEventId(this._commonEventId); | |
| } | |
| }; | |
| } | |
| const _Game_Interpreter_executeCommand = Game_Interpreter.prototype.executeCommand; | |
| Game_Interpreter.prototype.executeCommand = function() { | |
| const command = this.currentCommand(); | |
| if (!$gameParty.inBattle() && command && command.code !== 0) { | |
| $gameMap.countUpParallelCommand(); | |
| } | |
| return _Game_Interpreter_executeCommand.apply(this, arguments); | |
| }; | |
| const _Game_Map_updateEvents = Game_Map.prototype.updateEvents; | |
| Game_Map.prototype.updateEvents = function() { | |
| if (Input.isTriggered('control')) { | |
| this._isCommandCount = true; | |
| this._parallelCommandCount = 0; | |
| } | |
| _Game_Map_updateEvents.apply(this, arguments); | |
| if (this._isCommandCount) { | |
| console.log(`1frameあたりのイベント命令実行回数[${this._parallelCommandCount}]回`); | |
| this._isCommandCount = false; | |
| this._parallelCommandCount = 0; | |
| } | |
| }; | |
| Game_Map.prototype.countUpParallelCommand = function() { | |
| if (this._isCommandCount) { | |
| this._parallelCommandCount++; | |
| } | |
| }; | |
| const _SceneManager_changeScene = SceneManager.changeScene; | |
| SceneManager.changeScene = function() { | |
| if (this.isSceneChanging() && !this.isCurrentSceneBusy() && this._nextScene) { | |
| const methodName = (paramOutputMethodName ? 'SceneManager.changeScene() to ' : 'シーン遷移 to ') + | |
| getClassName(this._nextScene); | |
| this.calculatePerformance(_SceneManager_changeScene.bind(this), methodName, paramOtherLogInterval, false); | |
| } else { | |
| _SceneManager_changeScene.apply(this, arguments); | |
| } | |
| }; | |
| const _SceneManager_snapForBackground = SceneManager.snapForBackground; | |
| SceneManager.snapForBackground = function() { | |
| const methodName = paramOutputMethodName ? 'SceneManager.snapForBackground()' : '背景キャプチャ作成'; | |
| this.calculatePerformance(_SceneManager_snapForBackground.bind(this), | |
| methodName, paramOtherLogInterval, false); | |
| }; | |
| SceneManager.showDevTools = function() { | |
| if (!Utils.isNwjs()) return; | |
| const nwWin = require('nw.gui').Window.get(); | |
| if (!nwWin.isDevToolsOpen || !nwWin.isDevToolsOpen()) { | |
| const devTool = nwWin.showDevTools(); | |
| if (devTool) { | |
| devTool.moveTo(0, 0); | |
| devTool.resizeTo(window.screenX + window.outerWidth, window.screenY + window.outerHeight); | |
| nwWin.focus(); | |
| } | |
| } | |
| }; | |
| //============================================================================= | |
| // Scene_Map | |
| // 計測対象のメソッドです。 | |
| //============================================================================= | |
| const _Scene_Map_createDisplayObjects = Scene_Map.prototype.createDisplayObjects; | |
| Scene_Map.prototype.createDisplayObjects = function() { | |
| const methodName = paramOutputMethodName ? getClassName(this) + '.createDisplayObjects()' : | |
| 'マップ表示オブジェクト作成'; | |
| SceneManager.calculatePerformance(_Scene_Map_createDisplayObjects.bind(this), | |
| methodName, paramOtherLogInterval, false); | |
| }; | |
| //============================================================================= | |
| // Game_Player | |
| // 計測対象のメソッドです。 | |
| //============================================================================= | |
| const _Game_Player_performTransfer = Game_Player.prototype.performTransfer; | |
| Game_Player.prototype.performTransfer = function() { | |
| const methodName = paramOutputMethodName ? getClassName(this) + '.createDisplayObjects()' : '場所移動'; | |
| SceneManager.calculatePerformance(_Game_Player_performTransfer.bind(this), | |
| methodName, paramOtherLogInterval, false); | |
| }; | |
| //============================================================================= | |
| // DataManager | |
| // 計測対象のメソッドです。 | |
| //============================================================================= | |
| const _DataManager_saveGame = DataManager.saveGame; | |
| DataManager.saveGame = function(savefileId) { | |
| const methodName = paramOutputMethodName ? 'DataManager.saveGame()' : 'データセーブ'; | |
| return SceneManager.calculatePerformance(_DataManager_saveGame.bind(this, savefileId), | |
| methodName, paramOtherLogInterval, false); | |
| }; | |
| })(); |