Отправляет email-рассылки с помощью сервиса Sendsay

Программирование игр в Linux

  Все выпуски  

Программирование игр в Linux #4


Информационный Канал Subscribe.Ru

Программирование игр в Linux: OpenGL для графики 2D-игры

Часть 4

Рад приветствовать вас, уважаемые читатели. В этом выпуске мы продолжаем написание нашего 2Д движка. На этот раз мы сделаем сразу две вещи. Во-первых, создадим несложную оболочку вокруг разработанного класса Image для того, чтобы превратить его в анимацию (класс Animation). Кроме того, вы узнаете, для чего же был создан класс Resource - мы начнем делать несложный менеджер ресурсов.
Очень скоро вы поймете, что возможностей нашего движка хватит только на создание довольно простых игр, но подход к его архитектуре таков, что вы сможете быстро адаптировать его под свои требования. И конечно, этот опыт будет очень полезен для новичков.

В разработанном нами классе Image любое изображение рассматривается как набор кадров, который можно использовать как анимацию. Принцип работы очень прост: нужно по очереди показывать кадры из набора, с небольшой задержкой. Предполагаемые свойства анимации таковы:

  • Анимация не существует без изображений из Image
  • Кадры меняются с заданной скоростью
  • Анимацию можно играть заданное число раз и останавливать
  • Можно отключать и включать отрисовку анимации на экране
Все эти свойства реализованы в классе Animation:
class Animation
{
public:
        Animation(Image* frames);
        virtual ~Animation();
        
        // Набор кадров анимации
        Image* get_frames() const { return frames_; };
        int get_frames_num();
        
        // Задержка в миллисекундах
        void set_speed(int speed) { speed_ = speed; };
        int get_speed() const { return speed_; };
        
        // -1 означает бесконечное проигрывание анимации
        // 0  означает не играть анимацию (т.е. играть 0 раз)
        void play(int times = -1);
        void stop();
        bool is_stopped() const { return stopped_; };
        
        // Отрисовка изображения анимации
        void show() { visible_ = true;  };
        void hide() { visible_ = false; };
        bool is_visible() const { return visible_; };
        
        // Анимация отрисовывается также как и изображение
        void draw(int x, int y);
        
        // Переменные класса не показаны в этом листинге
};
Реализация класса очень простая. Наиболее значимая часть - функция draw, в которой помимо отрисовки очередного кадра происходит обновление параметров анимации. Для этого отслеживается время изменения предыдущего кадра анимации и если оно превысило заданный интервал (в нашем случае speed) - происходит смена текущего кадра на следующий в списке. Анимация считается полностью выполненной, когда время проигрывание последнего кадра истекло. Если пользователь класса задал количество проигрываний больше одного - все повторяется с начала. Как только анимация проигралась положенное количество раз, происходит остановка, которую, в прочем, можно вызвать и вручную. Также можно задать проигрывать анимацию бесконечное число раз. Реализация остальных методов класса сводится к изменению значения различных переменных.
Использовать такую конструкцию чрезвычайно просто: на этапе инициализации нужно установить скорость (метод set_speed), установить режим видимости (метод show) и включить проигрывание. Изменить эти параметры можно в любой момент в любом участке программы. В главном цикле необходимо добавить вызов draw.
Этот класс мы будем использовать как напрямую, так и в качестве вспомогательного в других конструкциях, о которых мы будем говорить в следующих выпусках.

Самое время заняться менеджером ресурсов. Многие из вас, внимательно просмотревшие исходный код этого выпуска, наверняка заметили, что в деструкторе класса Animation мы не удаляем изображение Image. Из прошлого выпуска известно, что объект Image является одним из видов ресурсов. Для управления ресурсами мы создадим наш менеджер Library. И так, основные задачи и свойства нашего менеджера ресурсов:

  • Существует в единственном экземпляре
  • Загружает ресурсы и управляет участками памяти, занятыми под ресурсы
  • Автоматически (без участия пользователя) освобождает память при уничтожении объекта
  • Возвращяет указатель на ресурс, тем самым экономя память под идентичные ресурсы
  • Каждый ресурс имеет уникальное строковое имя, по которому происходит его идентификация в менеджере ресурсов
Для текущей стадии нашего движка доступен один вид ресурсов - Image. Для удобного учета ресурсов очень подходит STL-контейнер std::map, в качестве ключа к которому используется уникальный строковый идентификатор. В этом контейнере мы будем хранить указатели типа Resource*. Напомню, что все ресурсные классы должны быть наследованы от Resource, тем самым не заботясь о конкретном типе, который может скрываться за указателем на базовый класс:
std::map<std::string, Resource* > resources_;
Наш единственный на текущий момент ресурс - изображение - принимает для инициализации несколько параметров (имя файла, количество колонок, столбцов и общее число кадров). Для корректной загрузки и инициализации изображений, необходимо отдельно хранить описание каждого ресурса в конфигурационном файле. Очень удобно использовать XML, но в этом выпуске у нас всего-лишь обычный текстовый файл с незатейливым синтаксисом вот такого вида:
# комментарий:
# filepath rows cols frames
image.png 2 4 8
Мы скорей всего перейдем к XML, но в следующих выпусках, т.к. не хотелось бы перегружать выпуск.
Для обеспечения существования менеджера ресурсов в единственном экземпляре очень хорошо подходит паттерн Singleton, который элементарно реализуется: статическая функция, возвращяющая указатель или ссылку на единственный экземпляр класса:
Library& Library::instance()
{
        static Library lib;
        return lib;
}
Таким образом, обращение к любому методу класса Library происходит сделующим образом:
Library::instance().any_method();
Функция instance возвращает ссылку на объект. Поскольку наш класс Animation является лишь надстройкой над Image, удобно воспользоваться так называемым фабричным методом для его создания. Ознакомиться с менеджером ресурсов подробнее можно из листинга его определения:
class Library
{
public:
        static Library& instance();
        ~Library();
        
        bool load_resources(const std::string& filepath);
        
        void release_resources();

        Image* get_image(const std::string& image_name);
        
        Animation* get_animation(const std::string& image_name);
        
private:
        Library();

private:
        typedef std::map<std::string, Resource* > storage_type;
        storage_type resources_;
};
Метод load_resources принимает в качестве параметра имя файла с описанием всех изображений-ресурсов. Освободить память, занимаемую этими ресурсами можно при помощи метода release_resources. Для получения указателя на изображение по его уникальному имени (в нашем случае - имени файла) служит метод get_image. Фабричный метод get_animation, для быстрого создания анимации, мы рассмотрели чуть выше.
Реализация класса совсем несложная. При загрузке, мы считываем строки с описанием изображений и на основе этой информации конструируем объект Image, изображение которого загружается в память методом load. Для данного случая с текстовым конфигурационным файлом используется sscanf.
Для поиска ресурса в хранилище по его имени используется метод std::map::find, хорошо описанный в любой доступной документации к STL. Поскольку в хранилище содержаться указатели на базовый класс Resource, удобно использовать оператор dynamic_cast языка C++, который идеально подходит для данного случая (преобразование типа от базового класса к конкретному наследнику). Можно также использовать reinterpret_cast (который преобразует что угодно во что угодно), но это не совсем корректно логически.

Пожалуй, достаточно для очередного выпуска рассылки. Как обычно, рекомендую загрузить исходный код для этого выпуска, поскольку без него очень сложно разобраться. Кроме того, рекомендую почитать документацию STL-контейнера std::map, так как он активно используется в менеджере ресурсов.
Должен заметить, что многие возможно не согласятся с некоторыми решениями нашего движка. Но это всего лишь один из множества распространенных вариантов архитектуры. Если у вас есть предложения, замечания или мысли по этому поводу, с огромным удовольствием можем обсудить это в форуме. Также, хотелось бы узнать мнение читателей насчет материала последних выпусков. Возможно кто-то имеет достаточно большой опыт и ему не интересна данная тема. А к новичкам одна большая просьба: пожалуйста расскажите насколько понятно изложение материала.
С нетерпением жду следующей встречи!
До свидания.


http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.plg
Отписаться

В избранное