概要
RPGアツマールには非同期ネットゲームを想定したAPIが存在します。
その中の共有セーブAPIを使ってみた記事です。
RPGツクールMVのアクタを共有するためにやったことをまとめます。
作ったもの
-
他のユーザーのアクタを仲間に入れる (←今回はこちらの解説になります)
-
シューティングゲームのリプレイデータの共有
共有セーブデータの保存
まずは保存する側になります。
特定のキーでセーブデータを保存すると、他のユーザーがそのセーブデータを読み取ることができます。これによってキャラクタのステータスを共有したりリプレイデータの共有を行うことができます。
共有セーブデータの保存
// APIの関数が存在するかどうかのチェック
var storageDefined = window.RPGAtsumaru && window.RPGAtsumaru.storage;
var exStorageDefined = window.RPGAtsumaru && window.RPGAtsumaru.experimental.storage;
var setItemsDefined = storageDefined && window.RPGAtsumaru.storage.setItems;
// プラグインコマンド実行時の処理を追加
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command) {
_Game_Interpreter_pluginCommand.apply(this, arguments);
// プラグインコマンドでSaveShareActorをしたときに実行
if (command === "SaveShareActor") {
if(setItemsDefined) {
// shareActorに共有するActorの情報を作る(後述)
var shareActor = internal.encodeSharedActor_($gameParty.leader())
// APIの実行
window.RPGAtsumaru.storage.setItems([{ key: "Atsumaru Shared", value: JSON.stringify(shareActor) }])
.then(function() {
// 成功時の処理
}, function(error){
// エラー時の処理
})
}
}
}
プラグインコマンドで発動するように実装しました。
セーブ保存のAPIで、Keyに Atsumaru Shared
を指定すると、保存された文字列を他のユーザーから読み取ることができるようになります。
https://atsumaru.github.io/api-references/storage
取得が終了するまでイベントの進行を待つには?
ネットワークを介した処理なので即座に終了するわけではなく、ネットワークの状況次第で時間がかかります。
その間はイベントの進行も止めておきたい場合、下記を追加します。
(他の公式プラグインと同じ方法です)
var _Game_Interpreter_updateWait = Game_Interpreter.prototype.updateWait;
Game_Interpreter.prototype.updateWait = function() {
var result = _Game_Interpreter_updateWait.apply(this, arguments);
return result || Boolean(this._waitForSetItemsPlugin)
};
window.RPGAtsumaru.storage.setItems
の1行前に this._waitForSetItemsPlugin=true
を記述して、
成功もしくはエラーのコールバックの中でfalseに設定します。
this._waitForSetItemsPlugin
がtrueの間はイベントの進行が止まります。
アクタの情報をjsonで保存する
var internal = {}
internal.encodeSharedActor_ = function(actor) {
var shareActor = {}
shareActor.id = actor._actorId
shareActor.classId = actor._classId
shareActor.exp = actor._exp
shareActor.paramPlus = actor._paramPlus
shareActor.equipWeapon = actor._equips[0]._itemId
shareActor.equipsSub = []
for(var idx=1;idx<actor._equips.length;idx++) {
shareActor.equipsSub.push(actor._equips[idx]._itemId)
}
return shareActor
}
保存する文字列は何を記録したいかによって変わりますが、今回は引数に渡されたアクタの情報を記録します。アクタの再現に必要な最低限の情報を連想配列に詰めて返します。外側に window.RPGAtsumaru.storage.setItems
に渡すときに JSON.stringify
で文字列にします。
共有セーブデータの読み取り
ここまででアクタ1人分の情報を保存できるようになりました。
次は他のユーザーから保存した情報を読み取る方法です。
準備として、保存した側が「ユーザー間通信の許可」を行っている必要があります。これは公式のプラグインがあるのでそれを利用します。これによって他のユーザーから共有セーブを読み取れるようになります。
ユーザー間通信の許可
自分が保存した共有セーブを他のユーザーから読み取るために行います。
https://atsumaru.github.io/api-references/common/interplayer/
AtsumaruInterplayerEnableExperimental.js
を導入して、
プラグインコマンドから EnableInterplayer
を実行しておきます。
共有セーブデータの読み取り
// APIの関数が存在するかどうかのチェック
var exStorageDefined = window.RPGAtsumaru && window.RPGAtsumaru.experimental.storage;
var getSharedItemsDefined = exStorageDefined && window.RPGAtsumaru.experimental.storage.getSharedItems
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command) {
_Game_Interpreter_pluginCommand.apply(this, arguments);
// (さっきの続き)
// プラグインコマンドでGetShareActorRecentUserをしたときに実行
else if( command === "GetShareActorRecentUser" )
{
if(getSharedItemsDefined)
{
// 取得したセーブデータ(Json)を$gameTempで保持する
$gameTemp._sharedActors = {}
// APIの実行
window.RPGAtsumaru.experimental.storage.getSharedItems(/*取得するUserIdの配列*/)
.then(function(result) {
// Keyの配列を取得する
var keys = Object.keys(result)
for(var idx=0; idx < keys.length;idx++)
{
var key = keys[idx]
if( Number(key) != /*自分のUserId*/) // 自身のUserIdは含めない
{
// JSON.parseで連想配列に直して保持する
var parsed = JSON.parse(result[key])
// 例) {12353:{id:15,classId:1542,...},52431:{id:12,classId:124,...}}
$gameTemp._sharedActors[key] = parsed
}
}
}, function(error){
// エラー時の処理
})
}
}
これで引数に渡したUserIdのユーザーの共有セーブデータを $gameTemp._sharedActors
で保持します。
例えばフレンドのリストを記録しておいてそのUserIdで配列を作れば、フレンドのセーブデータの取得ができます。
後述する「最近プレイしたユーザー」で取得すると、自分のUserIdが含まれるので除外しておく必要があります。
取得が完了するまでイベントを止めたい場合は、
「取得が終了するまでイベントの進行を待つには?」に記載したような待ちの処理を同様に入れてください。
最近プレイしたユーザーの取得
https://atsumaru.github.io/api-references/user
AtsumaruGetUserInformationExperimental.js
を導入して
プラグインコマンドから GetUserInformation
を実行しておきます。
一部改造してあります。
元の挙動だと変数( $gameVariables
)に保存するようになっていますが $gameTemp
に配列で保持します。
配列にしておくと後で window.RPGAtsumaru.experimental.storage.getSharedItems
に渡しやすくなります。
window.RPGAtsumaru.experimental.user.getRecentUsers()
.then(function(recentUsers) {
for (var i = 0; i < 100; i++) {
var user = recentUsers[i];
// 変数でなくgameTempに配列を保持、自身のIDは除く
if(user && user.id != 0 && Number(user.id) != /*自分のUserId*/)
{
$gameTemp._recentUserIds.push(user.id)
$gameTemp._recentUserNames.push(user.name)
}
// $gameVariables.setValue(offset + i, user ? user.id : 0);
// $gameVariables.setValue(offset + i + 100, user ? user.name : 0);
}
}, function(error) {
// エラー時の処理
}
自分の情報の取得
https://atsumaru.github.io/api-references/user
AtsumaruGetSelfInformationExperimental.js
を導入して
プラグインコマンドから GetSelfInformation
を実行しておきます。
ここまでの記述で /*自分のUserId*/
と書いてある部分がありますが、
これはこのプラグインで取得して $gameVariables
の中で保持しています。
$gameActorsにアクタの情報を構築
// $gameTemp._sharedActorsからgameActorへのコピー
internal.dstSharedActorId = 3
internal.setupSharedActor = function(userId)
{
if(!$gameTemp._sharedActors) return false
var sharedActor = $gameTemp._sharedActors[userId]
if(!sharedActor) return false
var dstActor = $gameActors.actor(RQ03x.dstSharedActorId)
var srcActor = $gameActors.actor(sharedActor.id)
// sharedActorから持ってくるもの
dstActor._classId = sharedActor.classId
dstActor._exp = Object.assign({},sharedActor.exp)
dstActor.changeExp(dstActor.currentExp(),false)
dstActor._paramPlus = sharedActor.paramPlus.concat()
// equipsのデコード
dstActor._equips = [];
for (var i = 0; i < dstActor.equipSlots().length; i++)
{
dstActor._equips[i] = new Game_Item()
}
dstActor._equips[0].setObject($dataWeapons[sharedActor.equipWeapon])
for(var idx=0; idx<sharedActor.equipsSub.length;idx++)
{
if(sharedActor.equipsSub[idx]>0)
{
dstActor._equips[idx+1].setObject($dataArmors[sharedActor.equipsSub[idx]])
}
}
// IDをもとにコピーするもの
dstActor.setName( srcActor.name() + "(助っ人)" )
dstActor.setNickname( srcActor.nickname() )
dstActor._faceName = srcActor._faceName
dstActor._faceIndex = srcActor._faceIndex
dstActor._characterName = srcActor._characterName
dstActor._characterIndex = srcActor._characterIndex
dstActor._battlerName = srcActor._battlerName
dstActor.setProfile(srcActor._profile)
dstActor.setTp(0)
dstActor.recoverAll()
dstActor.refresh()
return true
}
「アクタの情報をjsonで保存する」で作ったjsonから$gameActorsにコピーする部分です。
仲間に入れるUserIdを引数にこれを実行します。
internal.setupSharedActor(12345)
を実行すると、UserId=12345のユーザーのアクタが、ID=3のアクタ(アクタのデータベースで3番目)の位置にコピーされます。名前は末尾に「(助っ人)」という文字が付きます。
共有セーブから持ってきたデータを保持しておく用のアクタ(今回はID=3)を決めておいてその場所にコピーしています。
まとめ
共有セーブデータを使って $gameActors
の情報を共有する方法をまとめました。
- 共有セーブデータの保存
- アクタの情報を保存する
- 共有セーブデータを読み取る
- ユーザー間通信の許可
- 共有セーブデータの読み出し
- 最近プレイしたユーザーの取得
- 自分の情報の取得
- UserIdを引数に$gameActorsにアクタの情報を構築する