プログラミング経験ゼロから始めるSRPGStudioプラグイン制作

こんにちは!彩羽です。
SRPGStudioを利用してゲームを制作しています。プラグインもいくつか公開しています(https://github.com/iroha-code/SRPGStudio_Plugin

ゲームを開発するにあたり、

  • デフォルト機能には備わっていない独自の処理を追加したい!

  • 既存の機能やUIを改変したい!

と考えて、有志の方が開発しているプラグインを導入している…という方は多いのではないでしょうか。

また、本当は自分でプラグインを作って、自分だけのオリジナル機能を実現したい!と考えたものの、コードを目の前にして絶望された方も多いのではないでしょうか……。

私もプログラミング経験僅少の状態から、2年前くらいからプラグイン制作を開始し、今ではなんとか(バグもたくさん出しますが)自前のプラグインを公開できるまでになっています(できることが増えれば、とっても楽しいですよ!)

本稿は、私と同じようにプログラミング経験が少ない(全くない)方を対象に、なんとかやりたいことができるまでの第一歩を踏み出すための手助けをすることを目的とした記事になります。

なお、趣味で多少コードを書いているレベルの人間が書いた内容になりますので、不正確な内容・誤った内容を含む可能性が高いです。記事内容を鵜呑みにするのではなく、自分で調べるための一助として活用いただければと思います。そして、この記事を読んでくださった有識者の方は、ぜひ誤りに気づいたらご指摘いただけると嬉しいです……!

前置きはこれくらいにして、さっそく本論に入りましょう!

「Script」フォルダについて

まずは前提として、SRPGStudioが動く仕組みについて触れておきます。SRPGStudioのプロジェクトフォルダの中身は以下のようになっていますね。

画像
プロジェクトフォルダ

このうち、「Script」フォルダを開くと、さらに以下のようなフォルダが沢山入っています。

画像
「Script」フォルダの中身

この中にある、「eventcommand」フォルダを開くと、さらに以下のような、拡張子「.js」のファイルがたくさん入っています(拡張子が表示されない方は、ぜひ拡張子表示の設定をONにしてください!)

画像
「eventcommand」フォルダの中身

このひとつひとつがスクリプトファイル、つまりゲームの動作に関わるファイルです。1番上の「eventcommand-backgroundchange.js」を開くと、以下のようになっています。

画像
「eventcommand-backgroundchange.js」ファイルの中身

なお、このファイルは、名前のとおり「イベントコマンド:背景画像の変更」を使用したときに呼び出される処理を記述したファイルになります。

画像
エディタ側のイベントコマンド選択画面

ゲーム内で行われる処理内容の多くは、このScriptフォルダに入っているファイル内に記述されています(詳しく知りたい方は公式ヘルプへ!)。
こういった各ファイル内の膨大な行数の処理を実行することで、ゲーム内の処理が実行されているわけですね。

では、「背景画像の変更」に関する処理を変更するためには、この「eventcommand-backgroundchange.js」を修正すればよいのでしょうか?
答えはNoです。

「Plugin」フォルダについて

「Script」フォルダ内のファイルを直接上書きしても問題なく動作はするのですが、ひとつ問題があります。

それは、SRPGStudio本体のアップデートを行うたびに、「Script」フォルダがまるっと上書きされてしまうということです(多くの場合、SRPGStudio本体のアップデート=公式によるスクリプトファイルの修正 であるため)

このため、「Script」フォルダと(ほぼ)同じ働きをしてくれる「Plugin」フォルダが存在するというわけです。
既存の処理を修正したり、新しい処理を追加する場合には、この「Plugin」フォルダにファイルを入れていきましょう!(具体的なやり方は後述します)

JavaScriptについて

いよいよコードの中身に関する話です!

プログラミング言語にはいろいろな種類がありますが、SRPGStudioで使用しているのはJavaScriptという言語です(拡張子の.jsはJavaScriptを表しています)

ただし、「JavaScript 初心者」などでGoogle検索をかけて書き方を学ぶ…というのはオススメできません。

というのも、いわゆる「JavaScript入門」の多くがhtmlに導入することを前提としたものであり、SRPGStudioには直接関係がないからです。また、SRPGStudioのJavaScriptのバージョンが古いため、最新の記法に対応できない、ということもあるようです。

このため、汎用的なJavaScriptの学習を目的とせず、ただ単に「SRPGStudioのプラグインを書けるようになりたい!」という場合には、プラグラミングに必要な最低限の概念を学んだ後、すでに公開されているプラグインを参考にして作成・改変をするのが最も手早いと思います。

最低限知っておくべきこと

  • 演算子

  • 変数(SRPGStudioではlet、constは使えないので覚えなくてOKです)

  • 関数、返り値

  • if文、for文

  • コメントの書き方

  • 配列

etcについては、最低限知識がないと「何をやっているのかさっぱりわからない…!」となりかねません。最初から完璧に理解する必要はありませんので、ざっくりどんなものなのか、外部サイトなどを利用して理解しておいてください。

JSファイルを作る環境準備

さて、「.js」のファイルを作成するにあたり、コードを書くためのテキストエディタが必要です。最悪「メモ帳」アプリでもかまわないのですが、

  • コードの文法にしたがって色をつけてくれる

  • ファイルをまたいで文字列を検索できる(とても重要!)

の機能を持っていると編集しやすいでしょう。私はVSCodeを使用しています。特にこだわりがなければ同じツールを使ってみてくださいね。

画像
VSCodeの編集画面

プラグインを作る

いよいよ実際にプラグインを作ってみましょう!
最初は「既存の処理の数値を変える」だけの処理が易しいと思います。

変える数値は、戦闘におけるダメージ計算式でもUIの位置調整でもなんでもよいですが、ともかく先人のプラグインをもとにして作ることをおすすめします。というのも、一からプラグインを作る場合、そもそもどの関数を修正すればよいのか見当をつけるのが難しいからです(似たような名前の関数がたくさんあったりして混乱すると思います)。

今回は、投射攻撃のダメージ計算式を変更したいとします。公式プラグインの「calc-shootdamage.js」が投射攻撃のダメージ計算式を修正しているので、それを上書きすることにしましょう。

公式プラグインをダウンロードして、「Plugin」フォルダの中に移動します。

画像
「Plugin」フォルダ内
画像
開くとこんな感じ。まだ何も編集していません。

このプラグインでは、「投射攻撃の攻撃/防御はユニットの技に準拠する」とありますが、今回は

  • 攻撃については、ユニットの「技」→「力+技

  • 防御については、ユニットの「技」→「守備力+技

によって参照されるように修正してみましょう。

メソッドの上書きについて

ところで、プラグイン17行目に

AbilityCalculator.getPower = function(unit, weapon) {

とありますが、これはどういう処理なのでしょうか…?

「Script」フォルダ > 「singleton」フォルダ > 「singleton-calculator.js」ファイル の内部を確認すると、

var AbilityCalculator = {
	getPower: function(unit, weapon) {
		var pow;
		
		if (Miscellaneous.isPhysicsBattle(weapon)) {
			// 物理攻撃または投射攻撃
			pow = RealBonus.getStr(unit);
		}
		else {
			// 魔法攻撃
			pow = RealBonus.getMag(unit);
		}
		
		// 武器の威力 + (力 or 魔力)
		return pow + weapon.getPow();
	},
	
	getHit: function(unit, weapon) {
		// 武器の命中率 + (技 * 3)
     ︙

という記述が見つかります。実は、この中の

var AbilityCalculator = {
	getPower: function(unit, weapon) {
     ︙
        }

というのと、公式プラグイン内の

AbilityCalculator.getPower = function(unit, weapon) {
     ︙
}

は同じ処理を指しています。つまり、このプラグインでは上記の処理を上書きして、投射攻撃の攻撃力を計算する処理を変更していたわけです。

同様に、

AbilityCalculator.getHit = function(unit, weapon) {
     ︙
}

と記述すれば、命中率に関する処理を書き換えることができます。
ダウンロードしたプラグインが既存のスクリプト処理を書き換えている場合、このことを知っておくと、効率的に調べられるでしょう。

VScodeなら、「ファイル」→「フォルダーを開く」で「Script」フォルダを指定した後、画面の一番左についている虫眼鏡マークから検索画面に移動すると、フォルダ・ファイルをまたいだ一括検索が可能です。これで、いちいち.jsファイルを開かずとも、対象の箇所を探すことができます。

画像
VSCodeの検索機能

実際に編集してみる

さて、いよいよ編集してみます。実現したいのは以下の2点でした。

  • 攻撃については「ユニットの力+技」により計算

  • 防御については「ユニットの守備力+技」により計算

今回はプラグイン内に丁寧なコメントがあるので、コメントを参考に修正すればよさそうです。攻撃面の計算式については、22~23行目の

	// 投射攻撃
	pow = RealBonus.getSki(unit);

を修正すればよさそうですね。getSki(Ski は Skill のことですね)で「技」の値を参照しているため、力の値も加算されるよう、以下のように修正してみましょう。

	// 投射攻撃
	pow = RealBonus.getSki(unit) + RealBonus.getStr(unit);

防御面については、ぜひ自分でやってみてください。
テストプレイしてみて思ったとおりに動いているならばOKのはずです。

覚えておくべきこと

以上が最低限の「プラグインの作り方」になりますが、他にも、いくつか覚えておくべきことがありますので紹介します。(言い出すときりがないので、本当に最低限だけ…!)

root.logの活用

デバッグのお供で、とても便利なメソッドです!
root.log(); の中に入れた文字がコンソール画面に表示されるので、自分が考えているとおりに処理が進んでいるか確かめることができます。

画像
root.log('aaa'); を入れた場合。コンソール画面に'aaa'と表示されました

他にも、たとえば先程のプラグインに以下の記述を入れてテストプレイすると…

画像
root.log('pow = ' + pow); の行を挿入してみる

以下のようにコンソール画面に数式が表示されました。

画像
コンソール画面に数式が表示された

なお、右側の黒い画面(コンソールウインドウ)は、テストプレイ画面において「デバッグ」→「コンソールの表示」で表示できます。

(function() { … })(); で囲う

先程の公式プラグインを見ると、処理内容が

(function() {
   ︙
})();

で囲われているのがわかります。このように囲うことにより、この中で定義された変数やオブジェクトは、他のPluginから参照できなくなります。少なくとも既存処理の修正においては、この囲いをつけるようにしてください。

「プラグインの競合」を回避する

先程、「プラグインを作る」の項で、

AbilityCalculator.getPower

メソッドを上書きしましたね。ところでもし、魔法攻撃の計算式を修正する別のプラグインをダウンロードしてきて、「Plugin」フォルダに入れたらどうなるでしょうか?

AbilityCalculator.getPower = function() {
   ︙
}

という処理が2つ存在する場合、片方の処理はなかったことになってしまいます(いわゆるプラグインの競合です)。これを防ぐために、プラグイン制作にあたっては、できるだけ処理をalias化します。

ふたたび公式プラグインを参照し、今度は「battle-motioncolor.js」を開いてみましょう。すると、以下のように記述されています。

(function() {

var alias1 = Miscellaneous.getMotionColorIndex;
Miscellaneous.getMotionColorIndex = function(unit) {
	var color = alias1.call(this, unit);
	var cls = unit.getClass();
	
	if (typeof cls.custom.color !== 'number') {
		// クラスのモーション色が設定されていないため、通常のモーション色を使用
		return color;
	}
	
	return cls.custom.color;
};

})();

先程と同様に、

Miscellaneous.getMotionColorIndex = function(unit) {
   ︙
}

とありますから、「Miscellaneous.getMotionColorIndex」というメソッドを上書きしたものだとわかりますが、その1行上に

var alias1 = Miscellaneous.getMotionColorIndex;

とあり、さらに関数の内部には

var color = alias1.call(this, unit);

とあります。これは、もとの「Miscellaneous.getMotionColorIndex」メソッドを、まず変数「alias1」に保存し、その後、「alias1.call」の部分で保存したメソッドを呼び出しています。
つまり、もとの「Miscellaneous.getMotionColorIndex」メソッドの処理は残しつつ、このプラグインで新たな処理を加えているわけです。このようにすれば、「プラグインの競合」問題は起こりません。

※「call」メソッドの引数として、「this」と、もとの関数の引数すべてが必要となります。今回は、もとの関数の引数が「unit」ひとつだったので

alias1.call(this, unit);

で元の処理を呼び出しています。

this. とは

「Script」フォルダ > 「window」フォルダ > 「window-info.js」ファイル の内部を確認すると、

var InfoWindow = defineObject(BaseWindow,
{
	_message: null,
	_infoType: -1,
	_infoWidth: 0,
	_infoHeight: 0,
	_messagePager: null,
	
	setInfoMessage: function(message) {
		var messagePagerParam = this._createMessagePagerParam();
		
		this._messagePager = createObject(MessagePager);
		this._messagePager.setMessagePagerText(message, messagePagerParam);
		
		this._calculateWindowSize(this._messagePager.getTextContainerArray(), messagePagerParam.font);
		this._messagePager.setSize(this._infoWidth, this._infoHeight);
	},
        
     ︙

という記述から始まっています。ここで、たびたび出てくる「this.」とは何者でしょうか……?

結論から書くと、ここでの「this.」は「InfoWindow.」と(ほぼ)同じ意味です。その証拠に、ファイルをさらに下っていくと

    _calculateWindowSize: function(containerArray, font) {
     ︙
    },

という記述が見つかります。「setInfoMessage」メソッド内から、このメソッドを呼び出していたというわけですね。

なお、「setInfoMessage」メソッド内にある「this._messagePager」は、関数ではなく変数です。このような記述にすると、通常の変数と異なり、「InfoWindow」内の任意のメソッドから呼び出すことができるようになります。(複数の関数をまたいで使用できる変数だと思っておいてください)

スクリプトリファレンス

すでに述べたように、「Script」フォルダ内の.jsファイルで多くの関数が定義されていますが、もっと大元の処理は別で定義されています。それらが一覧で纏まっているのがスクリプトリファレンスです。

たとえば、関数内で「Unit」オブジェクトが定義されている場合、

var name = unit.getName();

で、そのユニットの名前を取得することができますが、これはスクリプトリファレンスに載っています
また、上に書いた「root.log」もスクリプトリファレンスに載っています

なお、これを覚える必要は全くありません。必要になったとき、辞書的に活用しましょう。

上達のステップ

まずは、前項のように、「数値を変えるだけ」系のプラグインをいくつか作ってみることをおすすめします。

「数値を変える」については、「プラグインを作る」の項で実践したように戦闘計算式をいじるものもありますが、UIの表示位置を変えるのもよいでしょう。ぜひ関連するプラグインを探してみてください。

なお、このとき改変元にするプラグインは、できるだけ行数が少ないものにするとよいでしょう。サイズが大きいと、どこで何の処理を行っているのか把握するのが難しくなるからです。

「数値を変える」のやり方がわかってきたら、次は「複数のプラグインを組み合せて新しい処理をつくってみる」のをおすすめします。

たとえば「ユニットメニュー画面にクラスタイプを表示したい」と考えたとしましょう。このとき、

  • ユニットメニュー画面を編集するプラグイン

  • (別の画面に)クラスタイプを表示するプラグイン

の2つがあれば、これらを組み合わせることで、クラスタイプをユニットメニュー画面に追加表示することができそうですね。

画像
デフォルトのユニットメニュー画面

こうしてできることを増やしていけば、いずれ他のプラグインを参考にしなくてもメニュー画面を編集できるようになったり、他の画面にもクラスタイプを表示できるようになったり… と、無理なくできることを広げられると思います。

あとがき

できるだけ簡潔な記事にするつもりが、書いているうちに長大になってしまいました……。書いている中で改めて実感しましたが、プログラミング自体初学の方にとって、SRPGStudioのプラグイン制作はかなりハードルが高いです。だからこそ、最初から全てを理解しようとしたり、いきなり難しいことに挑戦しようとすると、却って躓いてしまうと思います。

どんなに小さな書き換えであっても、自分が書いたプラグインが読み込まれてゲームが動く、という経験は結構感動的なものです。モチベーションを維持するためにも、小さなプラグインでも構わないからこまめに制作してゲームに取り入れていくのが長続きするコツかと思います!

長くなりましたが、ここまで読んでいただきありがとうございました。
これがきっかけにして、少しでも「プラグインを書く人」が増えれば嬉しいです!!

参考Link

SRPG Studio 公式の説明ページです。「開発者編」の内容をひととおり読んでおきましょう!

こちらも初心者向けにわかりやすく説明されています。「スクリプト」タブの内容をひととおり読んでおきましょう!

いいなと思ったら応援しよう!

ピックアップされています

srpg関連

  • 15本

コメント

ログイン または 会員登録 するとコメントできます。
プログラミング経験ゼロから始めるSRPGStudioプラグイン制作|彩羽
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1