Создание более реалистичного эффекта снега (Creating a more realistic snow effect)

Материал из Ren'Py Wiki
Перейти к навигации Перейти к поиску

Этот рецепт позволяет реализовать более реалистичный эффект снегопада, чем тот, который вы можете получить, используя SnowBlossom. Код в большей степени прокомментирован и должен работать с Ren'Py 6.9.0+ (он будет работать в более ранних версиях Ren'Py, если вы измените строку #116, чтобы использовать im.FactorZoom взамен im.FactorScale)

init python:
    
    #################################################################
    # Здесь мы используем случайный модуль для некоторый произвольных материалов (так как мы 
    # не хотим, чтобы Ren'Py сохранял случайные числа, мы будем их генерировать).
    import random
    
    # инициализация случайных чисел
    random.seed()
    
    #################################################################
    # Частицы снега
    # ----------------
    def Snow(image, max_particles=50, speed=150, wind=100, xborder=(0,100), yborder=(50,400), **kwargs):
        """
        Это создает эффект снега. Следует использовать эту функцию вместо того, чтобы инстанцировать
        SnowFactory напрямую (Ну, на самом деле не имеет значения, но это сэкономит набор текста, 
        если вы используете значения по умолчанию =D)
        
        @parm {image} image:
            Это изображение используется в качестве снежинок. Это всегда должен быть файл изображения
            или im объект, так как мы будем применять im трансформации на нем.
        
        @parm {int} max_particles:
            Максимально количество частиц на экране одновременно.
            
        @parm {float} speed:
            Основная скорость частиц по вертикали. Чем выше значение, тем быстрее частицы 
            будут падать.
            Значения ниже 1 будут изменены на 1
            
        @parm {float} wind:
            Максимальная сила ветра, которая будет применена к частицам.
            
        @parm {Tuple ({int} min, {int} max)} xborder:
            Горизонтальная границы диапазона. Случайное значение между этими двумя будет применено 
            при создании частиц.
            
        @parm {Tuple ({int} min, {int} max)} yborder:
            Вертикальная границы диапазона. Случайное значение между этими двумя будет применено 
            при создании частиц.
            Чем выше значения, тем дальше от экрана они будут создаваться.
        """
        return Particles(SnowFactory(image, max_particles, speed, wind, xborder, yborder, **kwargs))
    
    # ---------------------------------------------------------------
    class SnowFactory(object):
        """
        Фабрика, создающая частицы, используемые в эффекте снега.
        """
        def __init__(self, image, max_particles, speed, wind, xborder, yborder, **kwargs):
            """
            Инициализировать фабрику. Параметры такие же, как у Snow функции.
            """            
            # максимальное число частиц может быть одновременно на экране
            self.max_particles = max_particles
            
            # скорость частицы
            self.speed = speed
            
            # скорость ветра
            self.wind = wind
            
            # горизонтальный/вертикальный диапазон для создания частиц
            self.xborder = xborder
            self.yborder = yborder
            
            # максимальная глубина экрана. Наибольшие значения приводит к большим имениям 
            # размера частиц, но они также используют больше памяти. 10 это значение по умолчанию 
            # и оно должно подходить для большинства игр,
            # поскольку размеры частиц рассчитываются как процент от этого значения.
            self.depth = kwargs.get("depth", 10)
            
            # инициализация изображений
            self.image = self.image_init(image)
            

        def create(self, particles, st):
            """
            Вызывается изнутри каждого фрейма объекта Particles для создания новых частиц.
            Мы просто создадим новые частицы, если число частиц на экране меньше, чем 
            максимально число части, которое может быть.
            """
            # Если мы создаем новую частицу...
            if particles is None or len(particles) < self.max_particles:
                
                # генерируем случайную глубину для частицы
                depth = random.randint(1, self.depth)
                
                # Мы предполагаем, что частицы падающие далеко от экрана будут двигаться медленнее,
                # чем те, что падают вблизи экрана. Так мы изменим скорость падения частиц на основе
                # их глубины =D
                depth_speed = 1.5-depth/(self.depth+0.0)
                
                return [ SnowParticle(self.image[depth-1],      # изображение, используемое частицей 
                                      random.uniform(-self.wind, self.wind)*depth_speed,  # сила ветра 
                                      self.speed*depth_speed,  # вертикальная скорость частицы
                                      random.randint(self.xborder[0], self.xborder[1]), # горизонтальная граница
                                      random.randint(self.yborder[0], self.yborder[1]), # вертикальная граница
                                      ) ]
        
        
        def image_init(self, image):
            """
            Это вызывают изнутри для инициализации изображений.
            Создает список изображений с различными размерами, поэтому мы можем
            предугадать их все и использовать кэшированные версии, чтобы сделать 
            их более эффективными для памяти.            
            """
            rv = [ ]
            
            # создает массив изображений для каждого возможного значения глубины.
            for depth in range(self.depth):
                # Изменение размера и корректировка альфа значения на основе глубины изображения
                p = 1.1 - depth/(self.depth+0.0)
                if p > 1:
                    p = 1.0
                
                rv.append( im.FactorScale( im.Alpha(image, p), p ) )

            return rv
        
        
        def predict(self):
            """
            Это вызываемый изнутри объект Particles для прогнозирования используемых 
            изображений частиц. Предполагает возврат списка изображения для прогнозирования.
            """ 
            return self.image
            
    # ---------------------------------------------------------------
    class SnowParticle(object):
        """
        Представляет каждую частицу на экране.
        """
        def __init__(self, image, wind, speed, xborder, yborder):
            """
            Инициализирует частицы снега. Вызывается автоматически при создании объекта.
            """
            
            # Изображение, используемое этой частицей
            self.image = image
            
            # Для безопасности (и так как у нас нет снега, идущего от пола до неба o.o)
            # если вертикальная скорость частиц ниже, чем 1, мы используем 1.
            # Препятствует застреванию частиц на экране навсегда и не падению вообще.
            if speed <= 0:
                speed = 1
                
            # скорость ветра
            self.wind = wind
            
            # скорость частицы
            self.speed = speed

            # Последний раз, когда частица была обновлена (используется для расчета непредвиденных
            # задержек между обновлениями, иначе говоря лаг)
            self.oldst = None
            
            # горизонтальные/вертикальные положения частицы
            self.xpos = random.uniform(0-xborder, renpy.config.screen_width+xborder)
            self.ypos = -yborder
            
            
        def update(self, st):
            """
            Вызывается внутри каждого фрейма для обновления частицы.
            """
            
            # вычисление лага
            if self.oldst is None:
                self.oldst = st
            
            lag = st - self.oldst
            self.oldst = st
            
            # обновление положения
            self.xpos += lag * self.wind
            self.ypos += lag * self.speed
               
            # проверяет, если частица вышла за экран, то можно её уничтожить.
            if self.ypos > renpy.config.screen_height or\
               (self.wind< 0 and self.xpos < 0) or (self.wind > 0 and self.xpos > renpy.config.screen_width):
                ##  выводит "Мёртвый"
                return None
                
            # возвращает частицу как кортеж (xpos, ypos, time, image)
            # так как она предполагает, что горизонтальный и вертикальные положения - целые числа, 
            # мы должны преобразовать их (внутренней позиции используют float для плавных движений =D)
            return int(self.xpos), int(self.ypos), st, self.image

Пример (Example)[править | править код]

init:
    image snow = Snow("snowflake.png")

label start:
    show snow
    "Эй, смотри! Снег пошел."

Источник[править | править код]

Статья "Creating a more realistic snow effect" на английском языке


На главную страницу Книга рецептов Энциклопедии Ren'Py Книга рецептов Ren'Py Вернуться к началу статьи