I've been working on a new RMXP Tilemap script. It didn't work for what I needed it for (because it lags horribly when you zoom the whole thing out) and I haven't checked its speed against SephirothSpawn's original rewrite, but here it is. It does priorities correctly, unlike any rewrite before it, and it does this by making a Sprite for each 32x32 square. It also has animated autotiles. It is a Ruby equivalent to the built-in Tilemap class. The built-in is faster, of course, but can't zoom or have automatic Plane-like capabilities. Do with this what you will; it's probably best for educational purposes.
[rgss]#===============================================================================
# ** Tilemap
#-------------------------------------------------------------------------------
# rey meustrus and SephirothSpawn
# Version 2.0
# 2010-02-02
# SDK : Version 2.0+, Parts I & II
#-------------------------------------------------------------------------------
# * Credits :
#
# Thanks to Trickster for conversion formula for Hexidecimal to rgb.
# Thanks to trebor777 for helping with the priority bug from the 0.9 version.
#-------------------------------------------------------------------------------
# * Version History :
#
# Version 0.9 -------------------------------------------------- (??????????)
# Version 1 ---------------------------------------------------- (2007-05-30)
# Version 1.1 ------------------------------------------------- (2007-07-20)
# - Update : Removed pointless block. Move RPG::Cache.autotile to MACL.
# Version 1.11 ------------------------------------------------ (2007-07-31)
# Version 2.0 ------------------------------------------------- (2010-02-02)
#-------------------------------------------------------------------------------
# * Description :
#
# This script was designed to re-write the default RGSS Hidden Tilemap class.
# The script has many added features via read/write variables.
#-------------------------------------------------------------------------------
# * Instructions :
#
# Place The Script Below the SDK and Above Main.
#-------------------------------------------------------------------------------
# * Syntax :
#
# Get Autotile Tile Bitmap
# - RPG::Cache.autotile_tile(autotile_filename, tile_id[, hue[, frame_id]])
#
# autotile_filename : Filename of autotile
# tile_id : ID of tile (Found from RPG::Map.data)
# hue (Optional) : Hue for tile
# frame_id (Optional) : Frame of tile (for animated autotiles)
#
# * Tilemap Syntax
#
# - tile(tile_id) : Gets bitmap for tile_id
# - tile(x, y, z) : Gets bitmap at coordinates using @map_data
# - priority(tile_id) : Gets Z value for tile_id
# - priority(x, y, z) : Gets Z value at coordinates
#
# Readable Attributes :
# - viewport : Viewport used for sprites
# - width : Total pixel width of the Tilemap
# - height : Total pixel height of the Tilemap
#
# Readable/Writable Attributes :
# - map_data : 3D Table of Tile ID Data
# - flash_data : 3D Table of Tile Color Data
# - priorities : 3D Table of Tile Priorities
# - hue : Hue adjustment for all tiles
# - ox, oy : Tilemap layer offsets
# - zoom : Zoom value as fraction
# - tilesize : Zoom value as size of individual tile (normal is 32.0)
# - tileset_name : Name of Bitmap
# - autotile_names : Array of Autotile Filenames
# - visible : Tilemap Visible Flag
# - static : Static Flag (no animated autotiles)
# - is_a_plane : Behaves like a Plane (loops around edges)
#===============================================================================
#-------------------------------------------------------------------------------
# SDK Log
#-------------------------------------------------------------------------------
SDK.log('Tilemap', 'rey meustrus and SephirothSpawn', 2.0, '2010-02-02')
#===============================================================================
# ** RPG::Cache
#===============================================================================
module RPG::Cache
#-----------------------------------------------------------------------------
# * Constants
#-----------------------------------------------------------------------------
Animated_Autotile_Frames = 16
Empty_Bitmap = Bitmap.new(32, 32)
Default_Rect = Rect.new(0, 0, 32, 32)
Autotiles = [
[[26, 27, 32, 33], [ 4, 27, 32, 33], [26, 5, 32, 33], [ 4, 5, 32, 33],
[26, 27, 32, 11], [ 4, 27, 32, 11], [26, 5, 32, 11], [ 4, 5, 32, 11]],
[[26, 27, 10, 33], [ 4, 27, 10, 33], [26, 5, 10, 33], [ 4, 5, 10, 33],
[26, 27, 10, 11], [ 4, 27, 10, 11], [26, 5, 10, 11], [ 4, 5, 10, 11]],
[[24, 25, 30, 31], [24, 5, 30, 31], [24, 25, 30, 11], [24, 5, 30, 11],
[14, 15, 20, 21], [14, 15, 20, 11], [14, 15, 10, 21], [14, 15, 10, 11]],
[[28, 29, 34, 35], [28, 29, 10, 35], [ 4, 29, 34, 35], [ 4, 29, 10, 35],
[38, 39, 44, 45], [ 4, 39, 44, 45], [38, 5, 44, 45], [ 4, 5, 44, 45]],
[[24, 29, 30, 35], [14, 15, 44, 45], [12, 13, 18, 19], [12, 13, 18, 11],
[16, 17, 22, 23], [16, 17, 10, 23], [40, 41, 46, 47], [ 4, 41, 46, 47]],
[[36, 37, 42, 43], [36, 5, 42, 43], [12, 17, 18, 23], [12, 13, 42, 43],
[36, 41, 42, 47], [16, 17, 46, 47], [12, 17, 42, 47], [ 0, 1, 6, 7]]
]
#-----------------------------------------------------------------------------
# * Normal or Autotile Tile
#-----------------------------------------------------------------------------
def self.tile!(tileset_name, autotile_names, tile_id, hue = 0)
return Empty_Bitmap if tile_id == 0
return self.tile(tileset_name, tile_id, hue) if tile_id >= 384
# After this point, we are an autorile
filename = autotile_names[tile_id / 48 - 1]
autotile = self.autotile(filename)
# Reconfigure Tile ID
tile_id %= 48
# Creates Key
key = [filename, tile_id, hue]
# If Key Not Found
if not @cache.has_key?(key) or @cache[key].disposed?
# Creates Bitmap
bitmap = Bitmap.new(autotile.width / 3, 32)
# Collects Auto-Tile Tile Layout
tiles = Autotiles[tile_id / 8][tile_id % 8]
# Draws Auto-Tile Rects
tiles.each_with_index do |tile_position, i|
for frame_id in 0..(autotile.width / 96)
src_rect = Rect.new(tile_position % 6 * 16 + frame_id * 96,
tile_position / 6 * 16, 16, 16)
bitmap.blt(i % 2 * 16 + frame_id * 32, i / 2 * 16, autotile, src_rect)
end
end
# Saves Autotile to Cache
@cache[key] = bitmap
# Change Hue
@cache[key].hue_change(hue)
end
@cache[key]
end
#-----------------------------------------------------------------------------
# * Tile Rect
#-----------------------------------------------------------------------------
def self.tile_rect(tileset_name, autotile_names, tile_id)
return Default_Rect if tile_id == 0 or tile_id >= 384
autotile = self.autotile(filename = autotile_names[tile_id / 48 - 1])
# Configures Animation Offset
fc = (Graphics.frame_count/Animated_Autotile_Frames) % (autotile.width/96)
@cache[fc] = Rect.new(fc * 32, 0, 32, 32) if not @cache.has_key?(fc)
@cache[fc]
end
end
#===============================================================================
# ** Game_Character
#-------------------------------------------------------------------------------
# Remove $game_map.screen_y from Z processing so Tilemap doesn't have to
# process Z value every time screen_y changes
#===============================================================================
class Game_Character
#-----------------------------------------------------------------------------
# * Get Screen Z-Coordinates
# height : character height
#-----------------------------------------------------------------------------
def screen_z(height = 0)
# If display flag on closest surface is ON
if @always_on_top
# 999, unconditional
return 999
end
# Get screen coordinates from real coordinates and map display position
z = (@real_y + 3) / 4 + 32
# If tile
if @tile_id > 0
# Add tile priority * 32
return z + $game_map.priorities[@tile_id] * 32
# If character
else
# If height exceeds 32, then add 31
return z + ((height > 32) ? 31 : 0)
end
end
end
#===============================================================================
# ** Spriteset_Map
#===============================================================================
class Spriteset_Map
#-----------------------------------------------------------------------------
# * Alias Listings
#-----------------------------------------------------------------------------
alias_method :seph_tilemap_ssmap_inittm, :init_tilemap
#-----------------------------------------------------------------------------
# * Tilemap Initialization
#-----------------------------------------------------------------------------
def init_tilemap
# Original Tilemap Initialization
seph_tilemap_ssmap_inittm
# Set Tilemap Settings
@tilemap.tileset_name = $game_map.tileset_name
@tilemap.autotile_names = $game_map.autotile_names
end
end
#===============================================================================
# ** Tilemap
#===============================================================================
class Tilemap
#-----------------------------------------------------------------------------
# * Tilemap Options
#-----------------------------------------------------------------------------
Viewport_Padding = 2 # Number of tiles beyond viewport to refresh
Flash_Alpha = 64 # Alpha value of the color of flash data
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :viewport # Viewport for all sprites
attr_reader :map_data # 3D Table of Tile Settings
attr_reader :flash_data # 3D Table of Sprite Flash Colors
attr_reader :priorities # 3D Table of Tileset Priorities
attr_reader :hue # Hue Adjustment Value
attr_accessor

x # Bitmap Offsets
attr_accessor

y # Bitmap Offsets
attr_reader :zoom # Zoom Fraction
attr_reader :tileset_name # Tileset Filename
attr_reader :autotile_names # Array of Autotile Filenames
attr_reader :visible # Tilemap Visibility
attr_accessor :static # No Animated Autotiles
attr_accessor :is_a_plane # Loops around edges
attr_accessor :tileset # Deprecated
attr_accessor :autotiles # Deprecated
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize(viewport = nil)
@viewport = viewport
@map_data = nil
@flash_data = nil
@priorities = nil
@hue = 0
@ox = 0
@oy = 0
@zoom = 1.0
@tileset_name = nil
@autotile_names = []
@visible = true
@static = false
@is_a_plane = false
@sprites = {}
@disposed = false
@refresh_autotiles = true
@autotiles = []
end
#-----------------------------------------------------------------------------
# * Total pixel width
#-----------------------------------------------------------------------------
def width
return 0 if @map_data.nil?
@map_data.xsize * 32
end
#-----------------------------------------------------------------------------
# * Total pixel height
#-----------------------------------------------------------------------------
def height
return 0 if @map_data.nil?
@map_data.ysize * 32
end
#-----------------------------------------------------------------------------
# * Set Map Data
#-----------------------------------------------------------------------------
def map_data=(map_data)
return if @map_data == map_data
@map_data = map_data
refresh
end
#-----------------------------------------------------------------------------
# * Set Flash Data
#-----------------------------------------------------------------------------
def flash_data=(flash_data)
return if @flash_data == flash_data
@flash_data = flash_data
@sprites.each do |key, sprite|
if not flash_data.nil? and (hex = flash_data[*key]) != 0
sprite.color.red = (hex / 256) * 16
sprite.color.green = ((hex / 16) % 16) * 16
sprite.color.blue = (hex % 16) * 16
sprite.color.alpha = Flash_Alpha
else
sprite.color.alpha = 0
end
end
Graphics.frame_reset
end
#-----------------------------------------------------------------------------
# * Set Priorities
#-----------------------------------------------------------------------------
def priorities=(priorities)
return if @priorities == priorities
@priorities = priorities
refresh
end
#-----------------------------------------------------------------------------
# * Set Hue
#-----------------------------------------------------------------------------
def hue=(hue)
return if @hue == hue
@hue = hue
refresh
end
#-----------------------------------------------------------------------------
# * Set OX value
#-----------------------------------------------------------------------------
def ox=(ox)
return if @ox == ox
@ox = ox
@sprites.each { |key, sprite| sprite.ox = ox }
end
#-----------------------------------------------------------------------------
# * Set OX value
#-----------------------------------------------------------------------------
def oy=(oy)
return if @oy == oy
@oy = oy
@sprites.each { |key, sprite| sprite.oy = oy }
end
#-----------------------------------------------------------------------------
# * Get Tile Size
#-----------------------------------------------------------------------------
def tilesize
(zoom * 32).to_i
end
#-----------------------------------------------------------------------------
# * Set Tile Size
#-----------------------------------------------------------------------------
def tilesize=(tilesize)
self.zoom = tilesize / 32.0
end
#-----------------------------------------------------------------------------
# * Set Zoom Fraction
#-----------------------------------------------------------------------------
def zoom=(zoom)
return if @zoom == zoom
@zoom = zoom
@sprites.each do |key, sprite|
sprite.zoom_x = sprite.zoom_y = zoom
sprite.x = key.at(0) * tilesize
sprite.y = key.at(1) * tilesize
end
Graphics.frame_reset
end
#-----------------------------------------------------------------------------
# * Set Visibility
#-----------------------------------------------------------------------------
def visible=(visible)
return if @visible == visible
@visible = visible
@sprites.each_value { |sprite| sprite.visible = visible }
end
#-----------------------------------------------------------------------------
# * Set Tileset Name
#-----------------------------------------------------------------------------
def tileset_name=(name)
return if @tileset_name == name
@tileset_name = name
refresh
end
#-----------------------------------------------------------------------------
# * Set Autotile Names
#-----------------------------------------------------------------------------
def autotile_names=(names)
return if @autotile_names == names
@autotile_names = names
refresh
end
#-----------------------------------------------------------------------------
# * Get bitmap at coordinates
#-----------------------------------------------------------------------------
def tile(*args)
RPG::Cache.tile!(@tileset_name, @autotile_names, args_to_id(*args), @hue)
end
#-----------------------------------------------------------------------------
# * Get bitmap rect at coordinates
#-----------------------------------------------------------------------------
def tile_rect(*args)
RPG::Cache.tile_rect(@tileset_name, @autotile_names, args_to_id(*args))
end
#-----------------------------------------------------------------------------
# * Get priority at coordinates
#-----------------------------------------------------------------------------
def priority(x, y, z)
priority = @priorities[@map_data[x, y, z]]
return 0 if priority == 0
(y + priority) * 32 + 64
end
#-----------------------------------------------------------------------------
# * Find tile_id from args
#-----------------------------------------------------------------------------
def args_to_id(*args)
if args.size == 1
return args[0]
elsif args.size == 3
return @map_data[*args]
else
raise ArgumentError, "Only one (tile_id) or three (x, y, z) args allowed"
end
end
#-----------------------------------------------------------------------------
# * Dispose
#-----------------------------------------------------------------------------
def dispose
# Dispose Sprites
@sprites.each_value { |sprite| sprite.dispose }
# Set Disposed Flag to True
@disposed = true
end
#-----------------------------------------------------------------------------
# * Disposed?
#-----------------------------------------------------------------------------
def disposed?
@disposed
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
if @is_a_plane
# Get Visible X Tiles
x1 = @ox / 32 - Viewport_Padding
x2 = @ox / 32 + @viewport.rect.width / tilesize + Viewport_Padding
# Get Visible Y Tiles
y1 = @oy / 32 - Viewport_Padding
y2 = @oy / 32 + @viewport.rect.height / tilesize + Viewport_Padding
# Wrapped values
_x1 = x1 % @map_data.xsize
_x2 = x2 % @map_data.xsize
_y1 = y1 % @map_data.ysize
_y2 = y2 % @map_data.ysize
# Behave like a Plane
tsize = tilesize
each_visible_sprite do |x, y, z, sprite|
if x > x2 and x >= _x1
sprite.x = (x - @map_data.xsize) * tsize
elsif x < x1 and x <= _x2
sprite.x = (x + @map_data.xsize) * tsize
else
sprite.x = x * tsize
end
if y > y2 and y >= _y1
sprite.y = (y - @map_data.ysize) * tsize
elsif y < y1 and y <= _y2
sprite.y = (y + @map_data.ysize) * tsize
else
sprite.y = y * tsize
end
end
end
# If Not Static and Autotile Reset Frame
if Graphics.frame_count % RPG::Cache::Animated_Autotile_Frames == 0
refresh_autotiles unless @static
end
end
#-----------------------------------------------------------------------------
# * Refresh
#-----------------------------------------------------------------------------
def refresh
return if @map_data.nil? or @tileset_name.nil? or @autotile_names.size < 7
@sprites.each_value { |sprite| sprite.dispose }
@sprites.clear
# Passes Through X Coordinates
for x in 0...@map_data.xsize
# Passes Through Z Coordinates
for y in 0...@map_data.ysize
# Passes Through Layers
for z in 0...@map_data.zsize
# Collects Tile ID
id = @map_data[x, y, z]
# Skip if 0 tile
next if id == 0
sprite = find_sprite(x, y, z)
# Draw Tile
sprite.bitmap = tile(id)
sprite.src_rect = tile_rect(id)
sprite.z = priority(x, y, z)
end
end
end
Graphics.frame_reset
end
#-----------------------------------------------------------------------------
# * Refresh Auto-Tiles; only affects visible animated autotiles
#-----------------------------------------------------------------------------
def refresh_autotiles
each_visible_sprite do |x, y, z, sprite|
# Collects Tile ID
id = @map_data[x, y, z]
# Skip if 0 tile, or if not autotile
next if id == 0 or id >= 384
# Skip If Non-Animated Tile
bitmap = RPG::Cache.autotile(@autotile_names[id / 48 - 1])
next unless bitmap.width / 96 > 1
# Draw Tile
find_sprite(x, y, z).src_rect = tile_rect(id)
end
Graphics.frame_reset
end
#-----------------------------------------------------------------------------
# * Find or create sprite at coordinates
#-----------------------------------------------------------------------------
def find_sprite(x, y, z, create = true)
return @sprites[[x, y, z]] if @sprites.has_key?([x, y, z])
return nil unless create
sprite = Sprite.new(@viewport)
sprite.visible = @visible
sprite.x = x * tilesize
sprite.y = y * tilesize
sprite.ox = @ox
sprite.oy = @oy
sprite.zoom_x = sprite.zoom_y = @zoom
@sprites[[x, y, z]] = sprite
end
#-----------------------------------------------------------------------------
# * Dispose of sprite at coordinates
#-----------------------------------------------------------------------------
def dispose_sprite(x, y, z)
return unless @sprites.has_key?([x, y, z])
@sprites[[x, y, z]].dispose
@sprites.delete([x, y, z])
end
#-----------------------------------------------------------------------------
# * Iterate through visible sprites
#-----------------------------------------------------------------------------
def each_visible_sprite
# Get Visible X Tiles
x1 = (@ox / 32 - Viewport_Padding) % @map_data.xsize
x2 = (@ox / 32 + @viewport.rect.width / tilesize + Viewport_Padding) %
@map_data.xsize
# Get Visible Y Tiles
y1 = (@oy / 32 - Viewport_Padding) % @map_data.ysize
y2 = (@oy / 32 + @viewport.rect.height / tilesize + Viewport_Padding) %
@map_data.ysize
@sprites.each do |key, sprite|
send_sprite = true
if x1 < x2
send_sprite &= key.at(0).between?(x1, x2)
else
send_sprite &= key.at(0) > x1 || key.at(0) < x2
end
if y1 < y2
send_sprite &= key.at(1).between?(y1, y2)
else
send_sprite &= key.at(1) > y1 || key.at(1) < y2
end
yield key.at(0), key.at(1), key.at(2), sprite if send_sprite
end
end
end
[/rgss]
EDIT: Added flash data, which is criminally easy with this approach.