Показано с 1 по 10 из 29

Тема: Создание скриптов на RGSS для людей со средними знаниями и экспертов

Древовидный режим

Предыдущее сообщение Предыдущее сообщение   Следующее сообщение Следующее сообщение
  1. #4

    По умолчанию

    3. Обработка и хранение данных


    В этой главе вы научитесь тому, как следует хранить и обрабатывать данные. Она непосредственно касается насущного вопроса «RAM или CPU?» и поможет оптимизировать и улучшить ваш код.


    3.1. Зачем использовать циклы?


    Ответ на этот вопрос очень прост. Взгляните на два примера далее:

    Код:
    call_my_method
    x += do_this
    y += do_that
    call_my_method
    x += do_this
    y += do_that
    call_my_method
    x += do_this
    y += do_that


    Код:
    x = (x + 1) * 3 + 1
    x = (x + 1) * 3 + 2
    x = (x + 1) * 3 + 3
    x = (x + 1) * 3 + 4


    Даже в этих нескольких строчках очень много ненужного кода, а ведь со временем их будет становиться всё больше и больше и тогда даже простые операции будут тяжелы для понимания. В RGSS эта проблема решается с помощью циклов.

    Код:
    for i in 0...3
      call_my_method
      x += do_this
      y += do_that
    end


    Код:
    for i in 1..4
      x = (x + 1) * 3 + i
    end


    Приняв во внимание тот факт, что существует два вида циклов (определённый и неопределённый), мы можем использовать их не только для упрощения кода, но и для создания повторяющихся неопределённое количество раз операций.

    Код:
    loop do
      x = rand(10000)
      if x == 6284
        break
      end
    end


    Этот код будет выполняться до тех пор, пока в виде случайного числа не выпадет 6284. Это лишь краткое объяснение циклов и то, для чего их можно использовать (подробности смотрите в [IURL="http://rpgmaker.su/showthread.php/518-Создание-скриптов-на-RGSS-для-людей-со-средними-знаниями-и-экспертов?p=9620&viewfull=1#post9622"]главе 8[/IURL]).
    Примечание: Не путайте диапазоны в циклах. 0…3 выполнит итерации с 0 до 2, то есть не включая 3, тогда как диапазон 1..4 выполнит итерации с 1 до 4, включая 4. Помните об этом.


    3.2. Зачем использовать и методы и функции?


    И вновь ответ очень простой. Если у вас есть кусок кода, который вы часто используете, то хорошим решением будет поместить его в функцию. У классов функции называются методами, но это практически одно и то же. Помните, что не так важно поместить какой-то случайный код в функцию, куда важнее действительно объединить то что вам пригодиться. Когда вы называете свою функцию, то давайте ей имя, которое будет отражать её действие. Это поможет сделать код более читаемым, а также упростит его. Ещё очень полезно разбивать код на логические части, подгруппы и уже потом в функции. Далее вы найдёте пример хорошего внешнего вида у кода функции. Но вариантов могут быть тысячи.

    Код:
    def remove_enemy_from_map(enemy)
      id = enemy.get_sprite_id
      if id >= 0
        enemy.set_fading_flag
        enemy.character.active = false
        enemy.freeze_current_action
        @spriteset.sprite_takeover_fade(prepare_sprite_fade(id))
        remove_enemy_character(enemy.id)
        @hud.kills += 1
      end
      return id
    end



    3.3. Эффективное обращение к данным


    Есть множество путей для хранения и использования данных. Напомню, что RGSS — это скриптовый язык, код которого не компилируется, а интерпретируется. По сравнению с компилируемыми языками такая обработка в 10 раз медленнее или даже больше. Вот почему так важно поддерживать быструю работу вашего кода.

    Одной из критических областей в этом вопросе является структура данных. Наиболее часто используемые структуры: список, хэш и стек. Интересно отметить, что в RGSS класс Array (массив) поддерживает команды стека push и pop, тем самым позволяя использовать массивы для создания и списков и стеков.

    Списки работают по принципу FIFO (First In First Out), по-русски «первый пришёл — первый вышел». Это значит что первые добавленные данные в списке будут первыми удалёнными из него. Представьте себе трубу в которую вы заталкиваете один за другим резиновые мячики. Первый, который вы туда положите вылезет позже с другой стороны.

    Стеки работают по принципу LIFO (Last In First Out), «последний пришёл — первый вышел». Последние добавленные данные станут первыми при удалении. Представьте себе стопку тарелок. Если руководствоваться правилом при котором можно взять только одну тарелку, то, такой тарелкой окажется та, которая лежит сверху стопки.

    С хэшем ситуация обстоит несколько иначе. Хэш работает по принципу распределения адресов. В то время как массивы хранят данные последовательно, хэши хранит их в любом месте. Для доступа к данным используется ключ. Естественно что такой доступ наиболее эффективен, если вы управляете множеством данных, которые редко меняются и так называемая таблица хэша заполнена примерно на 75%. У массивов и хэшей сходное поведение в доступе к данным, хотя у хэшей оно более сложное вида O(n). В RGSS в RPG Maker вы в основном будете оперировать небольшим количеством. 1 МБ (в который поместятся 250000 4-байтовых целых чисел) — это относительно мало, учитывая что в среднем у пользователя в наши дни количество оперативной памяти (RAM) составляет 512 МБ. Даже при том что RAM означает Оперативная память (Random Access Memory) и к данным можно обратиться очень быстро где бы они не располагались, существует проблема с L1 и L2 кэшем процессора (CPU) который будет работать намного медленнее если вам придётся загружать данные со всех точек оперативной памяти, так как L2 кэш загружает блоки данных из RAM. А L1 загружает блоки из L2. Это гарантирует оптимальную производительность процессора, поскольку кэш-память L1 более ценна, чем L2, а та в свою очередь более ценна, чем обычная оперативная память. Именно поэтому никто не делает саму оперативную память по той же технологии, что и кэши L1 или L2: материально это не просто не окупается. По этой же причине процессоры с большой кэш-памятью стоят очень дорого, так что не зря я употребил слово «оптимально»; это оптимальный выбор между временем и деньгами. Если запрашиваемые данные находятся не в кэше, то это называется неудачным обращением в кэш и для загрузке нового блока потребуется время. По этой причине я рекомендую использовать хэши только при необходимости, когда это действительно имеет смысл, и ваш код будет более аккуратным и производительность не пострадает. Хорошим примером здесь станет очень быстрое определение столкновения, когда у вас есть хэш с координатами карты в качестве ключей и всеми соответствующими этим координатам персонажами на карте в качестве значений этих ключей.

    Никогда не используйте хэши для настроек! Не позволяйте пользователям устанавливать данные и/или менять настройки в виде хэша! Это очень грубая ошибка и она показывает людям насколько вы не понимаете те методы, которые сами же используете. Проблема заключается в том, что хэши менее эффективны по сравнению с массивами, если вы не храните в них большой объём данных, что редко происходит в RGSS.

    Есть лучшая альтернатива хэшам, которая работает намного быстрее и для понимания она намного проще. Эта альтернатива превосходно подходит для настройки пользователем и работает с данными подобно хэшам. Хитрость в том, что отображение данных распределено, но сами данные хранятся в оперативной памяти как единый блок. В практике же это намного легче для пользователей. Эта альтернатива — простой метод конверсии. В качестве аргумента метод получает ключ, который трансформируется в соответствующее значение. Единственный недостаток данного метода по сравнению с хэшем — это постоянное количество данных в нём. Вы не сможете изменить его состав во время игры. Но все непостоянные настройки так или иначе хранятся в специальных классах (смотрите раздел 7.2.), так что этот метод намного лучше по сравнению с хэшами.
    Пример: Допустим вы хотите, чтобы части экипировки вызывали какие-то специальные навыки. На словах это можно описать так: если используется EQUIP_ID, то возвращаем значение SKILL_ID:

    Код:
    def trigger_skill(id)
      case id
      when 1 then return 12
      when 4 then return 31
      when 7 then return 9
      when 12 then return 4
      when 81 then return 27
      end
      return 0
    end


    То же самое, но с использованием хэша:

    Код:
    TRIGGER_SKILL = {}
    TRIGGER_SKILL[1] = 12
    TRIGGER_SKILL[4] = 31
    TRIGGER_SKILL[7] = 9
    TRIGGER_SKILL[12] = 4
    TRIGGER_SKILL[81] = 27


    Или другим способом определения хэша:

    Код:
    TRIGGER_SKILL = {1 => 12, 4 => 31, 7 => 9, 12 => 4, 81 => 27}


    Последний пример ужасен. Его сложно понять, особенно тем, кто не знаком с программированием. Второй пример более приемлем, но всё же ANSI синтаксис в первом примере намного легче понять (особенно англоязычному пользователю — прим. перев.). Вы можете просто прочесть его “когда id равно 1 тогда возвращаем число 12, когда id равно тогда возвращаем число 31…” А вот второй пример прочесть не так то просто. Использование метода конверсии делает код проще для понимания, особенно если в нём придётся разбираться обычному пользователю и что-то самому изменять. Он так же использует лишь то количество оперативной памяти, которое ему необходимо для выполнения кода, хоть это и занимает БОЛЬШЕЕ пространство, однако хранится он как единый блок данных, позволяя быстрее получать к нему доступ в кэше процессора. Также имейте ввиду, что операции сохраняющие и загружающие что-либо в память в 10-15 раз медленнее, чем арифметически-логические операции. Доступ к одной части хэша требует некоторого времени, доступ к другой занимает ещё дополнительное время и так далее. Доступ же к одному и тому же методу в несколько раз быстрее. В обоих случаях для выполнения методов требуется перенос параметра (ведь получение элемента хэша с использованием ключа это тоже метод!), который сам по себе требует времени для сохранения данных в системном стеке и для возвращения адреса текущей команды. Поэтому это время я не принимаю во внимание при сравнении, здесь ни один метод не выигрывает (см. раздел [IURL="http://rpgmaker.su/showthread.php/518-Создание-скриптов-на-RGSS-для-людей-со-средними-знаниями-и-экспертов?p=9620&viewfull=1#post9620"]6.2[/IURL]).

    Другая возможность управления данными — использование массивов. Вы можете с легкостью манипулировать данными, добавляя их в конец массива, в начало, удаляя их из конца или из начала. Для этого используются 4 простые команды:

    Код:
    push 	– добавляет новые элемент в конец массива
    unshift – сдвигает массив и добавляет элемент в начало
    pop 	– удаляет последний элемент массива
    shift 	– удаляет первый элемент и сдвигает массив



    схема 3.3.1. – принцип работы команд push, unshift, pop и shift

    Чтобы получить больше информации, прочтите справку к RMXP. Помните, что вы можете добавить в массив всё что пожелаете. Ваши массивы могут содержать различные экземпляры разных классов. Ничто не мешает вам сделать массив с 3 целыми числами, одним героем и 5 строками. Так же можно использовать массив в методе, который должен вернуть несколько значений.

    Код:
    def throw_2_dices
      return [rand(6) + 1, rand(6) + 1]
    end



    схема 3.3.2. – доступ к данным и чтение в a) массиве b) хэше


    3.4. Модуль или класс?


    Вопрос который не даёт покоя многим скриптерам. Однако ответ на него довольно простой. Используйте классы для обработки экземпляров данных. Например, когда герой является экземпляром, как и в случае с числом или группой чисел. Представьте массив с сотней чисел. Классы же больше и поддерживают обработку с помощью методов. Метод — это практически то же самое, что и функция. Единственное отличие в том, что методы напрямую влияют на экземпляр собственного класса. Один класс не может использовать метод другого класса. Например, класс Bitmap покажет сообщение об ошибке «неопределённый метод», если вы попытаетесь использовать в нём метод size, который однако без проблем будет работать в экземпляре класса Array.

    Теперь поговорим о модулях. Модули не содержат данные (помимо особо важных данных), они — сборники методов, которые можно использовать там, где вы пожелаете. Многие скриптеры используют модули для хранения констант для настроек. Это хорошее решение, если у вас 10 или более опций. Это поможет избежать конфликты с другими скриптами, но если же их меньше, то в этом нет никакого смысла.

    Примерами классов служат различные окна в RPG Maker. Вот как можно использовать загруженный экземпляр класса:

    Код:
    win = Window_Status.new
    win.refresh
    win.x += 16


    Использование модулей происходит по-другому. Вам не нужен экземпляр для доступа к нему:

    Код:
    Input.trigger?(Input::B)


    Имейте в виду, что когда вы определяете методы модуля, то можете использовать как обычные варианты определения, например: def method_name и module_function :method_name, так и прямые def self.method_name или def NAME_OF_MODULE.method_name.

    Наверняка вы обратили внимание на Input::B. Так мы получаем доступ к константам внутри модуля. Знак :: называется оператором области видимости. Таким образом модули используются в основном для коллекции методов, функций и констант. В классах тоже можно создать метод вида def self.something и использовать его подобно модулю.

    Подробнее об объектно-ориентированном программировании написано в главе 8.

    Код:
    class Game_Party
      def self.load_from_savefile(file)
        tmp = Marshal.load(file)
        $game_party = tmp if tmp.is_a?(Game_Party)
      end
    end
    Game_Party.load_from_savefile(file)


    Этот пример очень хорошо работает, проверьте сами. Главное убедитесь что используете его на настоящем экземпляре класса IO. Вы можете использовать файл с сохранением и вызывать метод до EOF (End Of File) — конца файла. Вы обнаружите, что при этом загружается состояние партии из файла сохранения.


    3.5. RAM или CPU?


    В этой главе вы познакомились с основами обработки и управления данных. Данные могут стать опасным врагом, если их становится слишком много. Лучше разрешите процессору (CPU) конвертировать данные вместо поиска их в оперативной памяти (RAM), особенно если это постоянные данные. Конвертируемые CPU данные попадают в кэш, в отличие от данных из RAM. Это важно лишь для тех данных, которые распределены, а не сгруппированы вместе. Не спешите применять хэши, делайте это только в крайнем случае.



    [IURL="http://rpgmaker.su/showthread.php/518-Создание-скриптов-на-RGSS-для-людей-со-средними-знаниями-и-экспертов?p=9614&viewfull=1#post9614"]Вернуться к содержанию...[/IURL]
    Последний раз редактировалось Arnon; 29.10.2012 в 21:07.

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 4 (пользователей: 0 , гостей: 4)

Метки этой темы

Социальные закладки

Социальные закладки

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •  
Создание скриптов на RGSS для людей со средними знаниями и экспертов