地上の洞窟

どこにも行かず、液晶と「にらめっこ」し続ける人の物語。

【RGSS3】ダイナミックスクロール

ダイナミックスクロール
滑らか(?)で簡単なスクロール

指定の座標に向かって、加減速を交えた滑らかなスクロールを実行します。
中の計算式は超適当とか言えない ← ちゃんと修正したよ!

スクリプトコマンド一覧

DynamicScroll.scroll(x, y)

マップ座標 x, y が画面の中心になるようにダイナミックスクロールを実行します。

DynamicScroll.scroll_player

プレイヤーが画面の中心になるようにダイナミックスクロールを実行します。

DynamicScroll.scroll_event(event_id)

指定したidのイベントが画面の中心になるようにダイナミックスクロールを実行します。

更新履歴 バージョン 内容
2024/03/02 v2.1.0 スクロール時のキャラクターの表示のズレを
抑制する機構の追加
2024/01/23 v2.0.2 DynamicScroll.scroll_event(event_id)の使用時に
クラッシュする不具合の修正
2023/12/31 v2.0.1 移動速度が移動距離を上回ってしまった場合
計算がおかしくなる不具合の修正
2023/12/31 v2.0.0 計算式を正確なものに修正
2023/11/22 v1.0.0 初版

→スクリプト一覧へ


スクリプト(ダブルクリックで全選択)↓

=begin =========================================================================
 ■ 「ダイナミックスクロール」- DynamicScroll v2.1.0 by 地上の洞窟
================================================================================
  
  指定の座標に向かって、加減速を交えた滑らかなスクロールを実行します。

================================================================================
 ■ 機能一覧
================================================================================

  イベントのスクリプトコマンドなどから
  DynamicScrollの機能を呼び出して使用することが可能です。

  DynamicScroll.scroll(x, y)
 マップ座標 x, y が画面の中心になるようにダイナミックスクロールを実行します。

  DynamicScroll.scroll_player
  プレイヤーが画面の中心になるようにダイナミックスクロールを実行します。

  DynamicScroll.scroll_event(event_id)
  指定IDのイベントが画面の中心になるようにダイナミックスクロールを実行します。
  
================================================================================
 ■ 設定項目
=end #==========================================================================

module DynamicScroll
  

  # ACCEL -> ダイナミックスクロールの加速度
  # 1フレームに増減する加速度を指定

  ACCEL = 1 / 64.0

  
  # DEFAULT_MAX_SPEED -> ダイナミックスクロールの最高速度(デフォルト値)
  # 1フレームに移動する距離の最大値を指定

  DEFAULT_MAX_SPEED = 0.5


  # REDUCE_MISALIGN -> スクロール時のキャラクターの表示のズレを抑制

  REDUCE_MISALIGN = true


#===============================================================================
# ■ ここからソースコード
#===============================================================================

  class << self
    def scroll(x, y, max_speed = DEFAULT_MAX_SPEED)
      $game_map.dynamic_scroll(
        x - $game_player.center_x,
        y - $game_player.center_y,
        max_speed
      )
    end

    def scroll_player(max_speed = DEFAULT_MAX_SPEED)
      scroll($game_player.x, $game_player.y, max_speed)
    end

    def scroll_event(event_id, max_speed = DEFAULT_MAX_SPEED)
      return unless (event = $game_map.events[event_id])
      scroll(event.x, event.y, max_speed)
    end

    def set(x, y, max_speed = DEFAULT_MAX_SPEED)
      $game_map.set_display_pos(
        x - $game_player.center_x,
        y - $game_player.center_y
      )
    end
  end

  class Controller
    attr_accessor :target_x, :target_y
    @@last_speed = 0

    def initialize(x, y, tx, ty, max_speed)
      @x = x
      @y = y
      @target_x = tx
      @target_y = ty
      @dx = tx - x
      @dy = ty - y
      @pos = 0
      @distance = Math.sqrt(@dx * @dx + @dy * @dy)
      @x_rate = @dx / @distance.to_f
      @y_rate = @dy / @distance.to_f
      @start_speed = @@last_speed
      @max_speed = max_speed

      # real_max_speed
      @real_max_speed = Math.sqrt(ACCEL * @distance + @start_speed * @start_speed * 0.5)
      @real_max_speed = max_speed if @real_max_speed > max_speed
      @real_max_speed = @start_speed if @start_speed > @real_max_speed

      # accel
      @t_accel = (@real_max_speed - @start_speed) / ACCEL.to_f
      @distance_accel = (@start_speed + @real_max_speed) * @t_accel * 0.5
      
      # deaccel
      @t_deaccel = @real_max_speed / ACCEL.to_f
      @distance_deaccel = @real_max_speed * @t_deaccel * 0.5
      @distance_deaccel = @distance if @distance_deaccel > @distance

      # cruise
      @distance_cruise = @distance - @distance_accel - @distance_deaccel
      @t_cruise = @distance_cruise / @real_max_speed.to_f

      # time
      @duration = @t_accel + @t_cruise + @t_deaccel
      @timing_cruise = @t_accel + @t_cruise
      @count = 0
    end

    def moving?
      @count < @duration
    end

    if REDUCE_MISALIGN
      def scroll_x
        @x + (@pos * @x_rate * 32).round / 32.0
      end

      def scroll_y
        @y + (@pos * @y_rate * 32).round / 32.0
      end  
    else
      def scroll_x
        @x + @pos * @x_rate
      end

      def scroll_y
        @y + @pos * @y_rate
      end
    end

    def update
      if @count < @duration
        @count += 1
        if @count < @t_accel
          @pos = @start_speed * @count + ACCEL * @count * @count * 0.5
          @@last_speed = @start_speed + ACCEL * @count
        elsif @count < @timing_cruise
          i_cruise = (@count - @t_accel).to_i
          @pos = @distance_accel + @real_max_speed * i_cruise
          @@last_speed = @real_max_speed
        else
          i_deaccel = (@count - @timing_cruise).to_i
          @pos = @distance_accel + @distance_cruise + @real_max_speed * i_deaccel - ACCEL * i_deaccel * i_deaccel * 0.5
          @@last_speed = @real_max_speed - ACCEL * i_deaccel
        end
        if @pos.abs > @distance
          @pos = @distance
          @duration = -1
        end
      end
    end
  end
end

class Game_Map
  alias dscr_scrolling? scrolling?
  def scrolling?
    return true if @dynamic_scroll_x || @dynamic_scroll_y
    dscr_scrolling?
  end

  alias dscr_set_display_pos set_display_pos
  def set_display_pos(x, y)
    dscr_set_display_pos(x, y)
    @dynamic_scroll_controller = nil
  end

  alias dscr_update_scroll update_scroll
  def update_scroll
    if dynamic_scrolling?
      update_dynamic_scroll
    else
      dscr_update_scroll
    end
  end

  def dynamic_scroll(tx, ty, max_speed)
    if loop_horizontal?
      tx %= width
    else
      tx = 0 if tx < 0
      (m = width - screen_tile_x) < tx and tx = m
    end

    if loop_vertical?
      ty %= height
    else
      ty = 0 if ty < 0
      (m = height - screen_tile_y) < ty and ty = m
    end
    
    if @display_x == tx && @display_y == ty
      @dynamic_scroll_controller = nil
    else
      @dynamic_scroll_controller = DynamicScroll::Controller.new(
        @display_x, @display_y, tx, ty, max_speed
      )
    end
  end

  def dynamic_scrolling?
    @dynamic_scroll_controller
  end

  def update_dynamic_scroll
    return unless @dynamic_scroll_controller
    @dynamic_scroll_controller.update
    if @dynamic_scroll_controller.moving?
      @display_x = @dynamic_scroll_controller.scroll_x
      @display_y = @dynamic_scroll_controller.scroll_y
    else
      @display_x = @dynamic_scroll_controller.target_x
      @display_y = @dynamic_scroll_controller.target_y
      @dynamic_scroll_controller = nil
    end

    if loop_horizontal?
      @display_x %= width
    else
      @display_x = 0 if @display_x < 0
      (m = width - screen_tile_x) < @display_x and @display_x = m
    end

    if loop_vertical?
      @display_y %= height
    else
      @display_y = 0 if @display_y < 0
      (m = width - screen_tile_y) < @display_y and @display_y = m
    end
  end
end

class Game_Player < Game_Character
  alias dscr_update_scroll update_scroll
  def update_scroll(last_real_x, last_real_y)
    return if $game_map.dynamic_scrolling?
    dscr_update_scroll(last_real_x, last_real_y)
  end
end