Adding Pixelate Filter to RPG Maker MV 1.3.4

Pixels are cool if you’re a nerd.

I was using a plugin that created a pixelated effect on demand. But after updates to RPG Maker MV (and the Pixi library it’s based on), the plugin broke. When the original plugin writer updated the plugin, they removed the pixelate functionality altogether.

So I went on a search to bring back my crunchy pixels.

A good alternative

I discovered this wonderful library of filters for Pixi v4.

Once again, the pixelate functionality was broken. But Github user jeff-gold-marblemedia posted a quick fix.

Bringing that fix into the main code, and then pairing down the features I didn’t need, I ended up with a handy plugin.

The code

/*:
* @plugindesc v1.0.6 Pixelate filter for Pixi v4.
* @author pixi-filters https://github.com/pixijs/pixi-filters
* @help
* pixi-filters https://github.com/pixijs/pixi-filters
* Developers can make use of the filter like so:
* var filter = new PIXI.filters.PixelateFilter();
* filter.pixelSize = 50;
*/

/*!
* pixi-filters - v1.0.6
* Compiled Wed Aug 31 2016 08:40:25 GMT-0400 (EDT)
*
* pixi-filters is licensed under the MIT License.
* http://www.opensource.org/licenses/mit-license
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.filters = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({8:[function(require,module,exports){ // @see https://github.com/substack/brfs/issues/25 /** * This filter applies a pixelate effect making display objects appear 'blocky'. * * @class * @extends PIXI.AbstractFilter * @memberof PIXI.filters */ function PixelateFilter() { PIXI.Filter.call(this, // vertex shader "#define GLSLIFY 1
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat3 projectionMatrix;

varying vec2 vTextureCoord;

void main(void)
{
 gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
 vTextureCoord = aTextureCoord;
}", // fragment shader "#define GLSLIFY 1
varying vec2 vTextureCoord;

uniform vec4 filterArea;
uniform float pixelSize;
uniform sampler2D uSampler;

vec2 mapCoord( vec2 coord )
{
 coord *= filterArea.xy;
 coord += filterArea.zw;

 return coord;
}

vec2 unmapCoord( vec2 coord )
{
 coord -= filterArea.zw;
 coord /= filterArea.xy;

 return coord;
}

vec2 pixelate(vec2 coord, vec2 size)
{
 return floor( coord / size ) * size;
}

vec2 getMod(vec2 coord, vec2 size)
{
 return mod( coord , size) / size;
}

float character(float n, vec2 p)
{
 p = floor(p*vec2(4.0, -4.0) + 2.5);
 if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y)
 {
 if (int(mod(n/exp2(p.x + 5.0*p.y), 2.0)) == 1) return 1.0;
 }
 return 0.0;
}

void main()
{
 vec2 coord = mapCoord(vTextureCoord);

 // get the rounded color..
 vec2 pixCoord = pixelate(coord, vec2(pixelSize));
 pixCoord = unmapCoord(pixCoord);

 vec4 color = texture2D(uSampler, pixCoord);

 // determine the character to use
 float gray = (color.r + color.g + color.b) / 3.0;

 float n = 65536.0; // .
 if (gray > 0.2) n = 65600.0; // :
 if (gray > 0.3) n = 332772.0; // *
 if (gray > 0.4) n = 15255086.0; // o
 if (gray > 0.5) n = 23385164.0; // &
 if (gray > 0.6) n = 15252014.0; // 8
 if (gray > 0.7) n = 13199452.0; // @
 if (gray > 0.8) n = 11512810.0; // #

 // get the mod..
 vec2 modd = getMod(coord, vec2(pixelSize));

 gl_FragColor = color;

}"
);
  this.pixelSize = 4;
}

PixelateFilter.prototype = Object.create(PIXI.Filter.prototype);
PixelateFilter.prototype.constructor = PixelateFilter;
module.exports = PixelateFilter;

Object.defineProperties(PixelateFilter.prototype, {
  /**
   * This a point that describes the size of the blocks.
   * x is the width of the block and y is the height.
   *
   * @member {PIXI.Point}
   * @memberof PIXI.filters.PixelateFilter#
   */
  pixelSize: {
    get: function () {
      return this.uniforms.pixelSize;
    },
    set: function (value) {
      this.uniforms.pixelSize = value;
    }
  }
});

},{}],16:[function(require,module,exports){
// Require built filters
var filters = {
  PixelateFilter: require('./pixelate/PixelateFilter')
};

// Assign to filters
Object.assign(PIXI.filters, filters);

// Export for requiring
if (typeof module !== 'undefined' & amp; & amp; module.exports) {
  module.exports = filters;
}
},{"./pixelate/PixelateFilter":8}]},{},[16])(16)
});

Using the filter… for nerds

Chances are there’s a bit of vestigial nonsense near the end. My lack of familiarity with the way Pixi/Node works mean that I can’t optimize it much. But it’s working in all my use cases.

Toss this into a Scene_Map method to apply it. Putting it to good use is up to you.

this.pixelateFilter = new PIXI.filters.PixelateFilter();
this.pixelateFilter.pixelSize = 20;
this.children[0].filters = [this.pixelateFilter];

Changes to dual-wielding

Dual-wielding is the concept of holding a weapon in each hand instead of a weapon and a shield, or a two-handed weapon. It’s a fun way to give your characters more options for customization. The problem with the way RPG Maker MV handles dual wielding is that there’s no visual feedback from it. Your attack animation still only hits one time, no matter how many weapons you hold.

Plugin woes

I’ve been using a plugin to fix this issue. But it’s been giving me grief, randomly changing my character’s stats between each weapon swing and leaving them permanently altered. The plugin’s support was shaky anyway, so rather than spend time troubleshooting, I looked for an alternative.

A practical solution

RPG Maker forums user Arkzein shared a code snippet using Yanfly’s action sequences to simulate a proper dual-wielding effect.

It does what it should quite elegantly, allowing the user to swing each weapon individually and letting those hits have different strengths depending on the weapon’s strength. I added a few extra features to it, including

  • a check to see if the weapon fires long-distance, or if the player should approach their target before the attack begins.
  • a check to see if the user is carrying two weapons (so it acts like a regular one-hit attack for users with one weapon).
  • a check to see if the 2nd weapon has a different animation than the first (otherwise it just repeats the first animation).

Sample code

Here’s the snippet in full:


// M.o.v.e. forward a little i.f. firing a missile
if user.attackMotion() == 'missile'
  move user: forward, 48, 10
  wait for move
else
// M.o.v.e. to the target
  move user: target, front base, 10
  wait for move
end

// I.f. the user has multiple weapons + dual-wield
if user.weapons().length > 1 && user.isStateAffected(129)
 EVAL: user._weap1 = user.weapons()[0];
 EVAL: user._weap2 = user.weapons()[1];
 // Unequips second weapon
 EVAL: user.forceChangeEquip(1, null);

 // 1st weapon attack
 MOTION ATTACK: user
 MOTION WAIT: user
 action animation
 wait for animation
 action effect

 EVAL: user.forceChangeEquip(0, user._weap2);

 // 2nd weapon attack
 MOTION ATTACK: user
 MOTION WAIT: user
 // animation of 2nd weapon
 EVAL: target.startAnimation(user.weapons()[0].animationId);
 wait for animation
 action effect

 // Restores weapons
 EVAL: user.forceChangeEquip(0,  user._weap1);
 EVAL: user.forceChangeEquip(1, user._weap2);

else
 // Regular 1 weapon attack
 MOTION ATTACK: user
 MOTION WAIT: user
 action animation
 wait for animation
 action effect
end

The weird dots in the comments are because action sequences don’t have the concept of comments. So if I didn’t put dots in between the letters of “i.f.” it would be interpreted as a programmatic if statement.

Merging updated scripts

The developers supporting RPG Maker MV (the engine behind FCA) released a fairly significant bug into the code with version 1.3.2. It relates to the way skills are learned, and since that’s a significant factor in the gameplay of FCA, I decided to refrain from updating my codebase.

Updating to 1.3.4

They just recently fixed the bug. So I went through and updated all of the core scripts (from 1.3.1 to 1.3.4) and all of Yanfly’s scripts.

To my surprise, FCA still boots up properly and I’m even seeing a huge improvement in how battles perform (better frame rate). But I’m seeing more stuttering on the regular map screen.

Time to run the rest of my tests and see what’s broken!

Skills targeting friend or foe

A lot of skills in this game fall into both of these categories:

  1. Can target friend or foe
  2. Hit multiple targets at once

… which is more complicated that it seems. By default the engine lacks support for the player changing targets between friends and foes.

Attempt one

At first I used Yami’s Invert Target. It’s a buggy mess. A community-created compatibility patch fixed it for a while, but eventually it started to give me errors again.

Yanfly to the rescue

I recently switched to Yanfly’s Selection Control. It’s a solid plugin, compatible with Yanfly’s other works. But it still doesn’t support both of the conditions I mentioned. You can either have a skill toggle between friend and foe, OR have it hit multiple targets.

I compromised by writing this block of code for each of my multi-target skills:

for (var i = 0; i < target.friendsUnit().aliveMembers().length; ++i) {
 var member = target.friendsUnit().aliveMembers()[i];
 targets.push(member);
}

After the player selects a target, all of the target’s allies are added into the scope of the attack.

It’s still not the most elegant solution – the selection cursor only appears over one target at a time – but it’s stable and it works.