导航

萌即是正义!时不时分享一些ACG活动记录与有趣代码的小站!

侧边栏
热门文章
1推文
纯 AI 制作的视觉小说。 使用 GPT 6.1 Sol 制作。 剧本、美术、音乐、程序等全部都是 AI 完成的。 故事为 Happy Sugar Life 的 if 线。 剧本和美术多少有些小瑕疵,但是感觉无伤大雅,多抽抽卡应该是能抽出完美的内容出来。 试玩链接: https://www.wikimoe.com/web_demo/ai_game/happy-sugar-life-half-of-tomorrow/
热度
536
2推文
最近下北泽把月亮请了下来呢
热度
316
3推文
看了一场不一样的《超时空辉夜姬》。 上周六,也就是2026年9月19日,去池袋的电影院看了 ULTRA 4DX 版《超时空辉夜姬》。 所谓的 ULTRA 4DX 就是前面三面荧幕,椅子会随着剧情发生摇晃震动等效果。 周围还会模拟刮风、下雨、打雷、爆炸烟雾等效果。 印象最深刻的就是小辉夜姬打了个喷嚏,喷了我一脸鼻水的模拟效果😂。 其他像是 VR 竞技游戏环节的椅子晃动效果非常棒! 唯一美中不足的是,官方给出的效果图里三面荧幕看起来都很亮,实际上左右两面荧幕是明显要暗很多的,而且清晰度其实也不是很高。 但是考虑到左右两面荧幕平时不是盯着看的,而是用余光来看的这么一种使用场景,也还能接受。
热度
277
4推文
去六本木吃了《穹庐下的魔女联动咖啡》的联动咖啡。 这是一家电视台里的咖啡店,本来主打的其实是4000日元的水果套餐。 联动菜单里的蒙古包我觉得是最有意思的,青青草地上的白色蒙古包蛋糕。 《原论》三明治看起来像是厚厚的书,但其实本质就是2个厚厚的三明治。
热度
232
5推文
吃完联动咖啡后,去新宿逛了逛街。 意外的发现丸井百货有一大片芳文社的周边贩卖区。
热度
154
6博文
《夏目友人帐》圣地巡礼之人吉 风景篇
热度
143
7推文
第一次尝试联动酒吧,《超时空辉夜姬》联动酒吧。 昨天,也就是2026年9月20日,预约了位于新宿的 HUB 酒吧。 这是一家英国风格的连锁酒吧,现在被《超时空辉夜姬》的联动包场了。 这应该是我第一次去酒吧吧。 因为本身就是连锁酒吧,酒精饮料和菜品的口味都非常有保证(炸鱼薯条好吃呢!)。 官方也很贴心的在FAQ强调,即使你不喝酒,这里的无酒精饮料和菜品也非常不错。 看起来是想借助联动,改变酒吧的刻板印象,尽可能吸引更多原本不喝酒的消费群体。 这确实让我对酒吧这样的场所有了不小的改观,有可能下次还会去消费。 联动送的特典本来以为是小卡片,结果发现是相当有质感的金属牌子! 不过人工排队去前台点餐的机制不太好,饮料是现场做完自己端到位置上,食物类则是由工作人员送到位置上,就感觉效率并不是很高。
热度
135
8博文
探访日本的废校:《轻音少女》圣地巡礼之丰乡小学校旧校舍群
热度
117
9博文
【自制】《少女与战车:战车梦幻大会战》秋山优花里的圣葛罗莉安娜女学院潜入视频
热度
104
10博文
【H5小游戏】制作一个简单的丢鱼雷小游戏
热度
104
最新评论
广树管理员
2026-10-08 08:22
@Zrzzz:有没有变聪明不知道,肚子是饱饱的
广树管理员
2026-10-08 08:21
@Zrzzz:真实感拉满
广树管理员
2026-10-08 08:21
@Zrzzz:不,应该是谢谢GPT老师,是GPT老师做的。
Zrzzz
2026-10-08 00:58
吃完原论三明治 有没有感觉更聪明了( ´▽`)
Zrzzz
2026-10-08 00:56
看起来暖暖的月亮(˶‾᷄ ⁻̫ ‾᷅˵)
攻略中
空之轨迹 the 1st
暂无评分
Steam空之轨迹 the 1st
2026年8月02日 12时 ~ 攻略中
已累计游玩2个月6天
因父亲卡西乌斯失踪而踏上旅程的艾丝蒂尔与约书亚, 在巡游利贝尔王国的过程中, 经历了无数与未知同伴的相遇与别离, 而在旅途终点等待着两人的,究竟会是什么……?
节奏天国 奇迹之星
暂无评分
NS节奏天国 奇迹之星
2026年7月08日 20时 ~ 攻略中
已累计游玩3个月1天
《节奏天国:奇迹之星》是一款轻松又充满创意的音乐节奏游戏,玩家只需跟随音乐节拍按下按键,就能完成跳跃、敲击空罐、制作料理等各种有趣的小挑战。游戏延续了《节奏天国》系列简单易上手、节奏感强、画风幽默的特点,让玩家在一个个短小精巧的关卡中感受音乐与反应力结合的乐趣,适合喜欢节奏游戏和轻松休闲玩法的玩家体验。
告别回忆 -无垢少女- 致最爱的你
暂无评分
Steam告别回忆 -无垢少女- 致最爱的你
2026年6月24日 20时 ~ 攻略中
已累计游玩3个月15天
系列作发售至今经历了19年,做为其系列最终作的《告别回忆 ~无垢少女~》(以下略称为本篇)在2018年3月发售。本次的FanDisc和至今的系列作相同,带给玩家在各结局后的短篇故事。请充分享受和各个女主角们在欢乐结局后的幸福时光。
亚路塔:狐狸狐途的面包冒险
暂无评分
Steam亚路塔:狐狸狐途的面包冒险
2026年2月12日 19时 ~ 攻略中
已累计游玩7个月27天
一场席卷全世界的面包革命现在开始! 主人公查雅阴错阳差之下,开始了在荒废的岛屿上经营面包店的生活。 出外冒险搜集食材、研发崭新面包配方、遇见性格各异的伙伴们,建造更丰富的设施,用面包香气使无人问津的广场再次热闹起来!
fault - StP - LIGHTKRAVTE
暂无评分
Steamfault - StP - LIGHTKRAVTE
2025年5月29日 20时 ~ 攻略中
已累计游玩1年4个月
全球累计销量超过50万份的“fault”系列最新作!故事的舞台是一个融合了奇幻与科幻的超前世界——卢森海德王国。本作讲述了生活在此地的一个平凡又平庸的究极普通市民——名为果子的少年的故事。
PSN奖杯卡

PSN奖杯卡

归档
赞助商广告

RPG Maker MV 插件制作入门(2)

2017-07-13 16:42:42
JavaScript
上回的制作入门(1)中,我们制作了一个在角色名字后面追加ID显示的简单插件。可以通过插件参数设定初始显示,然后通过插件指令来切换显示。

这次也继续上回,拓展这个插件。

将状态保存到存档文件中
上次制作的插件虽然准备了插件指令,但有一个缺点。就是无法保存在存档中,就算通过指令开关,只要游戏重启又会回到初始状态。

因此这次,要将show_id的值保存到存档中,这样就算重启游戏也能维持状态。

3种存档文件
RPG Maker MV所生成的游戏中,有以下三种存档文件。
    config.rpgsave
    file*.rpgsave
    global.rpgsave

'config.rpgsave'是为了保存游戏设定的文件。标题画面以及游戏菜单的设置状态保存在这里。比如下面的设置画面。
plugin-dev-02-01.jpg
被保存的数据如下。
{
  "alwaysDash":false,
  "commandRemember":false,
  "bgmVolume":100,
  "bgsVolume":100,
  "meVolume":100,
  "seVolume":100,
}


'file*.rpgsave'作为实际的保存文件,保存着游戏各种状态。存档文件可以生成数个,*的部分为连号。这个文件所储存的内容将会在下一章说明。

'global.rpgsave' 是储存在存档选择画面中所使用的数据,在存档生成和更新时更新。比如对应下面的存档选择画面。
plugin-dev-02-02.jpg
所保存的数据如下。
[
  null,
  {
    "globalId":"RPGMV",
    "title":"RTK1 Dev1",
    "characters":[["Actor1",0],["Actor1",7],["Actor3",7],["Actor2",6]],
    "faces":[["Actor1",0],["Actor1",7],["Actor3",7],["Actor2",6]],
    "playtime":"00:00:04",
    "timestamp":1468556954489
  },
  {
    "globalId":"RPGMV",
    "title":"RTK1 Dev1",
    "characters":[["Actor1",0],["Actor1",7],["Actor3",7],["Actor2",6]],
    "faces":[["Actor1",0],["Actor1",7],["Actor3",7],["Actor2",6]],
    "playtime":"00:00:48",
    "timestamp":1468855189632
  }
]


游戏存档的文件内容
我们知道'file*.rpgsave' 是实际的存档文件,那么是否所有内容都存在其中?那么省略一部分,来粗略介绍下主要部分吧。
{
	"system":{
		"_saveEnabled":true,
		"_menuEnabled":true,
		"_encounterEnabled":true,
		"_formationEnabled":true,
		"_battleCount":0,
		"_winCount":0,
		// 中间省略
		"@":"Game_System"
	},
	"screen":{
		"_brightness":255,
		"_fadeOutDuration":0,
		"_fadeInDuration":0,
		"_tone":[0,0,0,0],
		// 中间省略
		"@":"Game_Screen"
	},
	"timer":{"_frames":0,"_working":false,"@":"Game_Timer"},
	"switches":{"_data":[null,null,null,null,null,null,null,null,true],"@":"Game_Switches"},
	"variables":{"_data":[],"@":"Game_Variables"},
	"selfSwitches":{"_data":{"1,3,A":true},"@":"Game_SelfSwitches"},
	"actors":{
		"_data":[
			null,
			{
				"_actorId":1,
				"_name":"Harold",
				"_nickname":"Sword boy",
				"_hp":450,
				"_mp":90,
				"_tp":0,
				"_hidden":false,
				"_paramPlus":[0,0,0,0,0,0,0,0],
				// 中间省略
				"@":"Game_Actor"
			},{
				"_actorId":2,
				// 以下省略
			},{
				"_actorId":3,
				// 以下省略
			{
				"_actorId":4,
				// 以下省略
			}
		],
		"@":"Game_Actors"
	},
	"party":{
		"_inBattle":false,
		"_gold":2000,
		"_steps":0,
		"_lastItem":{"_dataClass":"","_itemId":0,"@":"Game_Item"},
		"_menuActorId":0,
		"_targetActorId":0,
		"_actors":[1,2,3,4],
		"_items":{"1":6,"2":1,"10":50,"11":2,"12":2,"13":2,"14":2},
		"_weapons":{"1":1,"2":2,"4":10},
		"_armors":{"3":1,"4":1},
		"@":"Game_Party"
	},
	"map":{
		"_interpreter":{
			"_depth":0,
			"_mapId":0,
			"_eventId":0,
      // 中间省略
			"@":"Game_Interpreter"
		},
		"_mapId":1,
		"_tilesetId":1,
		"_events":[
			null,
			{
				"_x":4,
				"_y":1,
				// 中间省略
				"@":"Game_Event"
			},
			// 以下省略
		],
		"_commonEvents":[],
		// 中间省略
		"@":"Game_Map"
	},
	"player":{
		"_x":4,
		"_y":4,
		// 中间省略
		"_followers":{
			"_visible":true,
			"_gathering":false,
			"_data":[ // 省略
			],"
			@":"Game_Followers"
		},
		"_encounterCount":603,
		"@":"Game_Player"
	}
}


在上回制作入门(1)中,介绍过$game变量一览。上述游戏存档的各种元素,就是对应$game 变量的。

也就是说可以理解成游戏的存档文件就是将$game变量的值保存在里面。

利用哪个变量呢
这次又说了很长的前言呢...

既然知道这些 $game 变量被保存在存档里的话,就让我们利用吧!只要从中选择一个值来储存的话,就能任凭系统去读取保存了。

个人经常使用的是 $gameSystem ,所以这次也用这个吧。

关于保存时所使用的名字,需要考虑是否会和其他插件重名。这里推荐名称为,插件名或者名字前面追加数据种类。

实际处理
首先将使用 show_id 的部分进行拓展。
var _Game_Actor_name = Game_Actor.prototype.name;
Game_Actor.prototype.name = function() {
    var ret = _Game_Actor_name.call(this);
    var f = $gameSystem[N + "_show_id"];
    if (f === undefined ? show_id : f) {
      return ret + ":" + this.actorId();
    } else {
      return ret;          
    }
};


变更尽量少,一行就够了,仅仅将if的条件改变了而已。

增加的1行是将 $gameSystem 保存的值读取到f变量中处理。名称按照惯例为N定义的插件名加上"_show_id" 作为储存的变量名就行了。

然后if语句的判断,如果保存的值为空则 f 为 undefined ,所以作为替换将使用前面的 show_id 。如果有保存的值则使用保存的值。

使用保存值的部分是OK了,将值保存起来的处理还没有呢。就修正插件指令的部分吧。
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
  _Game_Interpreter_pluginCommand.call(this, command, args);
  if (command == N) {
    if (args[0] == "show_id") {
      if (args[1] == "on") {
        $gameSystem[N + "_show_id"] = 1;
      } else if (args[1] == "off") {
        $gameSystem[N + "_show_id"] = 0;
      }
    }
  }
};


嗯,这里的修正也不多呢。只是将$gameSystem设定的值替代掉show_id而已。

然后,这样就完成了对应存档了。补上一行,修改三行。仅此而已。

修正前是如下的样子
    show_id 的初始值由插件参数指定
    通过插件指令变更show_id的值
    show_id的值无法保存

修正后效果如下
    show_id的值由插件参数指定,以后不可以变更
    通过插件指令改变$gameSystem.RTK_Test_show_id的值
    实际执行的时候使用$gameSystem.RTK_Test_show_id的值
        但是如果找不到的话就使用show_id的值

show_id的变量从主角变成了作为初始值的配角。作为替代使用了 $gameSystem.RTK_Test_show_id 的变量,因为包含在 $gameSystem 中,所以会被存档保存。

这样的话有一个好处,只要不使用插件指令的话就不会消费存档文件的空间。

稍微下点功夫吧
在前面的章节中我们能保存了。作为目的应该足够了。这个章节作为附赠,稍微下点小功夫。

现在所使用的结构里,show_id的变量反应在插件参数里吧。也就是说是在游戏开发的时候设定的值,实际游戏发布后就无法变更了。

以此为前提,为了更加节约存档文件空间,我们将代码变成这样。
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
  _Game_Interpreter_pluginCommand.call(this, command, args);
  if (command == N) {
    if (args[0] == "show_id") {
      if (args[1] == "on") {
        if (show_id) {
          delete $gameSystem[N + "_show_id"];
        } else {
          $gameSystem[N + "_show_id"] = 1;          
        }
      } else if (args[1] == "off") {
        if (!show_id) {
          delete $gameSystem[N + "_show_id"];
        } else {
          $gameSystem[N + "_show_id"] = 0;
        }
      }
    }
  }
};


可能稍微有点复杂呢。这串代码的概念如下。
    如果和初始值相同的话就没有必要储存起来。
       不需要的值用delete删除掉

这次的值只有一个,所以可能并不需要特地去删除掉。但是「良好礼节挂心中的代码」是很重要的,要经常有这样的意识,所以稍微下了点功夫介绍了下例子。

想要认真处理储存的情况
这次的例子里,利用$game的变量难免有点偷工。这里说下如何好好处理。

首先保存的时候,扩张以下存档生成处理比较好。
var _DataManager_makeSaveContents = DataManager.makeSaveContents;
DataManager.makeSaveContents = function() {
  var contents = _DataManager_makeSaveContents.call(this);
  // 这里将自己的数据设定成contents
  return contents;
};


然后为了读取设定好的数据,扩张如下存档处理比较好。
var _DataManager_extractSaveContents = DataManager.extractSaveContents;
DataManager.extractSaveContents = function(contents) {
  var ret = _DataManager_extractSaveContents.call(this, contents);
  // 这里将会从contents中读取数据
  return ret;
};


追加插件的机能吧
难得机会,稍微追加下插件的机能吧。不仅仅是角色,追加敌人的ID显示吧!

战斗中,使用控制台来查找类。$game变量中 $gameTroop 有点像,作为起点吧。
plugin-dev-02-03.png


原来如此,在rpg_object.js文件的第4379行的位置呢。
Game_Enemy.prototype.name = function() {
    return this.originalName() + (this._plural ? this._letter : '');
};


plural 是什么呢?于是在源码中查找后,发现了以下的代码。相同组(Troop)内有一样名字的时候好像会设置 true 。就是那个呢,如果有一样的敌人就会在名字后面加上A或者B什么的。
  this.members().forEach(function(enemy) {
      var name = enemy.originalName();
      if (this._namesCount[name] >= 2) {
          enemy.setPlural(true);
      }
  }, this);


那么,敌人的name函数扩张起来吧。首先在不追加的情况下做下扩张的准备工作。


var _Game_Enemy_name = Game_Enemy.prototype.name;
Game_Enemy.prototype.name = function() {
  var ret = _Game_Enemy_name.call(this);
  return ret;
};



在有时间的情况下,在这个状态下进行游戏测试。在自己对代码进行调试前,先看看扩张的代码对游戏有什么坏影响没,提前打个预防针。

不,实际上真的有哦。烦恼了几个小时,结果发现是简单的第一步就拼写错误导致失败不断,何等的犯糊涂...不,应该不只我会这样。

那么追写实际的代码吧。不过和写角色的情况差不多...
var _Game_Enemy_name = Game_Enemy.prototype.name;
Game_Enemy.prototype.name = function() {
  var ret = _Game_Enemy_name.call(this);
  var f = $gameSystem[N + "_show_eid"];
  if (f === undefined ? show_eid : f) {
    return ret + ":" + this.enemyId();
  } else {
    return ret;          
  }
};


姑且,将使用的变量改成show_eid。所以在插件参数以及插件指令的部分也必须要扩张。

不过和角色差不多,这里就不细细说明代码了。最后会将代码全部丢上来,到时候再来确认追加的代码吧。

完成插件吧
那么,到此为止所说明的插件,结果也不错姑且就算完成了吧。

名字叫 'RTK_Test' 也不好呢。就改名'RTK_ShowID' 吧。在变更文件名的同时,别忘了修改最初N变量的定义。

帮助也很不充分(写着不包含插件指令,但是实际上是有的),需要各种追加和变更表述。

于是完成结果在这里 -> RTK_ShowID.js

因为有点长,可以借助GitHub的功能进行查看。源链接也可以储存下载js。

这就是插件开发相关的最低限度的说明。制作各种插件,尝试乐在其中吧!

那么,再会!

翻译自:https://github.com/yamachan/jgss-hack/blob/master/guide/plugin-dev-02.ja.md

收款.png


file_000000008460720687635c008459256b.png