4
Help us understand the problem. What are the problem?

posted at

updated at

RPGツクールMZでプラグインを作る上での最低限の心得

RPGツクールMZ を購入してから殆どプラグインしか書いていない自分ですが、
最近はそんなプラグイン開発作業もいろいろとパターン化しつつあります。

今回の記事では、俺がRPGツクールMZのプラグインを作る上での最低限の心得を書いていきます。

また、RPGツクール界隈は結構WEB開発の知識なく使っているクリエイターが多いので、
そういう人にもなるべくわかるように書きます。

プラグイン過去作

主にここに置いてます

執筆地点でリリース済のもの
- タイトルスキッププラグイン
- ネット回線が遅い場合エラーを吐くプラグイン
- フォーカスしてないときにゲームを停止させないプラグイン
- オートセーブによる事故を防ぐプラグイン
- キャラをリージョンタイルに沿って歩かせるプラグイン

心得1: Prettier を使え

Prettier とは、「コードフォーマッター」というツールの一つで、
簡単に言えば、汚いコードを一瞬で綺麗に整理整頓してくれるツールのことです。
例えば以下のような悪夢のようなコードがあったとき

const hogehoge
=function(
x)                                     {const obj={test:
5           }       ;       return 1+2 
+3                        + obj
.test;
                  }

コードフォーマッター」を通すだけでこうなります。

const hogehoge = function (x) {
  const obj = { test: 5 };
  return 1 + 2 + 3 + obj.test;
};

そんな神のようなツールのひとつが、「Prettier」です。
もう、チマチマと 1 文字づつスペースで間を開けて
見やすくする作業をする必要はない
んです。時代はオール電化です。

俺は基本的に Visual Studio Code と連携させて、
Ctrl + S (Mac なら Command + S) で保存するたびに、
Prettier が自動でコードを整形してくれるようにしています。

VSCode で Prettier を使えるようにする方法はこの記事を参考にすればいいと思います。
この記事では .prettierrc を使って詳細設定していますが、俺は面倒くさいのでVSCodeの設定を直でいじるか、初期設定のまま使っています。

さて、これを読んでいる読者の中には、このようなオートフォーマットに対応していない、メモ帳やサクラエディタなどでプラグインを書いているような輩もいると思います。

そういう人達は、オンライン版のPrettierを使うことをお勧めします。

Prettier オンライン版

上記サイトを開くと左半分と右半分にテキストエディタがあると思うので、
左半分のテキストエディタに、自分が書いたJavaScriptのコードをぶち込んでみてください。
エラーのないコードなら、問題なく右半分のテキストエディタに綺麗になったコードが出力されるはずです。

オートフォーマッタを使用したことがない人は、まずこのオンラインツールにリリース前のプラグインをぶち込んで整形してやるとよいでしょう。

心得2: プラグイン名は currentScript で取れ

プラグインのパラメータを取る処理を書くときに、
PluginManager.parameters の引数に
文字列でファイル名を書き込んでしまう人は多いと思います。

const params = PluginManager.parameters("H2A_NanikaNoPlugin");

でも、そういうことをしてしまうと、
後でファイル名を変更しようと思ったときに直すのが面倒ですし、
ユーザーが勝手にプラグイン名を変更したときにバグってしまいます。

なので、PluginManager.parameters の引数に与えるプラグイン名は
自動で取得できるようにするべきです。

こう書けばOKです。

const pluginName = document.currentScript.src.match(/^.*\/(.*).js$/)[1];
const params = PluginManager.parameters(pluginName);

この document.currentScript は、実行中の <script> タグそのものを取得することができます。
この処理では、そのタグのsrcからファイル名を正規表現で取得しています。

補足(2021/02/07)

ファイル名を変更されることによって
アツマールのプラグインリストが機能しなくなることを懸念してるなら、MITライセンスやめましょう。
MITライセンスは、ファイル名の変更まで利用者を制限できません。

指摘元

心得3: varlet は なるべく使うな

変数の値が途中で変わるとデバッグが面倒くさいです。

var を使ってしまうと、宣言した変数を何度でも宣言しなおすことができちゃいますし、代入もし放題です。
let は再宣言はできなくなるものの、やっぱり再代入しほうだいです。

しかし const で宣言すると、再宣言も再代入もできません。
つまりこれを多用することで、デバッグが楽になります。

var a = 0;
var a = 1; // 再宣言できてしまう。

let b = 0;
let b = 1; // エラー: Identifier 'b' has already been declared
b = 2; // 再代入はできる。

const c = 0;
const c = 1; // エラー: Identifier 'c' has already been declared
c = 2; // エラー: Assignment to constant variable.

ただし、それは数値や文字列、真偽値などのプリミティブ値を代入したときの話です。
オブジェクトや配列は、constだろうが中身を改変することができてしまいます。

const arr = [];
arr[0] = "hello";
arr[1] = "world";
console.log(arr); // ["hello", "world"]

const obj = { hoge: 1 };
obj.hoge = 100;
obj.foo = 50;
console.log(obj); // { hoge: 100, foo: 50 }

なのでもし、配列やオブジェクトも改変不可能にしたい場合は、
Object.freeze を使いましょう。

const arr = Object.freeze([1, 2, 3]);
arr[1] = 500; // エラーは起きないが...
console.log(arr); // [1, 2, 3] <- 値は変わらない

const obj = Object.freeze({ x: 1, y: 2 });
obj.z = 3; // エラーは起きないが...
console.log(obj); // { x: 1, y: 2 } <- 値は変わらない

心得4: アノテーションにもインデントを

ソースコードの頭にコメントとして書くアノテーション。
@param とか使って、パラメータやプラグインコマンドなどを定義する部分です。

パラメータが少ないならまあいいですけど、
物量が多くなればなるほど、見にくくて仕方なくなります。
セレクトボックスも多くなるほど、なんか見にくい感じになりますよね。

 * @command setting
 * @text 移動速度を変更
 * @arg walkSpeed
 * @text 移動速度
 * @type select
 * @option 指定しない
 * @value 0
 * @option 1: 1/8倍速
 * @value 1
 * @option 2: 1/4倍速
 * @value 2
 * @option 3: 1/2倍速
 * @value 3
 * @option 4: 標準速
 * @value 4
 * @option 5: 2倍速
 * @value 5
 * @option 6: 4倍速
 * @value 6
 * @default 0

あんまり知られていないことなんですが、
実は @ の前にどんなにスペースが空いていても、エディタは認識してくれます。
だからこんな風にインデントスペースを付けてやっても全然問題ないわけです

 * @command setting
 *   @text 移動速度を変更
 *   @arg walkSpeed
 *     @text 移動速度
 *     @type select
 *       @option 指定しない
 *         @value 0
 *       @option 1: 1/8倍速
 *         @value 1
 *       @option 2: 1/4倍速
 *         @value 2
 *       @option 3: 1/2倍速
 *         @value 3
 *       @option 4: 標準速
 *         @value 4
 *       @option 5: 2倍速
 *         @value 5
 *       @option 6: 4倍速
 *         @value 6
 *     @default 0

こっちのが見やすくないですか?

心得5: 例外処理もちゃんと書く

パラメータに変な値を入れたことでバグったりとか、
仕様外の使い方をされたときのために、
エラーメッセージを用意しておいたほうがいいです。

だって、プラグインを実際に使ったユーザーから、
「なんか英語がいっぱい出てきてゲームが固まりました!」って言われるの、
なんかうんざりするでしょ?

だから、プラグインでそういう「英語のエラー」が出る可能性のある部分は
自前の日本語のエラーメッセージが出るようにしてあげたほうが、
意味不明な苦情を言われる可能性も減るし、デバッグもしやすくなるわけです。

Uncaught TypeError: Cannot read property 'y' of undefined
プラグインパラメータのパースに失敗しました
どっちのエラーメッセージのほうがユーザーフレンドリーかつデバッグしやすいか
一目瞭然ですよね。
後者のエラーが出てくるなら、ユーザーのパラメータの書き方を疑うことができるし、
こっちが悪くてもパラメータのパース処理のデバッグから始めることもできる。
(日本語メッセージの後に開発者しか分からないようなログを入れておくと尚良し)

くだらない初歩的なエラーの原因を調べるのに長い時間を費やすよりも、
こちらの想定を超えたエラーの原因を調べたほうが、実に有意義ですから、
思いつく限りエラーメッセージを用意しておくと、後々マジで楽です。

とりあえず、コアスクリプトの関数を上書きしたり、クラスに新たに関数を組み込んだりしている場合は、throw new Error("エラーメッセージ"); というふうに書けばエラーメッセージが出てきてくれると思います。

強制的にエラーメッセージを表示させたい場合は Graphics.printError("見出し", "メッセージ"); を使うのもアリですが、
その場合も throw new Error("エラーメッセージ"); を書いたほうがいいですね。

心得6: JavaScript の新構文は思う存分使え

ツクーラーの中には、JavaScript = Jquery みたいな歪んだ思い込みをしている輩も多いと思いますが、
JavaScript は常に進化しており、2014 年から現代にかけても、だいぶ書き方や書きやすさが変わりました。

例えば昔はなかった書き方としてアロー関数というものがあります。
昔は、JavaScriptの関数といえばコレでした。

function hoge(x) {
  return x + 1;
}
var hoge = function(x) {
  return x + 1;
}

しかし今はこう書けちゃいます。

const hoge = x => x + 1;

ヤバイぐらい短いですよね。

ただし、この「アロー関数」は、クラス内等で this を使うことができません。
使おうとすると window を参照してしまいます。
なので、this を使わない場合にアロー関数を多用するのが良いです。

さて、JavaScriptの進化はこれだけじゃありません。
他にも、オブジェクトとオブジェクトを合体させたいなんて時、昔はこう書いてました。

var obj1 = { x: 1, y: 2 };
var obj2 = { z: 3 };
var mix = Object.assign({}, obj1, obj2);
// -> { x: 1, y: 2, z: 3 };

それが、今じゃこうです。

const obj1 = { x: 1, y: 2 };
const obj2 = { z: 3 };
const mix = { ...obj1, ...obj2 };
// -> { x: 1, y: 2, z: 3 };

ヤバイでしょ?めちゃくちゃ直感的です。
配列と配列の合体もコレが・・・

var arr1 = [1, 2, 3];
var arr2 = [5, 6, 7];
var mix = [].concat(arr1, [4], arr2, [8]);
// -> [1, 2, 3, 4, 5, 6, 7, 8]

こうです!

const arr1 = [1, 2, 3];
const arr2 = [5, 6, 7];
const mix = [...arr1, 4, ...arr2, 8];
// -> [1, 2, 3, 4, 5, 6, 7, 8]

文字列の結合なんかもこれが・・・

var value = 300;
var text = "this is " + value + "apples.";
// -> this is 300 apples.

こう!プラス地獄からはオサラバです。

const value = 300;
const text = `this is ${value} apples.`;
// -> this is 300 apples.

こういった新しい構文の他にも、
数字のゼロ埋めを簡単に実現できる padEnd, padStart といった関数が追加されたりなど、
使わなきゃ損するような機能が今や大量にあるので、
JavaScriptの知識がJQueryで止まってるような人は、
今すぐすべての知識をアップデートするべきです。

これらの新しいJavaScriptの書き方は、最新のブラウザであればだいたい動きます。
とりあえずこれを読むことから始めましょう。
https://mitsuruog.github.io/javascript-style-guide/

・・・

・・・えっ?
最新のJavaScriptに対応してない古いブラウザでも動くようにしたいって?
そういう人は、babel を使ったらいいんじゃないですかね。
ツクールMZの動作環境には iOS 12.0 Safari が含まれているので、
それに合わせた safari 12 設定で出力すればいいんじゃないかな(適当)

babel オンライン版

babel は、最新のJavaScriptを古いJavaScriptに変換するツールです。

心得7. セマンティックバージョニング表記をする

プラグインのバージョンが古いか新しいかをユーザー側で判断しやすいように、
バージョンをきちんと表記するべきですが、
可能ならセマンティックバージョニングを採用しましょう。

セマンティックバージョニングとは、1.0.0 のような表記方法です。
数字を 2 つのドットで区切っているのが特徴ですね。

詳しくはググってほしいんですが、とりあえず簡単に解説します。

バージョン番号を X.Y.Z としたとき、
X はメジャーバージョン、
Y はマイナーバージョン
Z はパッチバージョン といいます。

パッチバージョンはバグ修正などの小さな変更が行われたときに上がっていきます。
マイナーバージョンは前のバージョンの動作を維持したまま機能の改善や新機能の追加を行ったときに上がっていきます。
メジャーバージョンは前のバージョンに引き続き使おうとしても動かないような破壊的な変更を行ったときに上がっていきます。

また、マイナーバージョンが上がるとパッチバージョンは 0 に戻り、
メジャーバージョンが上がるとマイナーバージョンとパッチバージョンが 0 に戻ります。
ですから、以下のような上がり方をするわけですね。

1.0.0 -- 初リリース
1.0.1 -- 小さなバグ修正
1.0.2 -- 小さなバグ修正
1.1.0 -- 機能改善
1.1.1 -- 機能改善に伴うバグ修正
1.1.2 -- 小さなバグ修正
1.2.0 -- 新機能追加
1.2.1 -- 新機能追加に伴うバグ修正
2.0.0 -- リニューアル
2.0.1 -- 小さなバグ修正
2.1.0 -- 機能改善

このようなバージョンの付け方をすることにより、
数字を見るだけで前のバージョンと比べて
プラグインがどのように変わったのかがだいたいわかるようになります。

だから例えば手持ちのプラグインのバージョンが 1.0.2 で、
最新のプラグインが 2.0.0 だったら、
「このプラグインを最新にしたらいろいろ動かなくなるかも・・・」
心配してもらえる可能性をアップさせることができるわけです。

なので、バージョン表記はセマンティックバージョニングを採用したほうがいいです。

心得8. 配列ループを使いこなせ

配列全体に手を加える処理をループで書く場合、
for文はなるべく使わないほうがいいです。
なぜなら、事故が起きやすく、コード数も無駄に大きくなるからです。

まず、「事故が起こりやすい」所以ですが、以下のコードをご覧ください。

このコードは、
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] が入っている配列変数を
[20, 18, 16, 14, 12, 10, 8, 6, 4, 2] に変換しようとしているものです。

一見何も問題がなさそうなコードですが、ある重大なミスを犯しています。何が間違っているでしょうか?

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = [];
for (let i = 10; i => 1; i --) {
  result.push(array[i - 1] * 2);
}
console.log(result);

正解は i => 1 の部分です。
まあまあまあ・・・、別に重大ってほどでもないように見えますよね。
不等号が間違っているだけだし、対して重大でもないんじゃない?
・・・そう思うかもしれません。

でもこの書き方だと、心得6 でも解説した アロー関数 になってしまいます。
そう、これはつまり、こう書いているのと同じです。

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = [];
for (let i = 10; function(i) {return 1}; i --) {
  result.push(array[i - 1] * 2);
}
console.log(result);

さあ、実行したらどうなるでしょう?

答えは・・・、この処理は永遠に終わらず、ずっと無限ループします。
やがて処理が追いつかなくなり、フリーズするでしょう・・・。

不等号をちょっと間違えるだけで大惨事☆

次に「コード数も無駄に大きくなる」という話ですが、
JavaScriptには map filter forEach reduce など、
膨大なデータを簡単に扱うための関数が用意されており、
それらを使用すればfor文で書くのがアホらしく思えるぐらいに短く書けます。

まあ実際に見ていただきましょう。
このコードが・・・

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = [];
for (let i = 0; i < 10; i ++) {
  result.push(array[i] * 2);
}
console.log(result);
// -> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

こう書けちゃいます。

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = array.map(v => v * 2);
console.log(result);
// -> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

5行が3行に!しかも、let がひとつもない!
そういうことです。

もう、配列を扱うのにfor文でゴチャゴチャやるのは時代遅れです。
スマートかつスッキリとこなしていきましょう。

こういう配列の扱い方に関しては以下の記事を参考にしたらいいと思います。

【JavaScript】配列の反復処理 forEach/filter/map/reduceを扱う | WEBコンサルティング・WEB制作のフリーランス uiuifree

さて余談ですが、
JavaScriptは、いくら大量の空の配列を作っても、メモリを圧迫しません。
なので、以下のようなコードを書いても、全然問題ないわけです。

const hoge = new Array(1000000000);

この仕組みを利用して、for を使わずに決まった数の有限ループを書くこともできます。
以下のようなコードを書いても、全く問題ないわけです。

[...(new Array(100)).keys()].forEach(value => console.log(value));
// -> 0 1 2 3 4 5 6 7 8 ... 99

実際こういうループのやり方は、有名なJSライブラリであるlodashでも採用されています。
だから決して邪道ではなく、JavaScriptだからこそできるテクニックです。

おわりに

とまあ、こんなところです。
心得は他にもあったんですが、自分の好みによる所が大きいものもあったので、
そういうものは今回割愛しました。
あくまで、プラグイン作る上での最低限の、初歩的なものだけをピックアップした感じです。

まあでも、なんだかんだ言ってコーディングって正解とかはないので、
この記事は参考程度にとどめて、自分なりに書いていくんでもいいんじゃないですかね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
4
Help us understand the problem. What are the problem?