Jump to content
New account registrations are disabed. This website is now an archive. Read more here.
Sign in to follow this  
Guest Dubealex

Dubealex′s RGSS and Ruby Tutorials

Recommended Posts

Guest Dubealex

All content seen in this article was published by Dubealex of Creationasylum.net.

Sources:

Lesson 1

Lesson 2

Lesson 3

Lesson 1

BEFORE using any code from this tutorial, set your browser's character encoding to UTF-8, you will find this option in the VIEW menu.

Please, read this lessons fully, it took time to write and gather all the informations to do it. And I would not like to be asked a question about something I answered in a particular section of the lesson. As example: I am explaining the structure of a Object Oriented language in section 3, and RGSS/Ruby is Object Oriented, which means I won't necessarly write it again. (But I may just write a reference that informations can be found on Section 3.)

 

I may quote other website about Ruby and RGSS, and then explains what it means if it need details. When the text isn't inside quotation mark or inside a quote table, it means I wrote it.

 

In every step on the way, I may have "discarded" or "missed" some details about the true fundaments of some concept; this is because I am trying to explains RGSS, not complete programming. So I will stick to what is useful to know to be able to better comprehend how to code with RGSS, for RMXP Scripts.

 

Also note that the RGSS Reference I did is a good place to get raw syntaxes that RGSS uses to manipulate eveything, you can found that reference on my official website.

1. What is Ruby?

*From RubyCentral:

Ruby is a an exciting new, pure, object oriented programming language. While few people in the West have heard of Ruby yet, it has taken off like wildfire in Japan---already overtaking the Python language in popularity.

 

What makes Ruby so popular? Ruby weaves the best features of the best programming languages into a seamless, concise whole. Ruby is:

 

Powerful --
Ruby combines the pure object-oriented power of the classic OO language Smalltalk with the expressiveness and convenience of a scripting language such as Perl. Ruby programs are compact, yet very readable and maintainable; you can get a lot done in a few lines, without being cryptic.

 

Simple --
The syntax and semantics are intuitive and very clean. There aren't any "special cases" you have to remember. For instance, integers, classes, and nil are all objects, just like everything else. Once you learn the basics, it's easy to guess how to do new things---and guess correctly.

 

Transparent --
Ruby frees you from the drudgery of spoon-feeding the compiler. More than any other language we've worked with, Ruby stays out of your way, so you can concentrate on solving the problem at hand.

 

Available --
Ruby is open source and freely available for both development and deployment. Unlike some other new languages, Ruby does not restrict you to a single platform or vendor. You can run Ruby under Unix or Linux, Microsoft Windows, or specialized systems such as BeOS and others.

Most of all, Ruby puts the fun back into programming. When was the last time you had fun writing a program---a program that worked the first time; a program that you could read next week, next month, or next year and still understand exactly what it does? We find Ruby to be a breath of fresh air in the dense, often hectic world of programming. In fact, we see nothing but smiles after we present Ruby to programmers.

 

[...]

 

Ruby is a genuine object-oriented language. Everything you manipulate is an object, and the results of those manipulations are themselves objects

 

[...]

 

Like Perl, Ruby is good at text processing. Like Smalltalk, everything in Ruby is an object, and Ruby has blocks, iterators, meta-classes and other good stuff.

 

You can use Ruby to write servers, experiment with prototypes, and for everyday programming tasks. As a
fully-integrated object-oriented language
, Ruby scales well.

 

Ruby features:

 

Simple syntax,

Basic OO features (classes, methods, objects, and so on),

Special OO features (Mix-ins, singleton methods, renaming, ...),

Operator overloading,

Exception handling,

Iterators and closures,

Garbage collection,

Dynamic loading (depending on the architecture),

High transportability (runs on various Unices, Windows, DOS, OSX, OS/2, Amiga, and so on)

So, as you can see, Ruby is an Object Oriented Language; more details about what is an Object Oriented Language will follow. The fact that Ruby is open source allow you to freely download the tools to create Ruby programs from any website. CLICK HERE to see the available download on the official Ruby website.

2. History of Ruby

*From RubyCentral:

Ruby was created by Yukihiro Matsumoto (who goes by the handle "matz")

 

Influenced by Perl, Matz wanted to use a jewel name for his new language, so he named Ruby after a colleague's birthstone.

 

Later, he realized that Ruby comes right after Perl in several situations. In birthstones, pearl is June, ruby is July. When measuring font sizes, pearl is 5pt, ruby is 5.5pt. He thought Ruby was a good name for a programming language newer (and hopefully better) than Perl.

 

[...]

 

The following are quotes from Matz Post on Ruby Talk:

 

"Well, Ruby was born on February 24, 1993. I was talking with my colleague about the possibility of an object-oriented scripting language. I knew Perl (Perl4, not Perl5), but I didn't like it really, because it had smell of toy language (it still has). the object-oriented scripting language seemed very promising."

 

"I knew Python then. But I didn't like it, because I didn't think it was a true object-oriented language---OO features appeared to be add-on to the language. As a language manic and OO fan for 15 years, I really wanted a genuine object-oriented, easy-to-use scripting language. I looked for, but couldn't find one."

 

"So, I decided to make it. It took several months to make the interpreter run. I put it the features I love to have in my language, such as iterators, exception handling, garbage collection."

 

"Then, I reorganized the features of Perl into a class library, and implemented them. I posted Ruby 0.95 to the Japanese domestic newsgroups in Dec. 1995."

 

"Since then, highly active mailing lists have been established and web pages formed."

3. What is "Object Orientated" or OO?

An Object-Oriented language -- Known as OO -- is a language in which everything you manipulate is known as an "Object".

 

(Notes: A "language" is the collection of syntax, terms, rules and structure that is used to create a program. C++, Java and Ruby are all different "languages".)

 

Every OO languages have at least one similarity, which is that they are OO languages; the basic structure in which they operate is the same. An OO language concist of the following main components/functions:

 

-Class

-Methods

-Messages

-Arguments

-Variables/Constants

Dubealex's RGSS and Ruby Lesson Diagram 1

Explanation of the diagram:

- A class contains variables and methods definitions.

- A method contains variables definitions and code.

- A message may contains arguments.

- An object is an instance of a class.

- An object communicate via Messages and Arguments.

 

Example of Object Instances:

Let's see a class as a car factory, and an object as the cars themselves. The car factory contains all the necessary "functions" to built a car, and each car is viewed as differents instance of the same thing. Those car "instances" are then different objects, and can be modified without affecting the other car objects. When the car company need another car, they can simply ask their factory to built a new one; once a factory (a class) is built, it saves time as you don't have to manually re-do each car (object).

 

Example of Communication & Object Properties:

When you want to access a properties of an object, you need to send a message to it, so it will be able to give you an answer -- And when you want to change an object properties, you need to send a message too, so it can be modified. In RPG Maker XP, an hero have many properties; such as "Name", "HP" and "Level". When you open the main menu in your game, you can see those properties (Level, Name and HP) on the screen. In the background, what RPG Maker XP did is to send a message to the Hero object asking for information about its properties.

 

A method actually defines what messages an object will be able to receive, and what will happen when it receives it. In this sense, an object is linked to it's creator (the class) as each time a message is send or sent from and to the object, the relevant method in that class is executed.

 

Back to our Hero example: This means that the class defining an Hero in RPG Maker XP has a method for every message you can send to its objects instances. Meaning that you will found a method called "name" in there; this method will define what will happen when that message is sent/received.

4. What is RGSS?

RGSS means "Ruby Game Scripting System". RGSS uses Ruby as it's language, so if you know Ruby, you also know RGSS. The difference between Ruby and RGSS that are important to you to know is that RGSS should be seen as a "Scripting" language that uses already maded components. In Ruby only, if you want to show a picture on screen, you cannot just use the same syntax you used in RPG Maker XP RGSS, you would have to code all that function from the ground up.

 

Example of RGSS code to show a picture (More infos on that later on.):

image = Sprite.new

image.bitmap = RPG::Cache.picture(name of picture)

So, basically, you need 2 lines to show a picture on screen; because you are using a class named "Sprite" already programmed inside RPG Maker XP (Not available in the Script Editor.) And you are also using a module "RPG" also not found in the Script Editor. This all makes your works easier and faster to code your game.

 

But since RGSS uses Ruby, and that RPG Maker XP uses Ruby (Version 1.8.1), you can code a full new "Sprite" class and use it, or even add functions in it.

 

So, all this means that RGSS is fairly less complicated than Ruby, because the low-level operation are handled by RPG maker XP, you don't have to code them.

 

(Note: low-level operation means the thing you would need to code to show a picture, talk to the machine (to Windows) to read a file, display a file, etc.)

5. The RPG Maker XP Script Editor

RPG Maker XP comes with a Script Editor built-in to allow you to edit/add what we call "Scripts". Those scripts are actually RGSS code (Using the Ruby Syntax and power) that allow you to add or modify features in RPG Maker XP. When you open the Script Editor, you can see that many "Script" are already in there; those are the default scripts that make a default game run; as the Heros we talked earlier in section 3. The way Enterbrain classified the scripts is by "Class". Every class is in its own page (Although you can mix many class in the same page.)

 

To open the script editor, you can press F11, use the Tool Menu and choose "Script Editor" or use the toolbar icon.

Dubealex's RGSS and Ruby Lesson Diagram 3

What I mean by "Real class name" is that the name you give to a page (The "Page Name" section, to the bottom left of the window) has nothing to do with the class name, it's just the name of the script. It can be anything, it doesn't have to be the class name, since as I said earlier, you can add many different class in the same page.

Color Code:

The following color code is used when editing your scripts, take note that this color convention is almost the same as for Ruby Programming. (Some color tag were discarded...)

 

Blue:

Blue represent the code syntax, as def, class, end, when, if, case, etc.

Dubealex's RGSS and Ruby Lesson Example 3

Red:

Red represent numbers in your code. As 1, 2, 3 and 5.

Dubealex's RGSS and Ruby Lesson Example 4

Light Blue:

The lighter blue represents operators and operands in your code. As =, ||, + and *.

Dubealex's RGSS and Ruby Lesson Example 5

Green

The color green represent your comments. Comments are ignored from your code, it's like your note about what you are doing and what the code does.

Dubealex's RGSS and Ruby Lesson Example 6

Purple

Purple represent text (string) elements in your code, along side with the directory symbol of Ruby.

Dubealex's RGSS and Ruby Lesson Example 7

Script Editor Features:

The script editor of RPG Maker XP come with some basic features, as Search, Replace and Go To Line. Here's a list of those options.

 

Code View Features:

(Right-Click in the Code View window to see all the features)

 

-> The basic Cut/Copy/Paste/Undo from Windows are available.

-> Find (Control+F)

-> Replace (Control + H)

-> Go To Line (Control + G)

-> Zoom IN/OUT with Control+Mouse Wheel

 

Page List Features:

(Right-Click in the Page list View window to see all the features)

 

-> The basic Copy/Paste/Cut from Windows are available

-> Add a page (Insert)

-> Delete a page (Delete)

-> Find (Control + F)

 

There are also some small options in the "Page Name" section, to the bottom left of the window, that allow you to set the text alignment and use unicode characters; this is probably there because RPG Maker XP is a japanese product.

6. Vocabulary and Syntax

Now comes the time to dig up some code, very basic one, in order to show the primary syntaxes and terms you will uses in almost all your scripts. Let's begin by a simple program that upon execution, will simply print on screen the word "I am therefore I'm coded.". (I'm sorry to break the rule of the "Hello World" convention, but we needed a change !

class Say_Something

 

def initialize

print "I am therefore I'm coded"

end

 

end

 

Create a new page with the script editor, and copy/paste that code in it. Remember, you can name the page whatever you desire, but it is recommended to name it wisely. (Choose a name that represent the script functions. In this case, you could call it Say Something...)

 

Now, if we want to test that out, we need to create an event on your map that will execute that class. In this event, choose a "Call Script" command (the last option on the last page), and write this in it:

Say_Something.new
And test play your game, go talk to that event, and a window will pop-up saying "I am therefore I'm coded.".

 

So here's the explanation for this (line by line):

1 class Say_Something

2

3 def initialize

4 print "I am therefore I'm coded"

5 end

6

7 end

Line 1: We defined a new class named "Say_Something".

Line 3: We defined a new method named "Initialize".

Line 4: We added the code to be executed when the method "initialize" is called.

Line 5: We close our method "initialize" with the "END" keyword.

Line 7: We close our class "Say_Something" with the "END" keyword.

 

The method "initialize" is a default method that is executed when you use the .new syntax, as you did in your event's Call Script. So, when you said in your call script the sentence "Say_Something.new" you told RPG Maker XP to create an instance of Say_Something with the message "new"; which executed the "initialize" method.

 

You also saw that everything that is "defined" most be closed, with the END keyword. This applies to a lot of things in Ruby, and this is why it's a good idea to always indent your code. (See example of indentation below).

Dubealex's RGSS and Ruby Lesson Diagram 8

 

As you can see, we could called each syntaxed that need to be closed as "segments" of code. You can clearly see when each should start and end if your code is well indented.

 

But we said earlier that everything was an object, and we did not seem to see any "object" while doing our "Say Something" example. Fact is you actually did an object, but it died crybaby.gif. You could have written the following in your call script, and everything would have worked fine:

my_object = Say_Something.new
This would have the same effect, here we see that we created a new object named "my_object" that should be of class "Say_Something". Of course, since our class does nothing except displaying a message, it's kind of useless to specify an object name.

 

Let's tweak our "Say_Something" class so we can use objects in a better way, and you will also be introduced to "Arguments". Replace the code in "Say_Something" by the following code:

class Say_Something

 

attr_accessor :greeting

attr_accessor :name

 

def initialize(greeting, name)

@greeting=greeting

@name=name

end

 

def say

print @greeting + " " + @name

end

 

end

Then, in your event, edit the Call Script you previously did (right-click and choose Edit or press the spacebar.) And replace it's content by this one:

(I made extra line break to better classify the structure of what we do.)

message1=Say_Something.new("Hello", "Alex")

message2=Say_Something.new("Hello", "Peter")

 

print message1.name

print message2.name

 

message1.say

message2.say

Now go test play your game, go talk to that event, you will see 4 pop-up window displaying in turns the element you asked for. The first window will display the name given to the object "message1", the second window will do the same for "message2". Then, 2 other window will pop-up executing the message "say" that we told. "say" is a method in our class "Say_Something".

 

This means that you just created 2 objects, "message1" and "message2", which are now 2 instances of "Say_Something", that can be modified without altering the other.

 

Let's explain that code, line by line, once again:

01 class Say_Something

02

03 attr_accessor :greeting

04 attr_accessor :name

05

06 def initialize(greeting, name)

07 @greeting=greeting

08 @name=name

09 end

10

11 def say

12 print @greeting + " " + @name

13 end

14

15 end

Line 1: We defined a new class named "Say_Something".

 

Line 3: We defined an ATTR value, which stand for Attribute. Ruby is powerful in the sense that we can simply use the ATTR_ACCESSOR syntax to define a new attribute that will be readable and writable for every objects instance created by "Say_Something". So our first ATTR is named "greeting".

 

Line 4: We defined another ATTR named "name".

 

Line 6: We defined a new method named "initialize". As we said earlier, the initialize method is the method that is executed first when an object is created by the class. So we added the "Argument Receiver" that this method will be able to receive. In our case, those "argument" (or attribute) are "greeting" and "name". We put those argument receiver between paranthesis aside the method name. Take note that those "Argument Receiver" are "recipients"; by that I mean that the name you used between the paranthesis are variable names; which you have to use later on for it to have a purpose.

 

Line 7: Here we defined a new "Instance Variable" that will hold the data sent within "greeting". An instance variable always begins with an @ and is useable in every method of the same class, and most be defined in a method. By doing this, we told RPG Maker XP to take the recipient "greeting" and put it inside of "@greeting" to allow us to use it in another method.

 

Line 8: We do the same as on line 7, but with the "name" attribute.

 

Line 9: We close our method "initialize" with the END keyword.

 

Line 11: We defined a new method called "say".

 

Line 12: We coded what should happen when an object receives a message "say". In our case, we print the content of "@greeting", plus an empty space, plus the content of "@name".

 

Line 13: We close our method "say" with the END keyword.

 

Line 15: We close our class "Say_Something" with the END keyword.

 

Now let's explain the code of the Call Script, line by line, once again:

1 message1=Say_Something.new("Hello", "Alex")

2 message2=Say_Something.new("Hello", "Peter")

3

4 print message1.name

5 print message2.name

6

7 message1.say

8 message2.say

Line 1: We create a new object called "message1" that will be an instance of "Say_Something". Which means that everything we can do with that object is defined in our class "Say_Something". Since we added "Argument Receiver" in our "initialize" method in "Say_Something", we need to send those argument, or RPG Maker XP will close asking for them. To send an argument to a class, it work the same way as for setting them, you list them in between paranthesis aside the class name.

 

Line 2: We do the same to create another object named "message2".

 

Line 4: We use the Ruby command "print" to display the name stored in our object "message1". As you can see, it's simple, you can access objects attributes (properties) by using the name you defined in the ATTR of your class.

 

Line 5: We do the same for "message2".

 

Line 7: We now send a message to our object "message1", that message is "say". You can called that a "command" to, if you wish. So, you told "message1" to execute it's function "say"; then "message1" searched in it's class "Say_Something" to see if that command is defined... And it was, so our object returned the actions defined in the class; which is to print something on screen.

 

Line 8: We do the same for "message2".

7. Conclusion

Now you have the basic knowledge of what is the main structure of a Ruby program, and know all the basic thing you will need to understand Chapter 2 of this lesson; which should be out next week. I'm trying to do one each week.

 

Don't worry, the following chapters will be mainly about coding, since the basic stuff is already explained. And for those of you who wants the full list of syntax convention, as the difference between each variable type, it will be the first section of my chapter 2. I decided to put it in that chapter and not in this one because chapter 2 will contains a lot of code example, and it's better to have the reference on the same chapter as the main code is.

 

Feel free to reply with your feedback, and if you have correction to add, just reply so.


Lesson 2

The basic structure of a RGSS (Ruby) code was explained in Chapter 1. We learned how to do ATTR, how objects communicate and how to create simple class and objects.

 

In Chapter 2, we will learn all the basic components that you must be able to use in order to create your very own scripts. Those components are stuff like "For..Loop", "If Statement", "Case" and "Variable Managements". We will go through each of them individually, and we will create small programs showing off how each components work, and we will finish this chapter with some script example (exercise) that uses many components at once.

 

We will also learn how to manipulate many important component in RGSS, like the relation between a Scene and a Window, and how to do a Scene and a Window. We will thus create our own scene and display data in it. We won't do a complete CMS with menus and selectable items now, this will come in Chapter 3. (Otherwise this chapter would have been really too huge...)

1. Variables and Constants

In every program, you need variables and sometimes you need contants. To save space and time in this chapter, if you don't feel confortable with variables, I suggest you go read my Variable Tutorial named "RMXP Variables Demystified" and come back here once you read it.

Local Variables

A local variable begins with a lowercase letter. This kind of variable can be used only within the method it was created in. In the following sample, both local variable exist only within their method, they do not conflict each others.

 

Sample:

class Local_Variable

 

def method_1

local_variable=2

end

 

def method_2

local_variable=3

end

 

end

Instance Variables

An instance variable begins with a @ and must be created inside a method. This kind of variable can be used in any method contained in the same class. In the following sample, the instance variable created in method_1 is equal to 2, and we change it to 4 in method_2. An instance variable is global within a class, it exist for all the method inside that class.

Sample:

class Instance_Variable

 

def method_1

@instance_variable=2

end

 

def method_2

@instance_variable=4

end

 

end

Class Variables

A class variable begins with two @@ and must be created anywhere in the class, as long as it's not inside a method. It is recommended to put them below the class name definition. A class variable is shared among all objects of a class and all method from that class can use it. In the following example, we created a class that counts the number of objects it created so far.

 

Sample:

class Class_Variable

 

@@class_variable=0

 

def initialize

@@class_variable+=1

end

 

end

Global Variables

A global variable exist in the entire program, once created, it can be used in any other class. In the following sample, we created a global variable in a class, and we add 1 to it in another class.

Sample:

class Global_Variable_1

 

def initialize

$global_variable=0

end

 

end

 

class Global_Variable_2

 

def initialize

$global_variable+=1

end

 

end

Constants

A constant is a "container" that contains a certain value, it should not be changed; but in Ruby, you can... But only from within the class in which you changed it, the value stored in a constant always remains the one you set in it's definition. In the following sample, I created 3 class to show how constant can be used.

 

Sample:

A_CONSTANT=100

 

class Constant_Test_1

ANOTHER_CONSTANT=50

 

def initialize

print ANOTHER_CONSTANT

print A_CONSTANT

end

 

end

 

class Constant_Test_2

 

def initialize

print Constant_Test_1::ANOTHER_CONSTANT

print A_CONSTANT

end

 

end

 

class Constant_Test_3

A_CONSTANT+=1

 

def initialize

print A_CONSTANT

end

 

end

First, we created a global constant named "A_CONSTANT" and we set it to 100. You also saw that a global constant is defined outside a class; it will be useable in every class of your program. In the first class, we created a class constant, that is useable from within the class... But you can use it from another class when you use the right syntax. In the second class we actually accessed the constant we created in the first class. The syntax rule is the following:

Class_Name::CONSTANT_NAME

Using this syntax, you can define a constant inside a class, do whatever you want to it, and be able to access it from another class. In the third and last class sample, we actually add 1 to our global constant "A_CONSTANT", and we print it's content, which gives us 101. Try to execute all those class one after the other, and repeat it 2 times... I.E: Do this in a event Call Script:

Constant_Test_1.new

Constant_Test_2.new

Constant_Test_3.new

Constant_Test_1.new

As you can see, the constant "A_CONSTANT" is equal to 101 only when you call Constant_Test_3.new, and it stays at 101 because it always uses the static value found in "A_CONSTANT", which is 100. Simply put, a constant is constant, it doesn't really changes. Remember that a constant should not be created/modified inside a method.

2. Conditional Statements

In this section you will learn how to do "If Statement", "Case Stements", and so on. I already explained the basics of those syntax in the RGSS Reference, here's a link to it. You should go there, read it all, and come back here to read the details and see real script sample:

 

-> Go on the main website and go in RGSS Reference - Ruby Syntax.

 

When you do conditional statement, always remember to uses double sign, I.E:

if my_variable=0	  # <-- ISN'T CORRECT

if my_variable==0 # <-- IS CORRECT

A full list of sign to used in conditions can be found in the page given in the link above. There is multiple way of doing those conditions in Ruby, and I will give you some example that will give you the same result if run.

If Statements

Sample A:

class If_Statement_1

@@a_class_variable=10

 

def initialize

@@a_class_variable+=1

a_local_variable=13

 

if @@a_class_variable==a_local_variable

print "Yay, we are equal !"

else

print "Arg, it's not fair !"

end

 

end

 

end

If you run 4 times this class, I.E: Do this in a call script:

If_Statement_1.new 

If_Statement_1.new

If_Statement_1.new

If_Statement_1.new

You will get the message "Yay, we are equal !" at the third window; since we add 1 to our class variable each times you call that class, and the condition check if our class variable equals our local variable. So, the process that RMXP does when you call it 4 times is the following:

 

[First Time]

@@a_class_variable=10+1 --> gives 11

a_local_variable=13

 

The IF statement compare the two, and they aren't equal. The if statement thus execute the ELSE actions and show that message.

 

[second Time]

@@a_class_variable=11+1 --> gives 12

a_local_variable=13

 

The IF statement compare the two, and they aren't equal. The if statement thus execute the ELSE actions and show that message.

 

[Third Time]

@@a_class_variable=12+1 --> gives 13

a_local_variable=13

 

The IF statement compare the two, and they are now equal. The if statement thus execute it's main body actions, and show the right message on screen.

 

[Forth Time]

@@a_class_variable=13+1 --> gives 14

a_local_variable=13

 

The IF statement compare the two, and they aren't equal anymore. The if statement thus execute the ELSE actions and show that message.

 

Sample B:

class If_Statement_1

@@a_class_variable=10

 

def initialize

@@a_class_variable+=1

a_local_variable=13

 

if @@a_class_variable==a_local_variable then print "Yay" else print "Arg" end

end

 

end

This sample shows that you can write the If Statement in one single line if you want to. It can be useful for small condition. Here's 2 other sample with the same effect, but without the else handler:

 

Sample C:

class If_Statement_1

@@a_class_variable=10

 

def initialize

@@a_class_variable+=1

a_local_variable=13

 

print @@a_class_variable

if @@a_class_variable==a_local_variable : print "Yay" end

end

 

end

Here you can see that we can replace THEN by a semicolon : Those are synonims. We print the content of @@a_class_variable and we see that we print the message "Yay" only if it equals 13. So, as I said, it's useful for small stuff like this. You can also write that condition like this:

 

Sample D:

class If_Statement_1

@@a_class_variable=10

 

def initialize

@@a_class_variable+=1

a_local_variable=13

 

print @@a_class_variable

print "Yay" if @@a_class_variable==a_local_variable

end

 

end

In this sample, we can omit the END keyword, so it's even smaller. We inversed both component in the condition. So we will print "Yay" ONLY if @@a_class_variable is equal to a_local_variable. So, now you know there are many different ways of doing condition, and we will use them later in our more advanced example.

 

You can also use ELSE IF keyword to add condition into your If Statement, you aren't limited to one ELSE handler as in an event Conditional Branches... RGSS is programming, so it's more powerful and more flexible. Here some example:

 

Sample E:

class If_Statement_1

@@a_class_variable=10

 

def initialize

@@a_class_variable+=1

a_local_variable=13

 

if @@a_class_variable==a_local_variable

print "We Are Equal !"

elsif @@a_class_variable==12

print "I Equal 12 !"

elsif @@a_class_variable != a_local_variable

print "We aren't equal !"

end

end

 

end

As you can see, the trick with Else If is to remember to use elsif and not Else If.

 

Using Else If will force the program to take one of the choices in the If Statement, and never two at once. Otherwise, when @@a_class_variable would equal 12, we would have seen 2 messages, we would have seen "I Equal 12 !" and "We Aren't Equals", but the If Statement shows the first one that returns to be true, and skip the others. If you want to do this, you can simply do all your else if as new If Stamement, the program will thus check them all, and execute only those who happen to be true. Like this:

class If_Statement_1

@@a_class_variable=10

 

def initialize

@@a_class_variable+=1

a_local_variable=13

 

if @@a_class_variable==a_local_variable

print "We Are Equal !"

end

if @@a_class_variable==12

print "I Equal 12 !"

end

if @@a_class_variable != a_local_variable

print "We aren't equal !"

end

end

 

end

If you run this, you will see that when @@a_class_variable equals 12, two messages are shown, "I Equal 12 !" followed by "We Aren't Equal". However, in much case where you would use else if, it's better to use a Case Statement, explained below.

Case Statement

A case statement is used to check the content of a given variable, and list a group of actions that will happens at each given value. This is used a lot in Scene and Menus. We will still uses the exact same sample as above, we our class_variable:

 

Remember, you must change the names you used in your Call Script to the class name, in this section, it's Case_Statement_1.new

 

Sample A:

 

class Case_Statement_1

@@a_class_variable=0

 

def initialize

@@a_class_variable+=1

 

case @@a_class_variable

when 1

print "I am 1"

when 2

print "I am 2"

when 3

print "I am 3"

else

print "What Am I ?"

end

 

end

 

end

If you read my RGSS reference as I said above, you should not have any problem understanding the sample I just gave. As you can see, it really look nicer with a Case Statement than with else if. Once again, remember that a semicolon can replace the THEN keyword in conditional statement, so you can make your code smaller using this:

 

Sample B:

class Case_Statement_1

@@a_class_variable=0

 

def initialize

@@a_class_variable+=1

 

case @@a_class_variable

when 1: print "I am 1"

when 2: print "I am 2"

when 3: print "I am 3"

else print "What Am I ?"

end

 

end

 

end

We can also make "Ranges", a range is defined by specifying a start value and an end value, with points in the middle. (We will talk more about range later.)

 

Basic Range Rules:

START..END    -->Include the END value

START...END -->Exclude the END value

So, you can use those "Range" in your case statement, like this:

 

Sample C:

class Case_Statement_1

@@a_class_variable=0

 

def initialize

@@a_class_variable+=1

 

print @@a_class_variable

case @@a_class_variable

when 1..3: print "I am between 1 and 3"

when 4..6: print "I am between 4 and 6"

when 7..9: print "I am between 7 and 9"

else print "What Am I ?"

end

 

end

 

end

To test this, simply add more Case_Statement_1.new to your Call Script. 10 is enough to see all the test. You can also uses specific condition list, seperated by comas, like this:

 

Sample D:

class Case_Statement_1

@@a_class_variable=0

 

def initialize

@@a_class_variable+=1

 

print @@a_class_variable

case @@a_class_variable

when 1,3: print "I am wether 1 or 3"

when 4,6: print "I am wether 4 or 6"

when 7,9: print "I am wether 7 or 9"

else print "What Am I ?"

end

 

end

 

end

In this sample, the program check if the value found inside @@a_class_variable is one of those listed at each "when" keyword. So, (in this case) a comas (,) is like an OR keyword. Remember that you can also uses other thing instead of numbers (integers), like text (strings), like this:

 

Sample E:

class Case_Statement_1

@@a_class_variable="a"

 

def initialize

@@a_class_variable.next!

 

case @@a_class_variable

when "b": print "I am B"

when "c": print "I am C"

when "d": print "I am D"

else print "What Am I ?"

end

 

end

 

end

In this sample, we do a .next! command, that is used to increment a string to it's next logical value depending on the character found at the extreme right of the string.

 

Example:

 

a_string="ab"

a_string.next!

--> a_string now equals "ac"

 

another_string="name102"

another_string.next!

--> another_string now equals "name103"

 

So, back to our Case sample, we see that it's working with string too. I won't list all the others conditional statement, as Unless, While and Until, they are explained in my RGSS Reference, and by reading the If Statement and the Case Statement from this lesson, I'm sure you can understand them pretty well.

3. Loop

Once again, in the link given above (The RGSS Reference), there is a good For..Loop reference. Here's a working sample, once again using the same kind of code shown in previous sample:

 

Sample A:

class For_Loop_Sample

 

def initialize

a_local_variable=0

 

for i in 1..10

a_local_variable+=1

print a_local_variable

end

 

end

 

end

This will add 1 to "a_local_variable" and print it's content 10 times... from 1 to 10, including 10. Remember the Range rules I explained above, it's the same rule in a for loop. So if we would have written "0...10" instead of "1..10", it would have given us the exact same results.

 

Some of you may be confused by the i in the line... What the hell is that i for ? What I like to say is that the i stand for "Iteration", as Iteration is repeating. Here's an extract from the RGSS Reference:

[...]A For Loop will execute it's body (actions) until it reaches the end value. The For Loop actually "iterate" the actions found in its body and store the numbers of iteration in the variable i. We can use any numbers or string (text) as start and end value. This will create the For Loop range. A range is defined using points (..) or (...). Using two points, as in the example above will make a range starting from the start value and ending at the end value, including the end value. Using 3 points (...) will exclude the end value. [...]

I won't list all the possible syntax for a For..Loop, as I did them in the RGSS Reference, just go check it out. Your i value can be used to create condition from within your for..loop or to use as reference. Check the following example:

 

Sample B:

class For_Loop_Sample

 

def initialize

 

for i in 1..4

print $game_actors[i].name

end

 

end

 

end

In order to test that example, you need to have your 4 first hero defined in RPG Maker. Basically, our i value is equal to the value in the range where we are at, and increment at each loop. Here's what is the process that RMXP did:

 

[First Iteration]

i=1

--> We print name of hero ID#1 (i being a variable, $game_actors[ i].name is now known as $game_actors[1].name)

 

[second Iteration]

i=2

--> We print name of hero ID#2 (i being a variable, $game_actors[ i].name is now known as $game_actors[2].name)

 

[Third Iteration]

i=3

--> We print name of hero ID#3 (i being a variable, $game_actors[ i].name is now known as $game_actors[3].name)

 

[Forth Iteration]

i=4

--> We print name of hero ID#4 (i being a variable, $game_actors[ i].name is now known as $game_actors[4].name)

 

You can see that this kind of code is used a lot in RMXP, especially in Window_MenuStatus, where RMXP list the actors datas in a For..Loop. Just remember that you can use variable in the range, as this will have the same effect as the sample shown above:

 

Sample C:

class For_Loop_Sample

 

def initialize

a_loacal_variable=1

another_local_variable=4

 

for i in a_loacal_variable..another_local_variable

print $game_actors[i].name

end

 

end

 

end

4. Inheritance

You will have to understand the power and the structure of Ruby inheritance before trying to understand how exactly a window and a scene works. You can make a class "inherit" methods and other data (variables) from another class. Class working with inheritance are called SubClass and SuperClass in Ruby; this can also be known as ChildClass and ParentClass. Here's an example:

 

Sample A:

class Inheritance_Sample_1

 

def initialize

@a_local_variable=1

end

 

end

 

 

class Inheritance_Sample_2 < Inheritance_Sample_1

 

def initialize

super

print @a_local_variable

end

 

end

To test it out, simply do a Call Script in an event, and write this in it:

Inheritance_Sample_2.new

To make a class inherit from another, you write a < symbol aside it's name, followed by the name of the class you want to be the SuperClass. In our sample, "Inheritance_Sample_2" is the SubClass and "Inheritance_Sample_1" is the SuperClass. Which means we can access stuff from Inheritance_Sample_1 from Inheritance_Sample_2 if used correctly.

 

You can see the keyword "super" in our initialize method in the class "Inheritance_Sample_2"; what does this do ? Well, it simply execute the method in the SuperClass that has the same name as the one in which we wrote the "super" syntax. Meaning that we used "super" in the initialize method, this will have the effect of executing the initialize method of the SuperClass, in our case it's "Inheritance_Sample_1" --> here's the process that RMXP did with that code:

 

[Receives the .new command]

-Execute the initialize method of Inheritance_Sample_2

-Found a "super" keyword.

--> Check what is the SuperClass; found that it is Inheritance_Sample_1

- Execute the initialize method of the SuperClass Inheritance_Sample_1

--> The initialize method of Inheritance_Sample_1 set @a_local_variable=1

- The program then continue in the initialize method of Inheritance_Sample_2

--> @a_local_variable is now equal to 1

- We print @a_local_variable on screen

[End of program]

 

Here's a diagram to better visualize what I mean:

Dubealex's RGSS and Ruby Lession Diagram 9

So, as you can see, if we do a super keyword, it execute the method in the SuperClass, as putting a "super" keyword in method_a of the SubClass will execute the method_a of the SuperClass.... And so on.

 

You can also use the "super" keyword with arguments, as explained in Chapter 1, this can be seen in every Window class of RMXP; will be explained in section 5 below.

5. Scene and Window

RPG Maker XP interfaces are built with Scene and Window. Each scene is composed of many windows. The way it works is like having a lot of little blocks on a table, and we arrange the blocks a certain way to built what we desire. The table is the scene, and the blocks are the windows. So, it's logical to built a block before we can do a scene.... So let's do a window now:

Creating a Window

The basic of a window is simple, you may have seen it in all the Window_ script in the default RMXP Script Editor. We will built a new window called "My_Window". To test it when you're done, simply do a Call Script in an event and write this in it:

My_Window.new

My_Window code:

class My_Window < Window_Base

 

def initialize

super(0, 0, 200, 200)

end

 

end

*I'm aware of the fact that the window disapears after a while; it's normal, it's because of Scene_Map; we did not told Scene_Map to update that window. We'll talk about that later. Let's just built a simple window for now:

 

As you can see, this have the effect of creating a window on the top left corner of the screen. Here's the syntax:

 

super(x position, y position, width size, height size)

 

As you have learned in section 4, inheritance, "super" is used to execute a method in the SuperClass. Our window have Window_Base as SuperClass, so let's take a look at the initialize method of Window_Base:

 

Window_Base Initialize method:

def initialize(x, y, width, height)

super()

@windowskin_name = $game_system.windowskin_name

self.windowskin = RPG::Cache.windowskin(@windowskin_name)

self.x = x

self.y = y

self.width = width

self.height = height

self.z = 100

end

This is invoked each time you do a window, this defined it's sizes and position, it's windowskins and such other attributes. What is important to look at is the following line:

 

def initialize(x, y, width, height)

 

As explained in Chapter 1, those are Attribute Receivers (as I call them), so by using our "super(0, 0, 200, 200)" in the code of our window, RMXP actually did this:

 

[super keyword interpreted...]

- Read arguments sent with "super"

--> In Window_Base, we set the following attribute with the data sent with super:

-> x=0

-> y=0

-> width=200

->height=200

--> The window is created

-Back to our window code

[End of program]

 

This window is plain... It's empty. Let's write something in it.

 

My_Window Code (+Text):

class My_Window < Window_Base

 

def initialize

super(0, 0, 200, 200)

self.contents = Bitmap.new(width-32, height-32)

self.contents.font.name = "Arial"

self.contents.font.size = 24

self.contents.draw_text(0, 0, 200, 32, "Yay, some text !")

end

 

end

3 lines are necessary to add text. You must specify a font name, a font size, and write the text itself. Let's explain those line:

 

self.contents = Bitmap.new(width-32, height-32)

This tells that the content of our window should be of the "Bitmap" type (Bitmap is defined within RMXP, not in the Script Editor). This allow to write text in the window, basically. The argument we send is the size of that bitmap area. Usually, we set it equal to the window size, hence width and height. (Remember, width and height are defined in the SuperClass, and we set them with our super line above.)

 

Wanna know the big dark secret of the minus 32 ? I.E: width-32 ? It's because that arrow will appears pointing that data can still be viewed; this is probably because the bitmap is genuily larger than the window... dunno. We could have written (200-32, 200-32), it would have given the same result. (This could also be the reason of the text being cut-off in the borders of the window).

 

The 2 other lines are straight forward.

 

self.contents.draw_text(0, 0, 200, 32, "Yay, some text !")

This line actually defines your text. The arguments you send in the parenthesis are the same as with the creation of a window. i.e: (x position, y position, width size, height size). The last argument is simply the text to draw in the window.

 

*I am also aware that when looking at the Window code by default in the Script Editor, the text is actually drawn in another method, usually called "refresh". This is useful only if the content of the window will change as the window is shown. In other words, it is relevant to code it like that in Window_PlayTime, but not really in Window_Gold.

 

Now that we are able to create a basic window, we can try to built a scene that will show, let's say, 3 windows. Window #1 will show the playtime, the gold held and the map ID on which you are. Window #2 will list the hero present in your party one above another, and window#3 will show stats for the hero in your party.

 

(Take note that there isn't any window command (menu) in that scene yet. This will come later.)

 

Here's the basic layout of what we want to achieve:

Dubealex's RGSS and Ruby Lession Diagram 10

*OK ! I know, it's plain, it's ugly and kind of useless design... But we just want to know how to code a scene, without too many thing at the same time. Don't worry, I will explains how to work with selectable menu, even with multiple selectable menu, in chapter 3.

Creating Window #1

The first window is the window on the top that shows the playing time, the gold held and the map ID. This is the simplest window to code. You could put all three window code and the scene in the same page in the script editor, but as this is a lesson, it's better to create a page for each window, and another for the scene, that way you won't get confused in all the code. So, first thing first, create a new page and call it Window_1 and add this code in it:

 

(Important: You must add the page below the page containing the SuperClass of any script you do. This means that our window will work as long as they are below Window_Base. --> What I always do is add new custom script just above MAIN, all the time.)

 

Window_1 Code:

class Window_1 < Window_Base

 

def initialize

super(0, 0, 640,100)

self.contents = Bitmap.new(width-32, height-32)

self.contents.font.name = "Arial"

self.contents.font.size = 24

refresh

end

 

def refresh

self.contents.clear

self.contents.font.color = text_color(6)

self.contents.draw_text(0, 0, 100, 32, "PlayTime:")

 

#CODE TO SHOW PLAYTIME (Copied from Window_PlayTime)

@total_sec = Graphics.frame_count / Graphics.frame_rate

hour = @total_sec / 60 / 60

min = @total_sec / 60 % 60

sec = @total_sec % 60

text = sprintf("%02d:%02d:%02d", hour, min, sec)

self.contents.font.color = normal_color

self.contents.draw_text(100, 0, 120, 32, text)

#END OF CODE TO SHOW PLAYTIME

 

self.contents.font.color = text_color(6)

self.contents.draw_text(250, 0, 50, 32, "Gold:")

self.contents.font.color = text_color(0)

self.contents.draw_text(305, 0, 100, 32, $game_party.gold.to_s)

 

self.contents.font.color = text_color(6)

self.contents.draw_text(400, 0, 100, 32, "Map ID:")

self.contents.font.color = text_color(0)

self.contents.draw_text(480, 0, 100, 32, $game_map.map_id.to_s)

end

 

def update

if Graphics.frame_count / Graphics.frame_rate != @total_sec

refresh

end

end

 

end

As you can see, I seperated the script in 5 differents blocks, and I will explains those block lines by lines.

 

BLOCK 1:

 

1 def initialize

2 super(0, 0, 640,100)

3 self.contents = Bitmap.new(width-32, height-32)

4 self.contents.font.name = "Arial"

5 self.contents.font.size = 24

6 refresh

7 end

 

 

This is the initialize method, with what you have already read in this chapter, and in chapter 1, you should be able to know what is going on in this block. Line 1 to 5 were explained in above sections, so I won't repeats myself. The 7th line simply calls the "refresh" method, that is defined below.

 

BLOCK 2:

 

1 def refresh

2 self.contents.clear

3 self.contents.font.color = text_color(6)

4 self.contents.draw_text(0, 0, 100, 32, "PlayTime:")

 

5 #CODE TO SHOW PLAYTIME (Copied from Window_PlayTime)

6 @total_sec = Graphics.frame_count / Graphics.frame_rate

7 hour = @total_sec / 60 / 60

8 min = @total_sec / 60 % 60

9 sec = @total_sec % 60

10 text = sprintf("%02d:%02d:%02d", hour, min, sec)

11 self.contents.font.color = normal_color

12 self.contents.draw_text(100, 0, 120, 32, text)

13 #END OF CODE TO SHOW PLAYTIME

[...]

 

This is the first block that begins the method refresh. The first thing we do is clear the content of the window... why the heck do we want to do that in the first place ? Well, it's because we will refresh the content of that window in our later scene to show the play time running; and we do not want the text to show over and over again, or else it would get messy.

 

Line 2: self.contents.clear actually take care of that.

 

Line 3 and 4 were explained in above sections, they take care of showing the text "Playing Time:" in the window, at the desired location. The only thing that I haven't yet shown is line #3: self.contents.font.color = text_color(6). This line specify the color used to draw the text. The number between parenthesis at the end represent the color ID defined in Window_Base starting at line #39. (I.E: They are the same color ID used in messages event, using \c[iD].)

 

The code to show the playing time start at line 5. This code is the same as in Window_PlayTime, except for the location of the time in the window. If you wonder what is all that giberrish in the code, I will clear it out:

 

Line 6: @total_sec = Graphics.frame_count / Graphics.frame_rate

 

This create a new variable named @total_sec, and makes it equals to the total frame count that occured in the game divised by the frame per seconds (The frame rate - FPS). Basically, it can be converted like that:

 

- 12450 Frames has been counted since the begining of the game.

- The FPS (Frame Rate or Frame Per Second) is 30.

- If you want to know the numbers of seconds, you do 12450/30=415 --> 415 sec.

 

So, basing ourselves on that new seconds variable, we can define a number of minutes and a number of hours, since we all know that there are 60 seconds in a minutes, and 60 minutes in an hour.

 

Line 7: hour = @total_sec / 60 / 60

 

This line thus calculate the number of hours played. If we take the example from above, it would give us that result:

 

-> 415 / 60 / 60 = 0.115 --> 0 hour.

 

RMXP gives us 0 as an asnwer since it's using Integers in the equation; and Integers does not allow decimals. We'll talk about maths in chapter 3.

 

Line 8: min = @total_sec / 60 % 60

 

Ok, this is the line that may have confused you... What on earth is the use of the % ? Well, it does a modulo (known as mod). I already explained what was a modulus (modulo, mod) in my RMXP Variable Demystified. Here's an extract of it:

The modulus is the ramainder of a division. If we divide 17 by 3 on a calculator, its answer would be "5.666666667", but this anwser has nothing to do with modulus.

 

The mod function is defined as the amount by which a number exceeds the largest integer multiple of the divisor that is not greater than that number. Here's how it's working:

 

17 mod 3 = 2

Because 17 / 3 = 5 remaining 2, wich means that 17 = 3 * 5 + 2

 

Try this: Take a calculator and divide 17 by 3, it will give you 5.666666667.

Now, take only the full integer 5 (don't take what is after the decimal). Multiply 5 by 3, and it gives you 15. Then what remains from 17 - 15 ? Exactly, 2. This makes the 17 mod 3 = 2 take all it's sense.

So, this means that 60 modulo 60 would equals 0, since 60/60=1, and that 1 has no remaining value. All that means that this little line will reset the minutes variables to 0 when it has reached 60.

 

So, keeping our example, here's what it logically gives us:

 

(415 / 60) mod 60 = 6.916 --> 6 minutes (RMXP doesn't floor the value.)

 

I do not have to explain line 9, it's the same thing as line 8, but to count the number of seconds.

 

Line 10: text = sprintf("%02d:%02d:%02d", hour, min, sec)

 

sprintf is used to format a string (A text variable) using format code. What is between the quote marks " " are the format code. A format code is actually composed of "flag characters" and "field type". The flag character will set the view format of the resulting string, and the field type will specify how to interpret the argument sent into sprintf. Each implementation of sprintf begins with a % sign, and every other symbols added into a format code that isn't known as wether a "field type code" or a "falg character code" is shown as is in the resulting string. The argument sent in sprintf are listed after the format code, seperated with comas. The number of argument sent must be equals to the number of format code used (begins with a %). So, let's see what this line actually did:

 

We created "text", that is a local variable as explained in section 1, and we set it equal to the result of our sprintf command. Let's see each format code:

 

%02d

This is the first one, and the 2 other are identical. How does this work ? Well, the % sign tells the program that what follows is a format code. "0" is a "flag character" that is used to set the view format. 0 is to add the number 0 (zero) before the string instead of spaces, and the following number "2" tells the program to write 2 zeros. Just try to change this 2 by a 3, and you will see 3 zeros in your Window_PlayTime menus. The letter "d" tells the program to interpret the argument we will send as decimal numbers. Then, we wrote :, and since this isn't a code, it's written in the string as is.

 

So, this means that we format a new string that will look like that:

 

00:00:00

 

Yay, it's a clock ! There are many other format code, you can found them on the web, a website containing them can be found on my website links sections.

 

You can see that after the quote marks come the 3 other local variable we created on line #7, 8 and 9 (hour, min, sec). Here's a small diagram, and we can go to the next line:

Dubealex's RGSS and Ruby Lession Ex 10

Line 11 to 13 were also explained in earlier section and chapters, they show the text we just formated with our sprintf. Let's jump to the next block:

 

BLOCK 3:

1 self.contents.font.color = text_color(6)

2 self.contents.draw_text(250, 0, 50, 32, "Gold:")

3 self.contents.font.color = text_color(0)

4 self.contents.draw_text(305, 0, 100, 32, $game_party.gold.to_s)

 

This should not be too hard, it was, once again, already explained. I need only to explain a small thing about line #4.

 

Line 4: self.contents.draw_text(305, 0, 100, 32, $game_party.gold.to_s)

 

The .to_s at the end of the variable is to convert it into a string, so we can draw it as text. If we try to put a variable containing numbers there, an error will pop up saying "Cannot convert Fixnum into String". So we tell the program to convert it in the code, and it thus work fine.

 

I don't have to explain the next block, as it is the same thing as block 3; it just shows the map ID instead of the gold held. So let's jump to the last block, update:

 

BLOCK 5:

1 def update

2 if Graphics.frame_count / Graphics.frame_rate != @total_sec

3 refresh

4 end

5 end

6 end

 

This is our update method. As you can see, this method isn't used from within our window, it will be used by our Scene later on. The method update of our window most exist, because we will make it update in our scene. Window that doesn't have to be updated as they are seen don't need any update method. You will see what I mean when we will code our Scene later on. But let's explain the lines anyway:

 

(I'll skip the line that you are supposed to understand now, because chapter 1 and the earlier section of chapter 2 should have teached you those lines.)

 

Line 2: if Graphics.frame_count / Graphics.frame_rate != @total_sec

 

This is an If Statement as described earlier. And if you have read my RGSS Reference as I told eariler, you will know that != means "Not Equal To". So, what we do in that IF is the following:

 

We divise the total amount of frames that occured in the game by the frame rate (this gives us the total amount of seconds, as explained above), and we check if that answer is equal or not with our variable @total_sec (which contains the total amount of sec that was created when we show the window for the first time.) If it's not equal to it, we call the refresh method, and thus we clear the content, and recalculate the new time, and show it once again. So, this update method check if the time has changed, and when it does, we show the new time.

Creating Window #2

The second window is the one to the left, which list the hero in your party, with their class beneath them. Add a new page and name it Window_2.

 

Window_2 Code:

class Window_2 < Window_Base

 

#BLOCK 1

def initialize

super(0, 0, 200,380)

self.contents = Bitmap.new(width-32, height-32)

self.contents.font.name = "Arial"

self.contents.font.size = 24

 

#BLOCK 2

for i in 0...$game_party.actors.size

x = 0

y = i * 90

actor = $game_party.actors[i]

self.contents.font.color = text_color(6)

self.contents.draw_text(x, y, 200, 32, $game_party.actors[i].name)

self.contents.font.color = text_color(4)

self.contents.draw_text(x, y+32, 200, 32, actor.class_name)

end

end

 

end

This window contains less code, it's because it draw the text in a for loop. I won't explain BLOCK #1 as it was already explained in above sections. Let's jump once again to our next block:

 

BLOCK #2:

1 for i in 0...$game_party.actors.size

2 x = 0

3 y = i * 90

4 actor = $game_party.actors[ i]

5 self.contents.font.color = text_color(6)

6 self.contents.draw_text(x, y, 200, 32, actor.name)

7 self.contents.font.color = text_color(4)

8 self.contents.draw_text(x, y+32, 200, 32, actor.class_name)

9 end

10 end

11 end

 

Line 1: for i in 0...$game_party.actors.size

 

As shown earlier, this is our for loop. The .size command is a ruby command to know the size of an array, or a string. If you read my RMXP Variable Demystified, you'll know what is an array and such. $game_party.actors is an array variable containing the hero present in your party. Remember that an array index start at 0, not at 1, which means that the first hero in your party doesn't have the party ID#1, it have the party ID#0. So, this for loop actually iterate itself starting at 0, and finish at the amount of hero present in your party, excluding the last number. (Remember the range rules with the ... and .. points.) --> Why exclude the last number ? Here's why:

 

(This example take into consideration that 4 hero are present in your party)

 

[For Loop Start]

- We check the size of game_party.actors

- We have 4 party members, it thus equals 4

--> We will do 4 iteration in the for loop.

 

[First Iteration - 1/4]

- i=0

- refers to the game_party actor ID#0 (the first one)

 

[second Iteration - 2/4]

- i=1

- refers to the game_party actor ID#1 (the second one)

 

[Third Iteration - 3/4]

- i=2

- refers to the game_party actor ID#2 (the third one)

 

[Forth Iteration - 4/4]

- i=3

- refers to the game_party actor ID#3 (the forth and last one)

 

As you can see, ID0 is your first hero in the party. If we would have used 2 points in our range to include the last number, it would have given a loop that does 1 iteration that should not be done. (i.e: The array index start at 0, so look at this:)

 

index 0 = Hilda

index 1 = Arshes

index 2 = Eliette

index 3 = Dubealex

 

We thus have an array with a size that equals 4. (I hope you got it.)

 

Line 2 and 3: x = 0 -- y = i * 90

 

Remember that we are still inside the for loop body, which means we will do what is there multiple times. The first one is easy, x=0, this create a local variable and set it to 0. We will use that x variable later to place all our text at coordinate x (which is 0). If you change that 0 by another value, you will see that all the names will appears elsewhere in the window.

 

The second one, y = i * 90, again create a local variable y that we will again use to place the text in the y coordinates. The trick in that part is that we want each name to appears above another, not in the same spot ! Since we now know that our variable i contains the iteration value (as I said earlier), we can thus use that. We multiply it by 90, if you put another value in place of 90, you will simply see more space between each name, or less space, depending on which value you put there. So, here's an explanation of this process:

 

[First Iteration]

- i=0

- x=0

- y= i * 90

--> i equals 0, so 0 * 90 equals 0.

- We will draw the first name at coordinates [0,0] ([x,y])

 

[second Iteration]

- i=1

- x=0

- y= i * 90

--> i equals 1, so 1 * 90 equals 90.

- We will draw the second name at coordinates [0,90] ([x,y])

 

[Third Iteration]

- i=2

- x=0

- y= i * 90

--> i equals 2, so 2 * 90 equals 180.

- We will draw the third name at coordinates [0,180] ([x,y])

 

[Forth Iteration]

- i=3

- x=0

- y= i * 90

--> i equals 3, so 3 * 90 equals 270.

- We will draw the forth name at coordinates [0,270] ([x,y])

 

Line 4: actor = $game_party.actors[ i]

 

This line is to save space below, it wasn't a "necessary" line. But it's useful as it makes the line below smaller and more easy to understand. We also do not have to repeat the full variable name over and over again. Here's what it do:

 

It create a local variable name actor, and set it as being $game_party.actors[ i].

(Remember that i is the party hero ID.) --> Then, in later lines, you can just use line like actor.name to refer to $game_party.actors[iD].name, which is a long line to type, isn't ?

 

Line 6: self.contents.draw_text(x, y, 200, 32, actor.name)

 

As explained earlier, we now draw the text of our hero's at coordinates x (which is 0), and coordinates y (that is calculated at each new iteration). And we can use actor.name instead of the full variable name, to save time and space in the script.

Line 8: self.contents.draw_text(x, y+32, 200, 32, actor.class_name)

 

This line is similar to line 6, except for our y coordinates. We do y+32 because we want the class names to be written below the hero's name. (Remember, to go higher in the Y coordinate, you must lower the value, and to get lower, you must augment the value... I know it can be confusing at first.) We set 32 since we have text height of 32 everywhere, and it fits right. If you customize your script, you will have to put values that fits with your fonts and menus.

 

Ok, that's it for this window. It wasn't that complicated, once you get the hang of it, isn't so ?

Creating Window #3

The third and last window is the window to the right, the one that contains the heros attributes. This one looks like the window #2, but with a twist... We will use an if statement inside the for loop to make the hero appears in 2 column instead of all above another. So, just add another page in the script editor, and name it Window_3:

class Window_3 < Window_Base

 

#BLOCK 1

def initialize

super(0, 0, 440,380)

self.contents = Bitmap.new(width-32, height-32)

self.contents.font.name = "Arial"

self.contents.font.size = 24

 

#BLOCK 2

for i in 0...$game_party.actors.size

x = 0

y = i * 150

if i >= 2

x=250

y -= 300

end

actor = $game_party.actors[i]

self.contents.font.color = text_color(6)

self.contents.draw_text(x, y, 200, 32, actor.name)

offset_x=contents.text_size(actor.name).width+10

self.contents.font.color = text_color(4)

self.contents.draw_text(x+offset_x, y, 200, 32, "Lv: " + actor.level.to_s)

draw_actor_hp(actor, x, y+32)

draw_actor_sp(actor, x, y+64)

draw_actor_exp(actor, x, y+96)

end

end

 

end

Information about block 1 was explained in previous lessons. So I will skip to block 2:

 

Line 1: for i in 0...$game_party.actors.size

 

This will make a "for loop" starting at 0 and stoping at the size of our current game party, which is a max of 4 by default. THe game party info are stored as an array and thus start at 0, this is why the first hero in the party is known as 0, and the last as 3.

 

Line 2: x = 0

 

Here we define x as being 0 so the listing can start at the far left of the screen.

 

Line 3: y = i * 150

 

This line define the vertical position, and since we are doing a for loop, Y is defined as being the value of our loop (where we are at in the cycle) multiplied by 150 to let enough space to put the info we want for our hero.

 

Line 4: if i >= 2

 

This line is the If Statement. This is only this little if statement that allows us to have 2 column of stats instead of one. To better explain it, I will show you the complete If body, not just the first line:

4 if i >= 2

5 x=250

6 y -= 300

7 end

 

So, line 4 check if our i variable is bigger or equal than 2, and if it does, it will do what is inside the If body. Remember, when our i value is equal to 2 (or bigger) it's because 2 hero's where processed already. We wanted 2 hero in one column, and another 2 heros in another column. So when i equals 0 or 1, it is the 2 first hero to draw. When it's equals to 2 or 3, it the 2 last one.

 

Line 5: x=250

 

So, if 2 hero were already processed, we don't want the 2 other to be drawn at the same spot, so we change the X cooridnate to 250, which will draw our 2 last hero to the right of the first.

 

Line 6: y-=300

 

This line does almost the same thing as line 5. It modify the y value, but the fact is that we want the hero to have the exact same spacing between them, but since our i value is already at 2 when we do this, we must subtract the extra 300 that we got so we can draw the 2 other hero at the top of the new column. Here's a process sample for those who did not get that right:

 

[First Iteration]

- i=0

- x=0

- y= i * 150

--> i equals 0, thus 0 * 150 = 0 (y=0)

- Check the if statement, it return false, i isn't bigger or equal to 2.

--> New coordinates to draw text are [0,0]

[etc]

 

[second Iteration]

- i=1

- x=0

- y= i * 150

--> i equals 1, thus 1 * 150 = 150 (y=150)

- Check the if statement, it return false, i isn't bigger or equal to 2.

--> New coordinates to draw text are [0,150]

[etc]

 

[Third Iteration]

- i=2

- x=0

- y= i * 150

--> i equals 2, thus 2 * 150 = 300 (y=300)

- Check the if statement, it return true, i IS bigger or equal to 2.

--> We enter the If Statement body and execute the actions in it...

- x=250 (Change the value inside x to 250)

- y-=300

--> y equals 300, thus 300-300=0 (y=0)

--> New coordinates to draw text are [250,0]

[etc]

 

[Forth Iteration]

- i=3

- x=0

- y= i * 150

--> i equals 3, thus 3 * 150 = 450 (y=450)

- Check the if statement, it return true, i IS bigger or equal to 2.

--> We enter the If Statement body and execute the actions in it...

- x=250 (Change the value inside x to 250)

- y-=300

--> y equals 450, thus 450-300=150 (y=150)

--> New coordinates to draw text are [250,150]

[etc]

 

So, as you can see, we just create 2 aligned columns ! Let's jump to the other lines. Line 8 to 10 were already explained earlier...

 

Line 11: offset_x=contents.text_size(actor.name).width+10

 

This line create a local variable named "offset_x", that will be equals to the width of the name of the hero that we have drawn plus 10 (so there a bit of space between what we will write there...) This will be used when drawing the Level of the hero. Basically, we wants the Level to be drawn just aside of the hero's name, but since hero's name can be of many different sizes, we must use a variable to set the x position of the level text. contents.text_size(actor.name).width is a command that allow you to know the width of a text. We specify which text between the parenthesis, and we choose a attribute after the last point. To know the height, we just replace width by height.

 

Line 13: self.contents.draw_text(x+offset_x, y, 200, 32, "Lv: " + actor.level.to_s)

 

This is the line that write the Level of the heros. So, as you can see, we have set it's x position by adding our "offset_x" value to the existing x position. That way, the level is always aside the names. Remember the .to_s command... The level is an integer, we must convert it to a string before writing it.

 

 

Line 14: draw_actor_hp(actor, x, y+32)

 

Ok, if you are asking yourself where the "variable" draw_actor_hp come from, you are mistaken. draw_actor_hp is in fact a method name, since we have argument between parenthesis aisde it. But wait a minute... Where is that method ? It's not in our Window_3 code ? Well... Remember inheritance ? We have set Window_Base as the SuperClass of Window_3, so that method is bound to be inside Window_Base, isn't ? Let's check it out ! Let's open Window_Base, and run a search for "draw_actor_hp" ... Found it ? Here it is:

 

(I hate those jap giberrish comments.... anyway...)

 

Window_Base -> draw_actor_hp method code:

def draw_actor_hp(actor, x, y, width = 144)

# ??? "HP" ???

self.contents.font.color = system_color

self.contents.draw_text(x, y, 32, 32, $data_system.words.hp)

# MaxHP ???????????????

if width - 32 >= 108

hp_x = x + width - 108

flag = true

elsif width - 32 >= 48

hp_x = x + width - 48

flag = false

end

# HP ???

self.contents.font.color = actor.hp == 0 ? knockout_color :

actor.hp <= actor.maxhp / 4 ? crisis_color : normal_color

self.contents.draw_text(hp_x, y, 48, 32, actor.hp.to_s, 2)

# MaxHP ???

if flag

self.contents.font.color = normal_color

self.contents.draw_text(hp_x + 48, y, 12, 32, "/", 1)

self.contents.draw_text(hp_x + 60, y, 48, 32, actor.maxhp.to_s)

end

end

 

This is where all is coded... well, this code actually set the colors of the HP, set all the formatting. So, if you need to adjust stuff in your game, go there and have fun with the customization ! Back to our window now:

 

All the other lines actually calls method from Window_Base to draw the HP, SP and Exp of our heros on the screen. There's really nothing more to it. We simply add some value to our y position, so they can be all above the others.

 

It's now the time to create our magnificent scene...

Creating our Scene

Now that we have 3 window working right, we need a scene to show them on screen. Remember when I was talking about a table and blocks... The table is your scene, and you can arrange your blocks as you please on that table. If you look at your window code, you will notice that we only set their width and height, we never specified any X and Y position. i.e:

def initialize

super(0, 0, 440,380)

 

#(x position, y position, width size, height size)

It's because we will place those window where we desire in our scene. That way, if you wanted to have the Window_1 at the bottom, you can by modifying the scene, not the window. It's better that way, it's a better development habit.

 

Ok, with that said, let's create our table... arg... our scene ! Just add a page in your script editor, and name it Scene_Chapter_2.

 

Scene_Chapter_2 code:

class Scene_Chapter_2

 

#BLOCK 1

def main

@window_1=Window_1.new

 

@window_2=Window_2.new

@window_2.y=100

 

@window_3=Window_3.new

@window_3.x=200

@window_3.y=100

 

#BLOCK 2

Graphics.transition

loop do

Graphics.update

Input.update

update

if $scene != self

break

end

end

 

#BLOCK 3

Graphics.freeze

@window_1.dispose

@window_2.dispose

@window_3.dispose

end

 

#BLOCK 4

def update

@window_1.update

if Input.trigger?(Input::B)

$game_system.se_play($data_system.cancel_se)

$scene = Scene_Map.new

end

end

 

end

BLOCK 1:

1 def main

2 @window_1=Window_1.new

 

3 @window_2=Window_2.new

4 @window_2.y=100

 

5 @window_3=Window_3.new

6 @window_3.x=200

7 @window_3.y=100

 

This the begining of our main method for our scene. Ok... Why isn't there a method named initialize as in almost all other class... Why do a scene have a method called main instead of initialize ? Well, it's all up to Entrebrain, look in the MAIN script (to the very bottom), you will see that code around line #18:

while $scene != nil

$scene.main

end

As you can see, they check for .main (method main), so if you just play with it, and write "pepsi" instead of "main", it would give you "$scene.pepsi". Of course, now all your scene would not work anymore. You would need to go in all of them, and change their method main by pepsi, and voil? ®? But let's stick to what's there by default, main is pretty descriptive of what it does...

 

Line 2: @window_1=Window_1.new

 

Remember Chapter 1 ? We talk about objects and stuff like that, so you should know by now that we just created a new object name @window_1 that is of class Window_1. So, now we can manipulate our window_1 as we want, without affecting the real window.

 

Line 3 and 5 just does the same thing, but they "spawn" window 2 and 3 instead. Then we have lines 4, 6 and 7. Those lines specify our x and y position for the window we want. Wait a sec there... If you remember correctly chapter 1, we have said that to use something like @object.method, we need that method defined in the class of that object... And @window_2.x calls a method named x; this method isn't in our Window class. Let's look in Window_Base, it's SuperClass... Nope, it's not there. Let's then look in the SuperClass of Window_Base, which is Window...Oups, we don't have access to this class. The Window class is the class that built the in-game interface of RPG Maker XP. There is a lot of low-level code in there, thus Enterbrain just implemented it into RPG Maker, and we don't have access to it. But the method x is inside that Window class. You can simply open the help file of RMXP and go to the right section to see all the method inside Window. Just open it, and go in the SEARCH tool, type in Window, and choose the relevant section. The list is right there. You can translate it with Babel Fish.

 

With all that said, we simply configured our window where we want them, and that's all.

 

BLOCK 2:

1 Graphics.transition

2 loop do

3 Graphics.update

4 Input.update

5 update

6 if $scene != self

7 break

8 end

9 end

Line 1: Graphics.transition

 

This line make the scene appears. Once again, it's all interpreted by stuff already coded. This is why RGSS is simpler than Ruby, it's a scripting language. You can use already maded components. If you omit that line, the scene still works, you will just not see it on screen.

 

Line 2: loop do

 

This line start a loop, somewhat like a for loop, but it will repeat itself until the keyword "break" is met. In this case, we break the loop if the scene isn't the same as the scene you are viewing. This is done by the If Statement on line #6. This loop will start when you open the scene, and will always repeats itself.

 

Line 3: Graphics.update

 

This will update the graphics on screen, if you omit that line, you won't see any modification made to the window after the scene was first drawn. This mean that if you delete that line, you won't see the Playing Time running.

 

Line 4: Input.update

 

This update the input command the player can do in the game. This make sure that RMXP is always looking for a keypress made by the player. If you omit that line, you won't be able to use any interactivity in your scene.

 

Line 6, 7 and 8 were explained above, it's our If Statement checking if the scene is changed. If it's changed, we must stop that loop.

 

BLOCK 3:

1 Graphics.freeze

2 @window_1.dispose

3 @window_2.dispose

4 @window_3.dispose

This part is still inside our main method, this part take care of getting rid of our window when we exit the scene. You may be wondering why the window are disposed only when we exit, even if the main method is the first to be executed when you open a scene ? Well, it's because of the Loop. Just like a for loop, what comes after the loop in the code will be executed only when the loop is completed or stopped. So, all that code after the loop will be executed only when we will break that loop in the If Statement; as explained above.

 

Line 1: Graphics.freeze

 

This line take care of making a "transition" between both scene. Because in RMXP, while you play, you are always in a scene or in another. So, when you exit a scene, that line makes sure that the scene you were in actually fade out and do not dissapear instateneously (Did I write that word well ??). If you delete that line, you will see what I mean.

 

Line 2,3 and 4 actually take care of disposing of the window, so they aren't there anymore when you exit the scene. It will destroy the object we have created. That's why you may get errors if you try to access a window that have been previously disposed... You can't access an object that doesn't exist... It's purely logical.

 

This time we can found the method "dispose" in Window_Base, here it is:

def dispose

# Jap Comments

if self.contents != nil

self.contents.dispose

end

super

end

As you can see, that method only take care of getting rid of the content of the window, and there should be more stuff going on. So, once again, the code that is really responsible for disposing a window is in Window, not accesible by us. And you can clearly see that this dispose method call "super", meaning it's calling the method "dispode" in it's SuperClass, which is Window... The dark and mysterious class of RMXP.

 

BLOCK 4:

1 def update

2 @window_1.update

 

3 if Input.trigger?(Input::B)

4 $game_system.se_play($data_system.cancel_se)

5 $scene = Scene_Map.new

6 end

7 end

8 end

 

This block is our update method, which is called in our main loop as explained above. That's is what makes the update method repeats itself over and over again.

 

Line 2: @window_1.update

 

As explained in chapter 1, this will execute the update method of our class Window_1... Simply because we have defined @window_1 as being an object of class Window_1 in our main method. Now you know why we coded an update method in our Window_1 earlier. So, to refresh your memory, we add an if statement in this method, that check if the total second has changed. If it does changes, it then calls refresh and re-draw the time in the window. Since the update method of our scene is looping, the update method of our Window_1 is also looping... and so on. Pretty cool isn't ?

 

As you can see, we don't need any update for the 2 other window, we don't plan on modifying their content while the scene is open.

 

Line 3: if Input.trigger?(Input::B)

 

This is another If Statement, in all it's glory, that is checking the input made by the player. Know that Input:: is in fact a module implemented inside RMXP, we still don't have access to it. If that If Statement becomes true, meaning that IF the playing press the B key (Which is the Escape, X or the NumPad 0 key on the keyboard by default. You can change that in the menu while you play by pressing F1.)

Line 4: $game_system.se_play($data_system.cancel_se)

 

This line will play an SE (Sound Effect) when you leave the scene. This is purely for aesthetic purposes. But I will explain how it's working anyway, as almost everything in RMXP works the same, if you understand that, you will understand almost everything else.

 

$game_system is an object created when you start a game. It's created in Scene_Title, in the main method, here it is:

$game_system = Game_System.new

So, everything that you can do with that object is defined in it's class, which is Game_System, as I explained in my Chapter 1. So, by filtering all the method inside Game_System, you can have a broader idea of what you can with it. So, in that particular line, we are invoking the se_play method, that is requiering an argument, which we specified in the parenthesis. (All of this was in Chapter 1 anyway.) So, if we search in Game_System to found that method, we will found that:

def se_play(se)

if se != nil and se.name != ""

Audio.se_play("Audio/SE/" + se.name, se.volume, se.pitch)

end

end

This will play the SE. Take a look at how it's working, and you may have to follow the traces given by the objects/class names. You can thus try to understand anything by yourself. Become a "code hunter". Everything leave a trace, just follow it.

 

Example: You see se.name in the If Statement... where does it come from ? Well, you can tell that it's a local variable by the way it's written, and since there is a point and a command after it, it should means it's an object, isn't ?

 

Well, you aren't that far off, but in this very case, it's a bit darker. To know what is se.name and how it's working, you must first know that se is actualy the argument receiver of our method se_play. So, we need to check what we sent as argument... Let's see:

$game_system.se_play($data_system.cancel_se)

Okay, we sent $data_system.cancel_se as argument. What is $data_system ? Once again, it's an object, it's followed by a point. So check in Scene Title, you will see it's definition once again, and you will be able to know what it is. Here:

$data_system = load_data("Data/System.rxdata")

Oh boy, now we are going far far away. $data_system refer to the System.rxdata file found in your Data directory of your project. WHat does this file contains ? Well, when you open your datase in RMXP, the last tab is named System, well, that file stores all the stuff you set in that section. Which means that each tab in the database has it's rxdata file... So that's it, se.name is inside System.rxdata, our search ends here.

 

(Well, to be honest, I just improvised all this little "code hunt" section, and I think it's cool to learn, so I will do another tutorial called "Code Hunt" that will give tricks like that.)

 

Line 5: $scene = Scene_Map.new

 

This line actually change the scene. Once again, $scene is an object, the first occurance of it's definition is in MAIN, it define $scene as being of class Scene_Title.

 

So, now, to conclude this chapter, you just have to test play that scene to test it out. Make sure all your 3 Window and your Scene are in your Script Editor, and make a call script in an event with that in it:

$scene=Scene_Chapter_2.new

That will start the scene. To exit the scene, you press Escape.

 

In chapter 3 we will tackle some more advanced stuff. We will learn how to add a selection menu, and how to work with multiple selection menu in the same scene. We will also learn important component of Ruby, like how to do Alias and more advanced stuff, so your script can be more powerful.

 

I hope you like that tutorial, it took a hell lot of time to do... lol (I'm tired... I'm exhausted. Let's post that on the board, and relax for a while... by !)


Lesson 3

You can now download the complete script from this tutorial in a working project below...The project will be updated as the tutorial is updated.

RGSS Lesson

This chapter isn't finished yet. I decided to create it little by little, a section (or more) at a time; that way it should ease your suffering of waiting for it...Each time I update this chapter, you will see it in the NEWS of the forum and the WebSite. I'm sorry, but I have other thing to do, but I will come here once in a while and add a section.

All the major components you need to understand were explained in Chapter 2, and Chapter 1 covered the basic of what is RGSS and Ruby as seen in RMXP. With the knowledge acquiered with lesson chapter 2, you should be able to tackle chapter 3 with more ease.

 

Chapter 3 will cover the Selectable/Command Window; we will learn how to create and manage many different type of menus: Normal Menus, Horizontal Menu, Multiple Columns Menus, Multiple Menus Field (in the same scene). We will also learn some nice technique about the creation of CMS, like how to manage correctly your window in your scene, how to map a picture to replace the window, how to use the skin you want for each window, etc. As usual, we will also cover some bonus aspect of some syntax on the way as we see them. Those bonus info will now be called "Side-Note".

 

So, with that said, let's get going !

1. Selectable/Command Window Structure

Before trying to modify or create any menus, you should all know the structure of the selectable window and command window in RMXP. Basically, the Window Selectable take care of the cursor in the menus, where is the cursor and the help window; the Window Command take care of displaying the menu items in the window as the user specify them in his scene.

 

Let's just see how a simple menu is created and what RMXP did to create it. We will check the standard menu of the Scene_Menu class (This is the main in-game menu that appears when you press ESCAPE while playing.) So open the page named "Scene_Menu".

class Scene_Menu

#--------------------------------------------------------------------------

# * Object Initialization

# menu_index : command cursor's initial position

#--------------------------------------------------------------------------

def initialize(menu_index = 0)

@menu_index = menu_index

end

This is the initialize method of the Scene_Menu. In Chapter 2 we did not created any "initialize" method, we only did our "main" method... Why is there an intialize method here ? Well, it's to be able to specify where we want the cursor to appear in the menu when we call that menu from another scene. The position of the cursor in a menu is called a "Menu Index" in RMXP, so we will refer to that term from now on. So, if you check in Scene_Item near line #71, you will see a line that says: $scene = Scene_Menu.new(0) , this send an attribute to the Scene_Menu class, and the menu index will be 0, if you change the 0 by 1, the cursor will appear at another location in the menu.

 

Side Note:

def initialize(menu_index = 0)

On this line we have an attribute definition as explained in earlier chapter; it is defined in the parenthesis. As you can see, we "hard-coded" a value for the attribute with "menu_index=0". This has the effect of telling the attribute "menu_index" to equal 0 by default, so if we call the class without any attribute definition, it will equal 0 and it won't crash asking for attributes. This is useful if you want to create attribute for a method but you want default values so you don't have to send them everytime you use that particular method.

 def main

# Make command window

s1 = $data_system.words.item

s2 = $data_system.words.skill

s3 = $data_system.words.equip

s4 = "Status"

s5 = "Save"

s6 = "End Game"

@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])

@command_window.index = @menu_index

This is the definition of the actual menu. S may stand for "Selection", but it's not important to know it, what is important to know is that all the lines from s1 to s6 are what we call "Menu Items", those are the menu selections that will be listed in the menu window. Actually, what is defined in the s1 to s6 lines are string (text) that will be shown in the menu. Remember that s1 to s6 are actually variable that you can use later on; actually, you could call them the way you want, and you could even not use them. We use them to simplify our menu creation later on.

 

Side Notes:

s1 = $data_system.words.item

On this line, remember that "$" refer to a global variable. data_system refer to every data you may have set in the database of RMXP. So the line s1 actually take the word you specified in the database in the "items" text field.

 

Once all our menu item are defined, we need to create a window for the menu and send the needed attribute with it. The following line take care of this:

 

@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])

 

If you remember chapter 2, here we just create an object named "@command_window"; which is of class Window_Command. Window_Command objects need 2 attributes to be created. Let check the Window_Command class to check it out. Open "Window_Command" page:

class Window_Selectable < Window_Base

#--------------------------------------------------------------------------

# * Public Instance Variables

#--------------------------------------------------------------------------

attr_reader :index # cursor position

attr_reader :help_window # help window

#--------------------------------------------------------------------------

# * Object Initialization

# x : window x-coordinate

# y : window y-coordinate

# width : window width

# height : window height

#--------------------------------------------------------------------------

def initialize(x, y, width, height)

super(x, y, width, height)

@item_max = 1

@column_max = 1

@index = -1

end

Window_Command is a sub class of Window_Selectable, so we will have to take a peak at Window_Selectable later on. So, we see that the initialize metod need 2 attributes, Width and Commands. Hence the line from Scene_Menu:

 

@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])

 

The first attribute is 160, which is the width, and the second is [s1, s2, s3, s4, s5, s6] which concist of an array containing all our variables s1 to s6 that contains the string (text) for our menu items. Okay, now Window_Command uses a "super" syntax to initialize the method from it's super class "Window_Selectable":

 

super(0, 0, width, commands.size * 32 + 32)

 

We now need to open "Window_Selectable" and take a little peak at the initialize method in there. (Remember Chapter 2, we learned inheritance back then.)

class Window_Selectable < Window_Base

#--------------------------------------------------------------------------

# � 公開インスタンス変数

#--------------------------------------------------------------------------

attr_reader :index # カーソル�置

attr_reader :help_window # ヘルプウィンドウ

#--------------------------------------------------------------------------

# � オブジェクト�期化

# x : ウィンドウ� X 座標

# y : ウィンドウ� Y 座標

# width : ウィンドウ�幅

# height : ウィンドウ�高�

#--------------------------------------------------------------------------

def initialize(x, y, width, height)

super(x, y, width, height)

@item_max = 1

@column_max = 1

@index = -1

end

Ok, of course this class also have a super class named Window_Base; it's normal, window_base contains all the basic of creating a window on screen in RMXP. Window_Selectable need 4 attribute, as Window_Base they are the x position, y position, width and height of the window to create. Let's check the super line from our sub class "Window_Command":

 

super(0, 0, width, commands.size * 32 + 32)

 

So, the X and Y position are both 0, which means the window will be at position 0:0 on the screen. (which is the top-left corner of the screen.) the width is equal to a variable named "width"; which is sent by Scene_Menu in the line:

 

@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])

 

160 is the width. The height is defined as "commands.size * 32 + 32"; this part may have confused you a little bit... First of all, you need to remember that "commands" is actually an array variable defined as:

 

[s1, s2, s3, s4, s5, s6]

 

In ruby you can use many built-in "function" with every components, such as arrays. You can check all of those "funtions" at the following website:

 

http://www.rubycentral.com/book/builtins.html

 

If you check the website, the definition for "size" is:

Returns the number of elements in arr. May be zero.

So, what "commands.size" do is return a value that equal the number of item contained in the array. In our case, the size of our array "commands" is 6. So, the line "commands.size * 32 + 32" can now be known as:

 

6 * 32 + 32

 

Which is: 224... Our height is then 224. So our window is now 160X224 at position 0:0 on the screen. If we go way back to our Scene_Menu class to see our last line, which is:

 

@command_window.index = @menu_index

 

This line actually set the attribute "index" of our object "@command_window" equal to our instance variable "menu_index", that is 0 by default... But if another scene have sent an attribute when creating that menu, it will use that value instead; this is how, for example, that the cursor comes back to where it was when you return from the "Save Menu". "@command_window.index" will also be used later on in the Scene_Menu to know where the cursor is; in order to know what to do when the user press "Confirm" on a selection. (We will see that in depth later.)

 

There is other stuff to see, such as "Column Max, Row, Cursur Width, etc", all those components will be explained so you will be able to create the type of menus you want. But first, what you need to undestand is how a basic one column menu works, and what you can do with it. Once we have created this type of menus, we will create a multi-column menu, and so on with the other type of menus.

 

Now that the basic of a menu have been explained, we can do our own menu in our own scene. You should already be able to create your own window, your own scene and your own variable and objects because of chapter 1 and 2, so we will not go through all that again.Returns the number of elements in arr. May be zero.

2. Creating a Simple Horizontal Menu

First of all, we need a simple scene to create our menu in. I created a simple menu layout and window that you can copy and paste, we will use that layout for our menu. This layout concist of 3 window; one to display the menu, another one to display the help window, and another one to display the content of a selection in the menu. This will look like that: (When Empty...)

Dubealex's RGSS and Ruby Lesson Diagram 11

Just create a new page in your script editor, and paste all the following code in it: (You can call this page "Simple Horizontal Menu".

#===================================================

# â–? RGSS & Ruby Lesson -- Chapter 3 [sIMPLE HORIZONTAL MENU]

#===================================================

# By: Dubealex

# www.dubealex.com (Creation Asylum)

#

# Last Update: April 14, 2005

#===================================================

 

#===================================================

# â–¼ CLASS Scene_Chapter3_menu1 BEGINS (Simple Horizontal Menu)

#===================================================

class Scene_Chapter3_menu1

 

def initialize(menu_index = 0)

@menu_index = menu_index #Store the cursor position

end

 

#--------------------------------------------------------------------------------------------------------

 

def main

@window_a1=Window_a1.new #Content Window

@window_a1.update(false) #Decide whether to show/hide the content...

@window_a1.x=200

@window_a1.y=100

 

@window_a3=Window_a3.new #Help Window

@window_a3.update(" ") #Send the help text by default to the Help Window...

 

 

 

#This is what makes the scene update itself constantly:

Graphics.transition

loop do

Graphics.update

Input.update

update #Call the method DEF UPDATE starting below

if $scene != self

break

end

end

 

#Execute when exiting the scene:

Graphics.freeze

@window_a1.dispose

@window_a2.dispose

@window_a3.dispose

end

 

#--------------------------------------------------------------------------------------------------------

 

def update

 

@window_a2.update #menu

 

if Input.trigger?(Input::B) #Press ESCAPE to exit

$game_system.se_play($data_system.cancel_se)

$scene = Scene_Map.new

$game_map.autoplay #Because we play music in the menu...

end

 

end

#--------------------------------------------------------------------------------------------------------

 

#Options Method Start Here:

 

 

#--------------------------------------------------------------------------------------------------------

end #of the Scene !

 

#===================================================

# â–² CLASS Scene_Chapter3_menu1 ENDS (Simple Horizontal Menu)

#===================================================

 

 

#===================================================

# â–¼ CLASS Window_a1 BEGINS (Content Window)

#===================================================

class Window_a1 < Window_Base

 

def initialize

super(0, 0, 440,380)

self.contents = Bitmap.new(width-32, height-32)

self.contents.font.name = "Tahoma"

self.contents.font.size = 20

end

 

def update(content) #Receive the argument from the menu

if content == false #If false, don't show content

self.contents.clear

self.contents.draw_text(0, 0, 120, 32, "No Content...")

else #else, if true, show it...

self.contents.clear

self.contents.draw_text(0, 0, 440, 32, "SHOW CONTENT")

end

end

 

end

#===================================================

# â–² CLASS Window_a1 ENDS (Content Window)

#===================================================

 

 

#===================================================

# â–¼ CLASS Window_a3 BEGINS (Help Window)

#===================================================

class Window_a3 < Window_Base

 

def initialize

super(0, 0, 640,100)

self.contents = Bitmap.new(width-32, height-32)

self.contents.font.name = "Tahoma"

self.contents.font.size = 20

end

 

def update(help_text) #Receive argument, show relevant help text in window

self.contents.clear #Clear the content before displaying new data !

self.contents.draw_text(0, 0, 440, 32, help_text) #show the content of "help_text" variable

end

 

end

#===================================================

# â–² CLASS Window_a3 ENDS (Help Window)

#===================================================

This code won't work until you add the little menu below, because one window isn't yet defined, the menu itself. So read on, and test it after you inserted the menu in there.

3. Adding the Basic Menu

Now we must add the stuff we want in that scene. First thing first, we will design the menu and what it will do. So here goes:

 

Option 1: ITEMS --> Will open Scene_Items.

Option 2: INFOS --> Will display some text in the content window.

Option 3: MUSIC A --> Will play BGM A.

Option 4: MUSIC B --> Will play BGM B.

Option 5: EXIT --> Will exit as ESCAPE do.

 

Now that we know what we will do, we can go on and create the basic structure for that menu right now, and polish each function (method) for the options later. First of all, we will add in the simple horizontal menu layout in our Scene main method.

 

So open the page you created earlier "Simple Horizontal Menu" and go to line #30. We will add our menu there; paste the following code at line #30:

  s1 = "Items"

s2 = "Infos"

s3 = "Music A"

s4 = "Music B"

s5 = "Exit"

@window_a2 = Window_Command.new(200, [s1,s2,s3,s4,s5]) #Remember, 200 is the Width

@window_a2.y=100 #Set a position for the menu other than 0:0

@window_a2.height=380 #Force a new height for the menu window

@window_a2.index = @menu_index

To start it, now that the scene will work, create an event on your map and add the following Call Script in it:

$scene=Scene_Chapter3_menu1.new

If you go talk to your event, you'll see the scene, but the menu won't do anything except the fact that you can press ESCAPE to exit the scene and return to the map.

4. Adding the Help Display

Now we will add the Help Text display to the menu. The text that will be displayed in the help window (@window_a3) will changed depending on the cursor position on the menu. To allow this, we need to always know where the cursor is in the menu.... So we need to add the code in Def Update of our scene; since this is the method that is constantly being "called" while we are in our scene.

 

So go in Def Update (Still in the Scene Class !), and right below @window_a2.update add this code:

#THIS IS FOR THE HELP DISPLAY WINDOW:

case @window_a2.index #window_a2 is the menu... index is it's cursor position !

when 0 #Remember that the cursor position start at 0, because it's an Array !!

@window_a3.update("Open the item selection window") #item - s1

when 1

@window_a3.update("Display some info in the content window") #infos -s2

when 2

@window_a3.update("Play BGM A") #Music A - s3

when 3

@window_a3.update("Play BGM B") #Music B - s4

when 4

@window_a3.update("Exit the menu and return to the map") #Exit - s5

end

Now, if you test that again, you will see that when you move your cursor from option to option, the help window at the top will display the relevant tooltip for the selected option. How is it done ? Well, if you haven't guessed by looking at the code, read on:

 

Our Help Window is named Window_a3... here's the code for it:

1  class Window_a3 < Window_Base  

2

3 def initialize

4 super(0, 0, 640,100)

5 self.contents = Bitmap.new(width-32, height-32)

6 self.contents.font.name = "Tahoma"

7 self.contents.font.size = 20

8 end

9

10 def update(help_text) #Receive argument, show relevant help text in window

11 self.contents.clear #Clear the content before displaying new data !

12 self.contents.draw_text(0, 0, 440, 32, help_text) #show the content of "help_text" variable

13 end

14

15 endLine #1 to 9 should not be a problem; if you don't understand them, try to read chapter 1 and 2, it was covered in them. Line 10 create a new method called "Update" that we can call from our menu (from our scene) to update what is written at the top. We are asking for an argument that we named "help_text", and we display that argument on screen on line #12...

 

If we look at our menu code, we can see this line everytime we want to change the text displayed on the help window:

 

@window_a3.update("New Text")

 

@window_a3 is our object we created in our Def Main of our scene, and update is the method we created in our class Window_a3 (explained above). What follows is the argument (the help text to display) that is sent to that update method.

5. Adding the Option Functions

We all agree that a menu without any function isn't a really a complete menu... So now is the time to add those function. It's not that complicated, we need one method per option we have in the menu; in this case, we have 5 options, so we will need to create 5 method. So we will add them right now:

 

Remember the place where you added the Help Display code earlier, in Def Update (below @window_a2.update) ? Well, the method you will have to add in the scene goes right after that Def Update. Just look for the following comments to find exactly where:

#Options Method Start Here:

Now, below that line, paste this code, I will explain it after.

def item #s1, menu_index=0

$game_system.se_play($data_system.decision_se) #play an SE

$scene = Scene_Item.new #Syntax to change scene

end

 

def infos #s2, menu_index=1

@window_a1.update(true)

end

 

def music_a #s3, menu_index=2

Audio.bgm_play("Audio/BGM/song_a.mid", 100, 100) #Play custom BGM

end

 

def music_b #s4, menu_index=3

Audio.bgm_play("Audio/BGM/song_b.mid", 100, 100) #Play custom BGM

end

 

def exit #s5, menu_index=4

$game_system.se_play($data_system.cancel_se) #play an SE

$scene = Scene_Map.new #Syntax to change scene

$game_map.autoplay #Because we play music in the menu...

end

Side Note:

Using method for menu options

You do not need (it's not necessary) to use method for each option menu, especially if they are simple and short as this sample menu. But I decided to show it that way because sometimes, menu option could do long and more complicated operation, and in this case, it's better to break them down into different method. If you would want to test it without using method, you can simply replace each method call from within the Case Statement by the content of each method, and voilÃ? . I prefer to use method, that way it's better classified.

 

A little note, the lines that start with Audio.bgm_play have to be configured with the names of your own audio files, present in Audio\BGM. Change the name between parenthesis, and you are set to go.

 

There isn't much to explain, for those are very simple operations. I don't think I have to explain the code itself. Each of those method will be called by a new code we will add in our Def Update, just below the code we added for the Help Display window. So, look for this code in Def Update of our Scene:

@window_a3.update("Exit the menu and return to the map") #Exit - s5

end

 

And below it, add the following code:

#THIS IS FOR THE OPTION FUNCTIONS:

if Input.trigger?(Input::C) #Do the following if ENTER is pressed...

case @window_a2.index #window_a2 is the menu... index is it's cursor position !

when 0 #Remember that the cursor position start at 0, because it's an Array !!

item #item - s1

when 1

infos #infos -s2

when 2

music_a #Music A - s3

when 3

music_b #Music B - s4

when 4

exit #Exit - s5

end

end

Now, if you go and test your menu, it will be completed with all the function and help display. For the Content Window, you can just add as much line as you want or show any other stuff in place of text, you can tweak this code and do what you want with it, it's the goal of a tutorial after all alright.gif

 

I know this was kind of "basic", but you can tweak those code and do way more, like changing the bgm, showing pictures or changing window/picture from places to another places dynamically, instead of just showing the help window...

 

I do not have the time to continue doing tutorial at the moment...

Share this post


Link to post
Share on other sites

I see you went grave digging, I think it would be better to put them these into spoilers as I think it's kind of hard to read. As a giant lump page.

Share this post


Link to post
Share on other sites

Yeah certainly hard to read. Just moving this from the website to the forum. I probably spent 5 hours formatting this the first time and I'm not gona go through and fix it up. The article on the website will be deleted and the link to it redirects here, so that's why this particular topic was made.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...