Home > Tutorials > Ruby Game Scripting System > RGSS Windows - Lesson 2b: Simple Menu Remodel
RGSS Windows - Lesson 2b: Simple Menu Remodel 
Today we're going to talk about doing some very simple remodeling on an existing scene. We're going to take the default menu and change it up a little.
What I want to do:
- Replace the Play Time window with a Location window
- Move the command window, playtime window, and gold window to the right side of the menu
- Replace the character sprites with faces
Not a very big remodel, but it would be enough for a first project looking to be a little less "RTP."
To start, we're going to create a new window for the Location window.
Code:
class Window_Location < Window_Base
def initialize
super(0, 0, 160, 96)
self.contents = Bitmap.new(width - 32, height - 32)
end
end
So far, so good. However, I just ran into one teensy little problem. By default, you can't access a map's name easily. Don't worry, that easy enough to fix. First, we need a way to get the name from the $game_map global variable. Head over to Game_Map, and add this line under the attributes (attrs).
Code:
attr_reader :name # the name of the current map
Remember what we said about attributes?
Attributes are "hooks" into the internal data of an object. Normally, if I define an instance variable @name inside a class, no other object can see it! That's useful if the variable is only used internally, but not so great if you wanted to look at it or set it from outside that object.
Now, you could do this to make that instance variable readable:
Code:
class Something
def name
return @name
end
end
Code:
t = Something.new
print t.name
The return statement tells the interpreter to exit the method, remember? However, if you follow the return statement with a value, it "returns" that value from the method. Thus, someone outside class Something can "read" the instance variable @name.
To make it writeable, you could do this:
Code:
class Something
def name=(nm)
@name = nm;
end
end
Code:
t = Something.new
t.name = "hello"
This allows you to "write" to the @name variable from outside the Something class.
However, writing all that out every single time is something of a pain. So Ruby gave us 3 shortcuts.
Code:
class Something
attr_reader :name
end
is the same as
Code:
class Something
def name
return @name
end
end
Code:
class Something
attr_writer:name
end
is the same as
Code:
class Something
def name=(nm)
@name = nm
end
end
and
Code:
class Something
attr_accessor :name
end
is the same as
Code:
class Something
def name
return @name
end
def name=(nm)
@name = nm
end
end
Make sense? Feel free to ask questions if it doesn't.
Okay, back to the code. We need to get the map name from the stored database data. Fortunately for us, there's a way to load that data easily.
Code:
# load the name of the map
@name = load_data("Data/MapInfos.rxdata")[@map_id].name
load_data is a global function that is provided as part of RGSS. It reads rxdata files. In this case, we're asking it to read the MapInfos file, which will return an array of MapInfo objects. You can see the specs for the MapInfo class in the help file under Game Library -> RPGXP Data Structure.
The array is indexed by map id. So map 7 in your editor is at index 7 in the array you get back. Thus, load_data("Data/MapInfos.rxdata")[@map_id] gives you the MapInfo object for the current map. Now we have the name of the map in a convenient location.
Back to the Window in progress.
Code:
class Window_Location < Window_Base
def initialize
super(0, 0, 160, 96)
self.contents = Bitmap.new(width - 32, height - 32)
refresh
end
# clear and redraw the contents
def refresh
self.contents.clear
# draw the word "location" in blue
self.contents.font.color = system_color
self.contents.draw_text(4, 0, 120, 32, "Location")
# draw the name of the map in white
self.contents.font.color = normal_color
self.contents.draw_text(4, 32, 120, 32, $game_map.name, 2)
end
end
The only real surprises there are the font colors, so let's take a look at those.
self.contents.font contains the font properties for the drawing pane of the window. If you'd like to know more about the Font class, look in the Help file under Game Library -> RGSS Built-In Classes. By setting the font properties for a bitmap, you can change the look of the text drawn on the window. In this case, the color.
system_color and normal_color are methods defined in Window_Base (remember, anything Window_Base can do! ;) )
Here are all the "color" methods defined in Window_Base:
Code:
#--------------------------------------------------------------------------
# * Get Normal Text Color
#--------------------------------------------------------------------------
def normal_color
return Color.new(255, 255, 255, 255)
end
#--------------------------------------------------------------------------
# * Get Disabled Text Color
#--------------------------------------------------------------------------
def disabled_color
return Color.new(255, 255, 255, 128)
end
#--------------------------------------------------------------------------
# * Get System Text Color
#--------------------------------------------------------------------------
def system_color
return Color.new(192, 224, 255, 255)
end
#--------------------------------------------------------------------------
# * Get Crisis Text Color
#--------------------------------------------------------------------------
def crisis_color
return Color.new(255, 255, 64, 255)
end
#--------------------------------------------------------------------------
# * Get Knockout Text Color
#--------------------------------------------------------------------------
def knockout_color
return Color.new(255, 64, 0)
end
You can change them here, if you like, and they'll change all across your game. The Color class's initialize method takes 4 arguments. They are the red, blue, green, and alpha transparency values of the color. Each should be a value from 0 to 255. Alpha is optional, if you omit it is assumed to be 255. So:
Color.new(0, 0, 0, 255) is a completely opaque black, Color.new(255, 255, 255, 160) is a partially transparent white, and Color.new(90, 100, 140) is a completely opaque blue.
Okay, now that the window is complete, we need to add it to the scene. Since it's the same height and width of the steps window, all we need to do is replace the steps window with the location window.
Find this line in Scene_Menu:
Code:
@steps_window = Window_Steps.new
And rewrite it to look like this:
Code:
@location_window = Window_Location.new
Now all we need to do is a search and replace on @steps_window and replace it with @location_window. Hit Ctrl+H and do so.
Now run your menu. It should look like this:

Not too bad so far, right?
Now let's shift the windows on the left to the right side of the menu, shall we? The first thing we need to know is: how wide in pixels is that status window, exactly?
Go to Window_MenuStatus. Looks like it's 480 pixels wide:
Code:
def initialize
super(0, 0, 480, 480)
The RMXP screen is 640 pixels wide, and 480 pixels tall. The upper left pixel is at 0, 0. So to put something 480 pixels to the right, we'll need to put it at x coordinate 480. Simple enough, right?
Head back to Scene_Menu and look at the main method:
Code:
def main
# Make command window
s1 = $data_system.words.item
s2 = $data_system.words.skill
s3 = $data_system.words.equip
s4 = "Status"
s5 = "Save"
s6 = "End Game"
@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])
@command_window.index = @menu_index
# If number of party members is 0
if $game_party.actors.size == 0
# Disable items, skills, equipment, and status
@command_window.disable_item(0)
@command_window.disable_item(1)
@command_window.disable_item(2)
@command_window.disable_item(3)
end
# If save is forbidden
if $game_system.save_disabled
# Disable save
@command_window.disable_item(4)
end
# Make play time window
@playtime_window = Window_PlayTime.new
@playtime_window.x = 0
@playtime_window.y = 224
# Make steps window
@location_window = Window_Location.new
@location_window.x = 0
@location_window.y = 320
# Make gold window
@gold_window = Window_Gold.new
@gold_window.x = 0
@gold_window.y = 416
# Make status window
@status_window = Window_MenuStatus.new
@status_window.x = 160
@status_window.y = 0
# Execute transition
Graphics.transition
# Main loop
loop do
# Update game screen
Graphics.update
# Update input information
Input.update
# Frame update
update
# Abort loop if screen is changed
if $scene != self
break
end
end
# Prepare for transition
Graphics.freeze
# Dispose of windows
@command_window.dispose
@playtime_window.dispose
@location_window.dispose
@gold_window.dispose
@status_window.dispose
end
See how the x coordinates of the windows are defined for most of them? It's just a matter of changing them.
Code:
def main
# Make command window
s1 = $data_system.words.item
s2 = $data_system.words.skill
s3 = $data_system.words.equip
s4 = "Status"
s5 = "Save"
s6 = "End Game"
@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])
@command_window.index = @menu_index
# If number of party members is 0
if $game_party.actors.size == 0
# Disable items, skills, equipment, and status
@command_window.disable_item(0)
@command_window.disable_item(1)
@command_window.disable_item(2)
@command_window.disable_item(3)
end
# If save is forbidden
if $game_system.save_disabled
# Disable save
@command_window.disable_item(4)
end
# Make play time window
@playtime_window = Window_PlayTime.new
@playtime_window.x = 480
@playtime_window.y = 224
# Make steps window
@location_window = Window_Location.new
@location_window.x = 480
@location_window.y = 320
# Make gold window
@gold_window = Window_Gold.new
@gold_window.x = 480
@gold_window.y = 416
# Make status window
@status_window = Window_MenuStatus.new
@status_window.x = 0
@status_window.y = 0
# Execute transition
Graphics.transition
# Main loop
loop do
# Update game screen
Graphics.update
# Update input information
Input.update
# Frame update
update
# Abort loop if screen is changed
if $scene != self
break
end
end
# Prepare for transition
Graphics.freeze
# Dispose of windows
@command_window.dispose
@playtime_window.dispose
@location_window.dispose
@gold_window.dispose
@status_window.dispose
end
I've set the x coordinate of the playtime, location, and gold windows to 480, and the x coordinate of the status window to 0. But wait, we forgot the command window! No problem, we just add a line:
@command_window.x = 480
Like so:
Code:
def main
# Make command window
s1 = $data_system.words.item
s2 = $data_system.words.skill
s3 = $data_system.words.equip
s4 = "Status"
s5 = "Save"
s6 = "End Game"
@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])
@command_window.index = @menu_index
@command_window.x = 480
# If number of party members is 0
if $game_party.actors.size == 0
# Disable items, skills, equipment, and status
@command_window.disable_item(0)
@command_window.disable_item(1)
@command_window.disable_item(2)
@command_window.disable_item(3)
end
# If save is forbidden
if $game_system.save_disabled
# Disable save
@command_window.disable_item(4)
end
# Make play time window
@playtime_window = Window_PlayTime.new
@playtime_window.x = 480
@playtime_window.y = 224
# Make steps window
@location_window = Window_Location.new
@location_window.x = 480
@location_window.y = 320
# Make gold window
@gold_window = Window_Gold.new
@gold_window.x = 480
@gold_window.y = 416
# Make status window
@status_window = Window_MenuStatus.new
@status_window.x = 0
@status_window.y = 0
# Execute transition
Graphics.transition
# Main loop
loop do
# Update game screen
Graphics.update
# Update input information
Input.update
# Frame update
update
# Abort loop if screen is changed
if $scene != self
break
end
end
# Prepare for transition
Graphics.freeze
# Dispose of windows
@command_window.dispose
@playtime_window.dispose
@location_window.dispose
@gold_window.dispose
@status_window.dispose
end
Check out your menu now:

One last thing. We're going to add faces instead of character sprites to the Status Window. First, let's add a method to Window_Base to draw face graphics. Why to Window_Base? Because we might want to use it in another window some day, and it's better not to duplicate code. That way, if you want to change how it works someday you only have to change it in one place.
Code:
#--------------------------------------------------------------------------
# * Draw Face Graphic
# actor : actor
# x : draw spot x-coordinate
# y : draw spot y-coordinate
#--------------------------------------------------------------------------
def draw_actor_face_graphic(actor, x, y)
bitmap = RPG::Cache.picture(actor.name)
self.contents.blt(x, y, bitmap, Rect.new(0, 0, bitmap.width, bitmap.height))
end
This is pretty similar to the way we drew icons on the screen for the HUD. In this case, I have imported a picture that has the same name as the actor (Cecilia.png). Thus the actor.name, the name of the image will be the same as the name of the actor.
Now we head over to Window_MenuStatus. Notice this line:
Code:
draw_actor_graphic(actor, x - 40, y + 80)
This is the line that draws the character sprite on the window. If you're curious about it, head over to Window_Base and check it out.
We want to replace this line with one of our own, so we delete it. Then we add
Code:
draw_actor_face_graphic(actor, x - 64, y)
The x value will always be 0 (since x is 64). The y value will be 0 for the first actor, 116 for the second, etc. You may need to do some expirementation with placing objects on windows. Remember, higher x values are farther right, higher y values are farther down.
That's not too bad, but the text is overlapping the face:

So I move the name, level, and state over 40 pixels.
Code:
def refresh
self.contents.clear
@item_max = $game_party.actors.size
for i in 0...$game_party.actors.size
x = 64
y = i * 116
actor = $game_party.actors[i]
draw_actor_face_graphic(actor, x - 64, y)
draw_actor_name(actor, x + 40, y)
draw_actor_class(actor, x + 144, y)
draw_actor_level(actor, x + 40, y + 32)
draw_actor_state(actor, x + 90, y + 32)
draw_actor_exp(actor, x + 40, y + 64)
draw_actor_hp(actor, x + 236, y + 32)
draw_actor_sp(actor, x + 236, y + 64)
end
end
Well, that took care of the image overlap, but now status is overlapping level!

Let's move it up by actor class, shall we?
Code:
draw_actor_state(actor, x + 236, y)
I got the 236 from the HP and SP drawing methods. Again, experimentation is the key.

Other possible solution would have been to remove unnecessary data or to make the font size smaller. Be creative when you remodel. If you're doing something big, make a mock-up in a graphics program first to be sure everything will fit first!
Be sure to download the latest version of the Window Demo and check it out!
[url=http://www.pcis-studios.com/ccoa/Window%20Demo.exe]http://www.pcis-studios.com/ccoa/Window%20Demo.exe[/url]
Homework
Try a very simple remodel on a scene of your choice. Try remodeling the internal contents of at least one window. Do it in a new project, and don't be afraid of breaking things! That's how you learn. Post any questions or problems here.
|
|
Details
|
|
Tutorial:
|
RGSS Windows - Lesson 2b: Simple Menu Remodel |
|
Date Listed:
|
2008-06-08 |
|
Author:
|
ccoa
|
|
Total Hits:
|
3863 |
|
|