From 90c0e51e8833c5845558eaa9ac6a75c3f21b39e4 Mon Sep 17 00:00:00 2001 From: chardub Date: Sat, 26 Apr 2025 22:01:12 -0400 Subject: [PATCH] Caches expanded autotiles to reduce stutter caused by map connections --- .../006_Map renderer/001_TilemapRenderer.rb | 185 ++++++++++-------- 1 file changed, 105 insertions(+), 80 deletions(-) diff --git a/Data/Scripts/006_Map renderer/001_TilemapRenderer.rb b/Data/Scripts/006_Map renderer/001_TilemapRenderer.rb index f75123867..9b5373504 100644 --- a/Data/Scripts/006_Map renderer/001_TilemapRenderer.rb +++ b/Data/Scripts/006_Map renderer/001_TilemapRenderer.rb @@ -2,28 +2,28 @@ # #=============================================================================== class TilemapRenderer - attr_reader :tilesets - attr_reader :autotiles + attr_reader :tilesets + attr_reader :autotiles attr_accessor :tone attr_accessor :color - attr_reader :viewport - attr_accessor :ox # Does nothing - attr_accessor :oy # Does nothing - attr_accessor :visible # Does nothing + attr_reader :viewport + attr_accessor :ox # Does nothing + attr_accessor :oy # Does nothing + attr_accessor :visible # Does nothing - DISPLAY_TILE_WIDTH = Game_Map::TILE_WIDTH rescue 32 - DISPLAY_TILE_HEIGHT = Game_Map::TILE_HEIGHT rescue 32 - SOURCE_TILE_WIDTH = 32 - SOURCE_TILE_HEIGHT = 32 - ZOOM_X = DISPLAY_TILE_WIDTH / SOURCE_TILE_WIDTH - ZOOM_Y = DISPLAY_TILE_HEIGHT / SOURCE_TILE_HEIGHT - TILESET_TILES_PER_ROW = 8 - AUTOTILES_COUNT = 8 # Counting the blank tile as an autotile - TILES_PER_AUTOTILE = 48 - TILESET_START_ID = AUTOTILES_COUNT * TILES_PER_AUTOTILE + DISPLAY_TILE_WIDTH = Game_Map::TILE_WIDTH rescue 32 + DISPLAY_TILE_HEIGHT = Game_Map::TILE_HEIGHT rescue 32 + SOURCE_TILE_WIDTH = 32 + SOURCE_TILE_HEIGHT = 32 + ZOOM_X = DISPLAY_TILE_WIDTH / SOURCE_TILE_WIDTH + ZOOM_Y = DISPLAY_TILE_HEIGHT / SOURCE_TILE_HEIGHT + TILESET_TILES_PER_ROW = 8 + AUTOTILES_COUNT = 8 # Counting the blank tile as an autotile + TILES_PER_AUTOTILE = 48 + TILESET_START_ID = AUTOTILES_COUNT * TILES_PER_AUTOTILE # If an autotile's filename ends with "[x]", its frame duration will be x/20 # seconds instead. - AUTOTILE_FRAME_DURATION = 5 # In 1/20ths of a second + AUTOTILE_FRAME_DURATION = 5 # In 1/20ths of a second # Filenames of extra autotiles for each tileset. Each tileset's entry is an # array containing two other arrays (you can leave either of those empty, but @@ -38,10 +38,10 @@ class TilemapRenderer # Extra autotiles are only useful if the tiles are animated, because otherwise # you just have some tiles which belong in the tileset instead. EXTRA_AUTOTILES = { -# Examples: -# 1 => [["Sand shore"], ["Flowers2"]], -# 2 => [[], ["Flowers2", "Waterfall", "Waterfall crest", "Waterfall bottom"]], -# 6 => [["Water rock", "Sea deep"], []] + # Examples: + # 1 => [["Sand shore"], ["Flowers2"]], + # 2 => [[], ["Flowers2", "Waterfall", "Waterfall crest", "Waterfall bottom"]], + # 6 => [["Water rock", "Sea deep"], []] } #============================================================================= @@ -52,11 +52,11 @@ class TilemapRenderer attr_accessor :bitmaps def initialize - @bitmaps = {} - @bitmap_wraps = {} # Whether each tileset is a mega texture and has multiple columns - @load_counts = {} - @bridge = 0 - @changed = true + @bitmaps = {} + @bitmap_wraps = {} # Whether each tileset is a mega texture and has multiple columns + @load_counts = {} + @bridge = 0 + @changed = true end def [](filename) @@ -124,10 +124,10 @@ class TilemapRenderer def initialize super - @frame_counts = {} # Number of frames in each autotile - @frame_durations = {} # How long each frame lasts per autotile - @current_frames = {} # Which frame each autotile is currently showing - @timer_start = System.uptime + @frame_counts = {} # Number of frames in each autotile + @frame_durations = {} # How long each frame lasts per autotile + @current_frames = {} # Which frame each autotile is currently showing + @timer_start = System.uptime end def []=(filename, value) @@ -137,25 +137,49 @@ class TilemapRenderer set_current_frame(filename) end + EXPANDED_AUTOTILES_FOLDER = "Graphics/Autotiles/ExpandedAutotiles/" + def add(filename) return if nil_or_empty?(filename) if @bitmaps[filename] @load_counts[filename] += 1 return end - orig_bitmap = pbGetAutotile(filename) - @bitmap_wraps[filename] = false - duration = AUTOTILE_FRAME_DURATION - if filename[/\[\s*(\d+?)\s*\]\s*$/] - duration = $~[1].to_i + + # Try to load expanded autotile from cache first + cached_path = File.join("Graphics", "Autotiles/ExpandedAutotiles", "#{filename}.png") + if FileTest.exist?(cached_path) + echoln "Loading cached expanded autotile for #{filename}" + bitmap = RPG::Cache.load_bitmap(EXPANDED_AUTOTILES_FOLDER, filename) + + duration = AUTOTILE_FRAME_DURATION + if filename[/\[\s*(\d+?)\s*\]\s*$/] + duration = $~[1].to_i + end + @frame_durations[filename] = duration.to_f / 20 + + else + orig_bitmap = pbGetAutotile(filename) + @bitmap_wraps[filename] = false + duration = AUTOTILE_FRAME_DURATION + if filename[/\[\s*(\d+?)\s*\]\s*$/] + duration = $~[1].to_i + end + @frame_durations[filename] = duration.to_f / 20 + expanded_bitmap = AutotileExpander.expand(orig_bitmap) + + # Save expanded bitmap to cache for next time + Dir.mkdir(EXPANDED_AUTOTILES_FOLDER) unless Dir.exist?(EXPANDED_AUTOTILES_FOLDER) + expanded_bitmap.save_to_png(cached_path) + + bitmap = expanded_bitmap + orig_bitmap.dispose if orig_bitmap != expanded_bitmap end - @frame_durations[filename] = duration.to_f / 20 - bitmap = AutotileExpander.expand(orig_bitmap) + self[filename] = bitmap if bitmap.height > SOURCE_TILE_HEIGHT && bitmap.height < TILES_PER_AUTOTILE * SOURCE_TILE_HEIGHT @bitmap_wraps[filename] = true end - orig_bitmap.dispose if orig_bitmap != bitmap @load_counts[filename] = 1 end @@ -243,57 +267,57 @@ class TilemapRenderer attr_accessor :need_refresh def set_bitmap(filename, tile_id, autotile, animated, priority, bitmap) - self.bitmap = bitmap - self.src_rect = Rect.new(0, 0, SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT) - self.zoom_x = ZOOM_X - self.zoom_y = ZOOM_Y - @filename = filename - @tile_id = tile_id - @is_autotile = autotile - @animated = animated - @priority = priority + self.bitmap = bitmap + self.src_rect = Rect.new(0, 0, SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT) + self.zoom_x = ZOOM_X + self.zoom_y = ZOOM_Y + @filename = filename + @tile_id = tile_id + @is_autotile = autotile + @animated = animated + @priority = priority @shows_reflection = false - @bridge = false - self.visible = !bitmap.nil? - @need_refresh = true + @bridge = false + self.visible = !bitmap.nil? + @need_refresh = true end end #----------------------------------------------------------------------------- def initialize(viewport) - @tilesets = TilesetBitmaps.new - @autotiles = AutotileBitmaps.new + @tilesets = TilesetBitmaps.new + @autotiles = AutotileBitmaps.new @tiles_horizontal_count = (Graphics.width.to_f / DISPLAY_TILE_WIDTH).ceil + 1 - @tiles_vertical_count = (Graphics.height.to_f / DISPLAY_TILE_HEIGHT).ceil + 1 - @tone = Tone.new(0, 0, 0, 0) - @old_tone = Tone.new(0, 0, 0, 0) - @color = Color.new(0, 0, 0, 0) - @old_color = Color.new(0, 0, 0, 0) - @self_viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) - @viewport = (viewport) ? viewport : @self_viewport - @old_viewport_ox = 0 - @old_viewport_oy = 0 + @tiles_vertical_count = (Graphics.height.to_f / DISPLAY_TILE_HEIGHT).ceil + 1 + @tone = Tone.new(0, 0, 0, 0) + @old_tone = Tone.new(0, 0, 0, 0) + @color = Color.new(0, 0, 0, 0) + @old_color = Color.new(0, 0, 0, 0) + @self_viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + @viewport = (viewport) ? viewport : @self_viewport + @old_viewport_ox = 0 + @old_viewport_oy = 0 # NOTE: The extra tiles horizontally/vertically hang off the left and top # edges of the screen, because the pixel_offset values are positive # and are added to the tile sprite coordinates. - @tiles = [] + @tiles = [] @tiles_horizontal_count.times do |i| @tiles[i] = [] @tiles_vertical_count.times do |j| @tiles[i][j] = Array.new(3) { TileSprite.new(@viewport) } end end - @current_map_id = 0 - @tile_offset_x = 0 - @tile_offset_y = 0 - @pixel_offset_x = 0 - @pixel_offset_y = 0 - @ox = 0 - @oy = 0 - @visible = true - @need_refresh = true - @disposed = false + @current_map_id = 0 + @tile_offset_x = 0 + @tile_offset_y = 0 + @pixel_offset_x = 0 + @pixel_offset_y = 0 + @ox = 0 + @oy = 0 + @visible = true + @need_refresh = true + @disposed = false end def dispose @@ -361,7 +385,7 @@ class TilemapRenderer if tile_id < TILES_PER_AUTOTILE tile.set_bitmap("", tile_id, false, false, 0, nil) tile.shows_reflection = false - tile.bridge = false + tile.bridge = false else terrain_tag = map.terrain_tags[tile_id] || 0 terrain_tag_data = GameData::TerrainTag.try_get(terrain_tag) @@ -378,11 +402,12 @@ class TilemapRenderer end if tile_id < true_tileset_start_id filename = "" - if tile_id < TILESET_START_ID # Real autotiles + if tile_id < TILESET_START_ID # Real autotiles filename = map.autotile_names[(tile_id / TILES_PER_AUTOTILE) - 1] - elsif tile_id < single_autotile_start_id # Large extra autotiles + elsif tile_id < single_autotile_start_id # Large extra autotiles filename = extra_autotile_arrays[0][(tile_id - TILESET_START_ID) / TILES_PER_AUTOTILE] - else # Single extra autotiles + else + # Single extra autotiles filename = extra_autotile_arrays[1][tile_id - single_autotile_start_id] end tile.set_bitmap(filename, tile_id, true, @autotiles.animated?(filename), @@ -392,7 +417,7 @@ class TilemapRenderer tile.set_bitmap(filename, tile_id, false, false, priority, @tilesets[filename]) end tile.shows_reflection = terrain_tag_data&.shows_reflections - tile.bridge = terrain_tag_data&.bridge + tile.bridge = terrain_tag_data&.bridge end refresh_tile_src_rect(tile, tile_id) end @@ -447,7 +472,7 @@ class TilemapRenderer @tile_offset_x -= offsets[0] @tile_offset_y -= offsets[1] else - ret = true # Need a full refresh + ret = true # Need a full refresh end else ret = true @@ -556,7 +581,7 @@ class TilemapRenderer @screen_moved_vertically = false if $PokemonGlobal.bridge != @bridge @bridge = $PokemonGlobal.bridge - @screen_moved_vertically = true # To update bridge tiles' z values + @screen_moved_vertically = true # To update bridge tiles' z values end do_full_refresh = true if check_if_screen_moved # Update all tile sprites @@ -609,7 +634,7 @@ class TilemapRenderer coord.each do |tile| tile.set_bitmap("", 0, false, false, 0, nil) tile.shows_reflection = false - tile.bridge = false + tile.bridge = false end end end