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.

Tilemap script for open source RMXP re-implimentation

Script Title:
Tilemap.rb

Engine: RMXP

Detailed Description:
Hello guys! I'm working on reimplimenting RMXP. For those of you wondering why I'd want to do that, I'll explain later on down this page. For now, let me describe what I need to make this possible.

For my clone of RMXP (http://github.com/cstrahan/open-rpg-maker), I need to implement the Tilemap class. Unfortunetly, I'm not very familiar with the behavior of this class, so doing this myself would take for ever. If someone could write this for me, I'll be able to make much more progress on the rest of the codebase. The code is already rendering sprites successfully, which means that we can render the Tilemap by creating a sprite for each layer.

I tried looking through GlitchFinder's edit of SephirothSpawn's Tilemap script here:
viewtopic.php?p=494360#p494360

Unfortunately, it requires modification to RMXP's standard scripts, which won't work in my scenario. The reason why that isn't an acceptable solution is due to the fact that I want my implementation of RMXP to work with existing games without modifying their scripts.

So, if someone can rewrite Tilemap without any requirement of modifying/inserting scripts, I'll be that much closer to getting my open source version of RMXP finished!

Why rewrite RMXP?
I have many reasons!

  • 1) Cross platform support (Mac, Linux & Windows)
    2) Run games in the browser via Silverlight
    3) Using a full distribution of Ruby 1.8.7 or 1.9 (which would run much faster, and allow for advanced scripting)
    4) Using the Rubygame gem for OpenGL/SDL rendering
    5) Playing games on the PSP with Joyau

Screen shots:
N/A

Other Scripts I am using (in order):
N/A
 
Alright! So I've managed to refactor Glitchfinder's script. I still need to clean up a bit, and remove all the other modules (other than Tilemap).

Please give it a test and let me know if there's anything wrong! I'm hoping I'll have a decent replica of the Tilemap functionality pretty soon... then I can move on to an alpha release of my project.

Code:
 

#==============================================================================

# ** RPG::Cache

#==============================================================================

 

module RPG::Cache

  #--------------------------------------------------------------------------

  # * Auto-Tiles

  #--------------------------------------------------------------------------

  Autotiles = [

    [[27, 28, 33, 34], [ 5, 28, 33, 34], [27,  6, 33, 34], [ 5,  6, 33, 34],

     [27, 28, 33, 12], [ 5, 28, 33, 12], [27,  6, 33, 12], [ 5,  6, 33, 12]],

    [[27, 28, 11, 34], [ 5, 28, 11, 34], [27,  6, 11, 34], [ 5,  6, 11, 34],

     [27, 28, 11, 12], [ 5, 28, 11, 12], [27,  6, 11, 12], [ 5,  6, 11, 12]],

    [[25, 26, 31, 32], [25,  6, 31, 32], [25, 26, 31, 12], [25,  6, 31, 12],

     [15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12]],

    [[29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],

     [39, 40, 45, 46], [ 5, 40, 45, 46], [39,  6, 45, 46], [ 5,  6, 45, 46]],

    [[25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],

     [17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48]],

    [[37, 38, 43, 44], [37,  6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],

     [37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1,  2,  7,  8]]

  ]

  #--------------------------------------------------------------------------

  # * Autotile Cache

  #

  #   @autotile_cache = {

  #     filename => { [autotile_id, frame_id, hue] => bitmap, ... },

  #     ...

  #    }

  #--------------------------------------------------------------------------

  @autotile_cache = {}

  #--------------------------------------------------------------------------

  # * Autotile Tile

  #--------------------------------------------------------------------------

  def self.autotile_tile(autotile, tile_id, hue = 0, frame_id = nil)

    # Configures Frame ID if not specified

    if frame_id.nil?

      # Animated Tiles

      frames = autotile.width / 96

      # Configures Animation Offset

      fc = Graphics.frame_count / Animated_Autotiles_Frames

      frame_id = (fc) % frames * 96

    end

 

    # Reconfigure Tile ID

    tile_id %= 48

    # Creates Bitmap

    bitmap = Bitmap.new(32, 32)

    # Collects Auto-Tile Tile Layout

    tiles = Autotiles[tile_id / 8][tile_id % 8]

    # Draws Auto-Tile Rects

    for i in 0...4

      tile_position = tiles[i] - 1

      src_rect = Rect.new(tile_position % 6 * 16 + frame_id,

        tile_position / 6 * 16, 16, 16)

      bitmap.blt(i % 2 * 16, i / 2 * 16, autotile, src_rect)

    end    

    # Set hue

    bitmap.hue_change(hue)

    # Return Auto-Tile

    bitmap

  end

 

end

RPG::Cache::Animated_Autotiles_Frames = 16

 

 

module Tilemap_Options

  #--------------------------------------------------------------------------

  # * Tilemap Options

  #

  #

  #   Print Error Reports when not enough information set to tilemap

  #    - Print_Error_Logs          = true or false

  #

  #   Number of autotiles to refresh at edge of viewport

  #    - Viewport_Padding          = n

  #

  #   When maps are switch, automatically set

  #   $game_map.tileset_settings.flash_data (Recommended : False unless using

  #   flash_data)

  #    - Autoset_Flash_data        = true or false

  #

  #   Duration Between Flash Data Flashes

  #    - Flash_Duration            = n

  #

  #   Color of bitmap (Recommended to use low opacity value)

  #    - Flash_Bitmap_C            = Color.new(255, 255, 255, 50)

  #

  #   Update Flashtiles Default Setting

  #   Explanation : In the Flash Data Addition, because of lag, you may wish

  #   to toggle whether flash tiles flash or not. This is the default state.

  #    - Default_Update_Flashtiles = false

  #--------------------------------------------------------------------------

  Print_Error_Logs          = true

  Autoset_Flash_data        = true

  Viewport_Padding          = 2

  Flash_Duration            = 40

  Flash_Bitmap_C            = Color.new(255, 255, 255, 50)

  Default_Update_Flashtiles = false

end

 

#==============================================================================

# ** Tilemap

#==============================================================================

 

class Tilemap

  #--------------------------------------------------------------------------

  # * Public Instance Variables

  #--------------------------------------------------------------------------

  attr_reader   :layers

  attr_accessor :tileset

  attr_accessor :autotiles

  attr_reader   :map_data

  attr_accessor :flash_data

  attr_accessor :priorities

  attr_accessor :visible

  attr_accessor :ox

  attr_accessor :oy

  attr_accessor :refresh_autotiles

  #--------------------------------------------------------------------------

  # * Object Initialization

  #--------------------------------------------------------------------------

  def initialize(viewport)

    # Saves Viewport

    @viewport = viewport

    # Creates Blank Instance Variables

    @layers            = []    # Refers to Array of Sprites or Planes

    @tileset           = nil   # Refers to Tileset Bitmap

    @autotiles         = []    # Refers to Array of Autotile Bitmaps

    @map_data          = nil   # Refers to 3D Array Of Tile Settings

    @flash_data        = nil   # Refers to 3D Array of Tile Flashdata

    @priorities        = nil   # Refers to Tileset Priorities

    @visible           = true  # Refers to Tilest Visibleness

    @ox                = 0     # Bitmap Offsets         

    @oy                = 0     # Bitmap Offsets

    @dispose           = false # Disposed Flag

    @refresh_autotiles = true  # Refresh Autotile Flag

  end

  #--------------------------------------------------------------------------

  # * Setup

  #--------------------------------------------------------------------------

  def setup

    # Creates Layers

    @layers = []

    for l in 0...3

      layer = Sprite.new(@viewport)

      layer.bitmap = Bitmap.new(@map_data.xsize * 32, @map_data.ysize * 32)

      layer.z = l * 150

      layer.zoom_x = 1.0

      layer.zoom_y = 1.0

      @layers << layer

    end

    # Update Flags

    @refresh_data = nil

    @zoom_x   = 1.0

    @zoom_y   = 1.0

    @tone     = nil

    @hue      = 0

    @tilesize = 32

  end

  

  #--------------------------------------------------------------------------

  # * Map Data=

  #--------------------------------------------------------------------------

  def map_data=(map_data)

    # Save Map Data

    @map_data = map_data

    

    setup

    

    # Refresh if able

    begin ; refresh ; rescue ; end

  end

  #--------------------------------------------------------------------------

  # * Dispose

  #--------------------------------------------------------------------------

  def dispose

    # Dispose Layers (Sprites)

    @layers.each { |layer| layer.dispose }

    # Set Disposed Flag to True

    @disposed = true

  end

  #--------------------------------------------------------------------------

  # * Disposed?

  #--------------------------------------------------------------------------

  def disposed?

    return @disposed

  end

  #--------------------------------------------------------------------------

  # * Viewport

  #--------------------------------------------------------------------------

  def viewport

    return @viewport

  end

  #--------------------------------------------------------------------------

  # * Frame Update

  #--------------------------------------------------------------------------

  def update

    # Set Refreshed Flag to On

    needs_refresh = true

    # If Map Data, Tilesize or HueChanges

    if @map_data != @refresh_data

      # Refresh Bitmaps

      refresh

      # Turns Refresh Flag to OFF

      needs_refresh = false

    end

    # Update layer Position offsets

    for layer in @layers

      layer.ox = @ox

      layer.oy = @oy

    end

    # If Refresh Autotiles, Needs Refreshed & Autotile Reset Frame

    if @refresh_autotiles && needs_refresh &&

       Graphics.frame_count % RPG::Cache::Animated_Autotiles_Frames == 0

      # Refresh Autotiles

      refresh_autotiles

    end

  end

  #--------------------------------------------------------------------------

  # * Refresh

  #--------------------------------------------------------------------------

  def refresh

    unless priorities.nil?

      # Saves Map Data & Tilesize

      @refresh_data = @map_data

      @hue      = 0

      @tilesize = 32

      # Passes Through Layers

      for z in 0...@map_data.zsize

        # Passes Through X Coordinates

        for x in 0...@map_data.xsize

          # Passes Through Z Coordinates

          for y in 0...@map_data.ysize

            # Collects Tile ID

            id = @map_data[x, y, z]

            # Skip if 0 tile

            next if id == 0

            # Passes Through All Priorities

            for p in 0..5

              # Skip If Priority Doesn't Match

              next unless p == @priorities[id]

              # Cap Priority to Layer 3

              p = 2 if p > 2

              # Draw Tile

              id < 384 ? draw_autotile(x, y, p, id) : draw_tile(x, y, p, id)

            end

          end

        end

      end

    end

  end   

  #--------------------------------------------------------------------------

  # * Refresh Auto-Tiles

  #--------------------------------------------------------------------------

  def refresh_autotiles

    # Auto-Tile Locations

    autotile_locations = Table.new(@map_data.xsize, @map_data.ysize,

      @map_data.zsize)

    # Get X Tiles

    x1 = [@ox / @tilesize - Tilemap_Options::Viewport_Padding, 0].max

    x2 = [@viewport.rect.width / @tilesize +

          Tilemap_Options::Viewport_Padding, @map_data.xsize].min

    # Get Y Tiles

    y1 = [@oy / @tilesize - Tilemap_Options::Viewport_Padding, 0].max

    y2 = [@viewport.rect.height / @tilesize +

          Tilemap_Options::Viewport_Padding, @map_data.ysize].min

    # Passes Through Layers

    for z in 0...@map_data.zsize

      # Passes Through X Coordinates

      for x in x1...x2

        # Passes Through Y Coordinates

        for y in y1...y2

          # Collects Tile ID

          id = @map_data[x, y, z]

          # Skip if 0 tile

          next if id == 0

          # Skip If Non-Animated Tile

          next unless @autotiles[id / 48 - 1].width / 96 > 1 if id < 384

          # Get Priority

          p = @priorities[id]

          # Cap Priority to Layer 3

          p = 2 if p > 2

          # If Autotile

          if id < 384

            # Draw Auto-Tile

            draw_autotile(x, y, p, id)

            for l in (p+1)...@map_data.zsize

              id_l = @map_data[x, y, l]

              draw_tile(x, y, p, id_l)

            end

            # Save Autotile Location

            autotile_locations[x, y, z] = 1

          # If Normal Tile

          else

            # If Autotile Drawn

            if autotile_locations[x, y, z] == 1

              # Redraw Normal Tile

              draw_tile(x, y, p, id)

              # Draw Higher Tiles

              for l in (p+1)...@map_data.zsize

                id_l = @map_data[x, y, l]

                draw_tile(x, y, p, id_l)

              end

            end

          end

        end

      end

    end

  end     

  #--------------------------------------------------------------------------

  # * Draw Tile

  #--------------------------------------------------------------------------

  def draw_tile(x, y, z, id)

    rect = Rect.new((id - 384) % 8 * 32, (id - 384) / 8 * 32, 32, 32)

    x *= @tilesize

    y *= @tilesize

    if @tile_width == 32 && @tile_height == 32

      @layers[z].bitmap.blt(x, y, @tileset, rect)

    else

      dest_rect = Rect.new(x, y, @tilesize, @tilesize)

      @layers[z].bitmap.stretch_blt(dest_rect, @tileset, rect)

    end

  end

  #--------------------------------------------------------------------------

  # * Draw Auto-Tile

  #--------------------------------------------------------------------------

  def draw_autotile(x, y, z, tile_id)

    # Gets Autotile Filename

    autotile_num = tile_id / 48 - 1

    # Reconfigure Tile ID

    tile_id %= 48

    # Gets Generated Autotile Bitmap Section

    bitmap = RPG::Cache.autotile_tile(autotiles[autotile_num], tile_id, @hue)

    

    # Calculates Tile Coordinates

    x *= @tilesize

    y *= @tilesize

    @layers[z].bitmap.blt(x, y, bitmap, Rect.new(0, 0, 32, 32))

  end

  #--------------------------------------------------------------------------

  # * Collect Bitmap

  #--------------------------------------------------------------------------

  def bitmap

    # Creates New Blank Bitmap

    bitmap = Bitmap.new(@layers[0].bitmap.width, @layers[0].bitmap.height)

    # Passes Through All Layers

    for layer in @layers

      bitmap.blt(0, 0, layer.bitmap,

        Rect.new(0, 0, bitmap.width, bitmap.height))

    end

    # Return Bitmap

    return bitmap

  end

end

 

Cheers,
-Charles
 
SephirothSpawn":32cgx9j9 said:
That's not really Glitch's script. It is almost identical to the one I did, just a slight mod of mine. Not that I really care...

He sent me a PM about it, and I pointed him your way. You had been working on another version of your rewrite last I had checked, and it might prove useful in this case. As a side note, he's going to have quite a bit of trouble with his goals when it comes to input, because he has two real choices: either change things, or severely limit the keys it reads from the keyboard. Not to mention the issues of reading from a game controller or mouse.

Also, it might be worth noting that Enterbrain would not like an open source clone of their copyrighted material being made, no matter what the intentions were. Differences between American and Japanese copyright law can only hold a major corporation back for so long once they take notice.
 
SephirothSpawn":5mkk5hoz said:
That's not really Glitch's script. It is almost identical to the one I did, just a slight mod of mine. Not that I really care...
True. That's why I mentioned that it was an edit of yours (or at least I meant to).

Glitchfinder":5mkk5hoz said:
Also, it might be worth noting that Enterbrain would not like an open source clone of their copyrighted material being made, no matter what the intentions were. Differences between American and Japanese copyright law can only hold a major corporation back for so long once they take notice.

I suppose Enterbrain may not like it, but that would be sorta strange. I'm not planning on rewriting the part that makes the games - just the part that plays them. And, considering that I don't intend to use any copyrighted material, I don't see what they could complain about. The end result is that people will purchase RMXP and build their games on Windows, but they'll be able to deploy them wherever they see fit - Linux, Windows, Mac, whatever. I suppose I share the same goals as vgvgf's ARGSS:
http://argss.com.ar/forums/showthread.php?tid=10

Glitchfinder":5mkk5hoz said:
... he's going to have quite a bit of trouble with his goals when it comes to input, because he has two real choices: either change things, or severely limit the keys it reads from the keyboard. Not to mention the issues of reading from a game controller or mouse.

As for the Input module, I'll just make the extra keys available as an option. I don't want to break compatibility with existing games right from the start, so I'll do something creative... Although that might not be necessary. If the game is running on the "official" Ruby interpreter (MRI, or YARV), calls into the Windows API and dlls should work just fine - on Windows, of course. To make such games work cross platform would require taking out any scripts depending on the dll, and replace them with something that plays nicely with my version of the game engine. I'll probably just make full support for mouse and keyboard inherent with my engine, and distribute a script that switches it on.


Back to the topic of Tilemaps. I just finished rewriting Seph's script without any dependency on changing the rest of the game scripts. I'm now trying to find out why it is that the character's sprite renders on top of layer 2 and 3 when the character is below the middle of the map (on the y-axis). I think I'll sit down at some point and actually figure out how the Tilemap needs to be rendered and do a complete rewrite... but for now, this should work for testing purposes. I'm really not looking forward to it though - I've spent well over 25 hours working on this project just this weekend, and I really want to get it to a solid alpha or beta point here pretty soon. :huh:

Hopefully I'll have some good news here in little while. I have all of the data structures, Sprite/Bitmap finished, the main game loop successfully runs through a simple game, etc.

-Charles
 
cstrahan":2igws6qf said:
Back to the topic of Tilemaps. I just finished rewriting Seph's script without any dependency on changing the rest of the game scripts. I'm now trying to find out why it is that the character's sprite renders on top of layer 2 and 3 when the character is below the middle of the map (on the y-axis). I think I'll sit down at some point and actually figure out how the Tilemap needs to be rendered and do a complete rewrite... but for now, this should work for testing purposes. I'm really not looking forward to it though - I've spent well over 25 hours working on this project just this weekend, and I really want to get it to a solid alpha or beta point here pretty soon. :huh:

Actually, that's because the player's z position changes with its y position. Check out the z-related method in Game_Character.
 

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