Home > Tutorials > Ruby Game Scripting System > RGSS For Dummies Tutorial 4: Containers and Methods
RGSS For Dummies Tutorial 4: Containers and Methods
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:
Code:
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:
Code:
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:
Code:
array_name[index]
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:
Code:
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
Code:
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:
Code:
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:
Code:
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:
Code:
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:
Code:
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:
Code:
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:
Code:
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:
Code:
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:
Code:
- 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:
Code:
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:
Code:
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:
Code:
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:
Code:
greetings("String")
"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:
Code:
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:
Code:
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:
Code:
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:
Code:
abs(3)
abs(-6)
abs(2 - 6)
abs(23545)
abs(0)
x = -7
abs(x)
Here's another mathematical method:
Code:
def square (something)
p something * something
end
square(3)
square(15)
square(-4)
The square method just multiplies the value by itself. Another example:
Code:
def sum (value, another_value)
p value + another_value
end
sum(3, 6)
sum(1, 7)
And a text example:
Code:
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:
Code:
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:
Code:
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:
return value
Here are two of earlier examples re-written so that they return values instead of printing them:
Code:
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:
Code:
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:
Code:
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.
|
|
Details
|
|
Tutorial:
|
RGSS For Dummies Tutorial 4: Containers and Methods |
|
Date Listed:
|
2008-06-05 |
|
Author:
|
RPG
|
|
Total Hits:
|
5580 |
|
|