Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Scripting Process : Adding Attributes

Adding Attributes to Default Classes
By SephirothSpawn & Trickster


An Introduction
This Tutorial was designed to teach people how to add new attributes to the default RMXP classes whether they be the Game_Session classes or the Data Structure classes. We will begin with learning about instance variables and explore several ways to achieve adding attributes into classes.
   
A Quick look at Instance Variables
   Instance Variables are basically properties to objects in Ruby. An object is everything from a certain actor, item, weapon, etc. In Ruby, these objects are made from classes.  Classes are collection of variables and methods that basically run your game.
   
Instance variables are started with an @ symbol, followed by a letter or underscore followed by any combination of letters, numbers or underscores.


Code:
class Something
  # The following is a special method, automatically evoked with the .new call
  def initialize
    # The following are instance variables
    @instance_variable = 5
    @hp = 100
    @sp = 50
  end
end

 
Instance Variables are special variables that can be used throughout an entire class. They will be accessible in all methods of the class. By default, you cannot access these variables outside of the object. Meaning, if you want to see the value @instance_variable in some_object, you cannot view or modify this data. So what Ruby does is Public access to these variables by creating specialized methods to do so.
   
   Attr_Variables
Attr_Variables are quick versions of methods that allow you to read, write or both to an instance variable. Before we take a quick look at the actual attr_variables, lets look at the actual methods.
 
Code:
class Something
     def initialize
       @your_variable = 5
     end
     # The following method, allows you to read the data within your instance variable
     def your_variable
       return @your_variable 
     end
     # The following method allows you to write over the variable
     def your_variable=(new)
       @your_variable = new
     end
   end
    
   # First, we will create our object
   object = Something.new
    
   # To read our @your_variable within that class, we simply call the method .your_method
   p object.your_variable   ->   5
    
   # To Write over our variable, we will use an = sign, followed by a new value
    
   object.your_variable = 10
p object.your_variable    -> 10

As you can see, if you were to have a class with several instance variables, giving public access to these variables will quickly add more code lines than you could need. So what Ruby did was give us attr_reader, attr_writer & attr_accessor.
   
attr_reader basically emulates our first method, that will return our value (Reads). So you could eliminate that method, and add the line - attr_reader :your_variable
   
attr_writer emulates our second method, that modifies our variables (Writes). So now you can eliminate our second method and add the line - attr_writer :your_variable
   
   We now have:
 
Code:
class Something    attr_reader :your_variable
     attr_reader :your_variable
     def initialize
       @your_variable
     end
   end

We aren’t done yet though. This can still be simpler. Attr_accessor emulates both of our first and second method, allowing us to both read and write our variables.
 
Code:
class Something    attr_accessor :your_variable
     def initialize
       @your_variable
     end
   end

This is just one instance variable. Now imagine dynamic objects with several instance variables. This drastically helps in larger scripts.
   
   So why not always attr_accessor?
Many people often just make everything an attr_accessor (including myself). The main reason for this is setting up dynamic reader and writer method, that simply don’t just return an instance variable or simply overwrite it.
   
Lets say, in calculating defense of some object, you not only take the defensive value of the actor, but also add in any armor the actor has equipped. You would not make you @defense variable a direct reader, but instead make some dynamic method.
 
Code:
class Something    # The following is just a made up method. Don’t bother looking in RMXP for it.
     def defense
       n = @defense
       # The following line just passes through some make believe armors
       for armor in [@armor1, @armor2]
         # This just adds defense from our armor objects to our defense total
         n += armor.defense
       end
       # Now we return the collection of our defense and our armors defense
       return n
     end
   end

  Through this, you are able to create more dynamic attributes for your objects.
   
   Creating New Attributes
So, now that we know how to create new attributes to an object, we can now add new attributes to our classes. The most basic way of doing this is through new attr_variables into whatever class we want and going from there. What I have found is that by using several combinations of container classes (Arrays and Hashes) with methods in your classes gives you the most efficient way of doing this, but we will look at several ways of going about this.
   
   The “Throw a bunch of accessor in a classâ€
 
Trickster this is the first time you've ever successfully dumbed something down enough to where regular people can understand it. :P
 
nice!

I would just like to add a few things/examples about the reading/writing methods, I hope you don't mind?

Code:
[color=#0000FF]class[/color] Whatever

  attr [color=#00BFBF]:[/color]blah [color=#008000]# attr is an equivalent for attr_reader[/color]

[color=#0000FF]end[/color]

 

whatever [color=#00BFBF]=[/color] Whatever[color=#00BFBF].[/color]new

p whatever[color=#00BFBF].[/color]blah [color=#008000]# -> nil[/color]

and an example for attr_writer because so far I'm the only one who found a use for it :
(or at least I think)

But after all it depends on the way you script, you could do this in a million of other ways.

Code:
[color=#0000FF]class[/color] Test

  attr_writer [color=#00BFBF]:[/color]opacity [color=#008000]#opacity should be a value between 0 and 255[/color]

  [color=#0000FF]def[/color] initialize

    @opacity [color=#00BFBF]=[/color] [color=#BF0000]255[/color]

  [color=#0000FF]end[/color]

  [color=#0000FF]def[/color] opacity [color=#008000]# will return a value between 0 and 255 even if @opacity is actually greater or smaller[/color]

    [color=#0000FF]return[/color] [color=#00BFBF][[[/color]@opacity[color=#00BFBF],[/color] [color=#BF0000]255[/color][color=#00BFBF]].[/color]min[color=#00BFBF],[/color] [color=#BF0000]0[/color][color=#00BFBF]].[/color]max

  [color=#0000FF]end[/color]

[color=#0000FF]end[/color]
 

Jason

Awesome Bro

Havent finishe reading it yet but the [ COLOR] boxes are kinda annoying, it doesn't change the colour, just put an extra block there that I have to read past.
 
How do I make a second dimension? For instance, lets say i just want another mp gauge. How would I change things so there's a variable for each actor for each level?

EDIT: Crap, I just realized this tutorial is for XP. What are the changes to VX?
 
It all depends on how you want to level your mp stat. Here is a simple point-slope formula, so each actor starts with x and gains y every level.

Code:
module MP_Stats
  # Actor 1 - 500, 2 - 450, 3 - 400, 4 - 600
  Start = {1 => 500, 2 => 450, 3 => 400, 4 => 600}
  # Every actor not defined above - 400
  Start.default = 400
  # Actor 1 - 50, 2 -  34, 3 - 40, 4 - 55
  Gain = {1 => 50, 2 => 34, 3 => 40, 4 => 55}
  # Every other actor - 40
  Gain.default = 40
  def self.mp(actor)
    base = Start[actor.id]
    base += Gain[actor.id] * actor.level - 1
    return base
  end
end

class Game_Actor
  def maxmp
    return MP_Stats.mp(self)
  end
end

So to check maxmp, it gets a value from the MP_Stats module. You can check the MACL under Modules.Curve Generator for a few curve formulas.

It is a more complex thing to do, but once you do it once and figure it out, it's not too difficult to do it again.
 
Ok, I worked a bit and everything you said pretty much applies to VX ( i don't know what a ' is, but i assumed it was a " so i switched them when ' didn't work)

Also, this might be the wrong place, but whats the difference between mp and maxmp? or, how does the game deal with mp?

EDIT: actor.level doesn't work. the game doesn't know what it means. Is there a way to reference the actor's level?

EDIT2: Here's my script so far

class Game_Actor < Game_Battler
  MaxEnergy = { 1 => 110, 2 => 120, 3 => 130 }
  Default_MaxEnergy = 100
  Energy = { 1 => 110, 2 => 120, 3 => 130 }
  Default_Energy = 100
  MaxEnergyGain = { 1 => 11, 2 => 12, 3 => 13 }
  Default_MaxEnergyGain = 10
  attr_accessor :maxenergy
  attr_accessor :energy
  alias seph_tut_setup setup
  def setup(actor_id)
  seph_tut_setup(actor_id)
    if MaxEnergy.has_key?(actor_id)
      @maxenergy = MaxEnergy[@actor_id]
    else
      @maxenergy = MaxDefault_Energy
    end
    if MaxEnergyGain.has_key?(actor_id)
      @maxenergy += MaxEnergyGain[@actor_id] * level #<<<<<<<< The game thinks "level" is always 1
    else
      @maxenergy += Default_MaxEnergy[@actor_id] * level
    end
    if Energy.has_key?(actor_id)
      @energy = Energy[@actor_id]
    else
      @energy = Default_Energy
    end
  end
end

Do you know what I'm doing wrong?
 
' = single quote
" = double quote

I assumed mp was an stat like hp. Maxmp was just a method like maxhp. level is readable so not sure why that isn't working.

What exactly are you trying to do? Just tell me what stat you are trying to add, how it will be used and I'll help the best I can.
 
I'm adding an "Energy" system, which in every way just acts like mp. It's another tank to take costs from. What it should do right now is set everyone's energy to 100(default), then add 10(default) at every level(including 1).
 
First off, I suggest if you are making a new stat, make a module for this data. Fewer script complications this way.

Code:
    if MaxEnergy.has_key?(actor_id)
      @maxenergy = MaxEnergy[@actor_id]
    else
      @maxenergy = MaxDefault_Energy
    end

Don't use this. Use .default of hashes. It makes it so you dont have to check if a key is defined, it just returns the default.

I suggest making maxenergy a method, not an instance variable.
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top