Категории
Самые читаемые
onlinekniga.com » Компьютеры и Интернет » Программирование » Создание игр для мобильных телефонов - Майкл Моррисон

Создание игр для мобильных телефонов - Майкл Моррисон

Читать онлайн Создание игр для мобильных телефонов - Майкл Моррисон

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 68 69 70 71 72 73 74 75 76 ... 87
Перейти на страницу:

Удаление объектов

Говоря о повторном использовании и уборке мусора, следует упомянуть о последнем приеме оптимизации, связанном с удалением объектов из памяти. В традиционном программировании J2SE или J2EE вы создаете объекты по необходимости, а удаляются они сборщиком мусора Java, когда становятся ненужными. В J2ME все аналогично, но стандартный сборщик мусора – это не очень эффективное средство высвобождения памяти. Сборщик мусора запущен как низкоприоритетный фоновый поток, который определяет и удаляет неиспользуемые объекты. Объект является используемым до тех пор, пока он не выйдет за границы области видимости или не станет равным null.

Один из способов помочь сборщику мусора определить неиспользуемый объект – это по окончании работы с объектом явно присвоить ему значение null, тогда занимаемая память будет освобождена при первой же возможности. Все объекты рано или поздно будут удалены из памяти, однако этот прием позволяет ускорить удаление ненужных объектов сборщиком мусора.

Минимизация сетевых данных

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

Исключение ненужной графики

Вероятно, вы думаете, что графика очень важна в создаваемых вами играх, но в реальности это может быть не так. Например, если в вашей игре есть графика, отличающаяся углом поворота, то, вероятно, вы зря расходуете память. Класс Sprite позволяет поворачивать спрайтовые изображения (на углы кратные 90 0) или зеркально отображать их. Используя это, вы потенциально можете сократить объем графических ресурсов на 75 %.

Рассмотрим пример High Seas, созданный в главах 12 и 13. В этой игре спрайт пиратского корабля состоит из четырех фреймов (рис. 17.1), которые содержат изображения корабля, повернутого на север, восток, юг и запад. Эти положения соответствуют поворотам одного спрайтового изображения на 90 0, следовательно, можно избежать применения всех изображений, если использовать возможности класса Sprite (рис. 17.2).

Рис. 17.1. Спрайт пиратского корабля из игры High Seas состоит из четырех направленных спрайтов

Рис. 17.2. Все изображения, кроме одного, могут быть исключены, но динамически восстановлены с помощью преобразований

...

В копилку Игрока

Использование преобразования спрайтов может отрицательно сказаться на реалистичности некоторых элементов графики игры. Например, пиратский корабль в игре High Seas 2 показывает лицевую и обратную стороны паруса, а также тень, отбрасываемую кораблем. Хотя эти детали незначительны, они теряются, если использовать описанную выше методику (рис. 17.2). В результате пиратский корабль выглядит менее реалистично.

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

Приемы оптимизации Java-кода

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

Вы не должны задумываться об оптимизации скорости каждого бита создаваемого вами кода. Некоторые части кода вызываются чаще других. Зная это, вы должны оптимизировать именно такие фрагменты. Помните, что нереально оптимизировать каждую строку медленного кода игры. Ваша цель – оптимизировать те фрагменты, в которых это можно сделать, не прикладывая много усилий. Чуть позже вы узнаете, как выделить фрагменты кода, которым следует уделить особое внимание при оптимизации.

...

Совет Разработчику

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

Компиляция без отладочной информации

Возможно, самая простая оптимизация не подразумевает программирования вообще. Я говорю об исключении отладочной информации, которая по умолчанию включается в состав классов при использовании стандартного компилятора (javac). По умолчанию Java-компилятор включает дополнительную отладочную информацию в классы файлов, которая помогает отладчикам анализировать и идентифицировать код. По окончании отладки игры важно отключить отладочную информацию в компиляторе, используя ключ – g: none. Ниже приведен пример использования этого ключа:

javac -g:none MyMIDlet.java

К счастью, все примеры в этой книге, расположенные на прилагаемом компакт-диске, откомпилированы с выключенной отладочной информацией. Вам придется отключить опцию – g: none, если вы планируете отладить какой-либо из примеров.

Исключение ненужных вычислений

Следующая методика оптимизации – это простой прием программирования, исключающий ненужные вычисления. Такие вычисления проблематичны, поскольку они занимают время процессора. Ниже приведен пример кода, который выполняет ненужные вычисления:

for (int i = 0; i < size(); i++) a = (b + c)/i;

Несмотря на то что сложение (b + c) – это весьма эффективный фрагмент кода, лучше вынести его за пределы цикла:

int tmp = b + c;

for (int i = 0; i < size(); i++)

a = tmp/i;

Такое простое изменение может иметь значительный эффект, в зависимости от числа повторов выполнения цикла. Есть еще один прием оптимизации, который вы, вероятно, могли упустить. Обратите внимание на вызов метода size(). С точки зрения производительности, лучше сохранить возвращаемое им значение в отдельную переменную, чтобы избежать вызова метода при каждом повторе цикла:

int s = size();

int tmp = b + c;

for (int i = 0; i < s; i++)

a = tmp/i;

Исключение общих выражений

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

b = Math.abs(a) * c; d = e / (Math.abs(a) + b);

Повторный вызов метода abs() – трудоемкая операция, вместо этого лучше вызвать метод однократно, а результат сохранить во временной переменной:

int tmp = Math.abs(a);

b = tmp * c;

d = e / (tmp + b);

Преимущества локальных переменных

Возможно, вы не задумывались, но Java-коду требуется больше времени обратиться к переменным класса, чем к локальным переменным. Это связано с тем, как осуществляется доступ к двум различным типам данных. На практике следует использовать локальные переменные, а не переменные класса, если вопрос производительности критичен. Например, если внутри цикла происходит постоянное обращение к переменной класса, то целесообразно присвоить локальной переменной значение переменной класса и внутри цикла работать с локальной переменной. Ниже приведен пример кода:

for (int i = 0; i < 1000; i++) a = obj.b * i;

Как вы видите, внутри цикла обращение к переменной объекта obj выполняется 1000 раз. Оптимизация этого кода подразумевает замену переменной obj.b локальной переменной, к которой будет выполняться обращение в цикле:

int localb = obj.b;

for (int i = 0; i < 1000; i++)

a = localb * i;

Раскрытие циклов

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

Раскрытие цикла подразумевает его замену «грубым» эквивалентом. Чтобы лучше понять это, давайте рассмотрим пример:

for (int i = 0; i < 1000; i++) a[i] = 25;

Это, вероятно, выглядит как эффективный фрагмент кода, и на самом деле это так. Но если вы хотите ускорить его выполнение, то раскройте цикл:

int i = 0;

for (int j = 0; j < 100; j++) {

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

a[i++] = 25;

}

В приведенном примере вы сократили число повторений цикла на порядок (с 1000 до 100), но вы загрузили процессор операцией инкрементирования внутри цикла. В целом, приведенный код работает быстрее исходного, однако не ждите чудес. Раскрытие циклов может быть полезным в ряде случаев, но я не советую вам ставить этот метод оптимизации на первое место. Такой метод следует применять в играх, в которых важна каждая миллисекунда производительности.

Сжатие и затенение кода

1 ... 68 69 70 71 72 73 74 75 76 ... 87
Перейти на страницу:
На этой странице вы можете бесплатно читать книгу Создание игр для мобильных телефонов - Майкл Моррисон.
Комментарии