JUMP INTO JAVASCRIPT PART 1

The start of my teardown series for RPG Maker MV.

  • Trihan
  • 10/24/2015 12:58 AM
  • 33506 views
You knew it was coming. Let's get on with it.

[Insert cool title logo I haven't bothered to make yet here]

So as some of you will be familiar with by now, I've made somewhat of a name for myself by tearing down the default Ruby scripts for RPG Maker VX Ace in a series called Slip into Ruby. Now I'm doing the same thing for MV, but this one's going to be called Jump into Javascript because I love alliteration and I suck at naming things. Deal with it. I'm also submitting these as tutorials, which I should have been doing since the beginning.

This is going to be a bit different, I hope. I became uncomfortably aware, especially in later parts, that SiR is somewhat...wordy. And that's the nature of the beast, unfortunately, because what I'm trawling through is essentially a bunch of words. However! I learned a lot from doing that and I hope the new-ish format I've got in mind will work better. Basically, I'm just going to go ahead and steam through the default .js files, and as we cover new concepts I'll put up a little sidebar on that concept. So without further ado, let's get started!

Before I begin, let me start out by explaining that the back-backend of RPG Maker MV uses a 2D webGL renderer called Pixi.js, and a lot of base objects use Pixi objects as their prototypes (more on that later).

main.js

This is the functional equivalent of the Main script in VX Ace, and is pretty short.

PluginManager.setup($plugins);


Here we're calling the setup function of the PluginManager object, and passing in a global variable called $plugins, the contents of which is determined by the contents of the plugin manager.

[NEW CONCEPT: METHOD/FUNCTION CALLS]
Calling a method (or function) in Javascript is pretty much identical to Ruby: you put a dot, followed by the function's name, with a set of brackets which can contain parameters (values to pass in to the function).


[NEW CONCEPT: GLOBAL VARIABLES]
A global variable in Javascript begins with a $.


window.onload = function() {
    SceneManager.run(Scene_Boot);
};


window.onload is a built-in event in Javascript which will fire when the window for the application is first loaded. It calls the run method of the SceneManager object, passing in Scene_Boot, which is the scene that initialises the game.

[NEW CONCEPT: FUNCTIONS]
Where in Ruby you would have written "def methodname" inside a "class Classname" declaration, in Javascript you just type the objectname.function_name, and then use the = function() { } syntax as above, placing the code the function should run within the braces.

Note that functions require a set of brackets, even if they're not passing in any parameters, are contained within a set of curly braces {}, and end with a semicolon, as do all statements in Javascript.


rpg_scenes.js

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


This declares the initial function for Scene_Base, and simply calls its initialize method. As we'll see, initialize doesn't actually take any parameters itself, so instead we call apply, passing in "this" and "arguments".

[NEW CONCEPT: APPLY]
The apply method calls a function with a given "this" value and an array of arguments.


[NEW CONCEPT: THIS]
The "this" keyword in Javascript is similar to that of Ruby, and returns a reference to the object which called the code containing it.


Scene_Base.prototype = Object.create(Stage.prototype);
Scene_Base.prototype.constructor = Scene_Base;


This code is setting Scene_Base's prototype to a newly created object, passing in Stage.prototype as the prototype object. Then Scene_Base's prototype's constructor is set to Scene_Base.

[NEW CONCEPT: PROTOTYPE]
Javascript doesn't have inheritance in the classical sense of something like "class Window_Child < Window" resulting in the newly-defined class gaining all of the properties and methods of Window. Instead, what Javascript has is the concept of a prototype object, which is used to sort-of-but-not-quite tell another object that it should be getting its properties from the prototype.


[NEW CONCEPT: CHANGING PROTOTYPE CONSTRUCTOR]
By default, when you create a new object with a prototype its constructor will be implicitly taken as the constructor of that prototype; if you want to create any new behaviour for the "child" object, you need to explicitly set its constructor to the name of that object instead.


Scene_Base.prototype.initialize = function() {
    Stage.prototype.initialize.call(this);
    this._active = false;
    this._fadeSign = 0;
    this._fadeDuration = 0;
    this._fadeSprite = null;
};


Here we're declaring the initialize function of Scene_Base's prototype. First, we call the initialize function of Stage.prototype, passing in "this". We're setting a number of instance variables: _active is set to false, _fadeSign is set to 0, _fadeDuration is set to 0, and _fadeSprite is set to null.

[NEW CONCEPT: INSTANCE VARIABLES]
Javascript has instance variables similar to those in Ruby. Where we used an @ symbol to denote instance variables in Ruby, we use an underscore _ in Javascript. Note that unlike Ruby, where the @ is a syntax requirement, using an underscore here is merely a convention and isn't required.


Scene_Base.prototype.create = function() {
};


Here we're declaring the create function for Scene_Base's prototype, which has no code in it.

Scene_Base.prototype.isActive = function() {
    return this._active;
};


Here we're declaring an isActive function for Scene_Base's prototype, which returns the value of _active for the current instance of the containing Scene_Base object.

[NEW CONCEPT: RETURN]
Return works more or less the same way as it did in Ruby, and provides a final value which can be stored in a variable when a function is called. For example, if _active is false, "var myVar = Scene_Base.prototype.isActive()" would result in myVar being false.


Scene_Base.prototype.isReady = function() {
    return ImageManager.isReady();
};


The isReady function of Scene_Base's prototype returns the value of the isReady function from ImageManager.

Scene_Base.prototype.start = function() {
    this._active = true;
};


The start function of Scene_Base's prototype sets the instance's _active variable to true.

Scene_Base.prototype.update = function() {
    this.updateFade();
    this.updateChildren();
    AudioManager.checkErrors();
};


The update function of Scene_Base's prototype calls the instance's updateFade function, then its updateChildren function, and finally the checkErrors function of the AudioManager object.

Scene_Base.prototype.stop = function() {
    this._active = false;
};


The stop function of Scene_Base's prototype sets the instance's _active variable to false.

Scene_Base.prototype.isBusy = function() {
    return this._fadeDuration > 0;
};


The isBusy function of Scene_Base's prototype returns true if the instance's _fadeDuration is greater than 0, or false otherwise.

Scene_Base.prototype.terminate = function() {
};


The terminate function of Scene_Base's prototype has no code.

Scene_Base.prototype.createWindowLayer = function() {
    var width = Graphics.boxWidth;
    var height = Graphics.boxHeight;
    var x = (Graphics.width - width) / 2;
    var y = (Graphics.height - height) / 2;
    this._windowLayer = new WindowLayer();
    this._windowLayer.move(x, y, width, height);
    this.addChild(this._windowLayer);
};


The createWindowLayer function of Scene_Base's prototype declares a variable called width, set to the return value of the Graphics.boxWidth function; a variable called height, set to Graphics.boxHeight; a variable called x, set to half of (Graphics.width - width); and a variable called y, set to half of (Graphics.height - height). The scene instance's windowLayer property is set to a new instance of the WindowLayer object, and then the property calls its move function, passing in the coordinates and sizes. Finally, we call the addChild function of the scene, passing in its windowLayer property.

[NEW CONCEPT: VAR]
var is simply Javascript's way of declaring variables. You don't have to explicitly declare variable types like you do in some other languages.


Scene_Base.prototype.addWindow = function(window) {
    this._windowLayer.addChild(window);
};


The addWindow function of Scene_Base's prototype takes a single parameter, window, and calls the addChild function on the instance's _windowLayer, passing in the window parameter.

[NEW CONCEPT: PARAMETERS]
Within the brackets of a function declaration, you can specify parameters to pass in to the function. This allows you to provide the function with extra information it can use in its code; for example, if I declare a function called myFunction which takes a parameter called myValue, I can refer to myValue in the function's code and its initial value will be whatever it was when I called the function.


Scene_Base.prototype.startFadeIn = function(duration, white) {
    this.createFadeSprite(white);
    this._fadeSign = 1;
    this._fadeDuration = duration || 30;
    this._fadeSprite.opacity = 255;
};


The startFadeIn function of Scene_Base's prototype takes two parameters, duration and white; first we call the createFadeSprite function of the instance, passing in white; _fadeSign is set to 1; _fadeDuration is set to duration if it contains a value, or 30 if it doesn't; and finally the _fadeSprite object's opacity property is set to 255 (fully visible).

NEW CONCEPT: OR ASSIGNMENT]
When initialising the value of a variable based on a parameter, you'll usually do something along the lines of "var myValue = parameter;". However, that parameter may not always necessarily be set, in which case it will be null, and if we have default values to be used in the event that no value was passed we can use logical OR assignment. The basic syntax is "[variable] = [parameter/othervariable] || [defaultvalue];. This will set the variable to its default value if the first assignment couldn't be done for some reason.


Scene_Base.prototype.startFadeOut = function(duration, white) {
    this.createFadeSprite(white);
    this._fadeSign = -1;
    this._fadeDuration = duration || 30;
    this._fadeSprite.opacity = 0;
};


The startFadeOut function of Scene_Base's prototype takes two parameters, duration and white; first we call the createFadeSprite function of the instance, passing in white; _fadeSign is set to -1; _fadeDuration is set to duration if it contains a value, or 30 if it doesn't; and finally the _fadeSprite object's opacity property is set to 0 (fully transparent).

Scene_Base.prototype.createFadeSprite = function(white) {
    if (!this._fadeSprite) {
        this._fadeSprite = new ScreenSprite();
        this.addChild(this._fadeSprite);
    }
    if (white) {
        this._fadeSprite.setWhite();
    } else {
        this._fadeSprite.setBlack();
    }
};


The createFadeSprite of Scene_Base's prototype takes one parameter, white; first we check whether the current scene instance does not have a _fadeSprite defined. If it doesn't, we set its _fadeSprite to a new instance of ScreenSprite, and add the fadeSprite as a child of the scene.

If white contains a value, the instance's _fadeSprite calls its setWhite function, otherwise it calls setBlack.

Scene_Base.prototype.updateFade = function() {
    if (this._fadeDuration > 0) {
        var d = this._fadeDuration;
        if (this._fadeSign > 0) {
            this._fadeSprite.opacity -= this._fadeSprite.opacity / d;
        } else {
            this._fadeSprite.opacity += (255 - this._fadeSprite.opacity) / d;
        }
        this._fadeDuration--;
    }
};


The updateFade function of Scene_Base's prototype first checks whether the instance's _fadeDuration is greater than 0; if it is, a variable called d is set to the instance's _fadeDuration. If the _fadeSign is greater than 0 (meaning a fadein has started) the _fadeSprite's opacity is reduced by its current opacity divided by d; if the _fadeSign isn't greater than 0 (meaning a fadeout has started) the _fadeSprite's opacity is increased by (255 minus its current opacity) divided by d. Finally, the _fadeDuration is decremented.

[NEW CONCEPT: INCREMENT/DECREMENT OPERATORS]
In Javascript, just like in Ruby, there's a shorthand way to write var = var + 1 or var = var - 1, and that's to simply write var++ or var--.


Scene_Base.prototype.updateChildren = function() {
    this.children.forEach(function(child) {
        if (child.update) {
            child.update();
        }
    });
};


The updateChildren method of Scene_Base's prototype iterates through its children and for each child runs a new function where if the child has an update function, it calls it.

[NEW CONCEPT: FOREACH]
Calling forEach on a collection of items, such as an array, will iterate through them and run a defined function where the parameter contains the item being iterated.


Scene_Base.prototype.popScene = function() {
    SceneManager.pop();
};


The popScene function of Scene_Base's prototype calls the pop function of SceneManager.

Scene_Base.prototype.checkGameover = function() {
    if ($gameParty.isAllDead()) {
        SceneManager.goto(Scene_Gameover);
    }
};


The checkGameover function of Scene_Base's prototype checks to see if the isAllDead function of the global variable $gameParty returns true; if it does, we call SceneManager's goto function passing in Scene_Gameover.

Scene_Base.prototype.fadeOutAll = function() {
    var time = this.slowFadeSpeed() / 60;
    AudioManager.fadeOutBgm(time);
    AudioManager.fadeOutBgs(time);
    AudioManager.fadeOutMe(time);
    this.startFadeOut(this.slowFadeSpeed());
};


The fadeOutAll function of Scene_Base's prototype sets a variable called time to the return value of calling slowFadeSpeed() divided by 60; AudioManager calls its fadeOutBgm, fadeOutBgs and fadeOutMe functions, passing in the time variable; and finally, the instance calls its startFadeOut function, passing in its slowFadeSpeed.

Scene_Base.prototype.fadeSpeed = function() {
    return 24;
};


The fadeSpeed function of Scene_Base's prototype is hardcoded and simply returns 24, which is the number of frames a fade takes.

Scene_Base.prototype.slowFadeSpeed = function() {
    return this.fadeSpeed() * 2;
};


The slowFadeSpeed function of Scene_Base's prototype is hardcoded and returns the fadeSpeed (24) multiplied by 2 (48).

--------------------

I think I'll leave part 1 there, gonna keep these ones relatively short; especially the first one, as I want to gauge how people like the "new" format and see if anyone has any requests or suggestions. Until next time!

Posts

Pages: 1
I'll be reading this later when my head is a bit better, but neato!
Frogge
"nothing can beat the power of gay"?
11606
Thx for dis Trihan-kun <3
Yellow Magic
I'll never regain the bones I lost from my loneliness and sorrow
2993
Good tutorial - thanks for this! I've had an on-off interest in JS for years, and the language itself is booming thanks to the stupid amount of popular frameworks going around right now, so the fact that RMMV utilises it too makes me a very happy bunny.

I guess one thing you might want to clarify is the difference between Javascript functions and methods. Also, it'd be cool if you could elaborate a bit more on prototypes, as it's a pretty important topic as far as Javascript goes (EDIT: Just had another look at the prototype section, and you might also want to define 'property').

Looking forward to your explanation of how the plugins themselves are anonymous function expressions...8)
kentona
One of RMN's Top 10 Admins of all-time
20443

Here's that title image you wanted. Add text as needed.
(it's pretty sloppy)
This helped me understand quite a few things I've been trying to figure out for a while now. Thank you!
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3029
No worries! I'm aware that I kind of dropped off the face of the earth again (been mentally busy with work and other stuff) but more is coming.
Notoh
*Deeply Thinking*
1908
Ah, java... how you give me bad headaches.

Good tutorial! I enjoyed reading.
Pages: 1