Jump to content
Tsukihime

[RGSS3] Creating your own window handlers

Recommended Posts

This tutorial describes how you can create your own window handlers for any windows that you use for your scene. Some other environments call them "event listeners". For more information you can read the wiki article on the general concept.

 

Handlers allow the window to communicate with the scene. For example, when you're in the item menu and selecting an item to use, when you press the OK button, the item window will tell the scene that the OK button was triggered, and the scene calls the "on_item_ok" method accordingly.

 

But how does the window know that it has an OK handler?

And how does the window know what method to call?

 

This tutorial will answer the two questions, and you should be able to create your own custom handlers.

 

trans.gifAssigning Handlers

 

Handlers are usually assigned to windows in the scene, using the `set_handler` method. This method is defined as follows in Window_Selectable:

#--------------------------------------------------------------------------
# * Set Handler Corresponding to Operation
# method : Method set as a handler (Method object)
#--------------------------------------------------------------------------
def set_handler(symbol, method)
  @handler[symbol] = method
end
The symbol is just any name you want to give for your handler.

The method is the method that will be called when this handler is called.

 

Look at how Scene_Item creates the item window:

def create_item_window
  wy = @category_window.y + @category_window.height
  wh = Graphics.height - wy
  @item_window = Window_ItemList.new(0, wy, Graphics.width, wh)
  @item_window.viewport = @viewport
  @item_window.help_window = @help_window
  @item_window.set_handler(:ok, method(:on_item_ok))
  @item_window.set_handler(:cancel, method(:on_item_cancel))
  @category_window.item_window = @item_window
end
The important part is these two lines:
@item_window.set_handler(:ok, method(:on_item_ok))
@item_window.set_handler(:cancel, method(:on_item_cancel))
The scene assigns two handlers to the item window, an "ok" handler, which will call the scene's "on_item_ok" method, as well as a "cancel" handler, which will call the "on_item_cancel" method. Both of these methods are defined in the scene.

 

The way these handlers are called is done in the `process_handling` method in Window_Selectable:

def process_handling
  return unless open? && active
  return process_ok if ok_enabled? && Input.trigger?(:C)
  return process_cancel if cancel_enabled? && Input.trigger?(:
  return process_pagedown if handle?(:pagedown) && Input.trigger?(:R)
  return process_pageup if handle?(:pageup) && Input.trigger?(:L)
end
You can see that the selectable window checks for four types of handles, depending on which button is pressed. If you press the "C" button, it will call the method associated with the "ok" handler. If you press the "B" button, it will call the method that is associated with the "cancel" handler.

 

Creating your own handler

 

What you have seen above is basically all there is to handlers: the scene assigns some handlers to the window, giving the handler a name and a callback method, and the window then checks whether any of the handlers are called and calls the appropriate method attached to the handler.

 

The steps for creating your handlers is summarized below.

  • Choose a name for your handler. Something intuitive would be nice.
  • Decide how you want this handler to be triggered. It can be a keypress, or any other boolean expression. For example, maybe you want a handler to be triggered every 5 seconds by checking some sort of timer.

     

  • Decide what methods will be triggered for each handler.

     

  • Call `set_handler` and assign a handler to your window, using the name and method that you picked above.

     

  • add the handling logic inside `process_handling` to your window according to what you have decided in step 2.
Following this procedure, you should be able to easily define custom handlers for your windows.

 

Example

 

Here is a working example that adds custom handlers to the item window in the item scene that will be triggered whenever you press the left or right keys. I use the procedure described above.

 

1. Decide on handler names

 

Since the handlers will be triggered when I press the left or right keys, I will just call them `:left` and `:right`

 

2. Decide how the handlers are triggered

 

They will be triggered when I press the left or right keys. This can be accomplished using the following calls:

if Input.trigger?(:LEFT)
if Input.trigger?(:RIGHT)
3. Decide what methods will be triggered

 

For demonstration purposes, I will simply print out "left" or "right" to the console, depending on which key I pressed.

def on_item_left
  p 'LEFT'
  @item_window.activate
end

def on_item_right
  p 'RIGHT'
  @item_window.activate
end
4. Assign the handlers to the window

 

This is straightforward enough at this point. Just calling the set_handler method and assigning the two handlers as required:

@item_window.set_handler(:left, method(:on_item_left))
@item_window.set_handler(:right, method(:on_item_right))
5. Add the handling logic to the window

 

This is also pretty much just copying what you see from the default method

return call_handler(:left) if handle?(:left) && Input.trigger?(:LEFT)
return call_handler(:right) if handle?(:right) && Input.trigger?(:RIGHT)
It is not necessary to check whether the handlers are defined if you are using a custom window because chances are they will be defined. Window_Selectable is only written that way because you may have windows that do not assign all of those handlers.

 

At this point, if you load up your game, add some items to your inventory, and then scroll through your item list, you should see the appropriate string printed out to the console if you press left or right.

 

Here is the full code:

class Scene_Item < Scene_ItemBase
  alias :th_input_handler_create_item_window :create_item_window
  def create_item_window
    th_input_handler_create_item_window
    @item_window.set_handler(:LEFT, method(:on_item_left))
    @item_window.set_handler(:RIGHT, method(:on_item_right))
  end

  def on_item_left
    p 'LEFT'
    @item_window.activate
  end

  def on_item_right
    p 'RIGHT'
    @item_window.activate
  end
end

class Window_ItemList < Window_Selectable
  alias :th_input_handler_process_handling :process_handling
  def process_handling
    return unless open? && active
    th_input_handler_process_handling
    return call_handler(:LEFT) if handle?(:LEFT) && Input.trigger?(:LEFT)
    return call_handler(:RIGHT) if handle?(:RIGHT) && Input.trigger?(:RIGHT)
  end
end
 
Edited by Tsukihime
  • Like 4

Share this post


Link to post
Share on other sites

very nice tutorial. its short, simple, and to the point. i like that you really explain the steps as what they do, why the do, and why its important to 'do'.

looking forward to seeing more of these.

Share this post


Link to post
Share on other sites

nice tutorial tsuki :) i'm waiting for your another rgss3 tutorial ^_^

Share this post


Link to post
Share on other sites

Is it possible to pass arguments? such as

 

@new_window.set_handler(:ok, method(a_methos(*args)))

btw, nice tutorial you wrote there (:

Share this post


Link to post
Share on other sites

You pass arguments when you call the callback method

@new_window.set_handler(:ok, method(some_method))

 

def some_method(your_arg)

...

end

Then your window handle processing would pass in arguments
def call_ok_handler

@handler[symbol].call("my arg")

end

Edited by Tsukihime

Share this post


Link to post
Share on other sites

hime. i have a problem when making handler in two windows using same button input.

i want to make two windows which switch to each other when i press shift.

ex:

currently active: windows a. press shift. deactivate windows a. activate window b. then press shift again deactivate window b and activate window a.

i think i set the handler right. and also give condition to button. but when i press shift. the windows didn't switch. (maybe switch but switch back immediately).

apparently the input:A stuck in memory and then when entering the conditional it immediately call the handler.

if i add Input.update in the method. it switch correctly. but i cannot press shift button again to return to previous window.

 

here's the code to insert the handler part.

window 1

 

class Window_MagsItem < Window_EquipItem
  alias est_mags_process_handling process_handling
  def process_handling
    est_mags_process_handling
    return process_shift   if handle?(:shift)   && Input.trigger?(:A)    
  end
  def process_shift
    call_handler(:shift)    
  end  
end

window 2

 

class Window_MagsSkill < Window_Command
  alias est_mags_process_handling process_handling
  def process_handling
    est_mags_process_handling
    return process_shift   if handle?(:shift)   && Input.trigger?(:A)    
  end
  def process_shift
    call_handler(:shift)    
  end
end

in Scene

 

  def create_item_window # i only include the part where i set the handler since it's long
    @item_window = Window_MagsItem.new(wx, wy, ww, wh)
    @item_window.set_handler(:shift, method(:on_item_shift))
  end
  def create_mags_skill_window # i only include the part where i set the handler since it's long
    @mags_skill_window = Window_MagsSkill.new(wx, wy, ww, wh)
    @mags_skill_window.set_handler(:shift, method(:on_mags_shift))   
  end
 
  def on_item_shift
    @item_window.deactivate
    @mags_skill_window.activate
    Input.update
  end
  def on_mags_shift
    @mags_skill_window.deactivate
    @mags_skill_window.select_last
    @item_window.activate
  end
 

if i remove the Input.update line. it will still @item_window which activate after i press shift in @item_window.

i also try adding Input.update to below method. but it have no effect.

does it have difference because the second window is child of window_command?

 

for now i modify to use ok handler and cancel handler to switch back to previous window

but i want to able to switch window using shift.

 

any advice? :D.

Edited by estriole

Share this post


Link to post
Share on other sites

It's the input updating that's causing issues, though I am not too sure why.

Share this post


Link to post
Share on other sites

Yeah I think there's something wrong with the input module. I also encounter same issue with parallel process event before.

I create event. When I talk to event. I make it show two choices: talk and move. When I choose move. The event will go to page with these setting: Paralel process. With condition input trigger left, right, up, down. But the problem is when using input trigger :c as condition(I want to make when press enter stop move mode). It will executed immediately instead. It's like the input :c already pressed (it read from previous page we press enter to choose 'move'. And it stuck). in that event I finally make turnaround way by adding another conditional selfswitch b on to that input :c. Then turn on selfswitch b when I already made 'movement'. Guess have to think another turnaround with this window too then >.<

Share this post


Link to post
Share on other sites

I found the problem.

I forgot to update this tutorial post, but you have to check that the window is active

def process_handling
  return unless open? && active
  est_mags_process_handling
  return process_shift   if handle?(:shift)   && Input.trigger?(:A)    
end
Then it should work.

 

In any case, there is no problem with the Input module. You are likely just not understanding how it works and think it is supposed to behave a certain way.

 

You should do more tests to understand the difference between trigger?, repeat?, press?, and how Input.update affects these methods. There is a reason why it is important to call Input.update.

Edited by Tsukihime
  • Like 1

Share this post


Link to post
Share on other sites

Ah yes. I thought the return unless open and active 'already' included in the aliased method. But I forgot. It since it's in the aliased method. It return from the aliased method only. Means back to the process handling method. Then execute the next line in the method. Thus extra return unless open and active is needed :D.

 

Thx hime for pointing it out.

Now I can finish the first part of my script.

Share this post


Link to post
Share on other sites

Great tutorial! I've been struggling through trying to read how the core modules do this, and then I stumbled on this. You're a life-saver! :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
Top ArrowTop Arrow Highlighted