RGSS For Newbies (Ongoing Series)

Discussion in 'Learning Ruby and RGSSx' started by Elder, Jul 5, 2018.

  1. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    Hey gales and guys!

    I'm known as Elder (or Gamezopher). A few of you may remember me for a game I've released a decade ago with RPG Maker VX. A long time ago I've released a commercial game with RPG Maker VX: https://en.wikipedia.org/wiki/Eternal_Eden

    I've decided to resurrect a series of tutorials I wrote 10 years ago, and if you people believe it's helpful, I'm planning to write more of them from now on. The goal is to help beginners to understand how RGSS works. Together we're going to dissect each part of RGSS bit by bit. Then we'll be ready to progress with more complex tutorials such a building complex features from scratch such as a battle system, custom menus, etc, to improve your RPG.

    Feel free to bookmark my website: http://www.gamezopher.com/ and ask questions or request tutorials.

    Feel free to ask questions if you want to know anything about RGSS.
     
    Last edited: Jul 8, 2018
    #1
    Zeriab, Knayter and Heirukichi like this.
  2. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    Part 1 - Hello World Program

    There is an ancient folklore in computer programming that says that your first program in any language should be a ‘Hello World’ display program. This tradition also states that such program should be very easy, as few lines as possible.

    Without further ado, let’s follow these steps:

    1-) Open RPG Maker (VX, VX Ace) and open the Script Editor (F11).
    2-) Delete every page of code there. Absolutely everything.
    3-) Insert a new entry and name it something like ‘My New Script’
    4-) Enter the following piece of code in the editor.

    Code:
    my_sprite = Sprite.new()
    my_bitmap = Bitmap.new(544, 416)
    my_sprite.bitmap = my_bitmap
    my_sprite.bitmap.draw_text(0, 0, 320, 300, "Hello World!", 2)
    
    loop do
      Graphics.update
    end
    Watch this video for the above steps:


    Let's break the code down

    To display text on the screen, we use two built-in classes from the RPG Maker class library: Sprite and Bitmap. Beware: the Bitmap class alone isn't enough to display something on the screen. It may sound a bit confusing for a newbie, but the best analogy for both Bitmap and Sprite classes is a soul/body relationship. The bitmap object contains the data (soul), and the sprite object is the visual container for the data (body). The Sprite class possesses a property called 'bitmap', which is nothing else than an encapsulated Bitmap class object. So, it makes it easy for us to branch things together: my_sprite.bitmap = my_bitmap.

    This Bitmap class requires a bit more labor. We initialize it to (544, 416), which is the same dimension of the smallest screen resolution. Then we used its method <em>draw_text</em> to print our 'Hello World!' message on the screen.

    Finally, we use the RGSS2/RGSS3 built-in module 'Graphics' and its update method Graphics.update. It refreshes the game screen and advances time by 1 frame. So, set it inside a loop statement: loop do(...)end; otherwise the screen is going to be black.

    Source: http://www.gamezopher.com/
     
    Last edited: Jul 8, 2018
    #2
  3. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    Part 2 - Plotting Pixels

    The current demo is the 'pixel' equivalent of our previous 'Hello World' program, except we're going to plot a green pixel in the middle of the screen. Follow these steps.

    1-) Open RPG Maker and open the Script Edition (F11).
    2-) Delete every piece of code there. Absolutely everything.
    3-) Insert a new script and name it to something like “My Script”
    4-) Enter the following snippet of code in the editor.

    PLOT A PIXEL ON THE SCREEN

    Code:
    my_bitmap = Bitmap.new(544, 416)
    
    my_color = Color.new(0,255,0)
    
    my_sprite = Sprite.new()
    my_sprite.bitmap = my_bitmap
    my_sprite.bitmap.set_pixel(272,208, my_color)
    
    loop do
      Graphics.update
    end 

    [​IMG]

    If you've followed the previous 'Hello World' tutorial carefully, you'll notice that only two lines are new. my_color = Color.new(0,255,0) is used to store a color in a variable from the Color class. It use the RBGA format, which stands for red, blue, green and alpha. Then my_sprite.bitmap.set_pixel(272,208, my_color) allows us to plot a single colored pixel on the screen at [x, y]. This method is declared like this: set_pixel(x, y, color). Pretty simple, huh?

    DRAW A SIMPLE LINE ON THE SCREEN

    Let's draw a simple line with a For-Loop statement.

    Code:
    screen_width = 544
    screen_height = 416
    y_axis = screen_height / 2
    
    my_sprite = Sprite.new()
    my_sprite.bitmap = Bitmap.new(screen_width, screen_height)
    my_color = Color.new(0,255,0)
    
    for x in 0..screen_width
      my_sprite.bitmap.set_pixel(x, y_axis, my_color)
    end
    
    loop do
      Graphics.update
    end 
    [​IMG]

    In this demo, a new pixel is plotted on the screen at each For-Loop increment. Keep in mind it's not the smartest way to draw a line on the screen. Under normal circumstances you'll want to use the Bresenham's line algorithm, which I'll cover in a future demo if requested.

    I've added a few more lines in the code. It's important to remember to avoid magic numbers as much as possible. For example, Bitmap.new(544, 416) is bad, because you have to guess what these numbers stand for. It's easier to understand the purpose of Bitmap.new(screen_width, screen_height). Still, for my upcoming demos I may still use some magic number for the sake of keeping code very short.

    FILL THE SCREEN WITH LINEAR GRADIANT COLORS

    Now that we're at it, let's fill the screen with gradient colors.

    Code:
    my_sprite = Sprite.new()
    my_sprite.bitmap = Bitmap.new(544, 416)
    my_color = Color.new(0, 0, 0)
    
    for x in 0..544
      for y in 0..416
        my_color.set(0, x, y)
        my_sprite.bitmap.set_pixel(x, y, my_color)
      end
    end
    
    loop do
      Graphics.update
    end
    [​IMG]

    The result is quite interesting. The rendering process is quite slow but these examples are mostly for the sake of testing. Surely later we'll be ready to test more complex and funnier scripts.

    One important mention about color. Each RGBA value is from 0 to 255. Values out of range are automatically corrected. So in the above demo, it doesn't matter whether the value exceeds 255 during the for-loop iteration.

    Source: http://www.gamezopher.com/
     
    Last edited: Jul 8, 2018
    #3
  4. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    Part 3 - Magic of using Viewports

    [​IMG]

    The easiest way to understand what a viewport do is to imagine them as layers or separate screens on a device.

    A viewport is a surface that can fill a portion of the display screen. In general, you'll want to fill the whole display area with the viewport's box: Viewport.new(0, 0, 544, 416). With that notion in mind, viewports can act like layers when juxtaposed on top of each other.

    But if you want, you can split your screen with two, three, four or more viewport zones. It's even possible to mimic the Nintendo 2DS with a bit of imagination. Sprites assigned to a viewport won't overlap with other viewports, they'll stay confined to the active one, unless dynamically programmed to move from one viewport to another.

    The following demo split the screen in two halves and assign a sprite in them. Given that these sprites are bigger than the viewports, they'll be cropped/clipped.

    Code:
    viewport1 = Viewport.new(0, 0, 544/2, 416)
    viewport1.z = 50
    
    viewport2 = Viewport.new(544/2, 0, 544/2, 416)
    viewport2.z = 100
    
    my_spriteA = Sprite.new()
    my_spriteA.viewport = viewport1
    file = "Graphics/Battlers/Behemoth"
    my_spriteA.bitmap = Bitmap.new(file)
    
    my_spriteB = Sprite.new()
    my_spriteB.viewport = viewport2
    file = "Graphics/Battlers/Demon"
    my_spriteB.bitmap = Bitmap.new(file)
    
    loop do
      Graphics.update
    end
    [​IMG]

    MIMIC 3DS LAYOUT WITH VIEWPORTS

    We can do a fun project and mimic the layout of the 3DS handheld console for testing viewports.

    Code:
    ### SETTINGS
    
    screen_width = 640
    screen_height = 480
    
    Graphics.resize_screen(screen_width, screen_height)
    
    bmp_3ds = Bitmap.new("Graphics/Pictures/nintendo_3DS")
    center_x = (screen_width/2) - (bmp_3ds.width/2)
    
    ### VIEWPORT FOR FULL SCREEN (BG)
    
    viewport_A = Viewport.new(0, 0, screen_width, screen_height)
    viewport_A.ox -= center_x
    viewport_A.z = 0
    
    my_sprite_A = Sprite.new()
    my_sprite_A.viewport = viewport_A
    my_sprite_A.bitmap = bmp_3ds
    
    ### 3DS TOP SCREEN VIEWPORT
    
    viewport_B = Viewport.new(center_x+79, 39, 314, 190)
    viewport_B.z = 100
    
    my_sprite_B = Sprite.new()
    my_sprite_B.viewport = viewport_B
    my_sprite_B.bitmap = Bitmap.new("Graphics/Battlers/Demon")
    
    ### 3DS BOTTOM SCREEN VIEWPORT
    
    viewport_C = Viewport.new(center_x+109, 280, 254, 192)
    viewport_C.z = 100
    
    my_sprite_C = Sprite.new()
    my_sprite_C.viewport = viewport_C
    my_sprite_C.bitmap = Bitmap.new("Graphics/Battlers/Behemoth")
    
    ### MAIN LOOP
    
    loop do
      Graphics.update
    end
    [​IMG]

    It's important to know that the viewport has two types of coordinates. Regular coordinates and offset coordinates. Regular coordinates can be set only during the object initialization: Viewport.new(0, 0, screen_width, screen_height). The regular coordinate is the absolute position of the viewport within the screen. Offset coordinates will shift the content inside the viewport horizontally and vertically.

    I've created three viewports for this scene. One is covering the entire screen and is used to display the Nintendo 3DS in the background. The 3DS image itself doesn't fill the entire screen, so used this simple formula, center_x = (screen_width/2) - (bmp_3ds.width/2), and then shifted the offset value horizontally, viewport_A.ox -= center_x, to center the image.

    The two other viewports are drawn over the background viewport. Just like the 3DS screens, their contents will never get outside the boundary of the rectangular area for each viewport. In the above snapshot, the monsters have been clipped to their respective viewport.

    TEST TOP SCREEN WITH GAMEPAD/KEYBOARD INPUT

    To see the top viewport in action, you can add the following snippet in the loop-do block:

    Code:
    loop do
      Graphics.update
     
      dir = Input.dir4
      Input.update
     
      case dir
      when 2
        my_sprite_B.y += 1
      when 4
        my_sprite_B.x -= 1
      when 6
       my_sprite_B.x += 1
      when 8
        my_sprite_B.y -= 1
      else
      end
    end
    source: www.gamezopher.com
     
    Last edited: Jul 8, 2018
    #4
  5. Knayter

    Knayter *teleport behind you* Nothing personal kid Veteran

    Messages:
    229
    Likes Received:
    479
    Location:
    Viet Nam
    First Language:
    Viet Nam
    Primarily Uses:
    RMVXA
    Thanks a lot for writing this!! I hope you can keep going in the future XD
     
    #5
  6. TheoAllen

    TheoAllen Self-proclaimed jack of all trades Veteran

    Messages:
    4,370
    Likes Received:
    4,937
    Location:
    Riftverse
    First Language:
    Indonesian
    Primarily Uses:
    RMVXA
    Not necessary. You will still need it especially if you're about to learn Window_Base, as it requires window tone from DataManager.init
    What you do is just to create a new script slot, and write everything there.

    Great tutorial btw, I'll be checking these if maybe I could contribute a bit.
     
    #6
  7. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    Other tutorials are coming, I've been jotting down some kind of roadmap for upcoming series. I'll maintain a good rate if I feel there's enough people interested in learning RGSS and more complex scripts from scratch.

    You can find the main table of contents for the ongoing 'Fundamentals of RGSS' series here: http://www.gamezopher.com/2018/07/03/this-is-a-test-post/
     
    Last edited: Jul 22, 2018
    #7
  8. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    RGSS: MAP, TILESETS and TILEMAP

    HOW TO LOAD A MAP?


    It's very easy to load a map in the RPG Maker script editor. We simply need to use the load_data() function. This function only needs the path+filename as a parameter. Example:

    Code:
    load_data("Data/Map001.rvdata2")
    
    We can use the sprintf() function with re-usability in mind (which borrows from the C language), but such explanation would require a longer in-depth tutorial. Anyway, here's an example of the usage:

    Code:
    map_id = 1
    @map = load_data(   sprintf("Data/Map%03d.rvdata2", map_id)   )
    
    What's interesting about the above snippet is that this function allows to use a special flag in the string format that acts like a dynamic variable. The %3d sequence allows to pad a digit with three leading zeros. More lines of code would be required if such feature had to be written by hand. So, it's wise to remember the sprintf() function for this kind of situation.

    As you can see, it's very easy to load a map via scripting. P.S. The load_data() function can be used for all rvdata files, as demonstrated in the next section.

    The Map data structure comes with so many properties such as width, height, tilemap data, linked tilesets, etc. In a future tutorial, we'll learn how to create a procedural map from scratch by code.

    TILESETS

    RGSS has a data structure called Tileset. I find the term a bit misleading because in truth the structure holds more than only one tileset. There are in fact 9 slots referring to a tileset type as follow: A1, A2, A3, A4, A5, B, C, D, E. The RPG Maker team should have called this class something like Tilesets or TilesetPack to strengthen the idea it's a group of tilesets, not just one.

    When you organize tilesets within the RPG Maker's tileset editor, each 'pack' of tilesets are stored in an array, and this array is then saved as "Tilesets.Rvdata2" within the Data folder.

    What's more, each map in RPG Maker is associated to one of those packs of tilesets stored in "Tilesets.Rvdata2". A map has in fact a property called tileset_id which refer to an index of that array.

    TILESET DIAPORAMA DEMO

    Here's a quick demo that allows to display each image from a pack of tilesets associated to a map on the screen. You can cycle throughout the list of tilesets associated with a map using the Left and Right arrows.

    Code:
    map_id = 1
    @map = load_data(sprintf("Data/Map%03d.rvdata2", map_id))
    
    @tilesets_rvdata = load_data("Data/Tilesets.rvdata2")
    @ts_pack = @tilesets_rvdata[@map.tileset_id]
    
    @ts_images = []
    
    @ts_pack.tileset_names.each do |str|
      @ts_images.push( Bitmap.new("Graphics/Tilesets/" + str) ) if str != ""
    end
    
    @show_tileset = Sprite.new
    
    i = 0
    
    loop do
      Graphics.update
     
      Input.update
     
      if Input.trigger?(:LEFT)
        i -= 1 if i > 0
      elsif Input.trigger?(:RIGHT)
        i += 1 if i < @ts_images.length-1
      end
     
      @show_tileset.bitmap = @ts_images[i]
     
    end
    
    [​IMG]

    First, we load our packs of tilesets in a variable that serves as an array: @tilesets_rvdata = load_data("Data/Tilesets.rvdata2"). Then, we find the tileset pack we need with our map's tileset id property: @tileset = @tilesets_rvdata[@map.tileset_id]. The Tileset data structure doesn't hold an array of images per se, it holds only the filenames in an array. So we create an array of bitmaps in which we use those filenames as the main parameter: @ts_pack.tileset_names.each do |str| @ts_images.push( Bitmap.new("Graphics/Tilesets/" + str) ) if str != "" end.

    In summary, remember that a map hold a tileset_id that refers to the index of the 'tilesets.rvdata2' array. Each pack of tilesets has an array of 'filenames' that refer to image files in the folder. You use those filename to create bitmap objects in the code. There's more related to the Tileset data structure, such as tile collision, but it will be for another future lesson.

    TILEMAP

    RPG Maker has a built-in feature called Tilemap. This feature magically takes care of the rendering process of tiles assigned to it. Coding your own rendering engine would take a significant amount of efforts. It's something I plan to teach, if there's enough requests in the future. For the time being, the Tilemap class provided by the RPG Maker team does the trick with very little intervention from the coder—even if it's somewhat limited for complex mapping layouts. Here's the simplest way to init the Tilemap class:

    Code:
    @map = load_data("Data/Map001.rvdata2")
    
    @tilemap = Tilemap.new
    @tilemap.map_data = @map.data
    
    In the following demo, the map's tileset image will switch at x interval of time, while preserving the tilemap data.

    Code:
    ### SETTINGS
    
    screen_width = 640
    screen_height = 480
    Graphics.resize_screen(screen_width, screen_height)
    
    ### LOAD EXISTING MAP
    
    map_id = 1
    @map = load_data(sprintf("Data/Map%03d.rvdata2", map_id))
    
    ### CREATE TILEMAP
    
    @viewport = Viewport.new(0,0,screen_width, screen_height)
    @tilemap = Tilemap.new(@viewport)
    @tilemap.map_data = @map.data
    
    ### LOAD EXISTING TILESETS
    
    path = "Graphics/Tilesets/"
    
    tileset_arr = []
    tileset_arr[0] = "Outside_A1"
    tileset_arr[1] = "Inside_A1"
    tileset_arr[2] = "World_A1"
    
    ### MAIN LOOP
    
    i = 0
    
    loop do
      @tilemap.bitmaps[0] = Bitmap.new(path + tileset_arr[i])
     
      i += 1
      i = 0 if i > 2
     
      Graphics.update
      Graphics.wait(30)
    end
    
    [​IMG]

    So, that's the basic of it. Of course, there's more to learn, but that will be for another lesson. Stay tuned.

    SOURCE: http://www.gamezopher.com/2018/07/03/this-is-a-test-post/
     
    Last edited: Jul 23, 2018
    #8
  9. TheoAllen

    TheoAllen Self-proclaimed jack of all trades Veteran

    Messages:
    4,370
    Likes Received:
    4,937
    Location:
    Riftverse
    First Language:
    Indonesian
    Primarily Uses:
    RMVXA
    Now that you brought Tilemap class in this tutorial, now I'm curious as of how it structured. Currently, it's the only RGSS3 mystery that yet have been solved for me. By I mean structure, the table structured by table of integer. How it represent which grid to render, how is their relation, and how is map data like passability is handled, etc. Knowing that, maybe I could play with "randomly generated" map later.
     
    #9
  10. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    Yup, it's part of another upcoming tutorial I've prepared in my road map (which should be soon). For this first batch, I try to keep things as simple as possible.

    I will probably rework the above tutorial on my website and explain things a bit differently.

    It's difficult to edit tutorial to more than one sources, so keep on eye on my blog as well for updates.
     
    Last edited: Jul 23, 2018
    #10
  11. TheoAllen

    TheoAllen Self-proclaimed jack of all trades Veteran

    Messages:
    4,370
    Likes Received:
    4,937
    Location:
    Riftverse
    First Language:
    Indonesian
    Primarily Uses:
    RMVXA
    Good luck, I reckon that explaining how autotiles works won't be an easy matter too.
     
    #11
  12. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    I'm currently writing a roadmap, which can keep us busy with tutorials for the rest of the year. There are going be three ongoing series.
    • 'Coding Fundamentals' Series. It will teach coding in general to help to start with RGSS. This course will feaure a quick primer of the Ruby language. Binary and Hexadecimal System. Bitwise operations. Regular Expressions. Data Structures and Algorithms. Etc.
    • 'RGSS Fundamentals' Series. It will deal will the basics of RGSS with fun demos and workshops.
    • 'Dissection of RPG Maker VX ACE Default Scripts' Series. This course will offer an analysis of every sections of code written by the RPG Maker team, from A to Z, and how you can tweak it for your needs with no hassle.
    • 'RGSS++' Series. A few years ago I rewrote the entire scripting section of RPG Maker from scratch for my own personal needs. The structure is friendlier, easier to understand and much shorter. I'd like to revise them and teach how to do it until it's fully available for the public.
    I'm also planning a few bonus projects (maybe with the support of Patreon) such as creating a few projects with RGSS, with all zipped projects and sources. I have plans for mini-games (Tic-Tac-Toe, Chess, Cards). Creating slick HUDs and Menu systems. More advanced projects such as a battle system. Advances graphics effects, etc.

    Let's hope it will be handy. Keep an eye on this thread. More updates are coming on www.gamezopher.com as well.
     
    Last edited: Jul 26, 2018
    #12
  13. Elder

    Elder Villager Member

    Messages:
    20
    Likes Received:
    4
    Location:
    Québec, Canada
    First Language:
    French
    Another tutorial will be added soon, I had so much to plan. In the meantime, you can follow my endeavor to rewritte the code in the script section of RPG Maker VX Ace from scratch. There are two locations you can follow my progress:

    location 1: www.gamezopher.com/
    location 2: https://trello.com/c/TAkGnQ84/76-summary
     
    #13
    Thefirelion likes this.

Share This Page