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

Мы наблюдаем общество, которое все больше зависит от машин, но при этом использует их все неэффективнее. (с) Дуглас Рашкофф

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

Опубликовано
Комментарии 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

В этом посте я хочу озвучить что же не так со многими скриптовыми языками. Я очень давно не люблю что JavaScript, что Python, что PHP, но какое-то время не мог чётко сформулировать причину. И вот недавно разбираясь в исходниках opentype.js, я полностью понял что же не так с этими скриптовыми языками.

Главный аспект программирования это управление сложностью, то есть код должен быть простым и понятным для чтения. А значит разрабатывая проект программист должен уменьшать сложность конкретных участков кода, чтобы логика каждого просматриваемого участка кода была понятной другому человеку. Это как жонглирование, или же жонглировать 20-ю яблоками, или например 7-ю, разница почти в три раза. Также и в программировании, уменьшая количество сущностей, которые нужно держать в голове в один момент времени упрощается мысленное жонглирование. По некоторым исследованиям говорят, что оптимальное количество сущностей должно быть не более 7-и, по этой причине, например, советуют не использовать в одном методе более 7 переменных.

Поэтому от профессионала требуется определённая дисциплина, чтобы писать код понятный не только машине, но и другим людям. Многие языки в этом помогают, например наличие классов, наличие строгой типизации, наличие хорошего синтаксиса приближенного к человеческому восприятию и так далее. Так вот JavaScript не имеет из этого ничего, в результате мы имеем очень низкий порог входа, что очень радует людей по складу ума далёких от программирования и в добавок к этому получаем кучу некачественного кода. Да, на JavaScript можно писать хорошо, можно в имена переменных добавлять постфикс или префикс для обозначения типа, эмулировать классы (но про интерфейсы придётся забыть), не злоупотреблять динамической типизацией и прочее. Но для этого человек должен быть сильно дисциплинированным, и вот парадокс – хорошо дисциплинированный человек не посмотрит в сторону JavaScript, Python, PHP и прочего. Разжёвывать дальше недостатки, которыми болеют Python и PHP, у меня нет желания, мысль и так ясна.

Такие языки будут привлекать только людей, которые не испытывают тягу к программированию по-настоящему, а лишь людей с тягой к деньгам, ведь JavaScript и Python на мой взгляд очень раскручены корпорациями и пузырь зарплат разработчиков на скриптовых языках чрезмерно надут. И как вы думаете какого качества проекты в итоге получатся от выше описанных кодеров?

Теги ,
Автор

Опубликовано
Комментарии 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 с помощью своего велосипеда
Также с уверенностью могу сказать, что просторы для оптимизации ещё существуют.

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

Теги , ,
Автор