ANDROIDGRAPHICS І ANDROIDPIXMAP: ПОДВІЙНА ВЕСЕЛКА

&nbsp

Отже, повертаємося до нашої найулюбленішою темою: програмування графіки

Однак є ще один аспект, вивчення якого ми відклали до поточного моменту, а саме: що робити з екранами різного розміру і дозволу

Обробка різних розмірів екрану і дозволів

Android підтримує різні дозволи екрану починаючи з версії 16 Вона може обробляти дозволу від 240 х 320 до 480 х 854 пікселя на деяких нових пристроях (в книжкової та альбомної орієнтації показники міняються місцями) Ми вже бачили ефект від застосування різних дозволів екрану і його фізичних розмірів: малювання з урахуванням абсолютних координат і розмірів у пікселях може привести до самих різних результатів

На рис 51 зображено, що відбувається, коли ми визуализируем прямокутник 100 х 100 пікселів, верхній кут якого знаходиться в точці (219 379) на екранах 480 х 800 і 320 х 480

Рис 51 Прямокутник 100 х 100 пікселів, верхній кут якого знаходиться в точці (219 379) на екранах 480 х 800 (ліворуч) і 320 х 480 (праворуч)

Така різниця нас не влаштовує з двох причин Ми не можемо написати нашу гру для якогось конкретного дозволу Друга причина не так очевидна Справа в тому, що на рис 51 я прийняв за аксіому, що у обох екранів однакова щільність (тобто кожен піксель має однаковий фізичний розмір на обох пристроях), але в реальності такого не буде

Щільність

Як правило, щільність визначається в пікселах на дюйм або пікселах на сантиметр (іноді ви можете зустріти вираз точка на дюйм, але це не зовсім вірно) У Nexus One екран 480 х 800 пікселів при фізичному розмірі 8 х 4,8 см НТС Нього має екран 320 х 480 пікселів при фізичному розмірі 6,5 х 4,5 см Таким чином, 100 пікселів на сантиметр по обох осях в Nexus One – це приблизно 71 піксель на сантиметр по обох осях в НТС Нього Ми можемо легко підрахувати пікселі на сантиметр за допомогою наступного рівняння:

Піксели на сантиметр (по осі х) = ширина в пікселах / ширина в сантиметрах або цього:

Піксели на сантиметр (по осі у) = висота в пікселах / висота в сантиметрах

Як правило, достатньо підрахувати пікселі на сантиметр тільки під одним осі, так як пікселі мають квадратну форму (якщо чесно, тут всього три пікселя, але ми зараз не про це)

Якого розміру буде наш прямокутник 100 х 100 пікселів в сантиметрах На Nexus One у нас буде прямокутник 1×1 см, а на НТС Нього – 1,4 х 1,4 см

Памятайте про це, коли будете малювати такі обєкти, як кнопки, які повинні бути досить великими, щоб на кнопку можна було натиснути пальцем і це легко відбувалося на екранах різного розміру Хоча на перший погляд здається, що дана особливість може становити велику проблему для розробника, насправді це не так Все, що потрібно, – упевнитися, що кнопки мають досить великий розмір на екранах з великою щільністю, таких як Nexus One Вони будуть автоматично збільшуватися на екранах з меншою щільністю

Співвідношення сторін

Ще одна проблема, з якою нам належить впоратися, – співвідношення сторін Співвідношення сторін екрану – це відношення між шириною і висотою екрану в пікселах або сантиметрах Воно підраховується наступним чином:

Співвідношення сторін в пікселах = ширина в пікселах / висота в пікселах або так: Фізичне співвідношення сторін = ширина в сантиметрах / висота в сантиметрах

Вживаючи слова ширина і висота, ми, як правило, говоримо про ширині і висоті в ландшафтній орієнтації Співвідношення сторін в Nexus One, фізичне і в пікселах, приблизно дорівнює 1,66 У НТС Нього фізичне і піксельне співвідношення сторін дорівнює 1,5 Що це означає На Nexus One ми маємо в своєму розпорядженні більше пікселів по осі х в альбомної орієнтації в порівнянні з тією висотою, яка доступна на НТС Нього На рис 52 показано, як виглядає гра Replica Island на обох пристроях

Рис 52 Replica Island на Nexus One (зверху) і НТС Нього (знизу)

ПРИМІТКА

У цій е використовується метрична система

На Nexus One трохи більше огляд по осі х Однак по осі у все виглядає точно так само Що ж творці Replica Island тут зробили

Як справлятися з різними співвідношеннями сторін

Replica Island використовує досить просту, але дуже ефективну хитрість для того, щоб впоратися з проблемою співвідношення сторін Спочатку гра створювалася для екрану 480 х 320 пікселів, включаючи всі спрайт (наприклад, робота і доктора), сам світ і елементи призначеного для користувача інтерфейсу (наприклад, кнопки внизу зліва та інформацію про статус вгорі екрану) Коли гра відображається на НТС Нього, кожен піксель спрайта в растровому відображенні відповідає одному пикселу на екрані На Nexus One під час візуалізації все масштабується, так що 1 піксель спрайта в растровому відображенні відповідає 1,5 пікселя на екрані Іншими словами, персонаж 32 х 32 пікселя буде на екрані мати розмір 48 х 48 пікселів Коефіцієнт масштабування можна підрахувати за допомогою наступних формул:

Коефіцієнт масштабування (по осі х) = ширина екрану в пікселах / цільова ширина в пікселах і Коефіцієнт масштабування (по осі у) = висота екрана в пікселах / цільова висота в пікселах

Цільові ширина і висота дорівнюють дозволу екрану, для якого було розроблено додаток Так, наприклад, в Replica Island це 480 х 320 пікселів У випадку з Nexus One це означає, що коефіцієнт масштабування по осі х дорівнює 1,66, а по осі у – 1,5 Але чому коефіцієнт масштабування на різних осях різний

Справа в тому, що екрани з різними дозволами мають різні співвідношення сторін Якщо ми просто растянем зображення 480 х 320 пікселів до зображення в 800 х 480 пікселів, оригінальна картинка буде розтягнута по осі х Для більшості ігор це буде некритично, так що ми просто намалюємо потрібні графічні обєкти для певного дозволу і растянем їх до реального дозволу екрану під час візуалізації (Згадайте метод BitmapdrawBitmap)

Для деяких ігор проте доведеться трохи попрацювати На рис 53 показана просто отмасштабовані з 480 х 320 до 800 х 480 пікселів Replica Island Крім того, накладено напівпрозоре зображення того, як це виглядає насправді

Творці Replica Island використовували тут вельми тонкий прийом: ми бачимо нормальне розтягнення по у-осі з коефіцієнтом масштабування, який ми тільки що вирахували (1,5) Але замість того, щоб використовувати коефіцієнт масштабування (1,66), який зробить зображення більш стислим, тут застосовується коефіцієнт масштабування у-осі Подібна виверт дозволяє всім обєктам на екрані зберегти співвідношення сторін Спрайт розміром 32 х 32 пікселя збільшується до 48 х 48 пікселів, а не 53 х 48 пікселів Проте це також означає, що наша система координат більше не обмежується (0, 0) і (479 319) Замість цього тепер її кордону (0, 0) і (533 319) Саме тому ми бачимо більше ігрового простору Replica Island на Nexus One, ніж на НТС Нього

Рис 53 Replica Island, розтягнута з 480 х 320 до 800 х 480 пікселів з накладеним напівпрозорим зображенням того, як це виглядає насправді на екрані 800 х 480

Зверніть увагу, що в деяких іграх даний метод непридатний Наприклад, залежність розміру світу від співвідношення сторін екрану може призвести до несправедливого перевазі власників більш широких екранів Наприклад, даний метод непридатний для таких ігор, як Starcraft 2 Однак якщо ви хочете помістити весь світ гри на одному екрані (як в Містері Номі), краще використовувати простий метод розтягування, так як з складнішою версією у нас будуть порожні зони з боків на більш широких екранах

Рішення попроще

У Replica Island є одна перевага: тут розтягнення і масштабування виконані за допомогою OpenGL ES, яке підтримує апаратне прискорення Поки ми обговорили тільки, як намалювати Bitmap і View за допомогою класу Canvas, який не використовує графічний процесор, але застосовує більш повільний в таких ситуаціях центральний процесор

Тепер застосуємо невелику хитрість: створимо фреймбуфер у вигляді екземпляра класу Bitmap, що має потрібне нам дозвіл Таким чином, нам не доведеться хвилюватися про реальний дозволі екрану, коли ми будемо створювати наші графічні обєкти або коли будемо їх візуалізувати Давайте припустимо, що дозвіл екрана однаково на всіх пристроях Коли ми закінчимо візуалізацію фрейма нашої гри, ми просто намалюємо цей фреймбуфер Bitmap розміром з SurfaceView за допомогою методу Canvas drawBitmap, який дозволяє зобразити розтягнутий Bitmap

Якщо ми хочемо використовувати той же прийом, що і Replica Island, просто треба підлаштувати розмір фреймбуфер по більшій осі (наприклад, по осі х в ландшафтній орієнтації і по осі у в портретної) Необхідно також упевнитися, що ми заповнили додаткові пікселі, щоб не було порожніх проміжків

Реалізація

Підібємо проміжні підсумки, склавши план дій

Створюємо графічні обєкти для фіксованого цільового дозволу (320 х 480 у випадку з Містером номом)

Створюємо Bi tmap такого ж розміру, як і наше цільове дозвіл, і направляємо всі виклики отрисовки до нього, працюючи у фіксованій системі координат

Коли закінчимо малювати кадр ігри, малюємо фреймбуфер Bi tmap, розтягнутий до розміру SurfaceView На пристроях з меншим дозволом масштаб зображення зменшиться, на пристроях з великою роздільною здатністю – Збільшиться

При масштабуванні переконаємося, що всі елементи інтерфейсу, з якими взаємодіє користувач, досить великі на екранах різної щільності Це можна зробити на стадії графічного дизайну обєктів, застосовуючи розміри реальних пристроїв разом з формулами, розглянутими вище

Тепер, коли ми вже знаємо, як поводитися з різними дозволами та плотностями екрану, можна обговорити змінні sealе Х і seale Y, з якими ми зустрічалися, коли реалізовували обробники Si ngl eTouchHandl ег і Mul ti touchhandl ег кілька сторінок тому

Весь код нашої гри буде орієнтований на роботу з фіксованим дозволом (320 х 480 пікселів) Якщо ми отримуємо події торкання на пристроях, які мають більш високу або більш низький дозвіл, х-та /-координати даних подій будуть описані в системі координат Vi ew, а не в системі координат нашого цільового дозволу Таким чином, необхідно перевести координати з оригінальної системи в нашу систему, засновану на коефіцієнті масштабування Ось як це можна зробити:

Перетворене торкання по осі х = реальне дотик по осі х (цільове кількість пікселів по осі х / реальна кількість пікселів по осі х)

Перетворене торкання по осі у = реальне дотик по осі у (цільове кількість пікселів по осі у / реальна кількість пікселів по осі у)

Розглянемо простий приклад, де цільове дозвіл одно 320 х 480 пікселів, а дозвіл пристрою – 480 х 800 пікселів Якщо ми торкнемося до середини екрану, то одержимо координати (240 400) Використовуючи дві попередні формули, отримуємо наступні дані, які є серединою нашої цільової системи координат:

Перетворене торкання по осі х = 240 (320/480) = 160 Перетворене торкання по осі у = 400 (480/800) = 240

Вирішимо ще один приклад, в якому реальний дозвіл одно 240 х 320, а екрану знову торкнулися в середині (120 160):

Перетворене торкання по осі х = 120 (320/240) = 160 Перетворене торкання по осі у = 160 (480/320) = 240

Як бачите, даний прийом працює в обох напрямках Якщо ми помножимо координати реальної події торкання на цільовий коефіцієнт, розділений на реальний коефіцієнт, нам не доведеться відволікатися на всі ці перетворення в коді гри Всі координати торкань будуть відображені в нашій фіксованою цільовій системі координат

Оскільки ми впоралися і з цією проблемою, прийшов час реалізувати останні кілька класів фреймворка нашої гри

AndroidPixmap: пікселі для кожного

Згідно з проектом інтерфейсу нашого Pixmap нам залишилося реалізувати не так вже багато Розглянемо код в лістингу 512

Лістинг 512 AndroidPixmapJava, реалізація Pixmap package combadogiсandroidgamesframeworkimp

Все, що нам потрібно зробити, – зберегти екземпляр класу Bitmap, а також його формат, який зберігається у вигляді одного з значень перерахування PixmapFormat Додатково ми реалізуємо необхідні методи інтерфейсу Pixmap, щоб можна було запитувати ширину і висоту Pixmap і його формат, а також упевнитися, що пікселі витягуються з оперативної памяті Зверніть увагу, що поле bitmap є приватним, так що у нас є до нього доступ з Androi dGraphics, який ми зараз і реалізуємо

AndroidGraphics: те, що потрібно для малювання

Інтерфейс Graphi cs, досить компактний і ефективний Він може малювати пікселі, лінії, прямокутники і Pi xmap в фреймбуфер Як ми вже говорили, ми будемо використовувати Bitmap в якості нашого фреймбуфер і малювати на ньому за допомогою Canvas Він також відповідає за створення екземплярів класу Pi xmap з файлу ресурсів Таким чином нам знову знадобиться AssetManager У лістингу 513 показаний код реалізації інтерфейсу Androi dGraphics

Лістинг 513 AndroidGraphicsJava: реалізуємо графічний інтерфейс package combadlogiсandroidgamesframeworkimp

Даний клас реалізує інтерфейс Graphics Він містить член AssetManager, який нам потрібен для того, щоб завантажувати екземпляри Bitmap, член Bitmap, що представляє собою штучний фреймбуфер, член Canvas, використовуваний для того, щоб намалювати штучний фреймбуфер, член Paint, який необхідний для малювання, і два члени Rect, які нам знадобляться для реалізації

AndroidGraphicsdrawPixmapO Останні три члена використовуються для того, щоб не створювати нові екземпляри класів при кожному виклику методу малювання Ми ж не хочемо, щоб збирач сміття збожеволів

У конструкторі отримуємо AssetManager і Bitmap, які представляють наш штучний фреймбуфер Зберігаємо їх у відповідних полях і додатково створюємо екземпляр класу Canvas, який буде малювати в штучному фреймбуфер, а також Paint, який ми використовуємо для деяких методів малювання

Метод newPixmapO завантажує Bitmap з файлу обєктів, використовуючи заданий PixmapFormat Ми починаємо з того, що переводимо PixmapFormat в одну з констант класу Android Config Далі створюємо екземпляр класу Options і встановлюємо бажаний формат кольору Після цього завантажуємо Bi tmap з ресурсу за допомогою BitmapFactory Якщо щось йде не так, генерується виключення Runt imeExcept ion В іншому випадку ми перевіряємо, в якому форматі фабрика BitmapFactory вирішила завантажити Bitmap, і переводимо його в значення перерахування PixmapFormat Памятайте, що BitmapFactory може вирішити ігнорувати наш бажаний формат кольору, так що необхідно буде перевірити, як вона закодувала зображення Нарешті ми створюємо новий екземпляр класу AndroidBitmap, заснований на примірнику Bitmap, який ми завантажили, і PixmapFormat, а потім повертаємо цей новий екземпляр викликає стороні

Метод clearO просто витягує червоний, зелений і синій компоненти з певного 32-бітного ARGB колірного параметра і викликає метод Canvas drawRGB, який очищає наш штучний фреймбуфер з цим кольором Цей метод не враховує ніякі альфа-значення певного кольору, так що нам не треба його витягати

Метод drawPixel малює піксел в нашому штучному фреймбуфер за допомогою методу Canvas drawPoint О Для початку встановлюємо колір в поле класу paint і передаємо ці дані методу малювання на додаток до х-і у-координатами пікселя

Метод drawLi ne малює лінію в штучному фреймбуфер, також використовуючи поле paint, щоб задати колір, який буде застосовуватися при виклику Canvas drawLine

Метод drawRect спочатку встановлює колір в Paint і атрибут стилю, щоб ми могли намалювати заповнений кольором прямокутник Для самого виклику Canvas drawRect нам буде потрібно перетворити параметри х, у, width і height в координати верхнього лівого і нижнього правого кутів прямокутника Для верхнього лівого кута просто використовуємо параметри х та у Для координат нижнього правого кута додаємо ширину і висоту до х і в і віднімаємо 1 Для прикладу уявіть, що ми визуализируем прямокутник, де х і у мають координати (10 10), а ширина і висота дорівнюють 2 Якщо ми не віднімемо 1, в результаті прямокутник на екрані буде розміром 3×3 пікселя

Метод drawPixmap, що дозволяє намалювати частина Pixmap, спочатку встановлює вихідний прямокутник і прямокутник призначення у відповідні поля, які використовуються для виклику методу малювання Оскільки ми малюємо прямокутник, нам буде потрібно перевести координати х і у, а також ширину і висоту в координати лівого верхнього і правого нижнього кутів Ми знову віднімаємо 1, інакше у нас вийде один зайвий піксель Далі виконуємо малювання за допомогою методу Canvas DrawBitmap, який також автоматично виконає змішування, якщо Pixmap, який ми малюємо, має глибину кольору PixmapFormat ARGB4444 або PixmapFormatARGB8888 Зверніть увагу, що нам необхідно привести параметр типу Pixmap до типу AndroidPixmap, щоб ми могли вибрати член bitmap для малювання за допомогою Canvas Так робити не рекомендується, але в даному випадку ми можемо бути впевнені що переданий примірник Pixmap дійсно представлятиме собою AndroidPi xmap

Другий метод drawPixmap просто малює весь Pixmap в штучному фреймбуфер в заданих координатах Ми знову використовуємо приведення для того, щоб дістатися до члена bitmap класу AndroidPixmap

Нарешті, у нас є методи getWidth і getHei ght, які просто повертають розмір штучного фреймбуфер, що зберігає і відображає екземпляр класу Androi dGraphi cs

Джерело: Mario Zechner / Маріо Цехнер, «Програмування ігор під Android», пров Єгор Сидорович, Євген зазноби, Видавництво «Пітер»

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*