Войти на сайт

Авторизация, ждите ...
×

ТЕМА: Отложенные задачи

Отложенные задачи 9 года 10 мес. назад #76652

  • Iren_Rin
  • Iren_Rin аватар
  • Вне сайта
  • Мастер
  • Сообщений: 247
  • Спасибо получено: 537
  • УчительПрограммист RubyПроект года 1 местоПроект месяца 1 местоКоммерсант
Зачастую нам нужно, чтобы какой-либо код исполнился не сразу, а через некоторое количество времени. Традиционно я в таких случаях использовал нити (Thread) в паре со sleep методом примерно так
Thread.new do
  sleep 0.5 #Зажержка в секундах
  puts "FINALY"
end
Такой способ неплох, и в мейкере нити есть, и они работают. Но есть одна неприятность - sleep не выдерживает точный интервал времени, а если таких нитей запустить много, то в моем случае вместо 0.5 секунды sleep выдерживал от 0.4 до 4 секунд, что неприемлемо.
Но было бы желание. Немного подумав я решил использовать метод Scene_Base.update_basic, который является фактически сердцем мейкера. Идея состоит в том, что мы складываем код, который нужно исполнить в блок, блок храним в очереди, и исполняем, когда прошло определенное количество фреймов. После исполнения блок из очереди удаляем. Получилось в итоге следующее:
class Scene_Base
  class << self
    def flush_queue #Метод для обнуления очереди
      @queue = []
    end
 
    def delay(frames, &job)  #Через этот метод мы передаем блок и количество фреймов
      queue << [frames, job] #Пакуем блок вместе с количеством фреймов в массив (сообщение), сообщение кладем в очередь
    end
 
    def tick #Этот метод мы дергаем в update_basic
      queue.each do |arr| #Мы пробегаемся по всей очереди
        arr[0] -= 1       #Уменьшаем на 1 счетчик каждого сообщения
        arr[1].call if arr[0] <= 0 #исполняем блок, если пришло время
      end
      clear_queue #запускаем очистку очереди
    end
 
    private
 
    def queue
      @queue ||= [] #вернет очередь, или сделает новую, если ее еще нет
    end
 
    def clear_queue
      queue.reject! { |arr| arr[0] <= 0 } # Удаляет исполненные сообщения
    end
  end
 
  alias original_terminate terminate
  def terminate
    Scene_Base.flush_queue #чистим очередь перед закрытием сцены
    original_terminate
  end
 
  alias original_update_basic update_basic
  def update_basic
    Scene_Base.tick 
    original_update_basic
  end
end

Использовать этот код очень просто - от Scene_Base наследуются все сцены в игре, а значит метод update_basic будет отсчитывать фреймы для нас в любой ситуации. Достаточно вызвать метод delay, передать ему количество фреймов для ожидания и блок кода, который нужно исполнить:
Scene_Base.delay 40 do
  $my_cool_variable = 'cool_value'
end

Хочу еще добавить, что код будет исполняться в основном потоке, это значит что игра будет ждать, пока исполниться ваш блок. Это нормально для 99% задач. Если же вы хотите сделать что нибудь длительное, но не желаете ждать, используйте нити:
Scene_Base.delay 40 do
  Thread.new do
    loop do
      #бесконечный цикл
    end
  end
end

Вот и все, надеюсь кому нибудь пригодится.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Lekste, DeadElf79, Ren310, strelokhalfer, Dprizrak1, Lipton, MaltonTheWarrior
Время создания страницы: 0.325 секунд