Window/Menu Scripting Tutorial

Started by Rune, July 20, 2008, 06:18:11 pm

Previous topic - Next topic

Rune

Some may remember this from one of the old CP forums, or even RMRK, this is basically my little tut on Windows, and will help towards you making your own CMSes and window scenes soon ;)

Introduction to Ruby/RGSS: ShowHide
RGSS was originated from ruby.
Ruby is a an exciting new, pure, object oriented programming language. While few people in the West have heard of Ruby yet, it has taken off like wildfire in Japan---already overtaking the Python language in popularity.

What makes Ruby so popular? Ruby weaves the best features of the best programming languages into a seamless, concise whole. Ruby is:
Powerful -- Ruby combines the pure object-oriented power of the classic OO language Smalltalk with the expressiveness and convenience of a scripting language such as Perl. Ruby programs are compact, yet very readable and maintainable; you can get a lot done in a few lines, without being cryptic.

Simple -- The syntax and semantics are intuitive and very clean. There aren't any "special cases" you have to remember. For instance, integers, classes, and nil are all objects, just like everything else. Once you learn the basics, it's easy to guess how to do new things---and guess correctly.

Transparent -- Ruby frees you from the drudgery of spoon-feeding the compiler. More than any other language we've worked with, Ruby stays out of your way, so you can concentrate on solving the problem at hand.

Available -- Ruby is open source and freely available for both development and deployment. Unlike some other new languages, Ruby does not restrict you to a single platform or vendor. You can run Ruby under Unix or Linux, Microsoft Windows, or specialized systems such as BeOS and others.
Most of all, Ruby puts the fun back into programming. When was the last time you had fun writing a program---a program that worked the first time; a program that you could read next week, next month, or next year and still understand exactly what it does? We find Ruby to be a breath of fresh air in the dense, often hectic world of programming. In fact, we see nothing but smiles after we present Ruby to programmers.
(Copied from another site, I have no understanding of Ruby, only RGSS)


Making a Window: ShowHide
To make a window, you need to make at least two definitions, def initialize, and def refresh.

def initialize

To start with, you need to name your window
For example:
class Window_MyWindow < Window_Base

Remember that RGSS is case sensitive, if something isnt the right case then you will most likely get an error, always check something is in the right case.

Now for def initialize, to start this, just type on a new line: def initialize
Simple enough?
Now type
super(0, 0, 128, 128)

This line calls the window
The numbers can be changed, the first number is the x co-ordinates of where the top left corner will appear on-screen, the second number is the y co-ordinates of the position of the window (both these are in pixels)
If both the first numbers are at 0 then the window will appear the top left corner of the screen
The third number is the x co-ordinates of the window itself (in pixels)
The last number is the y co-ords of the window (in pixels)
x goes up to 640 and y goes to 480
So a window can be at maximum, 640 x 480 pixels without going offscreen
(Try and keep the numbers as multiples of 32, they fit better this way)

Then, use
self.contents = Bitmap.new(width - 32, height - 32)

You don't change anything here, i'm not sure what this line does, so i've never edited it myself

Then, if you like, you can add
self.contents.font.name = $defaultfonttype
self.contents.font.size = $defaultfontsize

If you don't add this here, then you can add it in def refresh
you can change $defaultfonttype to "Tahoma", "Arial" or any other font, and you can replace $defaultfontsize to any number you want, this determines the font's size.

Then put refresh, then end.
so your Window so far should look like this:
class Window_MyWindow < Window_Base
  def initialize
    super(0, 0, 128, 96)
    self.contents = Bitmap.new(width - 32, height - 32)
    self.contents.font.name = $defaultfonttype
    self.contents.font.size = $defaultfontsize
    refresh
  end


def refresh

Now you've made the window, you need to give it some contents
def refresh
  self.contents.clear

I think this line clears the window so that suff can be added to it
You can now add the two font lines in if you like, once that's done you may want to use this
self.contents.font.color = normal_color

The colors can all be found in Window_Base, and to create a color, change normal_color to Color.new(r, g, b)
r = red g = green b = blue
self.contents.draw_text(0, 0, 128, 32, "TEXT!!!!")

This line creates text, the first and second numbers determine where the text appears (x and y), and the second two numbers determine thespace the text can fit into, (Number 3 = x, Number 4 = y (usually 32))
Always put text in quotation marks, otherwise the program sees it as a method and searches for the definition for it
To add icons, battlers, charsets etc, use this syntax
bitmap = RPG::Cache.type of bitmap("Name of bitmap")
self.contents.blt(x, y, bitmap, Rect.new(0, 0, x that bitmap has to fit in, y that bitmap has to fit in))

That should be pretty self explanatory...

So now your Window should look something like this
class Window_MyWindow < Window_Base
  def initialize
    super(0, 0, 128, 96)
    self.contents = Bitmap.new(width - 32, height - 32)
    self.contents.font.name = $defaultfonttype
    self.contents.font.size = $defaultfontsize
    refresh
  end
  def refresh
    self.contents.clear
    self.contents.font.color = Color.new(255, 0, 0)
    self.contents.draw_text(0, 0, 128, 32, "TEXT!!!")
    bitmap = RPG::Cache.icon("001-Weapon01")
    self.contents.blt(0, 64, bitmap, Rect.new(0, 0, 24, 24))
  end
end

Add that extra end at the end (so there's two ends) to round off your window
You will get a syntax error if there are too many or not enough ends


I couldn't think of anything to go between windows and Menu Systems, if you think there should be something in-between these, then just say so ;)

Most scripters start their scripts with comment lines, there are two ways (that I know of) of doing this
One is to put a # at the start of every comment line, note that you can put these after parts in a script on the same line, to explain what each line does, for example
$scene = Scene_Menu.new(3) #Calls the Menu and starts at the 4th option (Will be explained later in the tutorial)

But, you CANNOT use a comment, then use a scripting syntax, e.g.
#To call the menu# $scene = Scene_Menu.new(3)

The syntax itself will be seen as a comment and therefore ignored
To save the time and trouble of putting a # on every line, most scripters use these
=begin

This begins a section of comments
=end

And this ends it, pretty self-explanatory I think
All scripts released on forums or whatnot should include comments so that the user knows exactly what they're doing

def initialize

Now the comments are over, name your scene, for example, the default menu is called Scene_Menu, so we'll call it that.
So, start off with
class Scene_Menu

So that's over and done with, now type def initialize
The way I do my menus (the only way I know) may be different to what other scripters use
So... on the SAME LINE as def initialize, type (menu_index = 0), I think this presets the starting option for when you access the menu, in scripting, most things start at 0, so 0 = 1, 1 = 2, 2 = 3 etc etc etc
So your def initialize should look like this now,
def initialize(menu_index = 0)

Now, you need to define what menu_index actually is, to do this
@menu_index = menu_index

can be used.
Now end your def initialize
So your CMS should look like this now,
class Scene_Menu
  def initialize(menu_index = 0)
    @menu_index = menu_index
  end

Short eh?
That's only the very beginning

def main

Start off your def main as, well, def main
Now to make the options for your menu
s1 = "Items"

That would be an option for your menu, keep in mind that the options here don't start at 0, so s1 would be the first option, you could also put
s1 = $data_system.words.item

This searches the last page of the database for whatever you have named "Items", note that this only works for item, skill and equip, make all your options
Once you're done you should have something like this
s1 = $data_system.words.item
s2 = $data_system.words.skill
s3 = $data_system.words.equip
s4 = "Status"
s5 = "Save"
s6 = "Quit"

Remember that you can add options and remove them as you please, i've no idea why, but the program automatically sees these as menu_index
Now, it's decision time, is your menu not going to cover the whole screen? If so, do you want to see the map instead of blackness? If so, add this line
@spriteset = Spriteset_Map.new

Now, to make your options appear, you need to put them in a window, use this to do that
@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])

The number is the width (x) of the window, and the options appear at the end, remember to update these whenever you add or remove options
Now to set the index for Window_Command
@command_window.index = @menu_index

This sets Window_Command to menu_index
You can disable options if contitions are not met, like not enough characters
if $game_party.actors.size == 0
  @command_window.disable_item(0)
  @command_window.disable_item(1)
  @command_window.disable_item(2)
  @command_window.disable_item(3)
end

The == is not a typo, you need that to determine that if your party has no more, no less than 0 characters then those options will be disabled, notice they start from 0
Now to disable saving
if $game_system.save_disabled
  @command_window.disable_item(4)
end

That means that if save is disabled then "Save" will turn grey, you can actually disable it later
@playtime_window = Window_PlayTime.new
@playtime_window.x = #Set the x co-ordinates of where the playtime window will appear
@playtime_window.y = #Set the playtime window's y co-ords
@steps_window = Window_Steps.new
@steps_window.x = #The x co-ords of the steps window
@steps_window.y = #The steps window's y co-ords
@gold_window = Window_Gold.new
@gold_window.x = #The x of the gold window
@gold_window.y = #The y of the gold window
@status_window = Window_MenuStatus.new
@status_window.x = #The x of the menustatus window
@status_window.y = #The y of the menustatus window

Here you can set wher each window goes, and what each window is, a menu only really needs a command_window and a status_window, but you can add or remove windows as you please
Graphics.transition
loop do
  Graphics.update
  Input.update
  update

These lines set the transition
  if $scene != self
    break
  end
end

This means that if the current scene isn't the menu, then loop do breaks

  Graphics.freeze
  @spriteset.dispose
  @command_window.dispose
  @playtime_window.dispose
  @steps_window.dispose
  @gold_window.dispose
  @status_window.dispose
end

These lines dispose of all the windows when the menu is exited, you don't need the spriteset line if you didn't add the line earlier, and the end ends def main

def update

def update
  @spriteset.update
  @command_window.update
  @playtime_window.update
  @steps_window.update
  @gold_window.update
  @status_window.update

Again, don't put the spriteset line if you didn't before
These lines update each window every frame or so I think

  if @command_window.active
    update_command
    return
  end

These lines mean that if the cursor is on the command window, then it tells the program to go to the definition of update_command
  if @status_window.active
    update_status
    return
  end
end

These lines do the same but for the status window (the window you go to when you are selecting the characters)
And the last end ends def update

def update_command

def update_command
  if Input.trigger?(Input::B)
    $game_system.se_play($data_system.cancel_se)
    $scene = Scene_Map.new
    return
  end

These lines mean that if the cancel button is pressed then you go back to the map
if Input.trigger?(Input::C)
  if $game_party.actors.size == 0 and @command_window.index < 4
    $game_system.se_play($data_system.buzzer_se)
    return
  end

These lines mean that if the party has exactly 0 characters and the option you have chosen is less than 4 (starting from 0) Then it plays a buzzer and returns to the command window
case @command_window.index

This line means that the response changes for each option in the command window
when 0
  $game_system.se_play($data_system.decision_se)
  $scene = Scene_Item.new

This is the basic response for a selection, though it can get more advanced than that
when 1
  $game_system.se_play($data_system.decision_se)
  @command_window.active = false
  @status_window.active = true
  @status_window.index = 0
when 2
  $game_system.se_play($data_system.decision_se)
  @command_window.active = false
  @status_window.active = true
  @status_window.index = 0
when 3
  $game_system.se_play($data_system.decision_se)
  @command_window.active = false
  @status_window.active = true
  @status_window.index = 0

Those 3 are skill, equip and status, they appear the same, but for the responses, see def update_status
when 4
  if $game_system.save_disabled
    $game_system.se_play($data_system.buzzer_se)
    return
  end
  $game_system.se_play($data_system.decision_se)
  $scene = Scene_Save.new

That means that if save is disabled, then it plays a buzzer and returns to the command window
If save is enabled then it goes to the save screen
    when 5
      $game_system.se_play($data_system.decision_se)
      $scene = Scene_End.new
    end
    return
  end
end

This calls the end screen when Quit is selected
Then the ends end everything except class Scene_Menu

def update_status

def update_status
  if Input.trigger?(Input::B)
    $game_system.se_play($data_system.cancel_se)
    @command_window.active = true
    @status_window.active = false
    @status_window.index = -1
    return
  end

Those lines make it so when the cancel button is pressed and the status screen is active, then it goes back to the command window
if Input.trigger?(Input::C)
  case @command_window.index
  when 1
    if $game_party.actors[@status_window.index].restriction >= 2
      $game_system.se_play($data_system.buzzer_se)
      return
    end
    $game_system.se_play($data_system.decision_se)
    $scene = Scene_Skill.new(@status_window.index)

This makes it go to the skill screen, i've no idea what the first lines under when 1 do...
      when 2
        $game_system.se_play($data_system.decision_se)
        $scene = Scene_Equip.new(@status_window.index)
      when 3
        $game_system.se_play($data_system.decision_se)
        $scene = Scene_Status.new(@status_window.index)
      end
      return
    end
  end
end

The rest of the lines seem pretty self explanatory, that's the end of Scene_Menu, make sure there are enough ends at the end, you will get a syntax error if there aren't the right number of ends at the end

Showing Icons

What I do here, is make myself a brand spanking new window with the same co-ordinates as my at the top of my menu, before class Scene_Menu
class Window_Icons < Window_Base
  def initialize
    super(x position of your command window, y position of you command window, x of your command window, y of you command window)
    self.contents = Bitmap.new(width - 32, height - 32)
    refresh
  end

Now, like in the Making a Window section, you need to place the icons, like this

  def refresh
    self.contents.clear
    bitmap = RPG::Cache.icon("034-Item03")
    self.contents.blt(x, y, bitmap, Rect.new(0, 0, 24, 24), 160)
    bitmap = RPG::Cache.icon("044-Skill01")
    self.contents.blt(x, y, bitmap, Rect.new(0, 0, 24, 24), 160)
    bitmap = RPG::Cache.icon("013-Body01")
    self.contents.blt(x, y, bitmap, Rect.new(0, 0, 24, 24), 160)
    bitmap = RPG::Cache.icon("Status")
    self.contents.blt(x, y, bitmap, Rect.new(0, 0, 24, 24), 160)
    bitmap = RPG::Cache.icon("037-Item06")
    self.contents.blt(x, y, bitmap, Rect.new(0, 0, 24, 24), 160)
    bitmap = RPG::Cache.icon("039-Item08")
    self.contents.blt(x, y, bitmap, Rect.new(0, 0, 24, 24), 160)
  end
end

Where x and y are the x and y of where you want the icons to appear, if you want you can add a number at the end like I have, to set the opacity (transparency) of the icon
Then, you need to add it into your menu
Before the line that says
@command_window = Window_Command.new

Paste this:
@icon_window = Window_Icon.new
@icon_window.x = !!!
@icon_window.y = !!!!

Change the !!! to the x co-ordinates of your command windowand change !!!! to the y co-ordinates of it

Then where it says all the crap like:
@command_window.dispose

Insert:
@icon_window.dispose

Then find:
@command_window.update

And add:
@icon_window.update


That should be it, enjoy :)


Using Variables: ShowHide

Ingame Variables

You can use ingame variables in scripting, for example
$var = $game_variables[x]

Where x is the id of the variable, starting from 1
If you are making a window that uses an ingame variable, you can display it by using that syntax in def initialize above refresh and under the line that says
self.contents = Bitmap.new(width - 32, height - 32)

And using this line
self.contents.draw_text(0, 0, 96, 32, $var.to_s, 1)

The numbers have already been explained in the Making a Window section of this tut
The 1 centers the number, I think if you put a 2 there, I think it aligns it to the right, and if you don't put a number then it aligns to the left

Scripting Variables and Game class

The way I make scripting variables may be different to the way other scripters do it, if any scripters out there know any other ways, then post it ;)

class Game_Var
  attr_accessor :var
  def initialize
    $var = 0
  end
end

That seems pretty self explanatory, the $ makes var a global variable and the fourth line makes $var preset to 0
In the def initialize of the window you want the variable displayed in, put
@game_var = Game_Var

Change game_var and Game_Var to whatever you have called your game class
You can now display the variable the same way you would display ingame variables, but changing the values is different, you need to use a call script that I don't know yet, if I figure it out i'll post it here ;)


More to come ;) if you have any problems with anything here, just post it and i'll try and fix it ;)
Signature.

Hixomdido

Wait, So where am I supposed to put these codes? A new script?

WhiteRose

Quote from: Hixomdido on January 26, 2011, 10:21:29 pm
Wait, So where am I supposed to put these codes? A new script?


Yes; these aren't just things you can copy and paste, though. This topic is a tutorial on how to script RGSS, the RMXP programming language. If you're interested in starting RGSS, I'd recommend taking a look at this topic:
http://forum.chaos-project.com/index.php/topic,57.0.html

Also, please be more careful with your necroposting. In this case it was relevant, but this still probably wasn't the best place to ask the question, considering Rune hasn't been active for quite some time.