|
This tutorial describes the basics of manipulating selectable windows, such as transitioning from one window to another, seperation of input handling, and strategies for easily creating your own selectable windows.
If you intend to use RGSS to create custom systems, some portion of your design
time will probably be spent set up selectable windows that allow the player to
interact with your system's components. This tutorial will teach you how to set
up these windows. For this tutorial, I will be using code samples from my battle
debugger script. If you would like to see the entire script in a new window,
please [ click here ].
The first thing you should know about is
how to transition from one window to the next (or previous), when the user
selects a choice from the window or cancels. Take a look at code sample 1. The
snippet of code shown makes it so that during a test play or battle test, the
debug command window, monster data window, and turn count window appear. The
numbers next to the windows in the pictures correspond to the comment numbers in
the code sample. The first thing you should notice is that for windows 1 and 2,
the selectable windows, the values @windowname.active and
@windowname.visible are being modified. Let's take those two in turn. If
you want a window (selectable or not) to disappear from the screen in response
to a selection, then the statement @windowname.visible = false should be
used. Conversely, if you want a window to appear, use the statement
@windowname.visible = true. In the case of Code Sample 1, I've made the
action command window (Window #1) disappear and the main debug command window
(Window #2) appear. Additionally, I made the action command window inactive and
the main debug command window active. What does "active", mean? In the context
of RGSS, for a window to be "active" means that its cursor will move when the
user presses the arrow keys. Note that this does not mean that when the user
presses the decision key, a command will be selected. This is because of the
division of input processing, explained later. A window should never be
invisible and active, and in most cases, only one window should be active at a
time (there are exceptions, but these are for advanced users), so please check
your code carefully. Otherwise, the bug might be hard to track. Now, contrast
that with windows #3, and #4, the non-selectable information windows. "Active"
has no meaning for an information window, so I merely make the monster data and
turn count windows visible.
Code Sample 1
if Input.trigger?(Input::X) && ($BTEST || $DEBUG)
$game_system.se_play($data_system.decision_se)
@debugging = true
@actor_command_window.active = false # 1
@actor_command_window.visible = false
@battledebug_window.active = true # 2
@battledebug_window.visible = true
@battledebug_window.index = 0
@monsterdata_window.refresh
@count_window.refresh
@count_window.visible = true # 3
@monsterdata_window.visible = true # 4
return
end
|

Game screen before the code in Code Sample 1 executes

Game screen after the code in Code Sample 1 executes
Now, let's see what happens when the user cancels the window shown in the second
screenshot. Take a look at Code Sample 2. Other than some cleanup from the
debugger itself, you're probably not entirely surprised to find that the
true/false values shown in Code Sample 1 are just reversed in Code Sample 2.
When taken together, these two code samples provide the basic framework for
moving between selectable windows.
Code Sample 2
if Input.trigger?(Input::B)
@debugging = false
@sv_index = -1
$game_system.se_play($data_system.cancel_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
@actor_command_window.active = true
@actor_command_window.visible = true
phase3_setup_command_window
end
|
Now that the basic framework has been established, let's look at how the windows
differentiate between selections. That is, how the window knows what to do when
the user selects "Change Character HP" from the window above, as opposed to
"Change Turn Number" or some other command. The code highlighted in red in Code
Sample 3 shows the method for handling decisions. In your update_windowname
method, you should have a "if Input.trigger?(INPUT::C)" clause, with the a case
statement that checks the current "index" of the cursor. The index of the cursor
is the item number that is currently selected (see image below Code Sample 3 for
an example). Take note from the image that the index starts at 0. Notice that
each "when" clause within the case statement matches one of the possible values
of @battledebug_window.index. Let's take a look at the "when 0" case as an
example. Index 0 corresponds to "Change Character HP" so the code within that
clause will execute. In this case, the decision sound effect is played, the
battle debug window is made invisible and inactive, and the monsterdata and turn
count windows are made invisible in preparation for calling the
debug_actor_select method, which handles the arrow cursor for characters within
the battle debugger.
Code Sample 3
def update_battledebug
if @battledebug_window.active
@selected_entity = -1
end
if Input.trigger?(Input::B)
@debugging = false
@sv_index = -1
$game_system.se_play($data_system.cancel_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
@actor_command_window.active = true
@actor_command_window.visible = true
phase3_setup_command_window
end
if Input.trigger?(Input::C)
case @battledebug_window.index
when 0
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
debug_actor_select
when 1
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
debug_actor_select
when 2
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
debug_actor_select
when 3
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
debug_enemy_select
when 4
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
debug_enemy_select
when 5
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
debug_enemy_select
when 6
$game_system.se_play($data_system.decision_se)
for enemy in $game_troop.enemies
enemy.hp = 0
enemy.immortal = false
@battledebug_window.visible = false
@battledebug_window.active = false
@monsterdata_window.visible = false
@count_window.visible = false
start_phase5
end
when 7
$game_system.se_play($data_system.decision_se)
for enemy in $game_troop.enemies
if enemy.dead? && enemy.hidden == false
enemy.hp += 1
enemy.damage = -1
enemy.damage_pop = true
end
end
@monsterdata_window.refresh
when 8
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@left_window.visible = true
@right_window.visible = true
@left_window.active = true
when 9
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@battledebug_window.visible = false
@monsterdata_window.visible = false
@count_window.visible = false
debug_enemy_select
when 10
$game_system.se_play($data_system.decision_se)
for enemy in $game_troop.enemies
enemy.hidden = false
end
@monsterdata_window.refresh
when 11
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@itemset_window.index = 0
@itemset_window.active = true
@itemset_window.visible = true
when 12
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@turn_window.number = $game_temp.battle_turn + 1
@turn_window.active = true
@turn_window.visible = true
when 13
$game_system.se_play($data_system.decision_se)
@battledebug_window.active = false
@end_window.active = true
@end_window.visible = true
end
end
end
|

Possible values of @battledebug_window.index
You can see from the code in Code Sample 3 that the update_battledebug
method watches for the user to press the "B" or "C" keys, but you'll notice that
it doesn't watch for the user to press the arrow keys and move the cursor in
response. This is because of RGSS's seperation of input handling
responsibilities. Handling of arrow key input is done within the
Window_Selectable class, since the effects of pressing an arrow are
fairly common across all selectable windows. The effects of pressing keys other
than the arrows are left up to the scripter, as shown above. Simple setting
windowname.active to true will make the window respond to input from the
arrows, but in order to make the window respond to other key inputs you've set
up, you need to use the update method within the scene within which the
window is contained to check and see if the window is active each frame. If it
is, you call your update_windowname method. An example of checking to see
if the battle debug window is active is shown in Code Sample 4. The code that
will invoke the code in Code Sample 3 is shown in red.
Code Sample 4
def update_phase3
if @enemy_arrow != nil && @debugging
update_debug_enemy_select
elsif @actor_arrow != nil && @debugging
update_debug_actor_select
elsif @enemy_arrow != nil
update_phase3_enemy_select
elsif @actor_arrow != nil
update_phase3_actor_select
elsif @skill_window != nil
update_phase3_skill_select
elsif @item_window != nil
update_phase3_item_select
elsif @actor_command_window.active
update_phase3_basic_command
elsif @battledebug_window.active
update_battledebug
elsif @end_window.active
update_end
elsif @turn_window.active
update_turn
elsif @itemset_window.active
update_itemset
elsif @enemyhp_window.active
update_enemyhp
elsif @value_window.active
update_value
elsif @left_window.active
update_left
elsif @right_window.active
update_right
elsif @var_window.active
update_var
end
end
|
Now that you know how selectable windows work, you might be wondering how to
best take advantage of the variety of selectable windows. If you intend to use a
vertical window with one column, like the main debug command window shown in the
screenshots above, the built-in class Window_Command makes it easy to
create one. For horizontal windows with one row and multiple columns, your best
bet is to use a template like the one shown in Code Sample 5. You need to change
each item shown in red as listed below:
x: x position of the
window. y: y position of the window. width: width of the window. value:
the number of commands in the window. commnads: the commands in the
window. space: the amount of space in pixels between each command.
Code Sample 5
class Window_Horizontal < Window_Selectable
# ------------------------------------
def initialize
super(x, y, width, 64)
self.contents = Bitmap.new(width - 32, height - 32)
@item_max = value
@column_max = value
@commands = ["command1", "command2", "comannd3" ... "commandN"]
refresh
self.index = 0
end
# ------------------------------------
def refresh
self.contents.clear
for i in 0...@item_max
draw_item(i)
end
end
# ------------------------------------
def draw_item(index)
x = 4 + index * space
self.contents.draw_text(x, 0, 128, 32, @commands[index])
end
end
|
If you want a window with two or more rows and columns, and that selections
contained in that window are all of a similar type, then a window patterned
after Window_Item or Window_Skill is probably what you're looking
for. If the selections do very different things when selected, then you will
likely need to create a custom window class from scratch. That may seem daunting
at first, but once you master the concepts presented in this tutorial, you
should have a fairly good idea how to go about it.
|