General observations about the inner workings of RPG Maker MV, plus tips and such for writing plugins

About RPG Maker MV in general

Since I've been doing some things other then "the game thing" for some time, I've decided to maybe write about it too. Of course, the game is not forgotten. I just struggle to find motivation to do more writing for it, even though the ideas are there. It will be done when the time is right, so let's proceed to the actual theme of this post: me digging through rpg maker mv sources and figuring things out.

Basically, I was given rpg maker mv to help out on a certain game that shall not be mentioned here, since nobody would understand either way. In particular, I was asked to help out on the "plugin" part. What this means: the whole engine is actually written in javascript, unlike the previous versions which, I believe, were written in something else and had ruby for a scripting language. Now since it's written in JS, the whole engine is basically an open box. You can modify anything, the whole thing. They give you the whole engine to mess around with. Except the editor, which is still a separate program, though the editor simply spits out JSON that the engine interprets (so you can also change how it interprets it too).

Now, the developers of rpg maker did something clever: instead of just giving you the engine and saying "yeah you can mess around with it" they added a system of "plugins". In essense, plugins are javascript files that are loaded after the main game. Since they are loaded afterwards, you can then override any part of the engine in your file without touching the actual engine code. Since javascript is prototype-based, simply changine the prototypes overrides the behaviour of the engine, while also leaving the original code intact if you want to use it later.

That said, unfortunately the creators of the engine abandoned it in favor of their new, slightly modified version of it, called rpg maker mz, a few years ago. And are selling mostly the same engine with somewhat improved editor. So no more updates to MV. Though since the whole engine code is available, that's not a big problem. One can make it whatever they want, it's just another 2D game engine that is somewhat steered towards RPG top-down games, but can do just about anything. Hell, you could make a custom editor for its json data system too, and replace their proprietary one.

About plugins and the state of the community

As mentioned before, plugins are the way to modify how things work without actually touching the engine code. An improtant part of plugins are parameters they can receive, and which can have different types to make the editor show different inputs for them. A good/only/official explanation of all the types is available here.

RPG Maker has official forums, and these do have a specific place for plugin discussion, tutorials and other things for each version of the engine. It seems that most people using the forums, except the few veterans who have been using the engine for years, don't have much idea about how plugins work, or about the inner workings of the engine. Since the editor is so good, a lot of the time they don't have to.

Though it is somewhat painful to see people giving clueless suggestions on the forums, with outdated ways of doing things in JS or simply not understanding how it works. Sometimes people say something is impossible to do, even though you have the whole engine source, so really anything can be done with enough effort.

One thing in particular I have noticed, is that nobody uses modern javascript constructs, though they have been available for a while in NW.js (the thing rpg maker mv uses to run on desktop, it's basically a webkit renderer with node for javascript). For example, evreybody seems to use the pre-es6 way of defining classes, simply because that's how they write it in the engine code, because the engine itself was made in pre-es6 times, I believe. So you see things like this (take from here), though admittedly the post is from 2017, ES6 was already available at the time (ES6 came out in 2015):

  function Window_MyWindow() {
      this.initialize.apply(this, arguments);
  }

  Window_MyWindow.prototype = Object.create(Window_Base.prototype);
  Window_MyWindow.prototype.constructor = Window_MyWindow;

  Window_MyWindow.prototype.initialize = function(x, y, width, height) {
      Window_Base.prototype.initialize.call(this, x, y, width, height);
  };

  Window_MyWindow.prototype.refresh = function() {
      this.drawText("This is a test message", 0, 0, this.contentsWidth(), this.lineHeight());
  };

instead of the easier and equivalent way of doing the same thing in ES6:

  class Window_MyWindow extends Window_Base {
      refresh() {
          this.drawText("This is a test message", 0, 0, this.contentsWidth(), this.lineHeight());
      }
  }

These are basically equivalent. Thankfully the whole engine is written in the same way that classes work in ES6, so everything actually just works with the new syntax.

People also still use var instead of let and wrap their things in function that is called immediately instead of just creating a scope with {}, which would just as well hide all names if you use let instead of var.

Of course, these observations are made only from the forums and some rare GitHub code one can find. Perhaps the people behind big games, knowledgable in javascript, use all these features, but we'll never know that. Still, the bad old code suggestions given on forums are somewhat disappointing, I hardly found anything useful there, and had more success just reading the engine code instead.

The peculiarities of event processing and drawing

Now, event processing is quite interesting in this engine. You might want to know this, if you plan on using the events provided via the editor in a custom scene that does not inherir or use the Scene_Map utilities. Maps are scenes which draw tilesets, have events, and are basically where the gameplay is. But there are other types of scenes too. The other important one is Scene_MenuBase, which has some utilities for implementing menus. So, if you want to have a custom menu and have events in it, or show images in it with the built-in commands, you'll have to work a bit for it, because Scene_MenuBase doesn't process events or draw sprites!

After some digging through the code I have figured out several things about how eventing and sprites work, and that's where some interesting differences from other engines surface. Usually, you either have a global manager that draws things and a global manager that processes events, or the objects themselves draw things and process events. But rpg maker mv seems to use both at the same time: to process events, you have to first create a Game_Interpreter and save it somewhere, then ask a global manager $gameTemp to create a reservation for the common event you want to run, and only then ask the interpreter to actually get the event from $gameTemp. After that, just call its update function every frame to make it process whatever the common event does.

The code looks something like this:

  class Test_MenuBase extends Scene_MenuBase {
      start() {
          // Reserve a common event received as a parameter to the plugin
          $gameTemp.reserveCommonEvent(Number(params["testEvent"]))

          // Create an interpreter
          this._interpreter = new Game_Interpreter()
          // Actually get the reserved event into the interpreter and clear the reservation
          this._interpreter.setupReservedCommonEvent()
      }

      update() {
          // Update the interpreter every frame
          this._interpreter.update()
      }
  }

Now, it will run the events, but that's not all of it.. It won't just draw pictures for you! Pictures also use this strange combined approach to get drawn. So, if you want to draw pictures, you have to add a Spriteset_Base to your scene. Which has a very annoying built-in behaviour of drawing a black background, that you might want to remove. The Spriteset basically takes all requests for drawing images with the built-in draw image command and draws them. You can also draw them manually by adding a Sprite_Picture to your scene with a specified picture number, but Spriteset_Base does this for you. The Map scene has both the spriteset and game interpreter in it, so it will process events and draw pictures, but other scenes don't. So if you add them to your scene, they will be able to do it. Quite obvious. You can also ask another manager to draw a picture instead of doing it from the editor, since all editor commands go through these managers. For drawing pictures, the manager used is $gameScreen. For example, call $gameScreen.showPicture to do the equivalent of what the editor event "draw picture" does.

Here is an example of a scene that can both draw pictures and process events:

  class Spriteset_Menu extends Spriteset_Base {
      createBaseSprite() {
          // Call the base function to create all the sprites first
          super.createBaseSprite()

          // Remove the black screen background. There's really no use for it in menus,
          // since it obscures the background
          this._baseSprite.removeChild(this._blackScreen)
      }
  }

  class Test_MenuBase extends Scene_MenuBase {
      start() {
          // Call the base function to do whatever it does first
          super.start()

          // Create the sprite set and add it to the scene
          let spriteset = new Spriteset_Menu()
          this.addChild(spriteset)

          // Fetch the common event that will possibly draw pictures
          $gameTemp.reserveCommonEvent(Number(params["testEvent"]))

          // Create the interpreter and get the event into it
          this._interpreter = new Game_Interpreter()
          this._interpreter.setupReservedCommonEvent()
      }

      update() {
          var active = this.isActive()

          // If you want for timed things to work, like picture movement, you have to update the $gameTimer manager too
          $gameTimer.update(active)
          // Update the game screen manager every frame, so it draws things
          $gameScreen.update()

          // Updates the interpreter, so it works on the events
          this._interpreter.update()

          // Call to the parent class' update so it does whatever it is supposed to do too
          super.update()
      }
  }

That's about it

That's about all I have managed to learn recently, for the particular task that I may be doing in the near future. I'll probably have to dig some more into other systems later, like the combat system. But I haven't done that yet. I imagine all the peculiarities apply to other systems as well, if they were designed by the same people.

I've hardly been doing anything recently, so I thought I might as well write out my thoughts on whatever it is I have figured out with RPG Maker MV, and hopefully this may help someone in the future.

Thank you for reading my blog post :)

Comments