|
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 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:

*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:

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... :clap:
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 !)
|