Зачастую нам нужно, чтобы какой-либо код исполнился не сразу, а через некоторое количество времени. Традиционно я в таких случаях использовал нити (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
Вот и все, надеюсь кому нибудь пригодится.