Як створити java-гру для стільникового телефону

Введення

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

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

Для кожної з цих ігор буде докладно описана безпосередньо ігрова частина (тобто ігрова логіка), будуть прийняті деякі дизайнерські рішення, і на завершення ми поринемо у детальний розгляд всіх подробиць реалізації цих ігор на Java.
Вихідний код ігор, які ми будемо розробляти в цій статті, можна знайти і
завантажити тут: http://developer.java.sun.com/developer/releases/wireless_gaming_blueprints

Типи ігор
У багатьох людей, які коли-небудь грали в ігри на мобільних телефонах, обов’язково складається деяке думка про типи таких ігор. Серед них можна виділити основні:
• Стратегічні ігри (Mine Sweeper, Reversi, Bejeweled).
• Карткові ігри (Solitaire, Black Jack, Poker).
• Двомірні (2D) аркадні ігри (Galaxian, PacMan, Defender, Asteroids).
• Тривимірні (3D) аркадні ігри (Doom, Quake, Tony Hawk Pro Skater).
Ми не будемо обговорювати 3D-ігри, оскільки системні ресурси мобільних пристроїв майже завжди занадто обмежені як у плані доступної пам’яті і швидкості процесора, так і в графічних можливостях. Тому, оскільки ми хочемо перетворити наше підприємство у щось веселе і захоплююче, зупинимося на розробці двомірних (2D) аркадних ігор.

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

Апаратні ресурси
Малі пристрої зазвичай не мають достатньої кількості статичної та динамічної пам’яті. Загалом, статична пам’ять необхідна для того, щоб розмістити в собі файли класів вашої гри (упаковані в JAR-архів, природно), а кількість динамічної пам’яті визначає, скільки пам’яті буде доступно вашій грі під час її роботи. Таким чином, необхідно дуже обережно і дбайливо ставитися до використання як статичної, так і динамічної пам’яті і ретельно продумувати кожен свій крок на етапі розробки, оскільки будь-яке неправильне рішення може спричинити за собою перевищення ліміту використання пам’яті.
JAR-файл, що містить вашу гру, повинен бути досить малий (гарним варіантом буде розмір в межах від 10 до 40 кілобайт на одну гру), а кількість споживаної пам’яті під час виконання гри має бути обмежена в розумних межах. Щоб задовольнити цим керівним принципам, слід враховувати кількість створюваних вами об’єктів і частоту їх створення. MIDP Wireless Toolkit 2.0 (WTK) – це емулятор пристроїв, який при установці деяких параметрів може надати вам повну інформацію про використовуваної пам’яті. Однак візьміть до уваги, що MIDP може самостійно створювати об’єкти без вашого на те згоди. Таким чином, не всі об’єкти, які ви зможете спостерігати при моніторингу пам’яті, обов’язково створюються вашою грою.

Процесори, що використовуються в мобільних пристроях, зазвичай занадто малопотужні порівняно з тими високопродуктивними гігантами, які знаходяться у ваших десктопах або PDA. Частота роботи процесорів мобільних пристроїв може бути навіть 15-20 MHz. Крім того, найчастіше мобільні пристрої не мають ніяких графічних прискорювачів. Повільні процесори і відносно повільна графіка повинні змусити вас замислитися і всерйоз звернути свою увагу, на що ваша програма витрачає найбільше часу. Але це не означає, що ви повинні повністю віддаватися проблеми попередження всякого роду затримок у вашій грі. Замість цього краще слідувати перевіреному часом підходу при написанні програм, для яких продуктивність – основна причина головного болю (це можуть бути не тільки гри): спочатку зробіть так, щоб працювало, після зробіть так, щоб працювало правильно, а вже після цього зробіть так, щоб працювало швидко.
Цей підхід працює не завжди. Наприклад, якщо ваша гра – повноцінна 3D-аркада з безліччю об’єктних перетворень, поворотів, масштабуванням і тисячами полігонів, які потрібно промальовувати для кожного кадру, то для того, щоб змусити це працювати швидко на невеликих пристроях, буде потрібно гігантський працю і величезну кількість часу ( хоча JSR 184 дозволяє вирішити деякі проблеми за допомогою використання апаратних можливостей пристроїв). Що стосується 2D-ігор, то тут ви можете не піклуватися на початковому етапі про стратегію перемальовування. (Звичайно ж, якщо ви впевнені, що зможете зробити необхідні дії з оптимізації пізніше.) Можливі дії з оптимізації не обов’язково повинні включати повну перемальовування для кожного кадру. Коротенько проаналізуйте проектировку вашої гри і вільно визначите для себе, які поліпшення продуктивності (графічні, природно) ви зможете привнести пізніше; але ні в якому разі не забивайте собі голову якими конкретними деталями з реалізації цих поліпшень. Пам’ятайте, що поліпшення продуктивності коду часто виявляється набагато більш складним процесом, ніж безпосереднє кодування програми. Саме з цієї причини ми поділяємо секції оптимізації для обох з розглянутих нами ігор: спочатку ми визначаємо, в якій саме частині нашої реалізації проявляються явні затримки (низька продуктивність) і як вибрати найбільш оптимальний і швидкий варіант вирішення цієї проблеми.

Задоволеність користувача
Грати в ігри має бути цікаво, інакше б люди просто в них не грали. У випадку з калькулятором або текстовим редактором, які не завжди так вже цікаві, ми змушені ними користуватися, оскільки значущість цих додатків набагато переважує незручність і непривабливість їх використання. Зовсім інакше йде справа з іграми. Тут немає видимої вигоди або інших причин витрачати свій час і нерви на гру, якщо тільки вона не цікава і не приносить задоволення користувачеві. Таким чином, цільове призначення будь-якої гри – бути цікавою і сподобатися користувачеві. А в ситуації з стільниковими телефонами існує ряд факторів, які можуть дуже негативно вплинути на думку користувача про вашу гру.
По-перше, ми маємо занадто малий екран, на якому досить складно вмістити все, що необхідно, і саме так, як гадалося. Клавіші занадто малі і незручні у використанні. Це особливо важливо в іграх, для яких по ігровому контексту натискання клавіші повинно бути оброблено в межах однієї десятої частки секунди, щоб гравець зміг досягти успіху в грі. Звукові можливості також можуть бути надзвичайно обмежені: звуку може не бути зовсім або він буде, але всього лише у вигляді невеликого набору біп-сигналів або ж звичайного тонального генератора. У будь-якому випадку цього недостатньо, щоб надати своїй грі гарне звукове оформлення. Щоб залучити користувачів, гра повинна мати гідний зовнішній вигляд і зручне управління. Це означає, що різні ігрові екрани (сцени в грі) повинні виглядати як частина однієї гри. Це можна здійснити за рахунок використання постійної гами кольорів, фонових зображень і візерунків, іконок та іншого. Користувачі не повинні бачити повідомлень про помилки під час гри, не рахуючи критичних (наприклад: HTTP 404: not found). Графічне і звукове оформлення гри в ідеалі повинно створюватися людьми з художніми та естетичними нахилами.

Налагодження
Процес налагодження на самих мобільних пристроях дуже трудомісткий. Найкраще скористатися емулятором, наприклад, WTK, де це можливо, оскільки емулятори дозволяють вам отримати доступ до консолі, на яку ви без зусиль зможете виводити повідомлення за допомогою методу System.out.println (). Також дуже хороший прийом перевантаження або визначення методів toString () для всіх ваших об’єктів. З їх допомогою ви зможете отримувати повну інформацію про стан будь-якого об’єкта на конкретний момент часу. Ви також зможете просто закоментувати цю частину коду, щоб запобігти потраплянню її в реліз гри. При цьому ви залишаєте за собою можливість налагодження до наступних релізів. Можна, звичайно, скористатися вбудованим отладчиком вашої улюбленої середовища розробки (IDE) на Java, але це може спричинити за собою безліч проблем. Поки що для нас буде цілком зручним метод println ().
Інший варіант налагодження – демонстраційні режими у вашій грі. Ідеально демо-режим повинен залучати до демонстраційний процес гри всю її функціональність, наскільки це можливо. Це дозволить вам не тільки продемонструвати кінцевому користувачеві, на що здатна ваша гра, але й перевірити, чи не завалиться чи додаток при виникненні будь-яких позаштатних ситуацій. Це зробить процес налагодження помилок дуже легким. Замість того, щоб годинами сидіти і натискати на клавіші, намагаючись перевірити всі можливі варіанти гри, ви просто сидите і дивіться, як гра робить всю найскладнішу роботу (для виконання якої вона, власне, і була написана) за вас. Разом з висновком невеликих корисних налагоджувальних повідомлень ви отримуєте дуже потужний відлагоджувальний інструмент.

Автономний варіант гри
Опис

Як приклад автономної гри була вибрана дуже поширена версія аркади типу арканоид. Елементами гри є блоки різного кольору, дошка і кулька. Гравець управляє дошкою, яку він може пересувати праворуч або ліворуч. Шарик літає в межах ігрового поля, в той час як гравець повинен дотримуватися двох цілей, щоб виграти. Ігрове поле обмежено стінками ліворуч, праворуч і зверху. Нижня стінка, по якій пересувається дошка, відкрита. Щоб запобігти падінню кульки вниз, гравець повинен пересувати дошку до того місця, куди збирається впасти кулька, яка, відштовхнувшись від дошки, полетить по розраховується траєкторії. Гра організована в кілька рівнів. На кожному з них на ігровому полі знаходиться певна кількість блоків. Деякі з цих блоків відразу розпадаються при попаданні в них кульки, інші ведуть себе більш складно. Гравець повинен, скориставшись кулькою, збити всі збиваються блоки на ігровому полі, щоб перейти до наступного рівня. Якщо кулька падає на нижню межу екрану, і гравець не встигає відбити його за допомогою дошки, то гравець втрачає одне життя. При старті гравець отримує три життя. Як тільки користувач витратив всі свої життя, гра закінчується, і він програє. Під час гри на екрані відображаються індикатори, що показують статистичну інформацію про поточну грі: кількість залишилися життів, кількість зароблених очок і рекордну кількість очок.
Оскільки це порівняно проста гра, ми не будемо зберігати рекордну кількість очок постійно, а тільки на поточний сеанс роботи програми. Гра також містить екран-заставку, яка відображає назва гри і ім’я її автора. Крім цього, є ще й екран, який виводиться при програші. І, нарешті, вона має демонстраційний режим, в якому дошка контролюється самою грою. Це дуже корисно як для демонстраційних цілей, так і для тестування і налагодження.

Проектування
З наведеного вище опису ми можемо виділити кілька сутностей, які будуть грати ключову роль в проектуванні гри. Це кулька, дошка та блоки. Причому всі ці сутності мають загальні властивості: положення, розмір, графічне представлення, а також обробник зіткнень з іншими об’єктами (сутностями). Все це властиво спрайт (sprite). Оскільки в MIDP 1.0 немає підтримки роботи зі спрайтами (у MIDP 2.0 вона вже є), нам доведеться робити все самим. Це не страшно, так як необхідна функціональність наших спрайтів порівняно мала.
Якщо трохи подумати, то можна прийти до висновку, що блоки і дошка мають багато спільного. Тому в плані функціональності вони схожі, і ми можемо реалізувати ці дві сутності в одному класі.
Щоб реалізувати модель визначення зіткнень об’єктів, скористаємося спрощенське методом прямокутників. У цьому випадку кожен з об’єктів на ігровому полі буде визначатися наступними параметрами: координатою X, координатою Y, а також висотою і шириною. Для дошки й блоків ці параметри повністю визначають їх фактичний розмір. Однак для кульки таке подання – досить грубе наближення його натуральної форми. Щоб досягти більш натурального поведінки кульки при такому поданні, потрібно трохи зменшити його розміри (наприклад, 75% діаметра кульки в якості однієї сторони його прямокутника). За визначенням два об’єкти вважаються що зіткнулися, якщо їх прямокутники перекриваються, тобто накладаються один на одного.
Для того щоб надати нашій грі більш привабливий вигляд, ми намалювали об’єкти в псевдотрехмерной формі. Кожен об’єкт відкидає тінь, яка пересувається разом зі своїм об’єктом. Крім того, об’єкти схильні до ефекту освітлення. Верхні краї, звідки імовірно світить джерело світла, світліші, тоді як нижні трохи затемнені.

Тепер на стадії проектування введемо в нашу гру ще три сутності (це дуже спростить реалізацію гри). Перша сутність буде групувати блоки на всіх рівнях. Назвемо її списком блоків (brick list). Цей список являє собою контейнер об’єктів, але крім цього він буде виконувати і функції видимості об’єктів. Наприклад, замість того, щоб перемальовувати кожен блок, ми просто даємо команду списку перемалювати всі блоки даного рівня. Так само і у випадку з обробкою зіткнень. Щоб не визначати індивідуально, не зіткнувся Чи кульку з якимось з блоків, ми покладаємо цю задачу на список, який і здійснить цю перевірку за нас. Другий введеної сутністю буде екран (screen). Ця сутність координуватиме будь-які дії, що відбуваються на ігровому полі (тобто що потрібно малювати, і в якому порядку). Стратегію перемальовування ми обговоримо далі в секціях Реалізація та Оптимізація. Нарешті, нам потрібна сутність, яка зв’язала б все це разом – щось, що могло б привести в дію нашу гру. З цілком зрозумілих причин назвемо цю сутність движком (engine).
Гра на даний момент спроектована так, що має всього чотири рівня. Проте їх кількість можна дуже легко збільшити в майбутньому. Для того щоб зробити функцію з розширення рівнів максимально простою, ми виносимо уявлення рівнів у вигляді даних поза вихідного коду. Якщо зробити так, то нові рівні можуть бути прочитані з бази даних пристрою або навіть завантажені з мережі без проблем з безпекою. Це означає також, що рівні може розробляти будь-який бажаючий, а не тільки програміст. Але для цього, природно, потрібно буде написати трохи зайвого коду, який буде здійснювати читання / завантаження рівнів із зовнішніх джерел.
Іншим об’єктом, на який потрібно обов’язково звернути увагу на етапі проектування, – це розмір екрану. Специфікація MIDP не встановлює якийсь певний розмір екрану і навіть не задає можливий мінімальний її розмір. Тут все залежить від розробників пристроїв. Саме вони встановлюють дозвіл для своїх пристроїв. Загальне рішення даної проблеми – допускати дуже малий розмір екрану. Таким чином, у більшості пристроїв розмір екрану співпаде або майже співпаде з обраним вами. У разі, якщо дозволу співпадуть, гра буде займати весь простір екрану пристрою. Якщо ж дозвіл пристрою виявиться більше, то гра буде центрована всередині фізичної області екрану. Наша гра – типовий приклад простої гри, коли можна вибрати більш досвідчений підхід для вирішення цієї проблеми. Оскільки розміри всіх об’єктів кодувати не так вже складно, ми можемо обчислювати їх при старті гри, спочатку визначивши фактичний розмір і дозвіл екрану пристрою. Це означає, що наша гра завжди буде використовувати максимум доступного їй екранного простору.

Реалізація
Як сказав Дональд Кнут: Попередня оптимізація – це джерело всіх зол. Цілком довіряючи цьому висловлюванню, ми почнемо з звичайної реалізації, щоб не ускладнювати собі життя. Як тільки гра почне працювати, ми визначимо частини програми, які необхідно буде оптимізувати. Далі будуть наведені досить цікаві елементи коду, що ілюструють подробиці ідей реалізації. Ці елементи представлені за принципом знизу-вгору. Це означає, що частини більш низькорівневого коду будуть розглядатися раніше, ніж високорівнева код.

Клас ThreeDColor
Цей клас реалізує безпечний шаблон перерахувань, який був раніше представлений у знаменитій книзі Ефективне програмування на Java (Effective Java Programming). Всі кольори, які можна використовувати, представлені у вигляді примірників змінних з модифікатором public.
Private-конструктор даного класу запобігає установку інших значень цих змінних. Методи brighter () і darker () використовуються для вибору відповідного кольору для освітлення і затемнення відповідно. Метод getRGB () повертає ціле RGB-значення кольору, який був встановлений за допомогою методу Graphics.setColor (int).

Клас Sprite
Цей клас є суперкласом для класів Ball і Brick. Кожен об’єкт класу Sprite має координати X і Y, ширину, висоту, визначають прямокутник, а також зсув тіні. Зсув тіні задає, наскільки далеко від об’єкта буде малюватися його тінь. Воно також показує, наскільки високо над екраном буде парити об’єкт. Прямокутник, який визначається в цьому класі, як ми вже говорили вище, використовується для вилову зіткнень об’єктів. Він створюється в конструкторі, в якому також визначається значення зміщення тіні згідно з фактичним розміром екрана. Даний клас також визначає абстрактні методи для малювання об’єктів і тіней у спрайтів. Реалізація цих методів передбачається вже в класах Ball і Brick.
Код класу Sprite: тут

Клас Ball
Цей клас займається рухом і промальовуванням кульки. Радіус кульки (або його розмір) визначається на основі поточних розмірів екрану.
Кожен екземпляр кульки має ряд змінних, які задають його швидкість (dx, dy), випадкові швидкісні перешкоди (xo, yo), а також його колірну схему (колір, яскравість і затемнення). Конструктор створює екземпляри змінних класу Sprite width (ширина) і height (висота) на основі значення радіуса кульки і, крім цього, встановлює колір.
Цей клас містить чіткі інструкції з приводу того, як потрібно промальовувати кульку. Метод move () встановлює нове положення кульки на ігровому полі з урахуванням поточної швидкості і перешкод. bounceHorizon-tal () і bounceVertical () підбирають і обчислюють відповідний коефіцієнт швидкості після зіткнення кульки з іншим об’єктом або стінкою. Щоб запобігти тупикову ситуацію, коли кульці нікуди більше рухатися або коли він починає циклічно рухатися по одній і тій же траєкторії (замкнутому шляху), методи bounce Horizontal () і bounceVertical () щоразу в циклі привносять трохи перешкод при обчисленні шляху. Це дозволяє уникнути зациклення гри. Методи paint () і paintShadow () використовуються для промальовування кульки.
Код класу Ball: тут

Клас Brick
Клас Brick визначає ряд основних атрибутів блоків: width (ширина), height (висота), step (крок), gap (розрив). Атрибути width і height являють собою ширину і висоту блоків за замовчуванням, step – це кількість рухів блоку при анімації, а gap – це відстань до суміжних з ним блоків. Всі ці значення теж обчислюються згідно поточному розміру екрана. Крім того, в цьому класі присутні визначення для символічних імен для кожного типу блоків: zombie (для неіснуючих блоків), standart (звичайні блоки), fixed (незруйновані блоки) і slide (блоки, які можуть переміщатися на місце своїх сусідів, якщо таке місце ще не зайняте). Нарешті, кожен блок має ряд атрибутів, що стосуються його позиціонування на ігровому полі, типу, колірної схеми, кількості очок, яке дається за руйнування цього блоку та посилання на список блоків, до якого він належить.
Більшість атрибутів блоку инициализируется конструктором. Метод clear () видаляє блок з ігрового поля, hit () знає, як поточний блок повинен відреагувати на наступне зіткнення з кулькою. IsFixed () повертає true, якщо цей блок не можна зруйнувати (тип fixed). Як і в попередньому класі, методи paint () і paintShadow () призначені для промальовування об’єкта.
Код класу Brick: тут

Клас BrickList
BrickList – це перший клас, що відноситься до високорівневих класам нашої гри. Цей клас займається розміщенням блоків на ігровому полі. Він знає, як потрібно розташувати блоки з урахуванням всіх їх параметрів, а також з розстановкою квітів. Конструктор BrickList використовує варіант розташування за замовчуванням. Метод moveBrick () переміщає блок з однієї позиції на іншу, checkForCollision () повертає значення – число більше нуля, якщо сталося зіткнення кульки з одним з блоків з поточного списку блоків. Повертає значення – це кількість очок, яке отримує гравець за збитий блок. Це значення береться з властивості відповідного збитого блоку. Після цього кількість зароблених гравцем очок збільшується на це значення. Зверніть увагу, яким чином метод checkCollision () заново використовує той же прямокутник для перевірки на зіткнення, тим самим виключивши з процесу перевірки на зіткнення будь непотрібне сміття (так, це є оптимізація).
Метод isClean () повертає true, якщо на ігровому полі (і, відповідно, у списку) не залишилося більше стандартних блоків. Таким чином, це означає, що рівень успішно пройдений; в іншому випадку цей метод повертає false. getNeighbor () повертає сусідить блок для блоку, переданого в якості аргументу і у відповідному напрямку. Якщо для цього блоку за вказаним напрямом немає більше сусідів, то цей метод повертає null. Методи paint () і paintShadow () відображають всі блоки поточного аркуша, по черзі викликаючи методи paint () і paintShadow () для кожного із своїх елементів-блоків.
Код класу BrickList: тут

Клас Screen
Цей клас заправляє всіма високорівневими операціями з малювання, які необхідні нашій грі. Він знає, як малювати різні екрани, які ми обговорювали в секції Опис цієї статті, а також може запитувати стан у класу Engine, щоб знати, який екран промальовувати. У заголовному екрані на ім’я автора накладена простенька анімація, так що ім’я автора піднімається з нижньої межі екрану. Для анімації цей клас використовує змінну wildcard_y. Крім того, методи keyPressed () і keyReleased () пересилають події, що надходять від клавіатури, в клас Engine.
Код класу Screen: тут

Клас Engine
Цей клас можна сміливо іменувати серцем нашої гри. Він зберігає всі шаблони рівнів у змінній pattern_list (представлені таким чином, що, подивившись на вихідні дані, можна без особливих зусиль уявити собі, що собою являтиме той чи інший рівень) і визначає ряд різних станів, у яких може перебувати гра. Це стану title, play, over і demo. Гра стартує, починаючи з стану title, коли відображається заставочную екран. Якщо протягом певного проміжку часу була натиснута яка-небудь клавіша, гра переходить в нормальний ігрове стан play. Інакше вона стартує демонстраційний режим (стан demo). У цьому стані гра грає сама в себе. Вийти в звичайний режим play можна, натиснувши будь-яку клавішу. У разі, коли гравець втрачає всі виділені йому життя, гра переходить у стан over. Якщо протягом певного часу гравець натискає будь-яку клавішу, гра переходить в режим play, інакше стартує режим demo. Основний цикл програми визначено у методі run (). Тут движок гри засинає на деякий час, після чого анімує всі об’єкти і, нарешті, перемальовує екран. Такий підхід використовується у величезній кількості аркадних ігор і зазвичай називається ігровим циклом.
У цьому циклі гра насамперед оновлює свій стан. Серед елементів, які грі потрібно оновити, – користувальницький введення, таймери, генератори випадкових чисел і ворожий AI (Artificial Intelligence – штучний інтелект). Після того, як стан гри було поновлено, екран перемальовується. Після перемальовування движок спить деякий час і потім починає весь цей процес заново. Щоб дати грі можливість працювати незалежно від нього, модуль движка запускається у своєму власному потоці. Це реалізовано за допомогою використання в класі Engine реалізованих методів інтерфейсу Runnable. Після чого створюється новий об’єкт класу Thread, і його конструктору передається об’єкт нашого движка. При цьому об’єкт thread першим справою запускає сам движок допомогою виклику методу run () класу Engine.
Зверніть також увагу на те, як відбувається обробка натисків гравцем клавіш. Код клавіші, утримуючи користувачем, записується в змінну при першому її натисканні. Як тільки користувач відпускає цю клавішу, значення цієї змінної з кодом клавіші скидається. Основний цикл програми пересуває дошку, керовану користувачем, в запрошенном напрямку, поки не відпустите клавішу.
Це означає, що для того, щоб перемістити дошку на інший край екрана, гравцеві не буде потрібно кілька разів натискати на клавішу. Він її просто утримує до тих пір, поки дошка не переміститься в потрібне йому місце.
Код класу Engine: тут

Оптимізація
Як тільки ми запускаємо гру в середовищі WTK або на самому пристрої, відразу впадає в очі той факт, що наш підхід до промальовування екрану занадто повільний. Анімація занадто різка і ривкова. У гру зовсім неможливо грати.
Треба щось терміново міняти. У нашому випадку ми просто перемальовували весь екран для кожного кадру. Згідно природі нашої гри зрозуміло, що це просто абсолютно нелогічний підхід, оскільки в грі у нас від фрейму до фрейму відбувається зміна тільки двох речей: поточної позиції кульки і дошки (якщо користувач активно її переміщує). Зрідка наш м’ячик соударяющихся зі стандартним блоком, який після цього зникає, або з ковзаючим (slide) блоком, який може переміститися на місце свого сусіда, а може і не переміститися. У цьому випадком нам доведеться перемальовувати ще й весь список блоків. Однак після ближчого вивчення ми помічаємо, що можна, власне кажучи, весь список і не перемальовувати. Можна просто стерти збитий стандартний блок зі списку або, якщо це slide-блок, видалити його в старій позиції і намалювати в новій. Таким чином, має сенс малювати список блоків в окремому буфері зображення і тільки потім копіювати цей буфер на екран під час перемальовування. Так, під час зміни списку (через збитий або переміщеного блоку) ми перемальовували змінилася частину списку в окремому буфері зображення.
Загалом, оптимізація має сенс у кількох областях. Графічна продуктивність – це одна з найбільш важливих областей, і ми повинні мати це на увазі. Однак перевантаженість в обчисленнях також дуже критична для вашої гри. Якщо ваша гра витрачає занадто багато часу на проходження ігрового циклу, потрібно звернути увагу на такі речі, як перевірка на зіткнення і інші обчислювальні розрахунки гри. Для початку спробуйте відключити всі уявні вам корисними внутрішні особливості реалізації і простежте, наскільки це зменшить або збільшить навантаження на процесор. WTK 2.0 має вбудований інструмент для моніторингу завантаженості процесора емулятором. Але будьте уважні: його цифри не завжди відображають реальну картину що відбувається на справжніх пристроях. Як тільки ви знайдете щось, що потрібно виправити, спочатку переконайтеся, чи дійсно ваша реалізація оновленого коду оптимізує роботу програми. Якщо ні, то вам слід пошукати інший, більш швидкий підхід до вирішення цієї проблеми. Також обов’язково зверніть увагу на елементи управління. Немає нічого більш відразливого, а в іграх особливо, ніж повільне і інертне управління, яке не дає користувачам гри проявити свої ігрові здібності.

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

Висновок
Найголовніший урок, який ви повинні були взяти з цієї статті: малювання – заняття дуже ресурсномістке. Якщо ви подивитеся на код початкової реалізації, а потім на оптимізований код, то не побачите між ними великої різниці. Тим часом, відмінності в продуктивності гри дуже великі. Якщо ви збираєтеся дійсно використовувати, а тим більше продавати, свою гру, вам обов’язково потрібно випробувати її у дії на реальному пристрої. Хоч WTK – це і чудовий інструмент для розробки, але в грі існують моменти, які можна виявити, лише запустивши її на реальному мобільному пристрої.

Наступного разу ми розглянемо мережевий варіант гри для мобільних пристроїв і ще раз поговоримо про проектування, реалізацію і оптімізірованності ігор на Java.

Коментування вимкнено .

  1. Георгий Бабухадия :

    посилання “тут” – биті

  2. autolord :

    Багато інформації, що не оченьінтересной на мій погляд …