Главная » Программирование игр под Android » 2D-ПРЕОБРАЗОВАНИЯ: МАТРИЦА МОДЕЛЬ – ПРЕДСТАВЛЕНИЕ – РАЗРАБОТКА ИГР ДЛЯ ОС ANDROID

0

 

До этого мы описывали статическую геометрию в виде списка треугольников. Здесь не было ни передвижения, ни поворотов, ни изменения масштаба. Однако даже когда сами параметры вершины оставались прежними (например, не изменялись ширина и высота прямоугольника, состоящего из двух треугольников вместе с координатами текстуры и цвета), нам по-прежнему приходилось создавать копию вершин, если мы хотели нарисовать такой же прямоугольник в другом месте. Посмотрите снова на листинг 7.11 и пока не учитывайте цветовые атрибуты вершин. Два прямоугольника различаются только значением у-координаты – на 200 единиц. Если бы мы могли передвигать эти вершины без фактического изменения их значений, мы могли бы описать прямоугольник Боба только один раз и просто отрисовывать его в разных местах. И это как раз то, для чего мы будем использовать матрицу Модель – представление.

Пространство мира и модели

Чтобы понять, как это работает, нам нужно воспринимать ортографический отображаемый конус немного по-другому. Он располагается в особой системе координат, которая называется пространством minа. Это пространство, куда в итоге будут попадать все наши вершины.

До этого момента мы описывали местоположение всех вершин в абсолютной системе координат, относящейся к области мира, с которым мы работаем (сравните с рис. 7.5). На самом деле мы хотим сделать определение местоположения независимым от системы координат области мира. Этого можно достичь, приписав каждой из наших моделей (например, прямоугольник Боба, космический корабль и т. д.) свою отдельную систему координат.

Это то, что мы обычно называем областью модели, системой координат, внутри которой мы описываем местоположение вершин модели. На рис. 7.19 показана эта концепция в 2D. Это также верно для 3D (просто добавьте ось z).

Рис. 7.19. Описание нашей модели в области модели, повторное использование и визуализация в разных местах области мира

На рис. 7.19 изображена одна модель, описанная с помощью экземпляра класса Vertices, к примеру, следующим образом:

В данном контексте мы пока опустим координаты цвета и текстуры. Теперь, когда мы визуализируем эту модель без каких-либо последующих изменений, она будет размещена в начале координат мира на конечном изображении. Если мы хотим визуализировать ее в другом месте, например, чтобы ее центр был в точке (200; 300) области мира, мы можем повторно описать положение вершин следующим образом:

При следующем вызове verti ces. draw модель будет визуализирована с центром в точке (200; 300). Но так работать немного неудобно, не правда ли?

 

Снова матрицы

Помните, мы вкратце уже говорили о матрицах? Мы обсуждали, как матрицы могут задавать такие трансформации, как перемещение, поворот и изменение размеров. Проекционная матрица, которую мы используем для переноса наших вершин на плоскость проекции, задает особый тип преобразования: проекцию.

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

Однако сначала нужно сообщить OpenGL ES, какой матрицей мы хотим управлять. В нашем случае это матрица Модель – представление, которая определена константой GL10 .GLMODELVIEW. Затем мы проверяем, установлена ли единичная матрица в качестве модельно-видовой. Фактически мы просто перезаписываем все, что уже было, – просто очищаем матрицу. Обратите внимание на вызов, описанный далее, – здесь происходит самое интересное.

Метод glTranslatef принимает три аргумента: перемещение по осям х у иг. Поскольку мы хотим, чтобы начало координат модели было перемещено в точку (200; 300) наблюдаемого пространства мира, мы задаем перемещение на 200 единиц по оси х и перемещение на 300 единиц по оси у. Поскольку мы работаем в 2D, просто игнорируем ось z и устанавливаем компонент перемещения равным нулю. Мы не описывали z-координату для наших вершин, так что она по умолчанию будет равна нулю. Если прибавить к нулю ноль, все равно получится ноль, так что наши вершины останутся в плоскости ху.

С этого момента матрица Модель – представление OpenGL ES кодирует перемещение на (200; 300; 0), которое будет применено ко всем вершинам, проходящим через конвейер OpenGL ES. Если вы снова посмотрите на рис. 7.4, то увидите, что OpenGL ES просто перемножит каждую вершину сначала на матрицу Модель – представление, а затем применит к вершинам проекционную матрицу. До сих пор матрица Модель – представление представляла собой единичную матрицу (настройка OpenGL ES по умолчанию). Следовательно, она не влияла на наши вершины. Наш небольшой вызов gl Trans. atef  спровоцирует эти изменения и переместит все вершины перед тем, как они будут спроецированы.

Конечно, все это делается динамически, а значения в экземпляре класса Vertices не изменяются вообще. Мы бы заметили какие-либо постоянные изменения в экземпляре класса Vertices, поскольку проекционная матрица уже бы его изменила.

Первый пример с использованием переноса

Для чего нам требуется перенос? Допустим, мы хотим визуализировать 100 копий Боба в разных местах мира. Более того, нам нужно, чтобы они двигались по экрану и изменяли направление каждый раз, когда столкнутся с границами экрана (или, вернее, границами нашей параллельной проекции конуса отображения, которые совпадают с границами экрана). Это можно было бы сделать с помощью одного большого экземпляра класса Verti ces, который содержит вершины 100 прямоугольников – одного для каждого Боба – и пересчитывает местоположение вершин для каждого кадра. Проще было бы использовать небольшой экземпляр класса Verti ces, содержащий только один прямоугольник (модель Боба) и применяющий его многократно, перенося его по матрице Модель – представление. Опишем нашу модель Боба:

Размер каждого Боба – 32 х 32 единицы. Мы также ассоциируем его с текстурой (будем использовать bobrgb888.png, чтобы видеть границы каждого Боба).

Боб становится классом

Опишем простой класс Bob, который будет отвечать за местоположение Боба и его перемещение в текущем направлении, основываясь на дельте времени, так же, как мы передвигали мистера Нома (единственная разница состоит в том, что мы больше не передвигаемся по сетке). Метод update будет следить за тем, чтобы Боб не вышел за видимые границы. В листинге 7.12 показан класс Bob.

Листинг 7.12. Bob.java

Каждый Боб помещается в случайное место в созданном нами мире. Все Бобы будут изначально двигаться в одинаковом направлении: 50 единиц направо и 50 единиц вверх в секунду (поскольку происходит умножение на deltaTlme). В методе update  просто перемещаем Боба в текущем направлении, основываясь на времени, а затем проверяем, покинул ли он границы конуса отображения. Если покинул, меняем направление и проверяем, находится ли он по-прежнему внутри конуса отображения.

Теперь предположим, что мы создаем 100 Бобов следующим образом:

Для визуализации каждого Боба делаем следующее (допустим, мы уже очистили экран, установили матрицу проекции и привязали текстуру):

Все достаточно просто, не правда ли? Для каждого Боба вызываем его метод updateO, который будет перемещать его и проверять, остался ли он в границах нашего маленького мира. Затем загружаем единичную матрицу в модельно-видовую матрицу OpenGL ES, чтобы у нас всегда было чистое состояние. Затем используем текущие х- и у-координаты Боба в вызове glTransltef . Когда мы затем визуализируем модель Боба в следующем вызове, все вершины будут перенесены относительно текущего месторасположения Боба – именно этого мы и добивались.

Все вместе

Сделаем общий пример (листинг 7.13). Листинг 7.13. BobTest.java; 100 Moving Bobs.

Наш класс BobScreen содержит текстуру Texture (загруженную из bobrbg888. png), экземпляр класса Vertices, который содержит модель Боба (простой текстурированный прямоугольник) и массив экземпляров класса Боб. Мы также определяем константу NUMB0BS, чтобы можно было изменять количество Бобов на экране.

Конструктор просто загружает текстуру и модель и приписывает константу NUM B0BS экземплярам класса Bob.

В методе update Бобы обновляются. Мы также проверяем, очищены ли буферы событий ввода.

В методе render  очищаем экран, устанавливаем проекционную матрицу, активируем текстурирование и привязываем текстуру Боба. Последние две строки отвечают за визуализацию каждого экземпляра класса Bob. Поскольку OpenGL ES запоминает их состояния, нам нужно устанавливать активную матрицу только один раз (в этом случае мы будем изменять матрицу Модель – представление в оставшейся части кода). Затем мы проходим по всем Бобам, устанавливаем матрицу Модель – представление, основываясь на текущем положении Боба, и визуализируем модель, которая будет автоматически смещена по матрице Модель – представление.

Вот и все. Лучше всего то, что мы снова использовали паттерн MVC, который уже известен нам из работы с Мистером Номом. Он действительно очень удобен при программировании игр. Логическая сторона Боба полностью отделена от его внешнего вида, что весьма неплохо, так как мы можем легко заменить его вид чем-то посложнее. На рис. 7.20 показан результат работы нашей программы после нескольких секунд работы.

Рис. 7.20. Очень много Бобов

Но на этом интересности с преобразованиями не заканчиваются. Помните, несколько страниц назад я упоминал, что мы еще поговорим о вращении и масштабировании?

Другие преобразования

Кроме метода gl Trans 1 atef  OpenGL ES также предлагает два других метода для преобразований: glRotatefO и glScalefO.

Вращение

Вот сигнатура метода glRotatef :

Первый параметр – это угол в градусах, на который мы хотим повернуть вершины. Но что же значат остальные параметры?

Когда мы что-то поворачиваем, делаем это по какой-либо оси. Что такое ось? Мы уже знаем три оси: х, у и z. Мы можем выразить эти три оси в виде векторов. Положительная ось х будет описываться как (1; 0; 0), положительная ось у – (0; 1; 0), а положительная ось z – (0; 0; 1). Как вы можете видеть, вектор фактически кодирует направление, в нашем случае в 3D -пространстве. Направление Боба – это тоже вектор, но в 2D -пространстве. Векторы также кодируют местоположение, например место Боба в двухмерном пространстве.

Чтобы описать ось, по которой будем поворачивать модель Боба, нужно вернуться к 3D-пространству. На рис. 7.21 показана модель Боба (с наложенной текстурой для ориентации), как она описана в 3D с помощью предыдущего кода.

Рис. 7.21. Боб в 3D

Поскольку мы не описали z-координаты для вершин Боба, он находится в х у- пространстве нашего 3D -мира, который фактически является пространством модели. Если мы захотим повернуть Боба, можем сделать это по любой оси (х, у, z) или даже по каким-то невообразимым осям, например с координатами (0,75; 0,75; 0,75). Тем не менее в случае с нашим графическим программированием в двух измерениях целесообразно вращать Боба в плоскости ху. Следовательно, мы будем использовать положительную ось z, которая может быть описана как (0; 0; 1) в качестве оси вращения. Поворот будет проходить против часовой стрелки по j оси z. Вызов glRotatef  приведет к тому, что вершины модели Боба будут повернуты так, как это показано на рис. 7.22:

Рис. 7.22. Боб, повернутый по оси z на 45°

Масштабирование

Мы также можем изменить размеры Боба с помощью glScalef следующим образом:

Исходя из начальной позиции Боба, мы получим ориентацию, как на рис. 7.23.

Рис. 7.23. Боб, размеры которого изменены в 2 раза по оси х и в 0,5 раза по оси у

Сочетание преобразований

Мы можем сочетать эффекты множества матриц, перемножая их, чтобы получить новую матрицу. Фактически все это делают методы glTransl atef , glScalefO, gl Rotatef  nglOrthof О. Они умножают текущую активную матрицу навременную матрицу, которую создают внутрисистемно, основываясь на параметрах, которые мы им передаем. Соединим поворот и изменение размеров Боба:

В результате модель Боба будет выглядеть, как на рис. 7.24 (помните, мы по-прежнему в пространстве модели).

Рис. 7.24. Боб, сначала измененный в размере, а затем повернутый

Что случится, если мы применим изменения в другом порядке:

На рис. 7.25 показан результат.

Рис. 7.25. Боб, сначала повернутый, а затем измененный в размерах

Ого, это не тот Боб, к которому мы привыкли. Что же здесь произошло? Исходя из кода, логично предположить, что рис. 7.24 и 7.25 будут выглядеть одинаково. В первом фрагменте мы сначала производим поворот, а затем изменяем размеры Боба, правильно?

Нет, неправильно. Порядок, в котором преобразования применяются к модели, соответствует порядку, в котором OpenGLES перемножает матрицы. Последняя матрица, на которую мы умножаем текущую активную матрицу, будет первой, которая будет применена к вершинам.

Если мы хотим изменить размер, повернуть и перенести Боба именно в таком порядке, нам нужно вызвать методы следующим образом:

Изменим цикл в нашем методе BobScreen.presents вот так:

Результат будет выглядеть, как на рис. 7.26.

Рис. 7.26. Сто Бобов, измененных в размере, повернутых и перемещенных (в таком порядке)

Когда я только начинал работать с OpenGL, я постоянно путал порядок операций с матрицами. Чтобы запомнить, как это правильно делать, я изобрел мнемоническое слово ПООПЕП: последним описан, первым применен (да, мне есть еще над чем работать в мнемонике).

Самый простой способ научиться правильно работать с модельно-видовыми преобразованиями – использовать их как можно чаще. Я предлагаю вам взять исходный файл BobTest. java и немного по изменять внутренний цикл для того, чтобы посмотреть различные эффекты.

Обратите внимание, что для визуализации каждой модели вы можете описать столько изменений, сколько захотите. Добавьте повороты, перемещения и изменения размеров. Поиграйте.

После рассмотрения этого примера мы теперь знаем практически все, что нужно, об OpenGL ES для написания игр 2D. Или нет?

Источник: Mario Zechner / Марио Цехнер, «Программирование игр под Android», пер. Егор Сидорович, Евгений Зазноба, Издательство «Питер»

По теме:

  • Комментарии