понедельник, 29 мая 2017 г.

Обновил свою игрушку Space Battle!
Сделал на основе технологий OpenGl. Полгода работы после работы.:) Наслаждайтесь!

В бесплатной версии доступны только 2 корабля и есть реклама, которая должна быть, если вас уничтожит космолёт Черный принц.

Бесплатная версия
https://play.google.com/store/apps/details?id=com.adc2017gmail.spacebattle01&hl=ru

Платная версия
https://play.google.com/store/apps/details?id=com.adc2017gmail.spacebattle&hl=ru


понедельник, 9 января 2017 г.

Ура, появилось моё первое приложение созданное в OpenGL ES!
Дайте поиграть своим маленьким братикам и сестричкам! Игра развивает скорость реакции и доброту к животным! Если понравилось, не поленитесь поставить 5 звездочек! :)
https://play.google.com/store/apps/details?id=com.adc2017gmail.az01

воскресенье, 2 октября 2016 г.

суббота, 3 сентября 2016 г.

­Изучаем OpenGL ES2 для Android

Урок №4. Текстуры


Перед тем как начать
   Если вы новичок в OpenGL ES, рекомендую сначала изучить предыдущие 3 урока.

  Основы кода, используемого в этой статье, взяты отсюда:

Результатом данного урока будет дельфин прыгающий над поверхностью моря.
Немного о текстурах
Текстура - это растровое изображение, которое накладывается на поверхность полигональной модели для придания ей цвета, окраски или иллюзии рельефа. Использование текстур можно легко представить себе как рисунок на поверхности скульптурного изображения.


Использование текстур также позволяет воспроизвести малые детали поверхности, создание которых полигонами оказалось бы чрезмерно ресурсоёмким. Например, шрамы на коже, складки на одежде, мелкие камни и прочие предметы на поверхности стен и почвы.
Качество текстурированной поверхности определяется текселями — количеством пикселей на минимальную единицу текстуры. Так как сама по себе текстура является изображением, разрешение текстуры и её формат играют большую роль, которая впоследствии сказывается на быстродействии и  качестве графики в приложении.
 Текстурные координаты
В OpenGL координаты текстур задают обычно в координатах (s, t) или (u,v) вместо (х, у). (s, t) представляет собой тексель текстуры, которая затем преобразуется в многоугольник.
В большинстве компьютерных координатных систем отображения, ось Y направлена ​​вниз, а Х – вправо, поэтому  верхний левый угол соответствует изображению в точке (0, 0).

Надо помнить, что у некоторых андроид системах память будет работать только с текстурами, стороны которых кратны 2 в степени n. Поэтому нужно стремиться, чтобы атлас с текстурами был соответствующих размеров в пикселях, например, 512х512 или 1024х512. Также, если вы не будете использовать POT текстуру (POT – Power Of Two, то есть степень двойки), вы не сможете применить тайлинг или автоматическую генерацию мипмапов. В данном случае под тайлингом понимается многократное повторение одной текстуры. Помните, правый нижний угол всегда имеет координаты (1,1), даже если ширина в два раза больше высоты. Это называется нормализованными координатами.
  В приложениях нередко используется множество маленьких текстур, причём переключение с одной текстуры на другую является относительно медленным процессом. Поэтому в подобных ситуациях бывает целесообразно применение одного большого изображения вместо множества маленьких. Такое изображение называют текстурный атлас (англ. Texture atlas). Под-текстуры отображаются на объект, используя UV-преобразование, при этом координаты в атласе задают, какую часть изображения нужно использовать.
Так как в нашем приложении есть три текстуры (небо, море и дельфин), они объединены в один атлас размером 1024х1024 формата png.

Как вы видите, я добавил еще одно изображение дельфина (в правом нижнем углу). Потом можно поиграться, и подключить его вместо того, что слева. Данный атлас сделан очень плохо, так как осталось много свободного места. Существуют алгоритмы и программы, которые позволяют упаковать изображения оптимальным образом. Например, как на фото.


Вес ( размер занимаемой памяти ) текстуры можно определить таким способом:
байт умножить на пиксель высоты и пиксель ширины, таким образом 32-битная текстура размера 1024 на 1024 занимает 4*1024*1024 = 4’194’304 байт.
16-битная текстура 1024 на 1024 займет уже только 2Мб, так что стоит подумать – стоит ли использовать 32-битные изображения или нет.
Существует аппаратное сжатие текстур, которое обычно позволяет в четыре раза уменьшить вес текстур. Однако, сейчас эти вопросы не главные, просто попутная информация к размышлению.
 В данном уроке будем использовать только метод GL_TEXTURE_2D, который позволяет надеть текстуру на плоскость (есть еще GL_TEXTURE_CUBE_MAP, который работает с текстурой развернутого куба, состоящей из 6-ти квадратов).

Как надеть текстуру?
Прежде чем надевать, выясним на что.
Один прямоугольник (состоящий из двух треугольников) будет лежать в плоскости х0у, на него наденем текстуру неба. Для этого в методе  private void prepareData() класса OpenGLRenderer создадим массив координат float[] vertices , куда занесем координаты не только треугольников, но и координаты соответствующей текстуры.
//coordinates for sky
 -2, 4, 0,   0, 0,
 -2, 0, 0,   0, 0.5f,
  2, 4, 0,   0.5f, 0,
  2, 0, 0,   0.5f, 0.5f,
Первые три числа строки - координаты левого верхнего угла неба  (-2, 4, 0), следующие два числа – координаты точки текселя (0,0), которые соответствуют нашей вершине треугольника. Обратите внимание на вторую точку (вторая строка), которая совпадает с левым нижним краем неба  (-2, 0, 0), для нее координаты точки текселя   (0, 0.5f), т.е.  s = 0 (левый край текселя), а t = 0.5, так как текстура неба занимает по вертикали только половину текселя.  Потом задаем третью точку (верхний правый край неба) и четвертую точку, чтобы нарисовать два треугольника методом GL_TRIANGLE_STRIP (смотри предыдущий урок).
Вторую плоскость (море) я сначала решил сделать перпендикулярно первой (небу), но потом несколько увеличил угол, понизив переднюю кромку моря для красоты фронтального вида на устройстве.
//coordinates for sea
  -2, 0, 0,       0.5f, 0,
  -2, -1, 2,      0.5f, 0.5f,
   2, 0, 0,       1, 0,
   2,-1, 2,        1, 0.5f,
Обратите внимание, как изменились координаты, вырезающие нам из атласа море.
Изображение дельфина я разместил на плоскость, которая параллельна небу и сдвинута на нас по оси 0Z на 0,5 единиц.
//coordinates for dolphin
-1, 1, 0.5f,      0, 0.5f,
-1, -1, 0.5f,      0, 1,
 1,  1, 0.5f,      0.5f, 0.5f,
 1, -1, 0.5f,       0.5f, 1,
Если возникнет желание поменять дельфина на другого, то это нужно сделать именно здесь.
Итак, мы прошли первый шаг и сделали соответствие между вершинами треугольников и точками текселя.

Второй шаг или, как загружаются текстуры
 Прежде, чем описать загрузку текстур, нужно разобраться с таким понятием, как текстурный слот. Именно к нему мы подключаем текстуру, с помощью его можем производить с ней разные манипуляции и менять её параметры.
Выбрать текущий слот для работы можно так:
 GLES20.glActiveTexture(GLES20.GL_TEXTUREx);
 где GLES20.GL_TEXTUREx – номер выбранного слота, например GLES20.GL_TEXTURE0,
константы прописаны для 32 текстур ( последняя GL_TEXTURE31 ).
 Для подключения текстуры к слоту используется процедура
 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture_id);
 Где: первый параметр – тип текстуры, второй – ссылка на текстуру.
 Эта процедура прикрепляет текстуру к текущему слоту, который был выбран до этого процедурой GLES20.glActiveTexture().
 То есть для того чтобы прикрепить текстуру к определенному слоту нужно вызвать две процедуры:
 GLES20.glActiveTexture(Номер_Слота);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, ссылка_на_текстуру);
 Важно помнить, что нельзя подключить одну текстуру одновременно к нескольким слотам.
Если вы ее переключили на другой слот и не установили текстуру для слота, к которому она была подключена ранее, то попытка прочесть скорей всего приведет к падению приложения. 
Как только мы поместили наш графический файл texture.png в папку ресурсов проекта drawable, система автоматически присвоила ему номер id (идентификатор ресурса - это целое число, которое является ссылкой на данный ресурс). Идентификаторы ресурсов хранятся в  файле R.java.
В классе TextureUtils есть метод loadTexture. Этот метод принимает на вход id ресурса картинки, а на выходе возвращает id созданного объекта текстуры, который будет содержать в себе эту картинку.
Итак, вначале передаем в качестве аргументов контекст и идентификатор ресурса графического файла
public static int loadTexture(Context context, int resourceId) {
 Потом создаем пустой массив из одного элемента. В этот массив OpenGL ES запишет свободный номер текстуры,  который называют именем текстуры textureIds
final int[] textureIds = new int[1];
     Потом генерируем свободное имя текстуры, которое будет записано в textureIds[0]
glGenTextures(1, textureIds, 0); 
    Первый параметр определяет, как много объектов текстур мы хотим создать. 
Обычно создаем всего одну. Следующий параметр — имя текстуры, куда OpenGL ES 
будет записывать id сгенерированных объектов текстур. Последний параметр просто 
сообщает OpenGL ES, с какой точки массива нужно начинать записывать id.
Проверяем, если ничего не записано, то возвращаем ноль.
if (textureIds[0] == 0) { return 0; }
   Флаг inScaled включен по умолчанию и должен быть выключен, если нам нужна не масштабируемая версия растрового изображения.  
  final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Загружаем картинку в Bitmap из ресурса
final Bitmap bitmap = BitmapFactory.decodeResource(
        context.getResources(), resourceId, options);
      Объект текстуры по-прежнему пуст. Это значит, что у него по-прежнему нет никаких графических данных. Загрузим наше растровое изображение. Для этого нам необходимо сначала привязать текстуру. В OpenGL ES под привязкой чего-либо понимается, что мы хотим, чтобы OpenGL ES использовал данный конкретный объект для всех последующих вызовов до тех пор, пока мы снова не изменим привязку. В данном случае мы хотим привязать текстуру объекта. Для этого мы используем метод glBindTexture(). Как только привяжем текстуру, сможем управлять ее свойствами, такими как данные об изображении.
Выбираем активный слот текстуры
glActiveTexture(GL_TEXTURE0);
Делаем текстуру с именем textureIds[0] текущей
glBindTexture(GL_TEXTURE_2D, textureIds[0]);
Создаем прозрачность текстуры. Если не написать эти две строки, наш дельфин будет
на черном непрозрачном фоне, как на скриншоте выше.
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND);
    Есть еще одна деталь, которую нам необходимо определить перед тем, как мы сможем использовать объект текстуры. Она связана с тем, что наш треугольник может занимать больше или меньше пикселов на экране по сравнению с тем, сколько пикселов есть в обозначенной зоне текстуры. Например, на экране мы можем использовать гораздо больше пикселов по сравнению с тем, что перенесли из зоны текстуры. Естественно, может быть и наоборот: мы используем меньше пикселов на экране, чем на выделенной зоне текстуры. Первый случай называется магнификацией, а второй — минификацией. В каждом из них нам необходимо сообщить OpenGL ES, как нужно увеличивать или уменьшать текстуру. В терминологии OpenGL ES соответствующие механизмы называются фильтрами минификации и магнификации. Эти фильтры являются свойствами объекта текстуры, как и сами данные изображения. Чтобы их установить, необходимо сначала проверить, привязан ли объект текстуры с помощью glBindTexture(). Если это так, устанавливаем их следующим образом:
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.
GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.
GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
О влиянии фильтров на изображение можно почитать здесь
http://www.learnopengles.com/tag/mipmap/ 
 Переписываем Bitmap в память видеокарты
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
Удаляем Bitmap из памяти, т.к. картинка уже переписана в видеопамять
bitmap.recycle();
    И напоследок снова вызываем метод glBindTexture, в котором в слот текстуры GL_TEXTURE_2D передаем 0. Тем самым, мы отвязываем наш объект текстуры от этого слота.
glBindTexture(GL_TEXTURE_2D, 0);
return textureIds[0];
 Еще раз, мы сначала разместили объект текстуры в слот GL_TEXTURE_2D,
glBindTexture(GL_TEXTURE_2D, textureIds[0]);
 потом выполнили все операции с ним, затем освободили слот. В результате объект текстуры у нас теперь настроен, готов к работе, и не привязан ни к какому слоту текстур.

Доступ к текстурам из шейдера 
В предыдущих уроках мы писали шейдеры в самом теле программы в виде объектов строки. Удобно вынести их в отдельный ресурс, как предложил
Таки образом, в папке проекта res создается папка raw, в которую закладываются два файла
vertex_shader.glsl и fragment_shader.glsl.
Вот их содержимое
vertex_shader.glsl

attribute vec4 a_Position;
uniform mat4 u_Matrix;
attribute vec2 a_Texture;
varying vec2 v_Texture;
void main()
{
    gl_Position = u_Matrix * a_Position;
    v_Texture = a_Texture;
}
      Здесь мы, как и ранее, вычисляем итоговые координаты (gl_Position) для каждой
 вершины с помощью матрицы. А в атрибут a_Texture у нас приходят данные по 
координатам текстуры, которые мы сразу пишем в  varying переменную v_Texture. 
Это позволит нам в фрагментном шейдере получить интерполированные данные 
по координатам текстуры.
 fragment_shader.glsl

precision mediump float;
uniform sampler2D u_TextureUnit;
varying vec2 v_Texture;
void main()
{
    gl_FragColor = texture2D(u_TextureUnit, v_Texture);
}
Вначале устанавливаем среднюю точность расчетов precision mediump float;
В GLSL существует специальный тип униформы, который называется sampler2D. Сэмплеры можно объявлять только во фрагментном шейдере
 uniform sampler2D u_TextureUnit;
 В нем у нас есть uniform переменная u_TextureUnit, в которую мы получаем номер слота тектуры, в котором находится нужная нам текстура. Обратите внимание на тип переменной. Напомню, что из приложения мы в эту переменную передавали 0, как integer. Т.е. переданное в шейдер число (а нашем случае 0) указывает на какой слот текстуры смотреть. 
В varying переменную v_Texture приходят интерполированные координаты текстуры из вершинного шейдера. И шейдер знает, какую точку текстуры надо отобразить в текущей точке треугольника.
Осталось использовать координаты текстуры и саму текстуру, чтобы получить итоговый фрагмент. Это выполнит метод texture2D, и в gl_FragColor мы получим цвет нужной точки из текстуры.
 Исходники скачиваем отсюда

Удачи вам и всего хорошего!

Основные источники:






вторник, 2 августа 2016 г.

­Изучаем OpenGL ES2 для Android

Урок №3. Освещение

Перед тем как начать

Если вы новичок в OpenGL ES, рекомендую сначала изучить уроки №1 и №2, так как данный урок опирается на знания предыдущих уроков.

Основы кода, используемого в этой статье, взяты отсюда:

В результате мы получим такую картинку на экране устройства или эмулятора.

воскресенье, 17 июля 2016 г.

Изучаем OpenGL ES2 для Android

Урок №2. Создание треугольников

Основу кода и идеи я черпал отсюда:
1.     Сатия Коматинени, Дэйв Маклин, Саид Хашими. Android 3 для профессионалов. Создание приложений для планшетных компьютеров и смартфонов.: Пер. с англ. – М.: ООО «И.Д.Вильямс». 2012 – 1024 с.


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

среда, 9 марта 2016 г.

Изучаем OpenGL ES2 для Android

Урок №1

Данная статья написана для новичков, которые (как и я) хотят научиться писать программы для Андроид с использованием OpenGL. Основные мысли и код взяты из замечательной книги Кевина Бразалера «OpenGL ES 2 for Android. A Quick-Start Guide by Kevin Brothaler» (1).
Зачем же пересказывать, спросите вы? Дело в том, что перед этой книгой я прочитал еще десяток статей на эту тему и код Кевина у меня сразу не пошёл (в книге среда разработки Eclipse, а у меня Android Studio). Поэтому, решил написать эту статью так, чтобы было понятно, прежде всего, самому себе.

воскресенье, 3 января 2016 г.

Открыл группу в Вконтакте для начинающих разработчиков.
Присоединяйтесь, начинаем вместе писать новую игру "Убей таракана!"
http://vk.com/public110391938

воскресенье, 15 ноября 2015 г.

Спрайты и работа с ними
Пишем игру под Андроид

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

понедельник, 26 октября 2015 г.

Пишем игру для Андроид
Часть шестая «Столкновения»

Столкновения в играх -  это событие, которое должно привести к другому событию. Например, в нашем случае, если  астероид упадет на базу, то должен произойти взрыв, раздаться звук взрыва и произойти изменения в самой картинке базы. Если астероид упал мимо базы, можно ограничиться меньшим взрывом и его звуком.
Столкновение внешне выглядит, как правило, в виде касания или перекрытия объектов, таким образом, в коде мы должны будем сравнивать определенные координаты тел, и делать выводы.
Координаты астероида меняются в методе update класса public class GameView.

воскресенье, 4 октября 2015 г.

Все разговоры о том, что игрушка вышла сложная - неправда! Я только что освободил тигра! Главное - научиться летать с ведром, когда словишь бело-синюю пилюлю! Смайлик «smile» Кстати, мы уже 29 в рейтинге!
Animal Zoo

пятница, 25 сентября 2015 г.

среда, 23 сентября 2015 г.

среда, 19 августа 2015 г.

Пишем игру для Андроид

Статья пятая. Предварительные настройки и создание лунной базы.

   Вообще-то, планировалось отработать столкновение астероида с базой, но тут я вспомнил, что у нас пока нет самой базы. Астероид летает на голубом фоне, и сталкиваться ему пока не с чем.

   Поэтому было решено создать две картинки лунной базы, которые будут являться фоном.


    Теперь у нас есть, что защищать.

понедельник, 17 августа 2015 г.

Пишем игру для Андроид


Статья четвертая. Игровой цикл.

Очень важно, какой тип игры вы создаете. Так как у нас игра динамическая, нам нужно всё время обновлять картинку на устройстве. Это значит сначала создать её, применив новые координаты объектов, а затем вывести на экран. Наше зрение устроено так, что если мы будем делать это меньше, чем за 0,04 с, то нам будет казаться движение объектов непрерывным. Но объекты могут быть разными по сложности прорисовки, а устройства, на которых вы играете – разными по быстродействию. Может так случиться, что на одних планшетах или мобильниках наше приложение будет «летать», так что пользователь не будет успевать играть, а на других – будет тормозить и глючить так, что пользователь, скорей всего, удалит её со своего устройства. Возникает мысль, проходить один игровой цикл за 0,04с (25 кадров (циклов) в секунду) на всех устройствах. Всё было бы хорошо, если бы все устройства могли это сделать. Представьте, что у вас 10 динамических объектов в игре, которые взаимодействуют между собой, порождая новые объекты, например, взрыв при столкновении, но надо не забыть воспроизводить звуки и реагировать на включения пользователя в игру. Я уже не говорю о реалистичной графике окружающего мира. Что же делать, если наше устройство не успевает в какой-то сцене создать игровой цикл? Решений несколько. Советую вам хорошенько проштудировать эту статью

Кому лень читать на английском, смотрите за вторник, 11 августа 2015 г. мой перевод
«Статья об игровом цикле», автор Koen Witters

Создаем игровой цикл

вторник, 11 августа 2015 г.

Статья об игровом цикле 

автор Koen Witters 

http://www.koonsolo.com/news/dewitters-gameloop/

перевод Валерий Старощук

    Игровой цикл – это сердце любой игры, ни одна игра не может обойтись без него. Но, к сожалению, для начинающих программистов нет хороших статей, которые бы раскрыли эту тему на должном уровне. Но не переживайте, вы только что натолкнулись на статью, которая описывает игровой цикл так, как он этого заслуживает. Благодаря моей работе, как программиста игр, я просмотрел большое количество кода маленьких мобильных игр и всегда поражался, как реализован в них игровой цикл. Вы можете спросить себя, как такая простая вещь, как игровой цикл, может быть реализована по-разному. Однако такое возможно, и я буду обсуждать все за и против самых популярных реализаций, что даст вам (на мой взгляд) оптимальное решение реализации игрового цикла.

Игровой цикл

понедельник, 10 августа 2015 г.

Пишем игру для Андроид

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


Мы отказались от стандартных кнопок Андроида, поэтому нужно иметь собственные изображения кнопки в нерабочем и нажатом состоянии. Я выбрал ненавязчивые кнопки, которые, как мне кажется, будут гармонировать с лунным пейзажем.


 Теперь нужно решить проблему расположения кнопки. Она в том, что на разных экранах наша кнопка должна располагаться примерно в одном и том же месте относительно заднего фона. Я воспользовался методом разметки TableLayout. Фактически мы разобьем экран на 9 прозрачных ячеек (3 строки и 3 столбца). В 8 ячейку вставим изображение светлой кнопки.

Пишем игру для Андроид

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

Мы отказались от стандартных кнопок Андроида, поэтому нужно иметь собственные изображения кнопки в нерабочем и нажатом состоянии. Я выбрал ненавязчивые кнопки, которые, как мне кажется, будут гармонировать с лунным пейзажем.


 Теперь нужно решить проблему расположения кнопки. Она в том, что на разных экранах наша кнопка должна располагаться примерно в одном и том же месте относительно заднего фона. Я воспользовался методом разметки TableLayout. Фактически мы разобьем экран на 9 прозрачных ячеек (3 строки и 3 столбца). В 8 ячейку вставим изображение светлой кнопки.
Но прежде, давайте создадим второе активити, на которое будем переходить при нажатии кнопки. Для этого наведите мышку на папку java, кликните правой кнопкой.