导航

萌即是正义!时不时分享一些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个厚厚的三明治。
热度
245
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 插件制作入门(1)

2017-07-08 10:03:53
JavaScript
本文为RPG Maker MV所使用的基于JavaScript的Game Scripting System (JGSS) 插件入门资料。需要有一定的JS(JavaScript)基础知识。

创建插件文件
以下为我经常使用的插件模板文件。

//=============================================================================
// RTK_Test.js	2016/07/30
// The MIT License (MIT)
//=============================================================================

/*:
 * @plugindesc 测试用插件
 * @author Toshio Yamashita (yamachan)
 *
 * @help 本插件不包含插件指令。
 * 由于是测试用的文件,实际使用的时候请适当重命名
 */

(function(_global) {
  	// 这里写上插件处理
})(this);


下载点此->RTK_Test.js

下图为RPG Maker MV的插件管理页面。JS中的注释@plugindesc 和 @author 行会各自显示在相应的位置。

插件面板.jpg

开发工具与游戏变量的基础
游戏中经常使用的变量,会以$data开头。其作为游戏基本的数据,会通过RPG Maker MV自动生成,data文件以*.json的形式保存。这些文件会在游戏开始时读取,基本上是不会发生变更的静态数据,本指导中会将这些称之为「$data变量」,被保存的值则称为「静态数据」。

与其对应的以$game开头的变量,在游戏内被使用,被更新的动态数据,这些都会保存在存档中。在本指导中将其称为「$game变量」,被保存的值则称之为「动态数据」。

在RPG Maker MV中测试游戏的时候按下F8就会启动开发工具。开发工具中的控制台是非常便利的东西。

实际启动控制台,输入$data 后,以$data 开头的变量会提示出一堆。$game也同样。控制台中的提示辅助功能,对帮助理解游戏内的状态有很大的帮助。
plugin-dev-03.pngplugin-dev-02.png

关于$data变量的第一个$dataActors,让我们稍微看一下吧。在控制台上选择这个变量名,然后按下回车键显示内容。

$dataActors的长度为5,第一个元素为null,其他四个为Object。那么我们输入$dataActors[1] 来获取第一个Object。
plugin-dev-04.jpg
name.jpg

在$dataActors[1]中的name显示角色设定名为 "挑战者" 。其他还有各种设定,但因为是$data变量,这些基本都是在数据库中所设定的值。

最下面的_proto_是这个Object的父元素,但因为是Object,并没有设置上级的类,只是单纯用作数据的Object。

那么接下来看下$game变量的第一个$gameActors 。

展开_proto_,这次不是单纯的数据Object,有定义名为Game_Actors的类。

plugin-dev-05.png


Game_Actors类中,定义了一个名为actors的函数。在控制台上输入这个函数名后,就能看到这个函数的定义(源码文件)。右边显示了这个js文件的名称和行数,想要知道更详细的内容不妨点击对照下源码。

控制台是非常便利的工具,请务必活用。

稍微多讲点 - 关于数据的阶层

研究下RPG Maker MV所生成的游戏,$data静态的世界(变量的定义)和$game动态的世界相当好的分离了开来。

系统开发遵循了MVC(Model-View-Control)模型,个人觉得$data的世界是Model,$game的世界接近Contorol层。

比如之前的Game_Actors中有actors函数。一眼看过去好像是一个很便利的函数,但是我觉得有如下设计意图。

$game层与$data层分离

    $game Object只能知道最低限度的$data Object
    从$game Object角度出发,$data层是抽象化的

稍微有点难,再讲得琐碎点就是。

不用知道$game Object 在$game层是怎么被Actor管理的
    $game Object想要知道 Actor 的情报的话,往往会通过Game_Actors 

Game_Actors这个类,是掌管$game世界关于Actor的全部的存在。就是SPE(single point of entry) 。将$game世界和$data世界进行适当的分离。

所以作为插件开发者,会写操作 $game 这边的很多代码。


var anActor = $dataActors[id];
var anActor = $gameActors.actors(id);



想要参阅角色情报的话,下面那行代码可能更合适。

写一个简单的插件吧!

前文有点过长了呢。总之第一步,我们先做一个简单的插件吧。

因为前面提到了Actor,所以我们稍微调戏下名字吧。首先我们在控制台或者源文件的rpg_objects.js中找到定义的函数。


Game_Actor.prototype.name = function() {
    return this._name;
};



非常简单的内容呢。那么我们在名字后面加上ID吧。在先前介绍的插件文件中,写上插件处理。


(function(_global) {
  	// 这里写上插件处理
    Game_Actor.prototype.name = function() {
        return this._name + ":" + this.actorId();
    };
})(this);



指令等目前还没改写,总之先运行下。

游戏界面.jpg


是不是很简单?

防止与其他插件冲突
在先前的例子中,我们调戏了Actor的名字。这个功能也不是完全没用,至少在开发的时候能用来检查ID。

这个插件自己用大概是没什么问题。但是,在网上公开,或者给别人用还是算了。现阶段这个插件并没有考虑冲突问题,是一个会造成麻烦的插件。

设想有一个插件有着相同的构造,同样是一款影响角色名字的插件。于是,两个插件一起读取后,最终会显示哪个?结果大概是先读取的被后读取的覆盖,然后执行后读取的。

就像争夺场地一样。Game_Actor.prototype.name这个函数所定义的场地只有一个,所以定义就会覆盖,前面的定义消失,只有最后的定义生效。这就是最简单易懂的插件冲突例子。

解决这个问题非常简单,不要覆盖定义,追加就好了。就像下面的代码。


(function(_global) {
  	// 这里写上插件处理
    var _Game_Actor_name = Game_Actor.prototype.name;
    Game_Actor.prototype.name = function() {
        var ret = _Game_Actor_name.call(this);
        return ret + ":" + this.actorId();
    };
})(this);




准备_Game_Actor_name这个变量,将前面的定义先保存起来。然后运行自己的插件代码的时候,先将保存的定义(函数)执行。再基于执行后的值,将自己处理的,也就是在末尾追加“:”和ID执行。

这个时候,想实现的处理和原来的值并无冲突。如果是要实现「和原先定义无关,在名字的地方只显示ID」这样一个插件的话,只用按照如下来写。


(function(_global) {
  	// 这里写上插件处理
    var _Game_Actor_name = Game_Actor.prototype.name;
    Game_Actor.prototype.name = function() {
        var ret = _Game_Actor_name.call(this);
        return String(this.actorId());
    };
})(this);



即便是不使用作为返回值的ret,也要将原先的定义提出来是一个「很好的礼仪」。在这个世界中有许许多多的插件,有许多意想不到的理由来使用函数的情况。所以,这个值哪怕是不使用,将函数单独拎出来,就能够防止插件之间的冲突。

制作插件不能只考虑自己逻辑上的效率,无时无刻考虑避免影响其他插件的运作,是一个非常重要的意识。

然后这里所使用的call函数,具体请参阅 JavaScript 参考书什么的。但是希望有一点不要忘记,下面这样的函数所获取到的参数(这次是name)请不要忘记传递给原本的定义。


(function(_global) {
  	// 这里写上插件处理
    var _Game_Actor_setName = Game_Actor.prototype.setName;
    Game_Actor.prototype.setName = function(name) {
        var ret = _Game_Actor_setName.call(this, name);
        // 这里写上自己的处理代码
        return ret;
    };
})(this);



上面的例子中,如果有其他返回的值就原封不动返回。现在setName函数什么都没有返回,所以最后的retuen没有意义。但是将来版本升级可能会有什么值返回过来,或者有什么会返回过来的插件存在。所以附上比较安心。

稍微多讲点 - 局部作用域
如上面的例子定义了一个_Game_Actor_setName的变量,这个变量只会在这个插件中有效,并不会影响其他插件。所以只要不与这个插件中的名字冲突,基本不怕和其他插件冲突。

这就是一般的局部变量的概念,在JavaScript中简单的「在函数中定义的定义(变量等)就会成为只在函数内有效」
(function(_global) {
    // 这里写上插件处理
    // 这里定义的函数和变量只在这个范围内有效
})(this);

因为这是JavaScript相关的话题,只是简单说明下。具体请查阅相关书籍和网站来学习。

题外话,插件中对于保持函数定义的变量名,可以基于原本的函数定义

开头加上下划线(_)
中途的点或者.prototype.的部分改写成下划线(_)

有如上命名习惯的插件作者有很多,我也是。

查阅参数吧!
这次试下利用插件参数吧。在插件的注释部分,@author的下面追加下面的注释。

* @param 角色名后显示ID
* @desc 角色名后显示ID (0:OFF 1:ON)
* @default 1

于是插件管理界面就有如下的参数。

插件面板.jpg


如下图能够变更值。

参数.jpg


那么,看下这个设定值在插件端的样子。以下是我常用的书写方式。
(function(_global) {
    // 这里写上插件处理
    var N = 'RTK_Test';
  	var param = PluginManager.parameters(N);
  	var show_id = Number(param['角色名后面显示ID'])||1;

    // 以下略…
})(this);


关于PluginManager.parameters 函数,通过以下的定义理解起来会比较快。可以明白大小写并没有关系(toLowerCase())。


PluginManager.parameters = function(name) {
    return this._parameters[name.toLowerCase()] || {};
};



最初的插件的名字定义变量‘N’纯属是本人的个人习惯。但是插件的名字可能会在代码中多次使用,将来也可能会改变文件名字,推荐开始的时候以某种形式来定义会方便日后使用这个定义。

定义的面板值会进入show_id ,最后的 ||1 是默认值。如果使用者在插件参数里输入了非数字的值,那么通过 Number() 函数会返回 NaN 。这个情况下 ||1 会发挥作用将值替换成 1 。

||1  是 JavaScript方式的省略写法,实际运行结果和下面的if语句处理是差不多一样的。


  var show_id = Number(param['角色名后面显示ID']);
  if (show_id != 0) {
    show_id = 1;
  }



那么利用这个值在插件内部来处理 ON/OFF 吧。


(function(_global) {
    // 这里写上插件处理
    var N = 'RTK_Test';
  	var param = PluginManager.parameters(N);
  	var show_id = Number(param['角色名后面显示ID'])||1;

    var _Game_Actor_name = Game_Actor.prototype.name;
    Game_Actor.prototype.name = function() {
        var ret = _Game_Actor_name.call(this);
        if (show_id) {
          return ret + ":" + this.actorId();
        } else {
          return ret;          
        }
    };
})(this);



嗯,没问题能跑起来。

但是 show_id 的值从外部无法变更话,为了不给游戏多余的负担,个人喜欢如下的写法。


(function(_global) {
    // 这里写上插件处理
    var N = 'RTK_Test';
  	var param = PluginManager.parameters(N);
  	var show_id = Number(param['角色名后面显示ID'])||1;

    if (show_id) {
      var _Game_Actor_name = Game_Actor.prototype.name;
      Game_Actor.prototype.name = function() {
          var ret = _Game_Actor_name.call(this);
          return ret + ":" + this.actorId();
      };
    }
})(this);


感觉只是爱好问题。只要有「show_id 的值从外部不会变更」这个条件,就自问一开始函数的替换本身是需要的吗?

但是,从接下来的章节开始,「show_id的值会从外部被改变」,所以在后面的例子中并没有使用。只是单纯的让人知道还有这种写法,仅供参考。 

通过指令来改变运作吧!
插件参数是用于游戏开发者设定插件用的,所以在实际游戏中无法自由变更。如果要在游戏中变更的话,就需要使用插件指令。

指令的标准如下。
RTK_Test show_id on
RTK_Test show_id off


那么,就让我们试着处理指令部分吧。追加Game_Interpreter.prototype.pluginCommand函数,处理插件独自的指令。
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") {
        show_id = 1;
      } else if (args[1] == "off") {
        show_id = 0;
      }
    }
  }
};


在pluginCommand函数中,最初的指令名是通过command参数传进来。因为很明显这个指令名是配合插件名的,所以这次也就配合事前定义的N来做比较确认。确认给自己的指令后,处理参数args,改变show_id变量的值。

这样子就能够通过事件中的插件指令,来控制开关游戏中ID的显示。

插件指令.jpg


总之完成了
姑且是将功能写出来了,这次就先这样吧!

一个给角色名字追加ID显示的简单插件。可以通过插件参数来设定最初是否显示,然后游戏中通过插件指令来开关显示。

但是有一个缺点就是状态无法保存。这个作为下次的课题来攻克吧!

以下就是这次所写的全部源代码。文件名和注释写好了的话,作为插件公开是没问题了吧。不过,是否真的需要这种插件就是另外个问题了...
//=============================================================================
// RTK_Test.js	2016/07/30
// The MIT License (MIT)
//=============================================================================

/*:
 * @plugindesc 测试用插件
 * @author Toshio Yamashita (yamachan)
 *
 * @param 角色名后面显示ID
 * @desc 角色名后面显示ID (0:OFF 1:ON)
 * @default 1
 *
 * @help 本插件包含插件指令。
 * 由于是测试用的文件,实际使用的时候请适当重命名
 */

(function(_global) {
	// 这里写上插件处理
	var N = 'RTK_Test';
	var param = PluginManager.parameters(N);
	var show_id = Number(param['角色名后面显示ID'])||1;

  var _Game_Actor_name = Game_Actor.prototype.name;
	Game_Actor.prototype.name = function() {
		var ret = _Game_Actor_name.call(this);
		if (show_id) {
			return ret + ":" + this.actorId();
		} else {
			return ret;          
		}
	};

	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") {
					show_id = 1;
				} else if (args[1] == "off") {
					show_id = 0;
				}
			}
		}
	};
})(this);


制作各种插件,尝试乐在其中。
那么,再会!

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

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

收款.png


file_000000008460720687635c008459256b.png