Отложенные задачи

Программист Ruby Коммерсант Проект года 1 место Учитель Проект месяца 1 место
Больше
11 года 4 мес. назад #76652 от Iren_Rin
Зачастую нам нужно, чтобы какой-либо код исполнился не сразу, а через некоторое количество времени. Традиционно я в таких случаях использовал нити (Thread) в паре со sleep методом примерно так
Code:
Thread.new do sleep 0.5 #Зажержка в секундах puts "FINALY" end
Такой способ неплох, и в мейкере нити есть, и они работают. Но есть одна неприятность - sleep не выдерживает точный интервал времени, а если таких нитей запустить много, то в моем случае вместо 0.5 секунды sleep выдерживал от 0.4 до 4 секунд, что неприемлемо.
Но было бы желание. Немного подумав я решил использовать метод Scene_Base.update_basic, который является фактически сердцем мейкера. Идея состоит в том, что мы складываем код, который нужно исполнить в блок, блок храним в очереди, и исполняем, когда прошло определенное количество фреймов. После исполнения блок из очереди удаляем. Получилось в итоге следующее:
Code:
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, передать ему количество фреймов для ожидания и блок кода, который нужно исполнить:
Code:
Scene_Base.delay 40 do $my_cool_variable = 'cool_value' end

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

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

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Время создания страницы: 0.086 секунд
Работает на Kunena форум