How to: User-friendly code and Plug & Play

A quick tutorial for making your code more user friendly and Plug and Play.

  1. Marin
    I myself absolutely suck(ed) at making scripts Plug and Play/user-friendly, and making it easy for people to implement a script/resource of mine. A perfect example of that is my old generation 7 project.

    I saw someone suggest a tutorial on how best to make Plug and Play scripts, so let's get started.


    • 1.) Classes can be overwritten and added to from elsewhere.
      Let's say you want to add an attr_accessor to the PokeBattle_Trainer class ($Trainer is an instance of that). What you could do is to tell people to locate the script section the class is in and add it there. That's not at all efficient, however. You can "open" the class in a different section and simply add the attr_accessor in there. In this example, we want to add an attr_accessor :quests to our PokeBattle_Trainer class. Our original class is in a conveniently named section called "PokeBattle_Trainer". We'll make a new section and write the following:
      Code:
      class PokeBattle_Trainer
        attr_accessor :quests
      end
      
      This will simply add the getter/setter to the class without removing anything. If the accessor already exists in the class, it will be overwritten. If not, it will simply be added.

      2.) Aliasing
      Ruby has a very convenient keyword called alias. It allows you to rename methods during runtime without actually changing the code you write. The method can still be called, but you'll have to call whatever you renamed the method to. An alias follow one, basic format:
      Code:
      alias new_name old_name
      
      old_name is the method you want to rename, and new_name is the name the method will get.

      Aliasing is often used when you need to add code at the start or end of the method. You can alias methods in the main scope (not in a class or module), but you can also alias methods in a class (most common), and even in a module (less common and a bit harder). Here's an example of how to alias a method in the main scope:
      Code:
      def my_method	  # This is our method
        p "Hello world!"	 # It prints "Hello world!"
      end
       
      my_method	  # We call our method above
       
      alias old_my_method my_method  # We rename our method above to old_my_method
      def my_method	  # We overwrite our method
        p "I have been aliased!"   # We do something different
        old_my_method	  # We run our initial method
      end
       
      my_method	  # We call our aliased method
      It works pretty much the exact same way for classes:
      Code:
       class Test			   # Initializing a new class
        def my_method			# Creating a new method
      	p "This is the first method!"
        end
      end
       
      myTestClass = Test.new		  # Creating an instance of our class
      myTestClass.my_method		  # Calling the method we just created
       
      class Test				# Opening the class back up
        alias my_old_method my_method	# Renaming our method to my_old_method
        def my_method			# Overwriting our method
      	p "This is the overwritten method!"
      	my_old_method			# Calling our old method
        end
      end
       
      myTestClass.my_method		  # Calling our method again
      
      We'll now do the exact same, but for a module:
      Code:
       module Test			   # Initializing a new class
        def self.my_method		  # Creating a new method
      	p "This is the first method!"
        end
      end
       
      Test.my_method			  # Calling the method we just created
       
      module Test			  # Opening the module back up
        class << Test			# Aliasing inside modules happens here
      	alias my_old_method my_method	# Renaming our method to my_old_method
        end
       
        def self.my_method		  # Overwriting our method
      	p "This is the overwritten method!"
      	my_old_method			# Calling our old method
        end
      end
       
      Test.my_method			  # Calling our method again
      
      As you can see, we can't just alias in the main module itself. We have to do class << ClassName and do the aliasing in there.


      As you can only add code above or below by using alias, you may sometimes want to copy some code from the original method. Don't go too overboard with this though, as the user can have changed the method (which would overwrite their changes)!


      3.) New methods and classes don't always need to be in one specific section.
      This is dead obvious, yet I failed at this myself at one point. If you add a new method that is related to the content in one section, it does not need to be in that section. For example, I made a new method for drawing text at one point and told users to put it in a section called "Messages". This is only adding to confusion, as putting it in there is not even necessary. My resource would be the only one making use of the method, so it could just go inside the script of my resource. That's something you should think about.
    • Constants and instance variables in one place.
      Let's say you're making a resource. It's a good habit to work with constants at the top of your class, or instance variables (@variable) at the top of whatever method initializes the class/sets up the module.

      Some examples of things you may want to make constants or instance variables:
      • Coordinates for sprites or other important things
      • Text color
      • Sound effect names/volumes
      • Paths to files
      • If you have moving elements (for transitions), move speed
      • If you have animating sprites, how often they animate (e.g. every X frames)
      Those are just some examples; if you deem something important or neat, consider making it easily changeable.
      Here's an example:
      Code:
      class MyUI
        SPRITE_PATH = "Graphics/Pictures/my_graphic"
        SPRITE_X = 255
        SPRITE_Y = 0
       
        def initialize
      	@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
      	@sprite = Sprite.new
      	@sprite.bitmap = BitmapCache.load_bitmap(SPRITE_PATH)
      	@sprite.x = SPRITE_X
      	@sprite.y = SPRITE_Y
        end
      end
      


      COMMENT your code
      You'll have likely heard it hundreds of times and chances are, you've got a great argument against doing so, too. Think from the perspective of who will be using your resource or else, though. Often times, people will want to do minor or major adjustments. By commenting here and there, you can be a big help to those people (which is what your aim is anyway when you're making a public resource, right?)
      Of course, commenting every line or every few lines is ridiculous. It would be a good practice to comment method calls that do important things that people might want to change (messages, positions of certain sprites/elements). Aside from the method calls, consider commenting what arguments your methods take. A big complaint which I myself have myself with some methods in Essentials is the lack of "documentation" on a method. For example, if a method has (*args) and you actively use that in the method (args[0], args[1], args[2], etc.), the user has no way of knowing what everything stands for. That's all the more reason to want to document that, as it would really annoy people.


    Of course, I speak as a relatively new programmer, and these are just some things I've encountered or found to work.
    chikorita likes this.