Jump to content
Sign in to follow this  
kyonides

Understanding RGSS

Recommended Posts

This thread will be one of those basic tutorials that will start by telling you the basics of crafting scripts on your own. In the worst case scenario, you will be able to make minor edits without much help from actual scripters.

Basic Concepts

object - pretty much anything in Ruby and RGSS, including numbers to some degree. It is the basic building lego block.

value - almost identical to object but it refers to what is actually returned by some script call, a method, etc. It is or might be contained by a variable.

variable - x or y or z are common examples of variables used frequently in Ruby or RGSS scripts. They are the containers of any specific value / object.

They can easily be treated as methods depending on how you have defined your custom class or module.

method - sometimes also known as attribute, a basic component of a class or module. They are attached to its class or module.

1.to_s calls the convert to string method, turning it into "1" at once. Now you can use it on any window!

class - a Class that can be initialized or constructed via the new method.

module - a Module that is used as a container. It is not supposed to be used as a class for you cannot make any copies of it. It will always keep all of its internal variables intact till you close your game. Not even loading a different saved game will alter it by default.

NOTE: Module itself is a class but one that cannot be initialized nor constructed by using new. All attempts to do such things will certainly fail.

When Should You Use a Class or a Module?

Pick a class if you need to make copies of tha object like Actors or Enemies or Items.

Use or call a module if you want to use system wide functions that might alter your game somehow. The best example would be the Graphics module.
Call Graphics.resize_screen(W, H) on RGSS3 (ACE) and you will be able to increase or decrease the window size a little bit.

NOTE: $game_temp or Game_Temp class could have been easily be defined as a Game_Temp module for all of its methods and internal values are temporary by nature and you don't need multiple copies of it.

When NOT to Use a Class

You don't need to create a new class if an Array or Hash object can easily help you with storing its values.

Module's Special Feature

It is definitely true that you cannot initialize a Module, meaning that you will ever get a copy of it. Yet, you can easily mix it into a Class.
From that moment all most of its variables and methods become the new methods of that specific Class.
Let us take a look at the following example of how you are supposed to define a module and also a brand new class.

module Test
  def running?
    true
  end
end

class Maker
  include Test
end

Did you see that include Test statement over there?
It means that Test has become an integral part of Maker class. Now you can call it like this:

klass = Maker.new
klass.running?

And it will return true as it current value.

What is true?

Anything that has been confirmed or exists, a truthy value.
Am I getting philosophical here?
Well, kind of. true represents what is really there, what can be found as many times as needed.

What happens if it is a lie or is incorrect or does not exist at all?

Then it would be false. Obviously false is the opposite of true just as much as a lie is the opposite of the truth.

A Caveat Has Been Found!

Ruby and RGSS have a weird but not so unusual feature in programming languages, namely the nil value.

Basically, nil is its official non existing value, even if its a value on its own right. XD
Curiously, it also works as a substitute for a false value in many cases.

Whenever an object does not exist because it has never been called or defined as a variable or class or module, it is certainly equal to nil.

Now comes the most intriguing feature that has been bugging you ever since you began reading this tutorial...

What the hell is that stupid dot . doing there?

Nope, it is not just a punctuation mark. It is a connection! It connects the method to its own class or method or variable or even to its number value.

OK... What does def stand for?

Short answer: definition.
It allows you to define a class or module's method.

And before you even ask it, know that the term end means, well, end! XD

It marks the end of the century!

OK, nope. It is the end of a method or class or module definition. That's all, folks!

Edited by kyonides

Share this post


Link to post
Share on other sites

The Lack of Interest into Fully Understanding a Programming Language

Many but many scripters around even to this day tend to misuse a feature of Ruby just because they found something similar in the default scripts. This has been true ever since RMXP came out as the first engine with some scripting capabilities and still returns every so often in VX Ace.

Defining a Class or Module

As I told you before, there is a simple way to create a class.

module MyModule
end

class MyClass
  include MyModule
end

And you can even include modules, not just one but many in a row! Just separate them by using commas.

So what's wrong with it?

Nothing. It has been correctly executed there. What I would like to criticize now is the lack of knowledge those scripters have shown over the years. It all begins with stuff like this.

class MyCommandWindow < Window_Selectable
end

Here many people fall in a trap that won't normally cause any issues except when they do it once again!
Yes, they repeat the same declaration over and over again every single time they define what we call the Parent Class or Super Class.

What it means is that MyCommandWindow is the Child Class of Window_Selectable, making it a window with menu features preincluded by default, even if they had been created in Window_Selectable only. And guess what? Window_Selectable class is a Child Class of Window_Base and the latter is the Child Class of Window!

As you can see the list can go way beyond your imagination, really it does.

But why do they keep repeating the same mistake over and over again?

That's because they lack some insight of Ruby inner workings like the C side of Ruby code. Yeah, Ruby is run on C functions. Every single Ruby Object is a special C struct.

Spoiler

 

What is a C struct?

Well, it's like a precursor of a C++ or Ruby class, initially only found in C but it spread to C++ as well. I can't talk about non C related languages so...

It pretty much has the same features as Ruby variables for you can easily access copies of your defined struct and its values in a similar fashion than Ruby, yes, with dots included. The difference is that they can also become pointers with its own connector instead of Values a la Ruby. Pointers are just memory references or addresses to a very specific point in, well, memory or RAM, that they have reserved for its own use. It is a small load of data to be passed to another struct pointer or any C function as an argument aka parameter.

 

The way I'll explain this issue is the following: declaring the class or module serves 2 main purposes.

  1. Define the class or module and its parent class or module (as in a module nested inside another module). Do this once if they do have a parent class.
  2. Reopen the class or module. That's it!

Ruby isn't like Java. The latter made it overly complicated to extend certain basic features like printing stuff on a shell or console.

Ruby just has OPEN CLASSES!

That's a nice feature that lets us add as much stuff as deemed necessary and alias (keep a reference to a preexisting method) a given method at will.

Downside: It might allow many people to rely on monkeypatching default classes as a way not to create child classes or delegate them to custom classes.

In CRuby you would use a function called rb_define_class("MyClass", rb_cObject) instead. Modules don't need a second parameter: rb_define_module("MyModule").

Internally it's used to determine if a class exists. Otherwise, it'll look for functions like rb_obj_alloc and stuff like rb_class_new and even an initializer., which can be your very own custom function or call your Ruby initialize method. A Ruby side initialize definition will certainly overwrite a C side one.

So whenever you repeat the whole statement, you keep telling the Ruby system that it should now check the parent class once again!

Why on earth!? It kept it in memory and won't forget it as long as the program is running. Thus, it makes no sense to insist on declaring that there.

And what comes next is especially directed at newcomers and slightly experienced scripters: stop doing it or you might end up making a mistake like declaring the wrong parent class!

This issue is similar to another one that keeps showing up in newcomers' codes, the excess of checks by calling too many if or unless or case statements to get to a single point where they simply need to change a variable's value, especially common whenever they need to toggle switches. But this topic might be something that should be handled in a separate post...

Edited by kyonides

Share this post


Link to post
Share on other sites

The Other Bad Practices Found in RGSS Scripts

We all should know by now how to declare an if or unless conditional statement. Happy with a sweat Yet, there are people out there that do weird stuff while doing so. In some cases the culprit was the RMXP's default code. For those guys that entered the scene after RMVX or RMVX Ace came out, I have no excuse. Confused

if variable == true
  print "It's true!"
end
if variable == false
  print "It isn't true..."
end

This is one of the most extreme cases seen so far.

For some reason, the guy checks the same variable twice, once for a truthy value and another for its opposite value. It is terrible! Angry

Just like in many other languages, there are other ways to check the same value appropiately, without repeating oneself.

if variable == true
  print "It's true!"
elsif variable == false
  print "It isn't true..."
end

There we can see how to define a second condition using elsif instead. It certainly means "else if" there.

If we were checking for totally different values, that solution would be fine. Even so, we are only checking what is its actual boolean value and under such circumstance, there is an easier way to deal with it.

if variable == true
  print "It's true!"
else
  print "It isn't true..."
end

Yes guys! That is all you needed to do from the very beginning! Laughing
Shocked But wait! There is more you need to learn here about those statements!

if is_variable
  print "It's true!"
else
  print "It isn't true..."
end

Grinning It is quite interesting how the code still works, don't you think? Thinking
But why does it work here? Who Knows?
There is an easy explanation. In Ruby, unlike C or C++, all objects return a truthy value if they are not equal to nil of the NilClass or false for obvious reasons. In C it would be quite common to see something like the following code:

if (result) {
  return "You were successful!";
} else {
  return "You failed!";
}

The negative way to declare that kind of statement would be:

if (!state) {
  return "LoadError: Failed to load script.";
}

A ! bang, yes, a ! bang there means negative value or opposite value or simply false.

And guess what? That does exist in Ruby as well! 😮

if !is_done
  print "In the works!"
else
  print "Done!"
end

Of course, newcomers might need to include the equality operator and the true value like this == true but in the professional world that is not used at all. The only reason why you would ever, if ever, use it would be to test if an object is equal to true or false or nil in a threefold check. And this is extremely uncommon, guys! Happy with a sweat

Toggling Switches

So how can we apply that knowledge to changing the boolean value of a Game Switch? Thinking

Just use the following code:

$game_switches[1] = !$game_switches[1]

A Curious But Nonsensical Idea

Some time ago somebody wrote me a message telling me how dangerous it would be to do the following while using modules and classes.

module Some
  ONEISTHERE = "Someone is there!"
  class Thing
    include Some
  end
end

OK, I got to say that it is idiotic per se, still, this person wanted me to warn you about it. The reasoning behind it was that it would create a terrible loop that should be avoided at all costs. Interesting conclusion. Thinking

[Mad Scientist Mode ON]

Let us test this theory, guys!

module_class_module_summary.jpg.f6d68d93a5bf2051204ece6bd406bbef.jpg

Nope, as we can see in the picture above, that is CERTAINLY NOT the case there!

What has happened was that we simply created a "new link" to the Constants already declared in the module above.

The only thing we have achieved was to come up with a redundant way to get to the same old Constant. XD

It was totally unnecessary for sure. =_=¡

[Mad Scientist Mode OFF]

Edited by kyonides

Share this post


Link to post
Share on other sites

When to Use the Module Class' include Feature

I have seen many times that people kind of abuse of the :: scope operator when they are fiddling with modules. That inspired me to revisit the modules to make sure they stop repeating themselves like crazy.

Basic Setup Modules

module SomeUser
  module Config
    DEFAULT_FONT_SIZE = 20
    DEFAULT_TITLE = "Some Label"
  end
end

So far there is nothing new to see here but everything changes Shocked once we start dealing with a specific Window class. Detective Let us take a look at some fake Window_TitleCommand Add-on.

class Window_TitleCommand
  alias :some_user_config_win_ttl_comm_draw_item :draw_item
  def default_font_size
    SomeUser::Config::DEFAULT_FONT_SIZE
  end

  def draw_item(index)
    some_user_config_win_ttl_comm_draw_item(index)
    contents.font.size = self.default_font_size
    text = SomeUser::Config::DEFAULT_TITLE
    contents.draw_text(4, 26, contents_width, 24, text, 1)
  end
end

As you can see above, we had to use the :: scope operator several times in our scriptlet. Happy with a sweat Honestly, I got to tell you that it is not elegant at all. Confused The more Constants you include the worse it will look like.

Let's make some modifications to the original code.

class Window_TitleCommand
  include SomeUser::Config
  alias :some_user_config_win_ttl_comm_draw_item :draw_item
  def draw_item(index)
    some_user_config_win_ttl_comm_draw_item(index)
    contents.font.size = DEFAULT_FONT_SIZE
    contents.draw_text(4, 26, contents_width, 24, DEFAULT_TITLE, 1)
  end
end

Wunderbar! Shocked Wonderful!

Now it looks quite neat! And we can even skip the creation of needless methods only to call the same long line of code we could find inside the default_font_size over and over again.

There is a caveat, though. You should NOT do that if you are working on a class that inherits the methods you have altered there. This is especially true if at least one of its child classes does not need such modifications at all. I seriously Serious recommend you to only include the modules in Baby child classes and nowhere else unless you know what you are doing.

Share this post


Link to post
Share on other sites
Posted (edited)

Assignment != Equality && Assignment != Identity && Equality != Identity

Let us review what an assignment actually is. It simply means that you pick either a Constant or a variable and set a value to that object and not any other around it. (Well, you could still do it but you got to have a valid reason for it or else do not even try to do that!)

The assignment operator is an = equal sign.

variable = actual_value

That means that whenever you use that variable or Constant, it will always return the last value you have assigned to that particular object. You should not alter a Constant's value, though. :S

Equality stands for something that has the save value as another object, be it a Constant or a variable.

Use two == equal signs to make the comparison between two objects that might be different or not.

CONSTANT = 1
@variable = 1
CONSTANT == @variable #=> Returns true

The === identity operator!

And finally, we got the identity operator === to help us define if an object has been placed on both sides of a given comparison.

Here you got to be careful because it is excesively strict when looking for any identical value or object.

range = 1..8
range === 5 #=> Returns true
range === range #=> Returns... false!
range == range #=> Returns... true!
Object.new === Object.new #=> Returns false, they are two different Objects with their own IDs.

Whenever you are using the case when statement to double check if a give variable is equal to another, it will use the === identity operator by default.

As we could see in the first example of the identity checks, the first test returned a true-ish value because it used the === operator with EVERY SINGLE VALUE of that Range object until it hit number 5.

Honestly, you should use the == equality operator for if and unless statements mainly because there is no specific reason to do otherwise.

When using the case when statement, make sure you will be comparing the same kind of objects like numbers or classes. It will fail if you made a mistake and tested the value of a variable, like number 5, against a class like Integer, even if number 5 is an Integer object on its own right.

Other Operators

By the way, the != operator simply means Not Equal.

The !=== Not Identical operator does exist as well, but it would be quite weird for you to find a good use for it anywhere.

The && double et symbol you could find on the title of this very same post simply means and.

A Terrible But Good Looking Error That Can Make You Break Your Head
 
You will not be able to tell why it is not working as intended because it will make you think everything is fine.
variable == 100 || 50

Yeah, everything looks terrific and it NEVER throws any error at your face at all.

Really guys, it never will. :wink: 100% guaranteed! :grin:
Still, it is a huge mistake under most circumstances. :confused:
 
The reasoning behind it is QUITE SIMPLE indeed.
How do you know if the test is always working as intended if it never fails?
 
Keep in mind that any object other than nil and false are treated as true by default.
 
So tell me why is that a problem for any novice scripter?
 
It is a huge problem because you wound up defining a conditional statement you never needed.
I mean, if you always get a true-ish value back, why bother at all?
You could simply remove it and the script would work as usually does.
 
But I need it to reject certain requests because it did not hit the 100% accuracy I need in my skill, item consumption, etc.
If that is true, then you better delete the "|| number" part at once.
I cannot. I need it to also work if it hits the 50% mark...
 
You get a yellow card for making such a noob mistake then.
Actually, the only way that would work is to define the condition like I do it right below this very same line.
variable == 100 || variable == 50

I would suspect you come from a different programming language that allow you to do that or you simply watched some video tutorial that was not Ruby specific at all.

Stop mixing language syntaxes right there!
Don't do it ever again!
It is for your own benefit, anyway. :P
Edited by kyonides
var == n || m

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
Top ArrowTop Arrow Highlighted