Submit Your Article


 
RPG Maker

Welcome Guest ( Log In | Register )


  Games Resources RPG Maker VX RPG Maker XP Scripts Tutorials Downloads

> [Scripting][Series]RGSS: Windows - Lesson 2a: Adding A Window to a Scene
ccoa
post Jan 9 2008, 07:12 AM
Post #1


Storm Goddess
Group Icon

Group: Revolutionary
Posts: 989
Type: Scripter
RM Skill: Masterful




Okay, Lesson 2, part 1.

Adding A Window to a Scene
Part One: Scene_Map


This time, we're going to do something you might actually use one day. wink.gif We're going to add a small HUD to a minigame that keeps track of the player's HP and number of items collected.

This is the minigame I've created:



Little Cecelia dropped her evil uncle's coin collection down a drain in his mansion. Now she needs to find them all and pick them up before he finds out she lost them! Unfortunately, her twisted uncle has a dangerous maze underneath his house. Cecelia needs to navigate the maze in under the time limit and pick up all the coins without falling off a ledge. Falling down costs her 10 HP. Feel free to download the game I'm linking at the end of the lesson and check out the events.

So we're going to need a new window, aren't we? Let's open up the script editor, and insert a new one named Window_HUD.

CODE
class Window_HUD < Window_Base

end


So far, so good. You should recognize this from last lesson.

CODE
class Window_HUD < Window_Base
  def initialize
    super(0, 0, 110, 96)
    self.contents = Bitmap.new(width - 32, height - 32)
    
    # get the first actor in the party to display their data
    @actor = $game_party.actors[0]
    # current hp of actor
    @hp = @actor.hp
    # current number of coins
    @coins = $game_variables[1]
    
    refresh
  end
  
  # clear the window and redraw it
  def refresh
    self.contents.clear
    
    # draw the hp icon on the window
    bitmap = RPG::Cache.icon('hp')
    self.contents.blt(0, 4, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the actual HP of the actor
    self.contents.draw_text(32, 0, 96, 32, @hp.to_s)
    
    # draw the coin icon on the window
    bitmap = RPG::Cache.icon('coins')
    self.contents.blt(0, 36, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the variable holding the number of coins
    self.contents.draw_text(32, 32, 96, 32, @coins.to_s)
  end
end


A little bit more meat than our last window, no? Let's go over the new stuff.

CODE
    # get the first actor in the party to display their data
    @actor = $game_party.actors[0]


Remember when I said $ indicates a global variable? $game_party is a global variable that contains a Game_Party object. Among other things, Game_Party keeps an array (numbered list of objects) of all the actors in your party named actors. So $game_party.actors[0] gets the first person in the party. Arrays are 0-based, which means that the object at the first position is always at index 0. It takes some getting used to, I know.

The @ symbol indicates that the variable is an "instance variable." In plain English, that variable belongs to Window_HUD, and no one outside Window_HUD can see it. So our Window_HUD object will always know what actor it's supposed to be looking at.

CODE
    # current hp of actor
    @hp = @actor.hp


This is how much HP the lead actor has. If you look at the Game_Actor class:

CODE
class Game_Actor < Game_Battler


Game_Actor is decended from Game_Battler. And Game_Battler has the following:

CODE
  attr_reader   :hp                       # HP


Attr are a special way of allowing things outside the class to access the class's instance variables. This is the only way anyone else can read or write to them. There are three types: attr_reader (read only, can't change), attr_writer (write only, can't see it), and attr_accessor (read or write). So if we were to change our class above to:

CODE
class Window_HUD < Window_Base
  attr_accessor :actor

  def initialize
    super(0, 0, 110, 96)
    self.contents = Bitmap.new(width - 32, height - 32)
    
    # get the first actor in the party to display their data
    @actor = $game_party.actors[0]
    # current hp of actor
    @hp = @actor.hp
    # current number of coins
    @coins = $game_variables[1]
    
    refresh
  end
  
  # clear the window and redraw it
  def refresh
    self.contents.clear
    
    # draw the hp icon on the window
    bitmap = RPG::Cache.icon('hp')
    self.contents.blt(0, 4, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the actual HP of the actor
    self.contents.draw_text(32, 0, 96, 32, @hp.to_s)
    
    # draw the coin icon on the window
    bitmap = RPG::Cache.icon('coins')
    self.contents.blt(0, 36, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the variable holding the number of coins
    self.contents.draw_text(32, 32, 96, 32, @coins.to_s)
  end
end


Then somewhere outside of this class we could do this:

CODE
@hud_window = Window_HUD.new
@hud_window.actor = $game_party.actors[2]


Which would change the @actor instance variable to point to the third (remember, 0-based!) actor in the party.

All of that means to say we just got the actor's current hp and stored it in an instance variable named @hp.

CODE
    # current number of coins
    @coins = $game_variables[1]


Remember those variables you set in events? $game_variables stores them all. So $game_variables[1] is the same as Variable 0001 in the event editor. So we've just taken whatever number is in variable 1, and put it in the instance variable @coins.

CODE
    # draw the hp icon on the window
    bitmap = RPG::Cache.icon('hp')
    self.contents.blt(0, 4, bitmap, Rect.new(0, 0, 24, 24))


This is how you draw an image on the window. The first line gets the bitmap of the image. I've imported an icon named hp.png and coins.png in the game. RPG::Cache gets the image and caches it for easier loading, the icon() method tells it to get something from the icon folder. For a list of RPG::Cache's methods, go to the help file and go to RGSS Reference Manual -> Game Library -> RGSS Built-In Modules -> RPG::Cache. There's a method for each type of resource. You can even add methods to it, but that's a lesson for another day.

The blt() (block transfer) method draws a bitmap on another bitmap (in this case, the window's bitmap).

This is the help file's description of blt:

QUOTE
blt(x, y, src_bitmap, src_rect[, opacity])
Performs a block transfer from the src_bitmap box src_rect (Rect) to the specified bitmap coordinates (x, y).
opacity can be set from 0 to 255.


Not all that helpful, is it?

x and y are the coordinates on the bitmap (window) to draw the new bitmap on. 0, 0 would be the upper left corner. src_bitmap is the bitmap to draw, in this case, the icon we just loaded. src_rect is trickier. This tells the interpreter how much of the bitmap to draw. Since my icon is 24x24 pixels, I've told it to draw a rectangle that starts at 0, 0 (upper left) and is 24 pixels wide and 24 pixels tall. In other words, the whole icon. However, I could just as easily done this:

CODE
Rect.new(12, 12, 12, 12)


Which would only draw the lower right 12x12 pixels, or this:

CODE
Rect.new(0, 0, 24, 1)


Which would only draw the very top row of 24 pixels.

Not that useful with icons, but this is how you can show just one frame of a characterset, among other uses. We may cover this later.

Opacity is optional. If you don't enter anything, as we have, it's assumed to be 255, or completely opaque. You can choose to enter a number between 0 (completely transparent, or invisible) and 255 if you wish. It could look like this:

CODE
    self.contents.blt(0, 4, bitmap, Rect.new(0, 0, 24, 24), 160)


CODE
    # draw the actual HP of the actor
    self.contents.draw_text(32, 0, 96, 32, @hp.to_s)


This is similar to what we did before, drawing some text on the screen. The only difference is that we're trying to draw a number, so we have to convert it to a string, first. the to_s() method takes a numerical object, like 50, and turns it into a string, '50'.

Okay, so now we have a basic window. Let's add it to the scene. Open up the script editor and click on Scene_Map. Find this line:

CODE
    # Make message window
    @message_window = Window_Message.new


Right after it, add:

CODE
    @hud_window = Window_HUD.new


We've now created a new window. But don't forget, we need to dispose it eventually! In the case of scenes, windows are disposed when the scene changes from one to another. You'll usually find them shortly below the main loop. Look for this line:

CODE
    @message_window.dispose


and add below it:

CODE
    @hud_window.dispose


Now we have a window that shows up directly on the map and is automatically disposed whenever the player enters the menu, battle, or any other scene. But wait! It doesn't do anything! No matter how the player's hp changes or how many coins he picks up, the HUD stays static. That can't be right.

We're going to add a method called update(). Update is a method intended to be called every frame.

CODE
class Window_HUD < Window_Base
  def initialize
    super(0, 0, 110, 96)
    self.contents = Bitmap.new(width - 32, height - 32)
    
    # get the first actor in the party to display their data
    @actor = $game_party.actors[0]
    # current hp of actor
    @hp = @actor.hp
    # current number of coins
    @coins = $game_variables[1]
    
    refresh
  end
  
  # clear the window and redraw it
  def refresh
    self.contents.clear
    
    # draw the hp icon on the window
    bitmap = RPG::Cache.icon('hp')
    self.contents.blt(0, 4, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the actual HP of the actor
    self.contents.draw_text(32, 0, 96, 32, @hp.to_s)
    
    # draw the coin icon on the window
    bitmap = RPG::Cache.icon('coins')
    self.contents.blt(0, 36, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the variable holding the number of coins
    self.contents.draw_text(32, 32, 96, 32, @coins.to_s)
  end
  
  # run each and every frame
  def update    
    # only refresh if something has changed.  Prevents lag.
    if (@hp != @actor.hp) or (@coins != $game_variables[1])
      # current hp of actor
      @hp = @actor.hp
      # current number of coins
      @coins = $game_variables[1]
      
      refresh
    end
    
    # do anything the parent windows need to do each frame
    super
  end
end


Let's take a closer look at this new method:

CODE
    # only refresh if something has changed.  Prevents lag.
    if (@hp != @actor.hp) or (@coins != $game_variables[1])


This is a conditional branch (yes, like the ones in the event editor). Like the ones in the event editor, it tests to see if something is true, and does something inside it if it is. The != means "not equal", so what this is saying is if the hp we stored isn't equal to the current hp or the number of coins we have stored isn't equal to the actual number we have picked up then do something. We could do without this and just refresh every frame, but that's a sure-fire way to cause lag. Only refresh your window if something on it changed.

CODE
# current hp of actor
      @hp = @actor.hp
      # current number of coins
      @coins = $game_variables[1]

      refresh


We reset our instance variables to the true current values in preparation to redraw them, then refresh.

CODE
    # do anything the parent windows need to do each frame
    super


Like the super in initialize, this calls the same method in the parent class. In this case, it's calling update in Window_Base.

Now we have an update method, but we need to set it up so it's called every frame. Head back to Scene_Map, and look for this line:

CODE
    @message_window.update


and add this below it:

CODE
    @hud_window.update


Now we almost have it, we just need a few refinements. First, we only want the window to show up during the minigame. Second, we want the window to be semi-transparent so the player can see what's behind it.

CODE
class Window_HUD < Window_Base
  def initialize
    super(0, 0, 110, 96)
    self.contents = Bitmap.new(width - 32, height - 32)
    
    # don't show this window by default
    self.visible = false
    
    # make the window semi-transparent so the player can see what's behind it
    self.opacity = 160
    
    # get the first actor in the party to display their data
    @actor = $game_party.actors[0]
    # current hp of actor
    @hp = @actor.hp
    # current number of coins
    @coins = $game_variables[1]
    
    refresh
  end
  
  # clear the window and redraw it
  def refresh
    self.contents.clear
    
    # draw the hp icon on the window
    bitmap = RPG::Cache.icon('hp')
    self.contents.blt(0, 4, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the actual HP of the actor
    self.contents.draw_text(32, 0, 96, 32, @hp.to_s)
    
    # draw the coin icon on the window
    bitmap = RPG::Cache.icon('coins')
    self.contents.blt(0, 36, bitmap, Rect.new(0, 0, 24, 24))
    
    # draw the variable holding the number of coins
    self.contents.draw_text(32, 32, 96, 32, @coins.to_s)
  end
  
  # run each and every frame
  def update
    # we're only visible if this switch is on
    self.visible = $game_switches[1]
    
    # don't update if we can't see the window.  Prevents lag.
    if !self.visible
      return
    end
    
    # only refresh if something has changed.  Prevents lag.
    if (@hp != @actor.hp) or (@coins != $game_variables[1])
      # current hp of actor
      @hp = @actor.hp
      # current number of coins
      @coins = $game_variables[1]
      
      refresh
    end
    
    # do anything the parent windows need to do each frame
    super
  end
end


Let's look at the changes.

CODE
    # don't show this window by default
    self.visible = false


This "hides" the window. The method visible is actually not in Window_Base, but in Window_Base's parent class, Window. Window is a hidden class, but you can find a list of its methods in the Help file under RGSS Built-In Classes. Since Window_HUD is descended from Window, it can do anything Window can do.

CODE
    # make the window semi-transparent so the player can see what's behind it
    self.opacity = 160


This makes the window semi-transparent. Feel free to play with transparencies until you get a feel for what's too transparent and what's not.

CODE
    # we're only visible if this switch is on
    self.visible = $game_switches[1]
    
    # don't update if we can't see the window.  Prevents lag.
    if !self.visible
      return
    end


Like $game_variables, $game_switches keeps all of the switches you use in the event editor. Switches are "booleans", which means they're either true or false (corresponding to on or off). The first line sets the visibility to the first switch (which I turn on when the minigame begins). In other words, if switch 1 is on, the window is visible. If it's off, it's not.

The next line is another lag prevention technique. The ! means not, so this says: If I'm not visible, then return. Return is a special keyword that tells the interpreter to exit the current method without looking at any of the code below it. You can also return a value, which we'll probably get into later. For now, all you really need to know is that this will exit the method without checking the number of coins or hp, saving on calculations.

Homework

Make a simple HUD that shows HP, SP, and gold. Or any other 2-3 values you choose. You can use plain text or icons. Hint: get the party's current gold via $game_party.gold.

Demo So Far

http://www.pcis-studios.com/ccoa/Window%20Demo.exe


__________________________
Go to the top of the page
 
+Quote Post
   
 
Start new topic
Replies
matty0828
post Apr 21 2008, 05:29 AM
Post #2


Level 4
Group Icon

Group: Member
Posts: 54
Type: None
RM Skill: Beginner




Great tutorial but is it compatible at all with VX?

I'm no scripter (nor am I good with graphics yet!) so if there are any changes that have to be made I'll probably get lost sad.gif

If it's too hard to change, is there anything similar to this for VX? Also, is there a way that I could find a HUD like the one you've used which I could use with Moghunter's Location Name VX script?

Sorry if this isn't possible and I've just wasted valuable post space but I was just wondering!
Go to the top of the page
 
+Quote Post
   

Posts in this topic
- ccoa   [Scripting][Series]RGSS: Windows - Lesson 2a: Adding A Window to a Scene   Jan 9 2008, 07:12 AM
- - SeeYouAlways   Hmm... Oh my. Wonderful! Another lesson! Y...   Jan 9 2008, 07:46 AM
- - Kinnison   Cool, an update Yeah, I was wondering is it poss...   Jan 9 2008, 08:11 AM
- - ccoa   Certainly is. I already showed you how to display...   Jan 9 2008, 08:35 AM
- - Punk   [Show/Hide] old hudBooyah! I made a nice hud. ...   Jan 9 2008, 01:37 PM
- - ccoa   Sweet, Punk. If there were extra credit, surely y...   Jan 9 2008, 02:11 PM
- - Punk   I just modified my little HUD and thanks ccoa.   Jan 9 2008, 08:32 PM
- - Dark Dragon   Ah I thought ccoa quit RMXP some time ago D= I...   Jan 9 2008, 09:06 PM
- - Kinnison   Whoops, sorry. I did read the step-by-step method....   Jan 9 2008, 11:07 PM
- - ccoa   You probably forgot to add the update to Scene_Map...   Jan 10 2008, 05:20 AM
- - Black Shadow   This is going to be so helpfull when Im working on...   Jan 11 2008, 04:07 AM
- - Kinnison   Is it this one? CODE@hud_window.update I did put...   Jan 11 2008, 06:43 AM
- - ccoa   QUOTE (Black Shadow @ Jan 11 2008, 04:14 ...   Jan 11 2008, 07:39 AM
- - Kinnison   Here's the script I made. CODEclass Window_HU...   Jan 11 2008, 07:58 AM
- - ccoa   Well, at a glance you defined the update method tw...   Jan 11 2008, 08:47 AM
- - Kinnison   Oh, okay. It's working fine now, thanks.   Jan 11 2008, 09:56 AM
- - Heavyblues   I just typed up the entire thing, but it's giv...   Jan 18 2008, 01:54 PM
- - ccoa   I really need to see the actual code to be of any ...   Jan 18 2008, 02:02 PM
|- - Heavyblues   RE: [Scripting][Series]RGSS: Windows - Lesson 2a: Adding A Window to a Scene   Jan 19 2008, 05:15 PM
- - ccoa   Aha! Ruby is case sensitive. Window_Hud and ...   Jan 19 2008, 05:17 PM
|- - Heavyblues   QUOTE (ccoa @ Jan 19 2008, 04:24 PM) Aha...   Jan 22 2008, 02:06 PM
- - Jadak   I get this error when I press new game: [Show/Hi...   Jan 21 2008, 03:09 PM
- - Synthesize   CODE super(0, 0, -110, -96) Why is this n...   Jan 21 2008, 03:25 PM
|- - ccoa   QUOTE (Synthesize @ Jan 21 2008, 03:32 PM...   Jan 22 2008, 05:42 AM
- - Jadak   hehe thanks, works...near perfectly now. My major ...   Jan 21 2008, 07:43 PM
- - Synthesize   CODE bitmap = RPG::Cache.icon('h...   Jan 21 2008, 08:25 PM
- - Jadak   o-o I would have thought known about the icons...   Jan 21 2008, 11:17 PM
- - Jadak   Hehe great! Thanks! Off I go to part2...   Jan 22 2008, 01:30 PM
- - darkkyros   This one I've been working on most of the day....   Jan 23 2008, 02:56 AM
- - ccoa   Great job, Jadak! Very nice, darkyros. For...   Jan 23 2008, 05:04 AM
- - Ulmarion   Having a minor issue. The text isn't displayin...   Feb 2 2008, 05:34 PM
- - ccoa   If your text isn't displaying, the most likely...   Feb 4 2008, 12:27 PM
- - KyoRen   Well, here's my entry. (Yay, my first post...   Feb 10 2008, 05:42 AM
- - Synthesize   @KyoRen: You are missing a bunch of 'end's...   Feb 10 2008, 05:13 PM
- - KyoRen   Ah yes, so simple that I can't believe I misse...   Feb 20 2008, 07:04 PM
- - donitsi   Hey, I just started to script and I need to thank ...   Feb 24 2008, 12:27 PM
- - Fieryarts   I've run into an error that I can't seem t...   Apr 11 2008, 05:44 PM
- - pim321   I tried to aply this to vx, it does work a little ...   Apr 12 2008, 06:19 AM


Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 

Lo-Fi Version Time is now: 19th June 2013 - 03:08 PM
RPG RPG Revolution is an Privacy Policy and Legal
eXTReMe Tracker