SLIP INTO RUBY - UNDER THE HOOD PART 3: GAME OBJECTS

Delve into the wondrous intricacies of the Game_Temp, Game_System and Game_Timer classes! A day early even! Because I love you.

  • Trihan
  • 03/30/2015 10:22 PM
  • 11276 views
Hello again sports fans! It's time for the nitty-gritty. Getting more under the hood than the hood. It's time...for GAME OBJECTS! And also



There are 31 Game Objects classes in RGSS3; I'm going to cover 3 of them per issue for 10 issues, and then give Game_Interpreter its own entry as it's really, really, really, really, really big. Really. Really. Big. Like, 1413 lines of code big. If it turns out that I have 3 in a row that aren't really that much content I might combine 6 into one update, but we'll see how it goes.

Game_Temp

Game_Temp handles temporary data that isn't needed in a save file, and is referenced by the global variable $game_temp.

First, the instance variables:

attr_reader   :common_event_id          # Common Event ID
  attr_accessor :fade_type                # Fade Type at Player Transfer


common_event_id is used to store the ID of the currently-reserved common event. We'll see more about this when we get to Game_Interpreter. fade_type stores the type of fade that occurs when the player is transferred to a new map, which again we'll see in more detail in Game_Interpreter.

def initialize
    @common_event_id = 0
    @fade_type = 0
  end


Really simple initialize method; all it does is set both instance variables to 0.

def reserve_common_event(common_event_id)
    @common_event_id = common_event_id
  end


Setter method for common_event_id. Takes the ID of the reserved common event as the parameter.

def clear_common_event
    @common_event_id = 0
  end


Clear method for common_event_id, and simply sets it to 0.

def common_event_reserved?
    @common_event_id > 0
  end


Returns true if common_event_id is greater than 0. (you'll notice that a convention in RGSS3 is for "check" methods which return a boolean to have a name ending in a question mark, which is actually quite good practice)

def reserved_common_event
    $data_common_events[@common_event_id]
  end


Getter method for common_event_id, though rather than returning the variable's value it looks up the common_event_idth element of $data_common_events, which is a global variable containing all the data on common events in the database.

And that's it! They get more exciting, promise.

Game_System

Game_System handles system data, and is referenced by the global variable $game_system.

As always, first we look at the instance variables:

attr_accessor :save_disabled            # save forbidden
  attr_accessor :menu_disabled            # menu forbidden
  attr_accessor :encounter_disabled       # encounter forbidden
  attr_accessor :formation_disabled       # formation change forbidden
  attr_accessor :battle_count             # battle count
  attr_reader   :save_count               # save count
  attr_reader   :version_id               # game version ID


SUPER MEGA SURPRISE LESSON GAIDEN

You'll have noticed by now that a lot of instance variables are defined as symbols, and that they are generally attr_accessor or attr_reader. There's also attr_writer, but that's less common. This is basically a shorthand way of creating not only an instance variable for the symbol, but also a getter and/or setter method, meaning you can read or write to the variable outwith the class.

To explain a little more clearly, currently you can do something like "$game_system.save_disabled = false" because it's an attr_accessor. Change it to an attr_reader or remove the line entirely and the game will crash with a NoMethodError or something similar.

Anyway, let's look at what we've got here.

save_disabled, menu_disabled, encounter_disabled and formation_disabled are exactly what they say on the tin: they determine whether the player is allowed to save, access the menu, fight battles or change party formation.

battle_count keeps track of how many battles the party has fought. save_count keeps track of how many times the game has been saved. version_id tracks the version number of the game. version_id is a built-in attribute of RPG::System, and is a random number used for update checks, which is changed every time you save your game.

def initialize
    @save_disabled = false
    @menu_disabled = false
    @encounter_disabled = false
    @formation_disabled = false
    @battle_count = 0
    @save_count = 0
    @version_id = 0
    @window_tone = nil
    @battle_bgm = nil
    @battle_end_me = nil
    @saved_bgm = nil
  end


Just initialising the instance variables. The booleans are initialised to false, the integers to 0. There are also some non-attr variables: window_tone, which contains the tone used for windows; battle_bgm, which contains the BGM used in battle; battle_end_me, which contains the victory theme; and saved_bgm, which contains the previously-playing BGM.

def japanese?
    $data_system.japanese
  end


Determines whether the game is in Japanese mode: this will always be true in the Japanese version of the editor, and false otherwise.

def window_tone
    @window_tone || $data_system.window_tone
  end


Getter method for window tone. Returns window_tone if it contains a value, or the window tone from $data_system (the default that was set in the database) otherwise. The former will only be true if you call $game_system.window_tone= directly or use the "Change Window Color" event command.

def window_tone=(window_tone)
    @window_tone = window_tone
  end


Setter method for window_tone.

def battle_bgm
    @battle_bgm || $data_system.battle_bgm
  end


Getter method for battle_bgm; works on the same principle as the window tone.

def battle_bgm=(battle_bgm)
    @battle_bgm = battle_bgm
  end


Setter for battle_bgm.

def battle_end_me
    @battle_end_me || $data_system.battle_end_me
  end


Getter for battle_end_me.

def battle_end_me=(battle_end_me)
    @battle_end_me = battle_end_me
  end


Setter for battle_end_me.

def on_before_save
    @save_count += 1
    @version_id = $data_system.version_id
    @frames_on_save = Graphics.frame_count
    @bgm_on_save = RPG::BGM.last
    @bgs_on_save = RPG::BGS.last
  end


This method is called before the game is saved. First, the save count is increased by 1. The version ID is updated and the number of frames which have elapsed is stored in @frames_on_save. Finally, the BGM and BGS are saved to their respective variables.

def on_after_load
    Graphics.frame_count = @frames_on_save
    @bgm_on_save.play
    @bgs_on_save.play
  end


Called after a game is loaded. Sets the number of elapsed frames to the value saved and starts playing the saved BGM/BGS.

def playtime
    Graphics.frame_count / Graphics.frame_rate
  end


Gets the amount of time played in seconds by dividing the number of frames which have elapsed by the frame rate in frames per second.

def playtime_s
    hour = playtime / 60 / 60
    min = playtime / 60 % 60
    sec = playtime % 60
    sprintf("%02d:%02d:%02d", hour, min, sec)
  end


Returns a string of the playtime in hours, minutes and seconds (hours being playtime divided by 60 divided by 60, minutes being playtime divided by 60 mod 60, and seconds being playtime mod 60). The sprintf format ensures two significant figures, so single digits are preceded with a leading zero.

def save_bgm
    @saved_bgm = RPG::BGM.last
  end


Simple method which stores the most recently played BGM in its instance variable.

def replay_bgm
    @saved_bgm.replay if @saved_bgm
  end


Method for resuming a saved BGM if one exists.

And that's it for Game_System!

Game_Timer

This class, funnily enough, is the one that handles timers. It's referenced by the global variable $game_timer.

def initialize
    @count = 0
    @working = false
  end


At initialisation we have two instance variables: count, which is the value of the timer in FRAMES; and working, which is a boolean determining whether the timer is running or not.

def update
    if @working && @count > 0
      @count -= 1
      on_expire if @count == 0
    end
  end


Update is called every frame. If the timer is running (@working is true) AND @count is greater than 0, we reduce @count by 1. If @count is now 0, we call on_expire.

def start(count)
    @count = count
    @working = true
  end


Simple method for starting a timer. The number of FRAMES is supplied and assigned to @count, and @working is set to true.

def stop
    @working = false
  end


Stopping a timer is even simpler: we just set @working to false.

def working?
    @working
  end


Checker method, will return true if @working is true.

def sec
    @count / Graphics.frame_rate
  end


Method for determining the seconds left on the timer. As @count is a value in frames, we divide by the frame rate to determine the actual number of seconds. For example, if the frame rate is 60FPS and we have a 30-second timer, @count will initially be equal to 1800. Calling $game_timer.sec will return 30 (1800 / 60).

def on_expire
    BattleManager.abort
  end


This is the method which is called when a timer expires, and simply aborts battle.

...wait, what? I say again.

Yep! I don't think this is particularly common knowledge, but if you start a timer in battle it will AUTOMATICALLY end battle once the timer expires! If you wanted a timer that didn't end battle, you would have to edit this method.

And so ends another edition of Slip into Ruby. Next week, we're going to cover Game_Message, Game_Switches and Game_Variables! And since the latter two are really super short, we'll probably throw in a couple more.

As always, any and all feedback is welcomed and appreciated. I do this for you guys, after all.

Until next time.

Posts

Pages: 1
This is likely a huge necropost, but since no one else has posted their thoughts, I may as well.

I always thought it annoying(!) when the battle ended due to the timer being zero.

Once, I tried to change the method by adding

'unless $game_switches (#) true?' (without the quotes and with brackets in place of parentheses because these forums hate brackets)
to the end of BattleManager.abort ...

That made Ruby throw a fit.
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3359
def on_expire
    BattleManager.abort unless $game_switches[#]
  end

You can do angle brackets by enclosing them in angle brackets in your post. :)

I think what's breaking it is that "true?"; $game_switches has no such method, and you appear to be missing a dot to make it a method call anyway.
author=Trihan
def on_expire
    BattleManager.abort unless $game_switches[#]
  end
You can do angle brackets by enclosing them in angle brackets in your post. :)

I think what's breaking it is that "true?"; $game_switches has no such method, and you appear to be missing a dot to make it a method call anyway.


I'm guessing that I misinterpreted how if a switch is on, it's read as 'true' in the syntax. Missing the dot, where exactly?

$.game_switches [#] (?)

Kinda feelin' the novice burn...
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3359
You can do just $game_switches[*] as a condition and it'll implicitly understand that you want to check it returns true for the value of the element, or if the switch class had a true? method defined you could do $game_switches[*].true?

Method calls will always have a dot between the name of the method and the object calling them.
def on_expire
BattleManager.abort unless $game_switches[#].true
end
So, it would look like this?

That's neat.

Hmm...
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3359
No, if you wanted to do it non-implicitly you'd do

BattleManager.abort unless $game_switches[#] == true
author=Trihan
No, if you wanted to do it non-implicitly you'd do

BattleManager.abort unless $game_switches[#] == true


How do you get the code to come up in those colors?

Or is it a manual edit?

Of course, the ==...implicitly equal to. I'm guessing that Ruby doesn't like =/= for inequal.
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3359
You'd use != for not equal to.

When you click the "code" tag it asks you what the language is. If you enter "ruby" it'll automatically colour the code for you.
def on_expire
    BattleManager.abort unless $game_switches[#] == true
  end
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3359
That's it! Though personally I'd just do

BattleManager.abort unless $game_switches[#]


I try never to use explicit method calls for boolean checks if all I'm doing is checking for truth, implicit true for the win!
Yay! Seems I was never too far off in the first place.

But aren't there other ways for boolean checks, such as if, else and elsif?

lol 'elsif' - for whatever reason, I think of 'Keebler Elves' when I see that term.
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3359
They follow the same principle. You can do

if $game_switches[#]


just as easily as unless.

Ruby is one of the few languages that supports this syntax of putting your condition after the line of code to execute.
Pages: 1