08-29-2019, 04:51 AM
(This post was last modified: 07-16-2023, 04:46 AM by kyonides.
Edit Reason: CRuby Code Explanation for Intermediate Learners
)
Quote:This topic is about Ruby itself and perhaps it might contain some gaming aspects every so often. Even so I don't mind if you take a look at previous posts regarding RGSS and its quirks.
Ruby Scripting
What's Ruby?
It's a programming language. Originally it was just object oriented thus it has been great for OOP.
Did you ever hear the term "spaghetti code"?
It's a derogatory term for messy code and some people apply it to OOP languages whenever they wanna criticize them for making objects out of everything. Ruby is no exception. In version 1.8.x its base class was Object, since 1.9.x or 2.0.x it's been BasicObject. So yeah, everything is an object in Ruby (and RGSS and Gosu and so on).
Is it Ruby's fault to be a messy bunch of code?
Nope, it isn't. It's a scripter's fault actually. Your coding style will determine if you're able to become a scripter or programmer, not the language. You can pick any programming language that suits your needs or your business's but it's up to you to make something useful out of it. If you're bad in programming in C or C++, you can making a mess of a program as well.
Is Ruby slow?
Well, Ruby version 1.8.x was no roadrunner for sure but it was decent for small or medium size projects. 1.9.x offered us some improvement, still, the 2.x series are something to take into account for it's becoming faster and faster. 2.6.x even allowed us to experiment with MJIT, Ruby's version of JIT, by memoizing method calls while running scripts.
Even so Ruby's been fast parsing text files. Rubygems allow Ruby to even deal with complex stuff like XML as well.
Why should I pick Ruby?
Again, that's up to you. Making the decision of making "small" games or Ruby on Rails / Sinatra apps, depends entirely upon your shoulders. Since we're on a forum dedicated to game making I'll assume you're interested in OOP and Ruby and you'll continue reading a series of post concerning this interesting topic.
The Basics
I'm a lazy scripter so I don't expect me to apologize if I don't go into specific details here and there. I'm not interested in getting feedback nor criticism for I'm doing this for free. If you don't like this guide, that's your sole problem. Go find another one.
Object Class
Since it's the main class in Ruby, fine, it was till BasicObject showed up, I'm gonna tell you about it. Whenever you run a script in a terminal window alias console or DOS depending on your Operating System, you're asking Object to deal with pretty much everything you've got to type or run there. Unless you create classes and modules, you're just dealing with the Object class.
Object runs a main method, namely "main". If we compare this with ye old RM scripts, it's the most basic main method ever. Without it Ruby wouldn't run not even for a second.
Does this mean that we need to create main methods in non RM scripts?
Nope. Don't even try that! That's something that the RM series introduced to their niche, cultist group of emerging game developers. How you run your games or scripts depends on where you wanna run them. A Ruby on Rails or Sinatra web app would use something entirely different, I can guarantee you they have nothing in common with RM.
How do I use the Object class?
Seriously? Well, you haven't been paying attention here then. I told you everything you don't create in classes and modules are objects, thus are part of the Object class. So something like:
puts "Hello SavePoint!"
...was run as part of the Object class. (Even if puts actually belongs to the Kernel module...)
What's that Kernel module you've mentioned here?
OK, let me find a way to easily explain it. Kernel is an ubiquitous module, it's automatically added to EVERY single object as a way to provide them with methods like puts without specifying such methods' origin. It just keep it simple for you to call them anywhere at anytime. It's in charge of some other stuff as well, but this guide won't cover it. Fine, I'll make another exception here:
hash = Hash(:m => 1)
#=> { :m => 1 }
Here Kernel module created a hash object for you. As an exception Hash is another method of the Kernel. Usually methods should be defined in lowercase. Still its purpose is to keep it simple for YOU.
We better start discussing methods here.
Methods
They are some of the basic contents of a class or module. If you ever need to define or modify some logic, use methods! They do exist to help you with that. You can call them like I did above by using puts or Hash. There are other ways to make them run.
my_concert = Concert.new
my_concert.start
...and a concert would be held somewhere and would begin once all assistants took their seats. (I'm guessing here. )
There you could find a curious but important method for Classes. It's the new method. In Ruby and even in Crystal new initializes a copy of the Concert class.
Why would we ever do that?
That's because you don't expect to organize a single concert in human history, do you?
In Ruby you call methods by connecting them to an object, either a class or an object, by typing a dot in between. Rarely you'd see some weirdos using :: instead, but just don't copy their weird style.
There's something interesting about the new method you better learn about right now, you NEVER define it in scripts! You could only define it if you were working on a rubygem or Ruby extension if you really got some C programming skills to ever achieve such a goal.
"CRuby new method"
From the Ruby side you never define new but you still have some sort of "replacement" called initialize.
In Ruby you don't need to create an initialize method for modules since they should never be initialized anyway. Classes allow you to define it, but Ruby also provides you with a default initialize method in case you don't wanna create your own. Most people prefer to create their own initialize method anyway.
initialize (called by CRuby rb_obj_call_init function) will be invoked by the new method which will pass its arguments down the line.
SIDE NOTE
Matz, Ruby's creator, didn't call initialize Ruby's constructor method in the original documentation he wrote many years ago that has been translated to English. Even so, people treat it as its constructor because it reminds them of C++ where a function with the class name will create the variables needed by the given class.
Why does new or initialize matter here?
I guess this was unavoidable... It's because you won't get a new copy of your class or anybody else's if you don't! By default you get just a single class, NilClass alias nil. That means it's not been initialized, constructed, created, defined, etc. It doesn't exist so far. Plus, you usually define custom values inside your initialize method. Try this!
value == 1
or
print value == 1
And you'd get either a false value in any version of RGSS or an error message telling you value has not been initialized and it doesn't even know if that's a method or a variable if using a later version of Ruby.
Why is that happening?
RGSS1 through RGSS3 DO allow you to use local variables without initializing them first.
The problem comes when you port them to mkxp or HiddenChest or other modern and open source projects that run RGSSx games. They always use later versions of Ruby that removed that behavior, breaking back compatibility for sure.
Objects You Should NEVER Initialize
Those would be a few classes indeed. They're true, false, nil, numbers and modules, of course!
You'll get plenty of errors if you ever try to do that.
Ruby classes are always open so you can initialize them as many times as you wish (and especially add them whatever you wanna include there). OK, if the script is protected or it's close source, you're out of luck... up to a certain point.
You can still add stuff to them but it might not be linked to their original implementations because some of them might be coded in C or C++. Think about the Bitmap or Sprite class in RM series, they're good examples of what you can't completely modify unless you wanna mess with their built in functions and making them stop working out of the blue. Still, you can add a few things here and there. (Warning! Your Ruby additions might not be as fast as you'd expect them to be.)
Defining an initialize Method
Code:
class MyClass
def initialize
@some_variable = 0
@some_boolean = false
end
end
There it is! We have defined our very first class!
We told Ruby to create a class called MyClass that you can build at will and it will get 2 default values for 2 specific instance variables, namely @some_variable and @some_boolean. They'll keep holding 0 and false values as long as your copy of your class exists.
How did we define a class to contain our methods? By calling class and providing it with a class name that uses CamelCase. Well, it can just begin with a single capital letter and that would be good enough to create it.
Don't forget! Keep exploring the map!
I mean, end all methods and classes and modules with a final end to close them.
But I can't do anything with it! I tried and got nothing!
That's true! We forgot to let our class modify those values at a certain point. Don't worry! We can define them now.
Code:
class MyClass
def some_variable() @some_variable end
def some_boolean() @some_boolean end
end
Why didn't you reuse the script posted above?
Because I told you I'm lazy! Plus all Ruby classes are open so I can extend them by defining new methods at will. Unless you were working on a script already, you can reopen that class as many times as needed. This has been quite common in the RM world where default script modifications reopened existing classes to alter their behavior or just add some new stuff.
Warning!
Don't mention the parent class there! There's no need for it! You only need to do that once!
Ruby has good long term memory, guys!
Huh? I wasn't planning to do that. What's a parent class anyway?
Yeah, I had to keep blabbing... Well, it's a class that was predefined as the original class for your set of custom scripts. In pure Ruby the very first case would be BasicObject, it's Object's parent class. In RGSS there is no BasicObject class but just Object as the top class.
Code:
class BasicObject
end
class Object < BasicObject
end
We could basically say that's how they were defined in Ruby. Your classes would already become Object class's children by default. Even so you can define something else as a class's parent class. The catch would be it'd exist by the moment you create the child class. Hit your head against the wall if you haven't. Just kidding!
By the way those 2 methods I created a while ago are called getters in many languages. Why? Because you can only get their current value.
But I wanna set their new values now! I need to!
You remind me of a certain dreamer... Anyway, you can do that by creating another two methods.
Code:
class MyClass
def some_variable=(new_value) @some_variable = new_value end
def some_boolean=(new_value) @some_boolean = new_value end
end
That's all you need to do to create their setters!
Wow!
? Actually I should have told you before there are other ways to create the same methods with less typing.
You're a mean scripter for sure!
? Am I? Anyway, here's how you can do it fast enough for those impatient visitors we got tonight.
Code:
class MyClass
attr_reader :some_variable, :some_boolean
attr_writer :some_variable, :some_boolean
attr_accessor :some_variable, :some_boolean
end
Nothing? ? No reaction coming from you now?
Nope, got nothing to say.
O_O Well, before I leave to grab some snack, I'll tell you something valuable. The first attr_ calls creates the methods for us, attr_reader is a method to create the first two methods you've seen above. attr_writer creates the last two of them.
But there's a third one there! I have spotted it already!
Yeah, my desperate detective in training, there's a third call, attr_accessor that actually creates both kind of methods, setters and getters.
I told you that you were really mean!
Excuse me for saving it for the very last moment you'd need to pay attention to my blabbing.
Happy Game Scripting Full Testing of Lag!
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE