Просторова сітка – РОЗРОБКА ІГОР ДЛЯ ОС ANDROID

Наша гармата буде обмежена прямокутником розміром 1 х 1 м обмежує прямокутник гарматного ядра буде розміром 0,2 х 0,2 м, а біля мішеней будуть обмежують прямокутники по 0,5 х 0,5 м кожен Щоб усе стало трохи простіше, обмежують прямокутники центровані щодо позиції кожного обєкта

Коли ми запускаємо наш приклад з гарматою, ми просто маємо в своєму розпорядженні деяку кількість мішеней в довільних позиціях Ось як ми можемо розмістити обєкти в нашому світі:

Константи WORLDWIDTH і WORLDHEIGHT визначають розміри ігрового світу Всі дії повинні відбуватися в прямокутнику, описаному координатами (0, 0) і (W0RLD WIDTH, WORLDHEIGHT) На рис 817 показаний невеликий макет нашого ігрового світу

Рис 817 Макет нашого ігрового світу

Так наш світ буде виглядати пізніше, але поки накладемо на нього просторову сітку Наскільки великими мають бути осередки просторової сітки Тут немає однозначної відповіді, але я зазвичай вибираю такий розмір, щоб осередок була в пять разів більше найбільшого обєкта в кадрі У нашому прикладі найбільший обєкт – це гармата, але з гарматою ми нічого не зіштовхуємо Тому краще визначити розмір комірки сітки згідно з наступними за величиною обєктами – мішенями Мішені у нас розміром 0,5 х 0,5 м Тоді осередок сітки повинна мати розмір 2,5 х 2,5 м На рис 818 показана сітка, накладена на наш світ

Рис 818 Наш ігровий світ, на який накладено просторова сітка з 12 осередків

У нас є певна кількість осередків – у випадку з цим гарматним світом їх 12 Ми присвоюємо кожному осередку унікальний номер, починаючи з нижньої лівої, яка отримує ID 0 Зверніть увагу, що верхні осередки виходять за межі нашого світу Це не проблема варто тільки переконатися, що всі обєкти нашого світу знаходяться в його межах Ми хочемо визначити, до якої комірці або осередкам належить обєкт В ідеалі ми хочемо обчислити ID осередків, в яких міститься обєкт Це дозволяє нам використовувати просту структуру даних для зберігання осередків:

Так, саме, ми описуємо кожну клітинку як список класів GameObject Сама просторова сітка, таким чином, являє собою масив списків класів GameObject

Давайте подумаємо, як ми можемо визначити ID осередків, в яких знаходиться обєкт На рис 818 показані кілька мішеней, які охоплюють два осередки Насправді маленький обєкт може розташовуватися одночасно в 4 осередках, а обєкт, більший за розмірами, ніж осередок сітки, може займати навіть більше чотирьох осередків Ми можемо гарантувати, що це не може відбутися, вибравши як розміру осередку нашої сітки число, кратне розміром найбільшого обєкта нашої гри Після цього залишиться тільки ймовірність, що один обєкт може міститися максимум в чотирьох осередках

Щоб обчислити ID осередки для обєкта, ми можемо взяти точки в кутах його би прямокутника і перевірити, в якій комірці знаходиться кожна точка Визначити клітинку, в якій знаходиться точка, просто: потрібно розділити її координати на ширину комірки в першу чергу Наприклад, у нас є точка з координатами (3, 4) і осередок розміром 2,5 х 2,5 м Точка перебуватиме в осередку з ID 5 на рис 818

Ми можемо розділити кожну координату точки на розмір комірки, щоб отримати цілочисельні 20-координати, як тут:

І з цих координат осередку ми можемо просто отримати ID осередки:

Константа cellsPerRow являє собою кількість осередків, яке потрібно, щоб покрити наш світ осередками по осі х:

Можна обчислити, скільки осередків припадає на стовпець, ось так:

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

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

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

І нарешті, нам потрібен метод, який буде повертати список обєктів в осередках для обєкта, який ми хочемо зіштовхнути з іншими обєктами Цей метод буде перевіряти, в яких осередках розташовується цікавить нас обєкт, знаходити список динамічних і статичних обєктів в цих осередках і повертати їх викликає стороні Звичайно, потрібно буде переконатися, що ми не будемо повертати дублікати, які можуть зявлятися, якщо обєкт знаходиться в декількох осередках У лістингу 810 показана велика частина коду Оскільки метод SpatialHarshGrid getCel 1 ds трохи заплутаний, ми обговоримо його пізніше

Лістинг 810 Частковий класу SpatialHashGridJava: реалізація просторово сітки

Як вже обговорювалося, ми зберігаємо два списки осередків: один – для динамічних обєктів, другий – для статичних Ми також зберігаємо кількість осередків в ряду і стовпці, щоб пізніше мати можливість перевірити, чи знаходиться точка, яку ми перевіряємо, всередині або за межами нашого світу Розмір комірки також слід зберегти Масив eel 1 sds – це робочий масив, який ми використовуємо для тимчасового зберігання ID чотирьох осередків, в яких міститься GameObject Якщо він знаходиться тільки в одній комірці, то тільки першому елементу масиву буде присвоєно ID осередку, яка повністю містить обєкт Якщо обєкт розташовується у двох осередках, перші два елементи масиву будуть містити ID осередків і т д Щоб позначити кількість ID осередків, ми призначимо всім порожнім елементів масиву значення -1 Список foundObjects також робочий, який ми будемо повертати за викликом getPotentialColliders Навіщо тримати ці два елементи, якщо можна створювати новий масив і список кожен раз, коли буде вимагатися Згадайте нашого старого знайомого – Страшного збирача сміття

Конструктор цього класу бере розмір світу і бажаний розмір комірки За даними параметрами ми обчислюємо, скільки осередків нам треба, і заповнюємо масиви осередків і списки, що містять обєкти, що знаходяться в кожній комірці Ми також инициализируем список foundObjects Всі створені нами списки ArrayList матимуть вихідний розмір в 10 GameObject Ми припускаємо, що ймовірність знаходження 10 GameObject в одній комірці дуже мала Поки це так, масиви не вимагають зміни в розмірах

Далі йдуть методи insertStaticOb ject і insertDynamicObject Вони обчислюють ID осередків, в яких міститься обєкт, шляхом виклику getCel 1 Ids і вставляють обєкт в підходящі списки Метод getCel 1 Ids заповнюватиме масив змінних eel 1 Ids

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

Метод clearDynami cCels буде використовуватися для очищення усіх списків динамічних обєктів Нам потрібно буде викликати цей метод перед кожним кадром, перш ніж ми будемо заново вставляти динамічні обєкти, що вже обговорювалося раніше

І нарешті, є ще метод getPotenti а 1 Col 1 i ders Він бере обєкт і повертає список сусідніх від нього обєктів, які містяться в тих же осередках, що і цей обєкт Ми будемо використовувати робочий список foundObjects для зберігання списку знайдених обєктів Знову ж, ми не хочемо створювати новий список кожен раз, коли викликаємо цей метод Все, що нам треба зясувати, – це в яких осередках знаходиться обєкт, переданий в метод Потім ми просто додаємо всі статичні і динамічні обєкти, знайдені в цих осередках, в список foundObjects і переконуємося, що в ньому немає дублікатів Застосовувати foundObjects contai ns для перевірки на дублікати, звичайно, не зовсім оптимально Але оскільки кількість знайдених обєктів в даному випадку ніколи не буде великим, він цілком прийнятний Якщо у вас виникнуть проблеми при реалізації, це перше, що варто оптимізувати На жаль, це не так і просто Звичайно, ми можемо задіяти Set, але тоді нові обєкти будуть створюватися усередині кожного разу, коли ми будемо додавати обєкт в Set Поки залишимо все як є, запамятавши, до чого повернутися в разі чого

Я не згадав метод Spatial HashGridgetCel 1 Ids У лістингу 811 показаний код Не бійтеся, він не такий страшний, яким здається на перший погляд

Лістинг 811 Залишок файлу SpatialHashGridJava: реалізація getCellldsO

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

Обєкт знаходиться в одній клітці Нижній лівий і верхній правий кути би прямокутника, таким чином, мають однакові координати комірки

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

Обєкт займає два осередки по вертикалі Нижній лівий кут знаходиться в одній комірці, в той час як верхній правий кут розташовується в комірці зверху

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

Цей метод окремо перевіряє кожен з окремих випадків Перший блок if перевіряє випадок з одним осередком, другий – випадок для двох горизонтально розташованих осередків, третій – для двох вертикально розміщених осередків, і останній блок для випадку, якщо обєкт перекриває чотири осередки сітки У кожному з чотирьох блоків ми переконуємося, що призначаємо ID осередку тільки в тому випадку, якщо відповідні координати комірки знаходяться всередині ігрового світу От і все, з чого складається цей метод

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

Всі разом

Застосуємо всі знання, набуті в цьому розділі, на маленькому гарному прикладі Розвинемо приклад з гарматою, який ми обговорювали кілька сторінок тому Використовуємо обєкт Cannon для гармати, обєкт Dynami cGameOb ject для гарматного ядра і кілька обєктів GameObject для мішеней Кожна мішень буде мати розміри 0,5 х 0,5 м, і вони будуть розташовані в світі в довільному порядку

Ми хочемо стріляти по цих мішенями Для цього нам знадобиться визначення зіткнень Ми можемо просто перебрати всі мішені і перевірити їх відносно гарматного ядра, але це буде занадто нудно Ми використовуємо наш чудовий новий клас Spati al HashGri d, щоб прискорити знаходження потенційно стикаються з ядром мішеней щодо поточної позиції ядра Однак ми не будемо вставляти в сітку ядро ​​або гармату, оскільки це нічого нам не дасть

Оскільки цей приклад вже досить великий, розділимо його на кілька лістингів Назвемо тест Colli si onTest і відповідний екран Colli si onScreen Як звичайно, ми дивимося тільки на екран Почнемо з елементів тіста і конструктора в лістингу 812

Лістинг 812 Частковий Colli sionTestJava: елементи і конструктор

Ми багато що запозичили з CannonGravityScreen Починаємо з визначення декількох констант, вказуючи кількість мішеней і розміри світу Далі йде екземпляр класу GLGraphics та обєкти для гармати, ядра і мішеней, які ми поміщаємо в список Є також Spati а 1 HashGri d, звичайно Для візуалізації світу нам знадобиться кілька сіток: одна для гармати, одна для ядра і одну ми будемо використовувати для відображення кожного обєкта Згадайте, що в BobTest ми застосовували тільки одну трикутну сітку для візуалізації на екрані ста Бобов Ми задіємо цей принцип і тут замість того, щоб заводити по примірнику Verti ces для кожної мішені Останні два члени класу ті ж, що і в CannonGravityTest Ми використовуємо їх, щоб стріляти ядром і застосовувати закони гравітації, коли користувач доторкається до екрану

Конструктор просто виконує всі ті операції, які ми вже обговорили раніше Ми инициализируем обєкти та мережі нашого світу Єдине, що тут цікаво, – додавання мішеней в просторову сітку як статичних обєктів

Розглянемо наступний метод у класі Col1isi onTest (лістинг 813)

Лістинг 813 Частковий Col1isi onTest java: метод updated

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

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

Остання частина методу – код визначення зіткнень Нам потрібно знайти в просторовій сітці мішені, які знаходяться в тих же осередках, що і ядро Для цього використовуємо метод SpatialHashGridgetPotentialCol 1 idersC Оскільки осередки, в яких знаходиться ядро, розраховуються прямо в методі, немає необхідності вставляти ядро ​​в сітку Далі проходимо в циклі по всіх обєктах, з якими можливе зіткнення, і перевіряємо, чи є дійсно перетин би прямокутника ядра з обмежуючим прямокутником обєкта, з яким можливо зіткнення Якщо є, ми просто видаляємо цю мішень з списку мішеней Памятайте, що ми додали мішені в сітку тільки як статичні обєкти

І все це і є закінчений механізм нашої гри Останній шматок головоломки, по суті, і є сам етап візуалізації, який навряд чи вас чимось здивує (лістинг 814)

Лістинг 814 Частковий Colli sionTestjava: метод present

Тут немає нічого нового Як і завжди, ми визначаємо проекційну матрицю і область перегляду і спочатку очищаємо екран Далі відображаємо всі мішені, знову використовуючи прямокутну модель, збережену в targetVertices Це, по суті, те ж, що ми робили в BobTest, але на цей раз ми відображаємо мішені Далі відображаємо ядро ​​і гармату, як ми робили в Coll isionGravityTest

Я змінив порядок розташування зображень, щоб ядро ​​завжди було над мішенями, а гармата завжди була над ядром Я також зробив мішені зеленими, викликавши glColor4f

Результат цього невеликого тесту точно такий же, як показано на рис 817 Коли ви стріляєте з гармати ядром, воно буде прокладати собі шлях через поле з мішенями Будь мішень, зачеплена ядром, буде видалена зі світу

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

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

Джерело: 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>

*

*