КОТ++
Прикладное программирование
Системное программирование
Программирование микроконтроллеров

Большинство хороших программистов делают свою работу не потому, что ожидают оплаты или признания, а потому что получают удовольствие от программирования. (c) Линус Торвальдс

Поиск по тегам

Опубликовано
Комментарии 0

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

Почему я не взял готовый движок? Во-первых, NIH, я уже писал про это (там я и сказал пару слов о движке), во-вторых, желание разобраться в работе движков. Движок был назван изначально EEngine – Egor Engine, это название я придумал ещё до момента, когда был выработан концепт движка, после того как уже был готовый движок у меня появилась небольшая капля скромности и название мне перестало нравиться. Но я его не стал менять, просто первую букву E нужно расшифровывать как-то иначе.

Концепт движка заключается в следующем: EEngine отрисовывает графику через GPU – graphics processing unit, на мобилках это видеочип, на компах это видеокарта. GPU по сути работает с 3D графикой, но мой движок 2D, а 2D для GPU это тот же 3D только без манипуляций осью Z, представьте плоский куб, у которого толщина равна 0, на передней и единственной грани рендерится (отрисовывается) текстура – спрайт.

Отрисовка производится шейдерами. Шейдеры – это программы для рендеринга, которые скармливаются GPU, они сообщают видеочипу как отрисовывать каждый пиксель. Так как GPU манипулирует только треугольниками, то в шейдере в общих словах алгоритм таков: рисуем два треугольника, два треугольника это по сути прямоугольник, потом берём текстуру и натягиваем поверх нарисованного треугольника – так мы получим отрисованный 2D спрайт. То есть каждый графический объект является прямоугольником из двух треугольников, такая конструкция называется мешом, меш – набор треугольников определяющих форму графического объекта.

Работа движка осуществлена с помощью паттерна «Игровой цикл», в общем виде игровой цикл должен выглядеть так:

while (1)
{
	processInput(); //обработка пользовательского ввода
	update(); //обработка логики игры 
	render(); //отрисовка
}

В бесконечном цикле вызываются у корневой сцены методы processInput(), render() и update(). Общая суть паттерна игровой цикл в том, что есть бесконечный цикл, а в нём последовательно обрабатываются пользовательский ввод (processInput), отрисовка (render), просчёт логики игры (update) и просчёт физики (физику не разбираю в этой статье). О игровых паттернах очень хорошо рассказано в этой книге, как раз таки в ней я о них и узнал.

Все графические объекты являются нодой (узлом) связного списка, в итоге возможность ноды иметь дочерние объекты порождает дерево графических объектов, отрисовка выполняется обходом по дереву, у каждой ноды берётся текстура и отрисовывается по необходимым координатам шейдером. Аналогичным образом идёт вызов метода update() у объектов. И аналогичным образом реализованы сигналы и слоты у элементов UI, реализовал работу сигналов-слотов будучи вдохновлённым Qt. UI элементы это те же графические элементы, но расширены функционалом сигналов-слотов для работы с пользовательским вводом. У UI элемента достаточно подписаться на определённое событие, добавив слот. Потом когда движок породит сигнал, что событие произошло, то также через обход дерева найдётся объект, который был подписан на этот сигнал.


root – корневая нода, с неё начинается обход дерева

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

После я принялся за разработку физического движка, к сожалению работу над физическим движком я не завершил до конца. Если у меня дойдут руки, то выложу материал по физическому движку.

Технические подробности (новичкам можно пропустить абзац): движок был написан на AS3, использован для мобильных игр на Adobe AIR, например эта. Движок я не выполнил отдельной библиотекой, поэтому исходники смешаны с исходниками самой игры по ссылке, потому что разрабатывал его параллельно с написанием игры. Изначально игра была написана чисто на Flash без движков, но настал момент, что потребовалась оптимизация, для оптимизации я решил написать свой движок. Шейдеры были написаны на AGAL – Adobe Graphics Assembly Language, адобовский мини ассемблер. Бесконечный цикл у меня реализован не особо привычном образом, флеш генерирует событие ENTER_FRAME строго соотвествуя FPS и я подключил цикл к этому событию.

Теги , , , , ,
Автор

Опубликовано
Комментарии 0

Кокос на момент написания статьи не поддерживает из коробки Dragon Bones спрайты. Поэтому сначала нужно подружить кокос и драгон бонс. Для этого нужно скачать исходники Dragon Bones C++ Runtime. Нам понадобятся из этого репозитория директории: DragonBones/src/dragonBones/dragonBones, Cocos2DX_3.x/src/dragonBones/cocos2dx, 3rdParty/rapidjson. В проекте должна быть такая структура:

Ваш проект
    |-- Classes
        |-- rapidjson
            |-- ...
        |-- dragonBones
            |-- animation
            |-- armature
            |-- ...
            |-- cocos2dx
            |-- ...
    |-- Resources
        |-- ...

Далее, если проект win32, то остаётся добавить все эти файлы в проект через Добавить->Существующий элемент…

Использовать Dragon Bones спрайты в коде не сложно, допустим ваш Dragon Bones проект называется man, тогда вы должны кинуть в папку Resources файлы: man_ske.json, man_tex.json, man_tex.png (эти файлы получаются через экспорт из программы Dragon Bones).
Подгрузит анимацию и добавит её на сцену следующий код:

#include "cocos2d.h"
#include "dragonBones/cocos2dx/CCDragonBonesHeaders.h"
...
auto factory = dragonBones::CCFactory::getFactory();
factory->loadDragonBonesData("man_ske.json");
factory->loadTextureAtlasData("man_tex.json");
auto armatureDisplay = factory->buildArmatureDisplay("Armature", "man");
scene->addChild(armatureDisplay);

Проиграть анимации по названию поможет следующий код:

armatureDisplay->getAnimation()->play("idle"); //зацикленно
armatureDisplay->getAnimation()->play("hit", 1); //один раз

Советую не наследоваться от класса CCArmatureDisplay, потому что я уже наступил на эти грабли и ловил непонятные ошибки времени выполнения. Поэтому лучше сменить отношение к классу CCArmatureDisplay с является на содержит, только так я смог избавиться от проблем непонятного происхождения во время выполнения программы.

Важно: для прорисовки всех объектов в анимации в игре – каждый объект должен иметь кость. Если у какого-то объекта не будет кости, то в игре этот объект не прорисуется, хотя сама программа Dragon Bones будет отображать.

Также в используемых c++ исходниках Dragon Bones существует проблема прорисовки спрайтов, если спрайт находится на границе видимой области сцены. Эта проблема фиксится в файле CCArmatureDisplay.cpp в методе DBCCSprite::_checkVisibility. Я подробно не разбирался в алгоритме работы этого метода, только в общих чертах, мне помогло увеличение рамки у прямоугольника visiableRect, исправленный фрагмент кода из метода _checkVisibility:

visiableRect.origin.x -= wshw * 4;
visiableRect.origin.y -= wshh;
visiableRect.size.width += wshw * 8;
visiableRect.size.height += wshh * 2;

В заключение хочу сказать, что очень хорошо чувствуется бесплатность Cocos2d и Dragon Bones, в них присутствуют различные проблемы, которых вероятно нет в платных аналогах. Но что кокос, что драгон бонс не стоят на месте, развиваются, а данная публикация должна помочь разобрать некоторые проблемы, связанные с этими инструментами.

Теги , , ,
Автор

Опубликовано
Комментарии 0

Недавно достаточное количество времени промучался с собиранием кокосовского проекта под android. И так небольшое количество туториалов, статей и видео зарубежных коллег, да и эти не точны, а вероятно относятся к старым версиям, поэтому и не работают. А на русском инструкций практически нет, а которые есть в основном бесполезны. Так как проблему сборки я решил, то хочу поделиться решением. Для особо внимательных – да да я снова занимаюсь разработкой игры.

И так, я предполагаю, что проект cocos2d-x у вас уже создан и вы уже посмотрели как выглядит этот проект на windows, к слову сборка именно под windows очень простая, достаточно открыть win32 проект в Visual Studio и скомпилить. Под android не так просто, нам понадобится Android Studio. Устанавливаем через Android Studio одну из актуальных Android SDK, и ещё ставим NDK, все эти действия легко делаются в Tools->Android->SDK Manager. Добавляем в переменную среды Path путь до cmake, cmake находится в папке с Android SDK %LocalAppData%\Android\sdk\cmake\3.10.2.4988404\bin (название папки с версией вероятно у вас может быть другое, сами посмотрите какое у вас). JDK нам устанавливать не нужно, в пакете Android Studio оно уже есть.

Выбираем NDK (если возникнут какие-то проблемы, то вероятно нужны ещё другие компоненты что на скрине, но у меня они уже были установлены)

Ставим путь до cmake

Теперь нам надо исправить следующие файлы: CMakeLists.txt (лежит в корне кокос проекта) и Android.mk (лежит в proj.android/app/jni). Если у вас HelloWorld проект и больше не добавляли никаких классов, то можно пропустить этот шаг. Суть в том, что нужно добавить недостающие пути к исходникам в эти файлы. В CMakeLists.txt нужно добавить и .cpp и .h файлы. В Android.mk добавить только все .cpp файлы, а также прописать все директории в которых есть .h файлы.

Пример исправлений в файле CMakeLists.txt:

<...> - тут остальная часть файла, которую менять не надо
list(APPEND GAME_SOURCE
     Classes/AbstractGameObject.cpp     
     Classes/AppDelegate.cpp     
     Classes/ChoiceMenu.cpp     
     Classes/Contestant.cpp     
     Classes/dragonBones/animation/Animation.cpp     
     Classes/dragonBones/animation/AnimationState.cpp     
     Classes/dragonBones/animation/BaseTimelineState.cpp     
     Classes/dragonBones/animation/TimelineState.cpp     
     Classes/dragonBones/animation/WorldClock.cpp     
     Classes/dragonBones/armature/Armature.cpp     
     Classes/dragonBones/armature/Bone.cpp     
     Classes/dragonBones/armature/Constraint.cpp     
     Classes/dragonBones/armature/DeformVertices.cpp     
     Classes/dragonBones/armature/Slot.cpp     
     Classes/dragonBones/armature/TransformObject.cpp  
     <...> - тут ещё куча файлов .cpp
     )
list(APPEND GAME_HEADER
     Classes/AbstractGameObject.h
     Classes/AppDelegate.h
     Classes/ChoiceMenu.h
     Classes/Contestant.h
     Classes/dragonBones/animation/Animation.h
     Classes/dragonBones/animation/AnimationState.h
     Classes/dragonBones/animation/BaseTimelineState.h
     Classes/dragonBones/animation/IAnimatable.h
     Classes/dragonBones/animation/TimelineState.h
     Classes/dragonBones/animation/WorldClock.h
     Classes/dragonBones/armature/Armature.h
     Classes/dragonBones/armature/Bone.h
     Classes/dragonBones/armature/Constraint.h
     Classes/dragonBones/armature/DeformVertices.h
     Classes/dragonBones/armature/IArmatureProxy.h
     Classes/dragonBones/armature/Slot.h
     Classes/dragonBones/armature/TransformObject.h
     <...> - тут ещё куча файлов .h
     )
<...> - остальная часть файла, которую менять не надо

Пример исправлений в файле Android.mk:

<…> – тут остальная часть файла, которую менять не надо
LOCAL_SRC_FILES := hellocpp/main.cpp \ ../../../Classes/AbstractGameObject.cpp \ ../../../Classes/AppDelegate.cpp \ ../../../Classes/ChoiceMenu.cpp \ ../../../Classes/Contestant.cpp \ ../../../Classes/dragonBones/animation/Animation.cpp \ ../../../Classes/dragonBones/animation/AnimationState.cpp \ ../../../Classes/dragonBones/animation/BaseTimelineState.cpp \ ../../../Classes/dragonBones/animation/TimelineState.cpp \ ../../../Classes/dragonBones/animation/WorldClock.cpp \ ../../../Classes/dragonBones/armature/Armature.cpp \ ../../../Classes/dragonBones/armature/Bone.cpp \ ../../../Classes/dragonBones/armature/Constraint.cpp \ ../../../Classes/dragonBones/armature/DeformVertices.cpp \ ../../../Classes/dragonBones/armature/Slot.cpp \ ../../../Classes/dragonBones/armature/TransformObject.cpp \ <…> – тут ещё куча файлов .cpp

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../Classes \ $(LOCAL_PATH)/../../../dragonBones/ \ $(LOCAL_PATH)/../../../dragonBones/animation \ $(LOCAL_PATH)/../../../dragonBones/armature \ $(LOCAL_PATH)/../../../dragonBones/core \ $(LOCAL_PATH)/../../../dragonBones/events \ $(LOCAL_PATH)/../../../dragonBones/factories \ $(LOCAL_PATH)/../../../dragonBones/geom \ $(LOCAL_PATH)/../../../dragonBones/model \ $(LOCAL_PATH)/../../../dragonBones/parsers \ $(LOCAL_PATH)/../../../dragonBones/cocos2dx \ $(LOCAL_PATH)/../../../dragonBones/textures \ $(LOCAL_PATH)/../../../Classes/rapidjson \ $(LOCAL_PATH)/../../../Classes/rapidjson/error \ $(LOCAL_PATH)/../../../Classes/rapidjson/internal \ $(LOCAL_PATH)/../../../Classes/rapidjson/msinttypes \ <…> – тут ещё остальные директории
<…> – остальная часть файла, которую менять не надо

Лайфхак как добавить исходные файлы если у вас их очень много. Зайдите в cmd в папку с исходниками, наберите:

dir /B /s | find ".cpp"

Выведет список всех .cpp в проекте, осталось только подкорректировать текст автозаменой и добавить пути в CMakeLists.txt и в Android.mk.

Почти готово, теперь открываем папку proj.android в Android Studio как проект. Главное иметь достаточно места на диске, сборка съест около 2-3 гигов. Нажимаем Build->Build APK в Android Studio, ждём минут 15, после можем радоваться собранному APK. В итоге всё достаточно просто если есть инструкция.

Теги , , , ,
Автор

Опубликовано
Комментарии 0

Давно не прогал под андроид, и вот опять. Говорил я уже или нет, но дико не люблю разработку под android, потому что с появлением новых версий андроида мы получаем несовместимость старых уже опробованных функций, а версии обновляются чаще чем пишутся статьи и туториалы. А если ещё добавить к этому уравнению обилие китайских прошивок, в которых поведение системы не так предсказуемо как в официальных, то при разработке на android вместо написания приложения приходится заниматься исследованиями того, какие функции работают иначе на различных прошивках.

В этой статье как уже было ясно, я бы хотел поговорить о создании файлов на android. И так, андроид предлагает нам два способа записи: на внутреннюю память и на внешнюю. Для записи на внутреннюю нам не нужны никакие разрешения, мы просто должны записать в:

context.getDataDir()
– этот метод вернёт скрытую директорию приложения, то есть файлы потом будут доступны только приложению, которое их записало. Хорошо, безопасность и всё такое, но, а что если нужно записать в галерею или документы, или просто в какое-то общее хранилище? Тогда мы должны писать во внешнюю помять и просить разрешения WRITE_EXTERNAL_STORAGE. В некоторых туториалах пишут, что это разрешение для записи на sd карту, но если следовать здравому смыслу, то под external понимается память, которая не internal, а internal – это память, которая доступна только одному приложению, которое и пишет эти файлы. То есть external – это не то внешнее что на sd карте, а то внешнее что доступно всем, то есть общее хранилище, которое нам и нужно. К слову, в Android Q уже не нужно это разрешение и там «гении» из гугла придумали что-то новое для разработчиков.

Добавляем разрешение, пишем код для записи в файл, но не работает. Ах да, с Android 6.0 у нас же появились runtime разрешения, и для записи во внешнее хранилище нужно запросить разрешение прямо из кода. Пишем:

//проверяем принято ли данное разрешение
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
{   
        //запрашиваем разрешение у пользователя
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}

Ага круто, теперь то мы действительно можем записывать файлы. Пишем:
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
File file = new File(path, FILE_NAME);
FileWriter fileWriter = new FileWriter(file, true);
fileWriter.append(data);
fileWriter.flush();
fileWriter.close();

Проверяем на телефоне, файл есть, так теперь подключаем телефон к компьютеру и ищем наш файл. Файла нет, класс! Windows не видит файл или android не даёт увидеть файл? Ну, конечно, виноват android, кто же ещё. Смысл в том, что android должен просканить media, найти новый файл и тогда каким-то чудом он покажет файл винде. Делается это так:
MediaScannerConnection.scanFile(context, new String[]{filePath}, null, null);

Таким образом мы заставляем андроид просканить медиа и показать файл в windows. К слову при перезаписи файла, надо снова выполнять метод scanFile, иначе windows не видит изменений. Но, конечно, вышесказанное справедливо только для windows 7, на windows 10 я не проверял, а linux вероятно всего и так всё увидит, но линуксом не пользуются рядовые пользователи, поэтому обсуждать тут это не буду.

На этом всё, я думаю в статью ещё есть что добавить, поэтому если есть неясные моменты на данную тему, то спрашивайте в комментариях.

Теги , , ,
Автор

Опубликовано
Комментарии 0

Привет, котаны! Я недавно подумал, что этот блог не совсем подходит для новичков в программировании. Т.е. я не даю каких-то основ или помощи начинающим программистам. И я решил, что это пора исправить. Эта статья откроет целый цикл статей, которые помогут освоиться в программировании новичку. Почему именно мой блог может помочь в этом? Ведь есть множество статей на данную тему и множество книг. Это верно. Но я не встречал публикаций, которые могут максимально точно заложить в новичка начальных знаний для некоторого понимания процесса программирования. Т.к. я практически самостоятельно прошёл путь от полного аномального непонимания программирования до уверенной разработки программных продуктов, то на своём опыте знаю что и как нужно объяснить человеку для этого. Сейчас прошу отойти от экранов опытных программистов, и остаться тем, кто только-только осваивается или хочет освоиться в мире программирования.

Базовые вещи, которые нужно знать, чтобы упростить понимание:
1. Программа – набор команд, которые компьютер выполняет по очереди.
2. Программа пишется, с помощью инструкций (команд), эти инструкции ещё называются операторами.
3. У программы всегда есть точка входа (место в коде откуда она запустится). Но так называемой точки выхода может и не быть, т.е. программа может и не быть (программа должна работать всегда).

Далее чтобы очень быстро окунуться в мир программирования лучше начать с практики. Т.е. предлагаю метод от к практике к теории. Ставите себе не очень сложную задачу по программированию, пытаетесь решить, в процессе решения возникнет много вопросов, которые просто нужно уметь правильно сформулировать и гуглить.

Например, я когда начинал изучать программирование почему-то захотел написать программу по переводу чисел из различных систем счисления в различные системы счисления. До этого я вообще ничего не умел и не знал в программировании. Некоторое время потупил в среде PascalABC и ко мне пришло хоть какое-то понимание. В итоге моя задача разбилась на несколько подзадач: ввод данных, разбиение числа на цифры, хранение этих цифр, проверка условий для перевода в нужную систему счисления из заданной, перевод, вывод рассчитанной информации. Далее эти подзадачи в голове разбивались ещё на подподзадачи и т.д. Годы практики и всё это тщательное обдумывание решения будет уже на автомате, нужен только опыт. А самое сложное это начать, преодолеть тот момент, когда мозг уже кипит, а понимания нет, просто нужно пытаться до тех пор пока не придёт это так называемое понимание.

Вот и вся базовая информация, которая поможет именно начать осваиваться в программировании. И для помощи начинающим в группе КОТ++ открываю обсуждение, в котором новички могут задать вопрос по теме программирования и я или другие опытные программисты возможно ответят советом, но это не точно

Теги , ,
Автор

Опубликовано
Комментарии 0

В данном посте хочу поделиться очень хорошим прогерским лайвхаком. С помощью этого трюка можно внедрить в основной код проверку на правильность ввода, на какие-либо другие пользовательские ошибки и многое другое. Ведь как это иногда бывает: писать основной код очень весело, а вот делать различные проверки на ошибки в ходе выполнения программы очень и очень скучно и не охото. Как только вы прочитаете этот пост всё будет иначе!

Нужно знать всего несколько правил:
1) не используйте в основном коде ифы (if) для проверки на ошибки в ходе выполнения;
2) не используйте коды ошибок, вместо них используйте классы ошибок (например в c# собственные классы ошибок наследуйте от Exception, в java от RuntimeException);
3) при проверке на ошибки используйте блоки try ... catch.

И это всё, что нужно знать чтобы лвлапнуться в мире кодинга!

Завершу примерами кода и примечаниями, которые прояснят всё ситуацию.

Проверка заполненности текстовых полей и их валидация (андроид). Смотрите как удобно, у нас нет огромного количества веток из ифов. Если пользователь сделал что-то не так, то мы с помощью throw выбрасываем ошибку и выполнение кода дальше не идёт, а вместо этого в блоке catch пользователю выдаётся сообщение об ошибке, которое зависит от типа исключения.
try
{
       String email = ((EditText)findViewById(R.id.login_edittext_email)).getText().toString();
       String pass = ((EditText)findViewById(R.id.login_edittext_pass)).getText().toString();

       if ((email.isEmpty()) || (pass.isEmpty()))
       {
                throw new EmptyEditTextException();
       }
       else if (!Utils.isValidEmail(email))
       {
                throw new NotValidEmailException();
       }

       ...//создаём параметры для входа
       account.login(params);
}
catch (NotValidEmailException e)
{
       Toast.makeText(getApplicationContext(), "Некорреткный email адрес", Toast.LENGTH_SHORT).show();
}
catch (EmptyEditTextException e)
{
       Toast.makeText(getApplicationContext(), "Не введён логин или пароль", Toast.LENGTH_SHORT).show();
}
Или вот ещё пример на c#. Проверяем сначала всё ли готово для печати, если нет, то показываем ошибку, если да, то начинаем печать. Опять же код мог бы быть куда более раздутым без try ... catch. Обратите внимание, что блоки try ... catch легко можно добавить в уже готовый код. Т.е. сначала можно реализовать основную логику программы, а потом спокойно добавить обработку ошибок.
try
{
       checkExistAdobeReaderPath(); //проверяем указан ли путь до Adobe Reader
       checkOpenedPdfFile(); //проверяем выбран ли PDF на печать
       /*если же какое либо условие не выполнилось то эти функции выкинут exception, а в catch мы их отловим)*/
       printDocument(); //отправляем на печать
}
catch (PdfFileNotOpenedException)
{
       MessageBox.Show("Pdf файл не выбран", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (AdobeReaderPathNullException)
{
       MessageBox.Show("Не указан путь к AdobeReader", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

Теги ,
Автор

Опубликовано
Комментарии 0

Котострофа

В этом посте напишу про небольшой совет, касательно gps и разработки с ним. Ранее у меня бывали ситуации, когда был заказ на приложение с функционалом с gps, а у меня ну вот никак этот gps на телефоне не работал почему-то, на эмуляторе с фиктивными геоданными тоже. Код приходилось писать в слепую, без возможности проверить на работоспособность. Я соответственно не понимал в чем дело и грешил на особенность телефона, особенность gps датчика, на всё что угодно. Но у заказчика код работал, и я не парился. Кто-то скажет, что это мой код просто кривожопый. Но моим аргументом будет то, что на телефоне не работало ни одно приложение с gps, в том числе и 2гис.

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

В заключение хочу сказать, что в своих приложениях лучше предлагайте пользователям помимо gps ещё и альтернативу. А также, если у вас перестал работать gps, то вайпните своё устройство)

Теги , , ,
Автор

Опубликовано
Комментарии 0

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

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

По той же причине важно тоже самое проделать для сайта, т.к. скорость загрузки сайта зависит от его объёма и в свою очередь влияет на конверсию.

Первый способ, который поможет уменьшить объём памяти, занимаемой картинками, это использование программы image catalyst. С помощью этой программы мне удалось сжать графику (я использовал png формат) для своей игры почти на 30% и на глаз такое сжатие не заметно. В этой программе есть два режима сжатия advanced и xtreme, я всегда использую xtreme.

Второй способ будет немного поинтересней. Заключается он в том, что однообразные картинки нужно положить на один холст и сохранить в виде одного изображения. Например, если изначально есть две картинки размерами 128×128, то на выходе мы должны получить одну 256×128, но содержащую в себе два изображения. Никогда не думал, что таким образом удастся сэкономить много памяти, но я ошибался. У меня были 5 картинок размером 128×128 и весили они в сумме 45кб, склеив я получил одну картинку 640×128 с пятью изображениями, но весом уже 17кб! Получается почти 40% экономии.

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

Ну а на сайтах использование таких спрайтовых методов ещё проще. Определением местоположения кадра в изображении занимается css. Это делается через свойство background-position. То есть сдвигая изображение можно использовать разные кадры на этом изображении.

Теги ,
Автор

Опубликовано
Комментарии 0

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

Разрабатывая очередную игру я столкнулся с проблемами производительности прорисовки графики стандартными средствами. Т.к. жутко не хотелось использовать Starling, самый популярный графический движок для AIR, одобренный адобовцами, то было решено изобрести велосипед.
После прочтения нескольких книг по Molehill в голове уже образовалась архитектура будущего графического движка для игры. За пару вечеров движок был написан.

Бенчмарк тесты показали такой фпс при 1200 анимированных объектах, анимация которых состоит из 4 кадров:
10 при отрисовке через обычный display list
22 с помощью starling
30 с помощью своего велосипеда
Также с уверенностью могу сказать, что просторы для оптимизации ещё существуют.

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

Теги , ,
Автор