|
>>> |
| Home > Articles > Tutorials > Ruby Game Scripting System (RGSS) > RGSS For Dummies Tutorial 4: Containers and Methods
|
|
RGSS For Dummies Tutorial 4: Containers and Methods |
|
Author: RPG
Updated: September 04, 2007
|
|
Introduction
|
|
A container is an object that contains other objects, a treasure box is a
treasure container and a cow is a milk container. In programming, you might need
to have many variables and placing variables in a container might make it easier
to handle them. The main containers you'd use are arrays and hashes. As for
methods, they are blocks of code that you can call. You ask a method to do
something; the method does whatever you want and might also return something
else to you. Of course, all that makes little sense but this is just the
introduction :) |
|
Contents
|
|
1. Arrays
1.1. for loop, revisited
2. Hashes
2.1. for loop, AGAIN
3. Blocks
4. Methods
4.1. Return!!
4.2. ?
4.3. rand
5. Conclusion
6. Summary
|
|
1. Arrays
|
|
In the first tutorial I told you about a city filled with John Smiths, everyone
shares that name which leads to many problems. The mayor, John Smith came up
with this brilliant idea: Everyone should have a number! So, everyone got a tag
on their head with their number, John Smith[0], John Smith[1], John Smith[300],
etc. So, although all the citizens share the same name, they have unique numbers
or indexes. It's useful to have one handle to describe many items; in
programming, you might need to store many values in many variables, maybe you
want to have a variable for each item the party owns. There are two main
problems, first of all you'd need to declare many variables, maybe 100 or more
depending on your game, each variable would need it's own name and own
initialization. The second problem would be that it's hard to keep count of all
those variables, because they aren't really a 'group'. You can't do one thing to
all of them at once or search through them to find something. Man, it'd take
ages to make a simple item system...
Don't worry though! Arrays are there to
make your life easier. An array is a container of variables, all those variables
hold one name and are referred using a number called an index. Once you
associate a name with an index you can deal with an array element just as you do
with normal variables. Note that an array itself is considered a variable, but
it stores many values. Enough with words, here's an example:
x = 13
arr = ['Alex is really stupid', 0, x]
p arr[0]
p arr[2]
|
Running this example would output "Alex is really stupid' and 13. First we
create a new variable called x and store 13 in it, then we create an array with
3 elements 'Alex is really stupid', 0 and x (13). Finally we print the first and
the third element. There are a few things to note, first of all, you create an
array like this:
array = [element1, element2, ...., elementN]
|
If you don't put anything between the brackets (just [ ]) you will be creating
an empty array. You can always add stuff to arrays but we'll discuss that in a
later tutorial. Secondly, an array can have any number of elements and
elements can be of any type (although most of the time they'd all share one
type). Another thing to note is how array elements are addressed:
Index is a value between 0 and the number of elements in the array - 1. This is important, arrays start with 0, so the first element of arr is arr[0], the last
element is arr[2] which is also the third element of an array of size 3. (Size
is the number of elements in the array) The neat thing about arrays is that
you can hold tons of values in one variable, you can just put all the items in
an array called items and then access any item by using its index. The array
class also provides many methods to deal with arrays, you can add more elements,
remove elements, sort elements, do something to all elements, etc. To give
you an idea about the importance of arrays, you can look at rmxp's default
script. RMXP creates arrays for the elements in the database. For example,
there's an item array, a weapon array, a hero array, etc. You can access those
arrays to get information from the database, and that's how the item menu works.
First, a for loops is used to iterate through all the database items (we'll see
how to do that in a moment), then a check is made to see if the party has at
least 1 item of that kind, if it does then the item will be drawn to screen.
Something like:
for item in $data_items
if $game_party.item_num(item) > 0
# draw item
end
end
|
You can try pasting that code as a script command in an event, and might like to change # draw item (placeholder comment) to something like:
print 'The party
has a/an ' + item.name
Don't worry about the weird stuff mentioned in this
example (for loop on arrays, something.something, etc.), I just wanted to show
you a practical example that gives you a better idea about arrays and their
uses. If it still sounds complicated, just think of an array as a list of
variables, you can access any variable by its index rather than using a unique
name. You can always add or remove items, and you can operate on all of them in
few lines.
|
|
2. for loop, revisited
|
for var in array_name
do something with var
end
|
Using a for loop to iterate over array elements is a piece of cake, var could be
any name you choose to represent the current element, and array_name is the name
of the array. The for would iterate over each element (starting from 0) and one
element would get 'focus' through each iteration, in other words, at the
beginning of the loop, var would hold the value of array_name[0] (first element
in array), and in the second iteration it would hold the value of array_name[1].
Here's an example:
numberz = [1, 2, 3, 4]
for c in numberz
p c * c
end
|
The output would be (1, 4, 9, 16). That's the squares of the array elements.
What happens is: the print statement would be executed 4 times (the
size of the array), each time the variable c will hold an element from the
array. First it'd hold the first element, which is 1, then it'd multiply it by
itself resulting in 1, then it'd do the same thing for 2 and get 4, and so on.
You think it was too complicated? Here's another example:
names = ['Alex', 'Joe', 'Tom', 'Bob', 'James']
for whatever in names
p whatever
end
|
This example just prints all the elements in the array, starting from Alex and
ending with James. I don't think 'whatever' is a good name for the for variable
though, using short names is generally a good idea. You can use a name that
reflects elements of your variable (like 'number' in this example, or 'item' in
the items example), because the for loop makes more sense then.
There's
another way to iterate over elements of an array, you can just treat the array
as a range of indexes from 0 to array size - 1 (bounds of the array), something
like this:
arr = ['woooo', 1, 250, 'man you really need to eat more cows', -2.8, false, 'for great justice']
for i in 0..arr.size - 1
p arr[i]
end
|
In this form, the variable following the for (i) is used as the index (since it
goes from 0 to arr.size - 1) and is used with the variable name to get the
element. As for the arr.size thing, you see... an array is an object, and
objects are cool thingies that make your life easier. Objects have properties
and actions (variables and methods), so if you have an object called hero, it
might have a property called name which tells you the hero's name, and to access
it you type hero.name. Don't get it? Forget it! Just know that array.size
returns the number of elements in an array, it'll all become clearer once we
enter the world of object oriented programming (next tutorial)
Which form to
use? Well, the second form involves more typing, but there are two advantages I
can think of:
1. You can use the counter variable (after the for) to know the number of the current iteration, which can be useful at times. You can do the
same thing in the first form (for element in array) by using a variable that you
increase manually or some other way, but it'd probably not be as efficient as
the second (0..array.size - 1) form. 2. You don't have to loop through the
entire array, you can change the start value and end value of the range to
whatever suits your needs. If you need to loop from the 2nd to the 4th element
for example, you might do: for i in 1..3 3. I can't think of anything else
at the moment. :P
So, use whatever suits you, if you need to loop over a part
of the array or want to keep count of iterations, use the second form.
Otherwise, use the first form.
|
|
2. Hashes
|
|
Arrays use numbers for indexes, arr[0] is the first element in arr and arr[size
- 1] is the last element. That's good, but there are times when you like to
group variables together (under one name) and yet you think that the number
index has nothing to do with the value itself. In a list, having a number for
index is good because you know the first item in the list has index 0 and the
second has 1 and so on. But things aren't always referred by numbers, let's say
you have some flying monkeys and you want to have an array to store their ages,
something like:
my_pets = [3, 6, 1, 10, 5]
p my_pets[1]
p my_pets[3]
|
The output would be 6 and 10.
The problem is... you will need to remember the number of the pet to know it's age, but in reality you wouldn't need to refer to
the pet by number. Referring to them by names would be better, picture something
like this:
p my_pets['Stalin']
p my_pets['Castro']
p my_pets['Bush']
|
With such an 'array', we could easily get Bush's (the monkey, not the idiot) age
by using his name. That's what hashes are for, a hash is basically a container
that stores keys and values pairs, a key is used to refer to the value just like
the index is used in array. The cool thing is that the key (and the value) could
be any object. It could be a string (like in the previous example) or a number
(no need for it to be ordered) or a boolean value etc. Hashes are similar to
arrays but instead of declaring elements inside [ ] brackets, you declare them
within { } brackets. Each element is a key and value pair like this:
key=>value. Here's an example that creates a hash:
teh_hash = { 'Moo'=>'Cow', 5 => 'Five', true => 1, 0.8 => false }
p teh_hash['Moo'] # Outputs 'Cow'
p teh_hash[0.8] # false
p teh_hash[true] # 1
p teh_hash[5] # 'Five'
|
To declare an empty hash, you use empty brackets { }. Remember that keys need to
be unique, or else you'd confuse the compiler. (Unique means... two keys can't
have the same name)
|
|
2.1. for loop, AGAIN
|
|
You can use the for loop on hashes too, but it's a bit different. You can either
iterate over the keys or the values, check the following example:
hash = { 'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3' }
for stupid_key in hash.keys
p stupid_key
end
for lame_value in hash.values
p lame_value
end
|
The first for loop will iterate over the keys, and the variable stupid_key (just
a random name) will hold the value of one key each time. The keys will then be
printed (p stupid_key) so the output will be 'key1', 'key2', 'key3'. Notice that
we use hash.keys after the 'in' in the for loop, you need to specify if you want
to loop through keys or values so you add .keys to the name of the hash if you
want to go over keys, and add .values if you want to loop over values. In the
second loop we loop over and print each value to get the input 'value1',
'value2' and 'value3'.
|
|
3. Blocks
|
|
A block is just a group of related computer instructions, it's just some lines
of code enclosed in something. Think of a block as a list of commands, or maybe
like a box with smaller boxes inside. Here's another way to think about blocks:
- Command 1
- Command 2
- Command 3
- Command 3 : 1
- Command 3: 2
- Command 3 : 2 : 1
- Command 3 : 2 : 2
- Command 3 : 3
- Command 4
- Command 4 : 1
- Command 4 : 2
- Command 5
|
Blocks don't need to be ordered though... here's a picture that might help you
understand things better:

This is a simple (fake) program that works like a calculator. Squares indicate
blocks, the biggest square (surrounding everything) is the page(s) where the
program is typed, the big box where all the smaller boxes are. On the left side
are some commands, most of the commands call things, and then there's an if
command that will always be false, and finally a print statement and an exit
command after some more calls. The output will be:
Answer is: 3
Answer is: 15
Answer is: 10
Answer is: 3
Answer is: 6
Answer is: 90
"Answer is: You are a loser!"
|
As I said earlier, a block is just some lines of code grouped together. The
control flow statements we discussed in an earlier tutorial had their own block
that ended with "end". For example:
if cow == true
# Block starts here
p "cow is true!"
p "that is so super!"
x = 2
p x
# And ends here
end
|
All the print statements (and the assignment statement) form the block for the if. As you can see, this block is some set of prints and one assignment that are
grouped together and will only be executed if the variable cow is true. The
statements in a block are like a group of close friends who do everything
together; they are either executed together or not executed at all.
The while
loop has a block as well, the block is executed while the condition is met. Same
goes for the other loops (for example, the for loop is executed over and over
with different values each time.)
Controlling the flow of your program isn't
the only propose of blocks though. You can also have a named block and then call
it whenever you need to. A named block is called a method or a function, and is
very useful. For example, if you use the same code in different places in your
program (like, you want to print a warning message every now and then), you can
create a block with these commands, give the block a name and then call it as
you please. Further more, you can pass (send) values to that block; for example,
a block might accept a name and print a greeting message to that name. Like
this:
def greetings (name)
p "Hello " + name
p "How are you doing today?"
end
|
The 'def' thing is just a keyword that tells the program that we are defining
(def stands for define) a method (a named block), the way of creating methods
varies from a programming language to another, but in ruby you'd often use def.
The next thing you see is greetings, it's the name of the block, and we use the
name to call the block later. Between the following parentheses is the variables
or values that the method requires or asks for. name is the variable that will
accept whatever is passed to the function. The actual block begins after the )
and with the p "Hello " + name. You call this method like this:
"String" could be any string, like "Alex" ,"Sephiroth" or whatever. Now, assume
that you are the Ruby interpreter (or that you are RMXP or the computer) and
that you are asked to read this command (greetings("String")) and execute it.
Here's what you will do:
- Look for a method (block) named greetings by looking for (def greetings)
- The greetings method accepts a string, so you pass the string between the parentheses in the call ("String")
- How to pass a value? Well, you see that greetings is defined like (def greetings(name)), you just create a variable called name and assign it to "String" (like doing name = "String")
- Now you enter the greetings block.
- You are asked to print (p) "Hello " + name, since name's value is now "String" you print "Hello String"
- You are asked to print "How are you doing today?" and you do that.
- You find 'end' on the following line, it tells you that the method ends here, so you go back to greetings("String") (the statement that called the method) and execute whatever follows it normally. |
If you don't understand all this then don't panic, it'll all be clearer once we
get to the examples. Just notice how we called 2 lines of script just by typing
one line, you can also call 100000000000000000 lines of script with 1 line, this
helps organize your program a bit. Also note that you can call greetings
whenever you want, as many times as you want and with any name you want.
Now, let's try to understand how that example pic works, here's the pic
again:

This is not really Ruby code, there isn't a command called "Call" and you don't
use "require" or req. before variable names. Actually, "require" has a different
meaning in Ruby. This is just an example to help you understand how methods
work.
First of all, we call the method sum with 2 values: 1 and 2. That's like writing (sum(1, 2)) in Ruby. The sum method is then executed with these
values, they are assigned to variables x and y and are then printed to screen.
Before calculating the sum though, a call to another method called answer_is is
made. You can normally call methods from within methods. In the same way, other
methods are called (subtract, multiply) with different values. Near the end of
the program, answer_is method is called to print "Answer is: ", notice that
answer_is doesn't require any variables because it doesn't need to operate on
any. In Ruby you call a method that doesn't accept a value by either using empty
parenthesis (answer_is()) or without any parenthesis (answer_is), when defining
it you do the same thing too (def answer_is() or just def answer_is).
|
|
4. Methods
|
|
I hope that "small" introduction gave you an idea about methods. If not, don't
worry, it might become clearer with some examples. A method is a named block
that can accept values and can return them as well. In other programming
languages, methods are often called functions (a method is actually a member
function of a class). Note that methods aren't executed unless they are called,
if you just create a method in the middle of your code it won't be called unless
you do it yourself. Think of method calls as buttons that do different things,
these things won't happen unless the button is pressed. A method definition
would generally look like this:
def name (value1, value2, value3,... etc.)
method's body
end
|
name is the name of the method, we use it to call the method later. Method names
are like variable names, they must start with small letter characters and can't
include spaces. value1, value2, value3,...etc are called parameters. Parameters
are variables that accept whatever is passed to the method. You can have as many
parameters as you like, and you can have a method that doesn't accept any value
(just use def name() or def name). method's body is the block to be executed
when the method is called, it can be any valid Ruby code. It could also be as
long as you want but having big methods might defeat the purpose of splitting
your program to pieces in my opinion. But then, having too many methods isn't a
good idea too (hard to remember)... just try to not go crazy and create a method
for every two line, and don't make a method with 200 lines of code either.
Method declaration ends with 'end', when the program reaches that (or encounters
a return statement, which will be discussed later) it will go back to thee
method call and continue program execution normally. So, how to call a method?
Easy:
name(value1, value2, value3,...etc.)
|
name is the name of the method you want to call, and value1, value2, ...etc. are the values you want to pass to it. Don't pass more values than the method
requires (in it's definition) or less than that (unless you use default values,
will be discussed later). To call a method that requires nothing to be passed to
it, you just use the method name without parenthesis, or with empty parentheses.
The values can be either direct values (1, 4, "hello world", true) or variables
holding the values, it could be also an expression like (1-2, "hello" + var, 3 +
5 - 2, var
I bet you're bored, aren't you? You want to see some actual
examples? Well, here's my favorite:
def abs (some_number)
if some_number >= 0
p some_number
else
p -1 * some_number
end
end
|
This method prints the absolute value of whatever number you pass to it. The absolute value doesn't have a negative sign, it's used for measuring things like
the height of someone or the distance between two objects, such values can't be
negative (you can't say -10 kilometers, for example). What the method actually
does is checking if the number passed to it is over or less than 0. If it's more
than 0 (positive) or equal to 0 then we just print it as it is (it's already
positive, so the absolute value of 3 is 3, and the absolute value of 19 is 19,
etc.). On the other hand, if the number is less than 0 (negative), we need to
get rid of the negative (-) sign. We do that by multiplying it by -1 (negative *
negative = positive, -number * -1 = +number, -3 * -1 = 3, -19 * -1 = 19) and get
a positive value. If you don't understand this example then I suggest going over
variables or/and control flow tutorials again. The two ends at the end of the
example are for the if statement and the method. Well, now that we have the
method, we can call it. Calling a method is really simple, you just do:
For example:
abs(3)
abs(-6)
abs(2 - 6)
abs(23545)
abs(0)
x = -7
abs(x)
|
Here's another mathematical method:
def square (something)
p something * something
end
square(3)
square(15)
square(-4)
|
The square method just multiplies the value by itself. Another example:
def sum (value, another_value)
p value + another_value
end
sum(3, 6)
sum(1, 7)
|
And a text example:
def story (name, age = 9999)
p "Once upon a time there was an idiot called " + name + "."
p "He was #{age} years old."
end
story ("Alex", 13)
story("Goku", 3)
story("ramirez")
story("Whatever")
|
This examples demonstrates an interesting way to declare parameters. This method
accepts a name and an age and then prints them, the #{age} thing inside the
second p's string just shows the value of age inside the text (you could also
write the first p as p "Once upon a time there was an idiot called #{name}."),
the output of the following calls would be about Alex, whose age is 13, about
the 3 years old Goku, the 9999 years old ramirez and the 9999 years old
Whatever. Notice how there's a (= 9999) after age in the definition of the
method. Also notice how we call the method with only one value ("ramirez", and
then "whatever") and without specifying the age. Yet the output of
story("ramirez") tells us that he's 9999 years old! You see, age has a default
value, the default value is used when no value is specified when calling the
method. You can have default values for as many parameters as you want and
you'll be able to call the method in different ways. Just note that you can't do
this (def something(value1 = 3, value2, value3)) or this (def something(value1,
value2 = 4, value3)) but you can do this (def something(value1 = 2, value2 = 4,
value3 = 5)) or this (def something(value1, value2, value3 = 4, value4 = 5)). In
other words, if a parameter has a default value, all parameters following it
must have a default value or you'll get an error. So, back to examples:
def fill(an_array, a_value)
for i in 0...an_array.size
an_array[i] = a_value
end
p an_array
end
|
This method assigns each element in an_array to a_value by looping over the
array elements and assigning them to the value, after that it prints the filled
array. Easy stuff~
|
|
4.1. Return!!
|
|
Methods can return values and act as variables, if the method abs would return
the abs value instead of printing it, there are many things we could do with
that value, like:
var = abs(5)
p "WOW" if abs(-9) > 3
abs(abs(-7))
p abs(22)
|
Instead of limiting the method to just printing, we can do many things with
method that return values. If you want to return a value you do:
Here are two of earlier examples re-written so that they return values instead
of printing them:
def abs (some_number)
if some_number >= 0
return some_number
else
return -1 * some_number
end
end
x = abs(19)
p x
p abs(7 - 12)
|
def sum (value, another_value)
return value + another_value
end
p sum(0, 6)
lol = sum(-1, 1)
if sum(lol, sum(5,6)) > 14
p "whatever"
end
|
The program execution ends once a return statement is encountered, you can have
many return statements but only one will be executed.
You can also use return just to exit the method without returning anything, to do that just use (return)
without a value. This is often done to check if the conditions for a method are
met. For example:
def age_check(age)
if age < 13
p "You are too young to enter this method! Exiting the method now!"
return
end
p "You are old enough to enter the method, yay!"
end
|
|
|
4.2. ?
|
|
Sometimes you will see method names ending with ?, this is just a style used to
give more information about the method. ? is used to indicate that the method
returns a Boolean value, that is, it returns a true or false. For example, a
method called file_exists? returns true if file exists and false otherwise, a
method called include? will return true if the element is included in an array
and false otherwise, etc. Using questions as method names is a good idea if the
method returns a Boolean variable, but it's just a 'good idea', you don't have
to add ? if you don't want to.
|
|
4.3. rand
|
|
Ruby comes with some built-in methods, and one of them is rand(max). rand is a
method that returns a random number between 0 and max - 1. You can then directly
use the number or store it in a variable or whatever. Here's an example:
var = rand(10)
if var > 5
p "Over 5!"
elsif var < 5
p "less than 5!"
end
|
|
|
5. Conclusion
|
|
Containers are objects containing more objects, just like a box with many items.
Each container allows you to access its elements by using a unique value called
the index. Arrays are containers representing a numbered list of item where
numbers start from 0. The number of the element is used as an index to access
that element. The for loop can be used to iterate over elements of the array.
Hashes are another type of containers; they can have anything for the index. You
can use the for loop on hashes to loop over keys or values.
This tutorial took me over 3 hours to write it. I tried to make it as simple as
possible but I know I've failed. Understanding methods is essential, so try
reading this tutorial again if you are unclear about something. I actually think
methods are really easy to understand, a method is just a block of code that is
called, you can pass values to methods, and also return values from them, what's
hard about that?
The next tutorial will introduce classes and object oriented programming, so I guess it'll be less boring than this one. :P
|
|
6. Summary
|
- An array is a container that can hold multiple values, a unique numeric index is used to refer to (access) each value. - An array index always ranges between 0 and size of the array - 1 (array.size - 1) - You can use a for loop to iterate over arrays - A hash is a container that can hold multiple values. a unique key is used to refer to (access) each value. - A hash key is an object that can be of any valid data type. - You can use a
for loop to iterate over hash keys or values.
- A block is a group of related commands. - A method is a named block that can be called from other parts of the program. - The def keyword is used to define a method. - You can pass values to methods; the values are accepted by the method's parameters. - You can have a default value that'll be used for a parameter if no value is passed to it. - Methods can return values using the return statement. - The method is exited once a return statement is encountered. - ? is often used
at the end of a method's name to indicate that the method returns a Boolean value. - rand(max) is a built-in method that returns a random number between 0 and max - 1 - Having 3 eyes makes you special. |
|
|
|