• Какой проект начать разрабатывать, чтобы продемонстрировать свои знания "работадателю"?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    У разработчиков часто руки чешутся что-нибудь свое накатать. И не всегда это прямо какие-то практические проекты. Чаще это просто примеры работы какого-либо подхода, какие-либо тесты, какие-нибудь наброски. И часто это все сваливается в личном пространстве гитхаба.

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

    Именно такая свалка произвольного кода, созданная с творческим подходом, созданная чисто для себя (а не для собеседований), без ограничений в решениях свыше, дает лучшее представление о знаниях и способностях человека.

    Советую выбрать проект именно из своих желаний. Из того, за что руки чешутся взяться.
    А если хочется не для себя, а для собеседований, то лично я советую сразу начать стыдиться такого проекта и никому его не показывать.
    И еще советую прочитать вот это: blog.gamedeff.com/?p=64 Актуальность текста лежит далеко за пределами геймдева.
    Ответ написан
    Комментировать
  • Как решить проблему с ООП?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Да вы, батенька, король контента!

    Эт шутка. Хоть и не смешная для самоделкиных из области геймдева. :)
    Так, а кроме шуток. Не надо вообще так делать. Тип наносимого урона/снаряда/юнита/орудия никогда не должен описываться его собственным классом. Все юниты должны быть одного класса, все пули - тоже, все здания - тоже.
    У тебя должен быть только набор общих классов для сущностей. Конкретные параметры (что откуда вылетает, как дамажит и как это все визуально выглядит) должны лежать в ресурсах.
    Рекомендую почитать: habrahabr.ru/post/255561 Изложенное куда более важно для области любимых самокатов на С++.

    Хотя... Если тебе так надо реализовать на классах, а не на ресурсах, то тебе надо просто организовать фактическую связь между сущностями. Давай предложу один страшный вариант... Дебажить это все чур самому.
    class TowerParent
    {
    public:
    	// Это тип для той штуки, которая будет создавать нам нужную боеголовку.
    	typedef FireParent* (*FireConstructor)( float, float, int, float, float, float );
    	
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    	
    	FireParent* towerFire;
    protected:
    	FireConstructor	fire_constructor; // инстанция конструктора боеголовок.
    	
    	// Вот так мы будем определять тип конструируемой боеголовки.
    	template< class fire_t >
    	inline void DefineFireType(){
    		fire_constructor = TowerParent::template MakeFire<fire_t>;
    	};
    	
    private:
    	// А вот так мы будем конструировать боеголовку.
    	template< class fire_t >
    	static FireParent* MakeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    
    SimpleTower( int _col, int _row, float _posX, float _posY, float _attackRange, float _angle ){
    	DefineFireType<Bullet>();
    };
    
    RocketTower(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle){
    	DefineFireType<Rocket>();
    };


    Конструирование делается через:
    towerFire = fire_constructor( .... );

    Еще одна поправка. Не Rocket, а Missile. Потому что ... :)

    UPD:
    Еще один вариант, уже менее страшный - это шаблонизация базового класса:
    template< class fire_t >
    class TowerParent
    {
    public:
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    
    	fire_t* towerFire;
    
    protected:
    	inline fire_t* MkeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    class Bullet : public FireParent<Bullet>
    {
    public:
    	Bullet(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    
    class Rocket : public FireParent<Rocket>
    {
    public:
    	Rocket(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    Ответ написан
    1 комментарий
  • C++ teplate class. Пoмогите! Почему вылетает segfault при попытке запихнуть std::string?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Штука первая, делать надо не так:
    array = (Type*) malloc(100);
    а так:
    array = new Type[ 100 ];
    или, лучше, вот так:
    array = static_cast<Type*>( new uint8_t[ 100 * sizeof( Type ) ] );


    Штука вторая. У тебя в выделенном блоке памяти мусор лежит. Надо сперва память подготовить. Делаем вот так:
    memset( array, 0, 100 * sizeof( Type ) );
    Это приведет к очистке памяти. Но и после этого память все еще нельзя использовать.

    Следом надо переделать вот так:
    void push_back(Type elem) {
        new( array ) Type();
        array[0] = elem;
      }

    или так:
    void push_back(Type elem) {
        new( array ) Type( elem );
      }
    Ответ написан
    Комментировать
  • Как реализовать вычисление булевой функции на Java?

    @MarkusD
    все время мелю чепуху :)
    Тебе поможет польская нотация, синтаксическое дерево и синтаксический анализ.

    Я бы сделал так.
    Прохожу по входной строке слева направо, разбивая ее на лексемы (токены, символы). Правило разбиения такое: если символ под кареткой просмотра является буквой - это операнд, иначе пробуем распознать оператор.
    Далее, получив список лексем, я прошелся бы по нему с целью построения дерева операций (польская нотаця, не забывай!). Этот шаг даст мне список операций и операндов. Дерево всегда будет бинарным, даже если там будет унарная операция, это все равно лишь один узел дерева.
    Далее я бы составил таблицу значений операндов и вычислил бы по ней значение получившегося дерева операций.

    Тебе может помочь вот этот репозиторий:
    https://github.com/FrankStain/c-script

    И, в частоности, вот этот код:
    https://github.com/FrankStain/c-script/blob/master...

    Это Object Pascal, но не стоит его чураться. Почитай, покури.

    Вноси уточнения в комментах, я буду дополнять свой ответ.
    Ответ написан
    1 комментарий
  • Что должна вернуть функция NULL, если ничего не найдено?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Так... давай-ка немного поправим твое понимание предмета.

    В С/С++ существует такой тип даных, как указатель на данные. Его синтаксис выглядит так:
    int* value;

    Запись буквально означает что 'value' у нас будет хранить адрес на область данных с предположительной типизацией в четырехбайтовое знаковое целое. Внимательно! 'value' хранит только указатель, никаких данных о размере блока этих данных нет, никаких строгих оговорок о типе этих данных нет (только предположение что это 'int').

    При формировании переменной оная, обычно, ничем никак не инициализируется. То есть, после определения нашего 'value' в его значении лежит любой немыслимый мусор. Внимательно! Нет никаких способов (кроме как обратиться и словить AV/SIGSEGV) определить что значение 'value' ссылается на правильный адрес. Поэтому этот самый мусор, который в 'value' и содержится, можно спокойно использовать как адрес блока данных и при обращении по этому адресу получить от ОСи по рукам.

    Вопрос! Как этого избежать?
    Есть очень простой и очень старый выход - определить некую магическую константу, которая точно смогла бы символизировать чистоту указателя (что указатель не одержит адреса). Именно такой константой 'NULL' и является.
    Адрес на блок данных может быть абсолютно любым! Он может быть даже 0xA0L. Но если в значении указателя записан 0 (это и есть NULL), значит указатель чист - он не содержит в себе адрес на блок данных.

    Итак! NULL - это не зло. NULL - это признак чистоты указателя!

    Теперь перейдем к "тонкостям и нюансам нолика"...

    Попробуй собрать такой код:
    typedef int* p_int;
    
    p_int value = p_int();
    printf( "ptr : 0x%08x\n", value );

    Вывод в консои будет : "ptr : 0x00000000". О чем это говорит? Это говорит о том, что возвращаемое инициализатором указателя значение (вот эта вот запись: "p_int()") всегда эквивалентно NULL.

    Можно было бы с точно таким же холоднокровием написать вот так:
    p_int value = NULL;
    И все осталось бы по прежнему.

    Дело в том, что NULL имеет тип "void*", а этот тип можно преобразовать абсолютно к любому иному указателю.
    И NULL самостоятельно приводится к нужному типу в операторе присвоения указателя этого типа. Ноль - он и в Африке ноль.

    Попробуй собрать такой код:
    int* value = NULL;
    delete value;


    Если бы 'value' небыл инициализирован, то с превеликой вероятностью оператор delete привел бы к падению приложения. 'delete' воспринял бы мусор как правильный указатель и попробовал бы освободить память по этому указателю, а так как это мусор, ОСь эту попытку забрила бы, выдав программе красную карточку. Вместе с тем, оператор delete спроектирован так, чтобы не обращать внимания на NULL. Оператор просто тихо завершается если видит нулевой адрес.
    Вывод: чистота NULL не нуждается в большей очистке! :)

    Вот так. Надеюсь, моя простыня текста хоть немного да поможет тебе.

    А по вопросу о функции - да возвращай NULL! Так все делают, чем ты хуже? ;)
    Ответ написан
    Комментировать
  • Какие ЯП не требуют кучу прикладнухи для устройства на работу?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    LUA скриптер может знать только LUA и работать без затруднений.
    Python скриптер может знать только свой ЯП и спокойно работать.

    Уяснить тебе стоит одну очень важную вещь. Один лишь нужный ЯП знать для работы может только скриптер.
    Для разработчика-же крайне важны знания как можно более широкой информационной периферии своей области.
    Ищи игровые студии/компании которым нужны LUA/Python скриптеры. Но запомнить надо еще одну вещь - для очень многих людей это дорога без возврата.
    Ответ написан
    1 комментарий
  • Как продвинуть отзыв на 1е место/страницу в Google Play?

    @MarkusD
    все время мелю чепуху :)
    В Google Play отзывы сортируются. Внимательнее погляди на панельку просмотра отзывов. По умолчанию они сортируются по их полезности. Стало быть, как вкопанный на первом месте стоит именно самый полезный отзыв.
    Вопрос в том, как этот отзыв сделать самым непревзойденно полезным. Ответ тоже прост.
    1. Существуют специализирующиеся на продвижении товаров компании. Суть работы такой компании сводится к тому, чтобы привлекать в свои ряды любителей халявы. Халявщику дают задание продвинуть своим голосом тот или иной товар. Халявщик за свое действие получает монетку в долларах или рублях. Зависит от компании.
    2. Существуют компании, в ведении которых находятся реальные бот-сети - тысячи и миллионы зараженных вирусом/трояном пользовательских хостов. Бот-сеть способна безупречно продвинуть любой товар на любую позицию.


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

    Очень много текущих топовых товаров на GP/AppStore продвинуты с использованием одного или обоих способов. При чем, изначально такие услуги оказывались только в отношении магазина Apple. И лишь сравнительно недавно раковая опухоль перекинулась на Android.
    Ответ написан
    1 комментарий
  • Предподчтительные средства разработки под C++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Не хотелось бы показаться экстремистом, но все же...
    В бизнесе нет места хобби, это даже я (фанатик и наркоман кодописания, получающий кайф просто от написания кода) был вынужден осознать.
    В бизнесе важнее всего малое время разработки с соблюдением как можно более высокого качества конечного продукта. Если у тебя получится показать приемлемую скорость выполнения задач с использованием консольных редакторов текста, отладчика и средств менеджмента кода, то ни кто и слова не скажет.
    Однако, практика показывает что в бизнесе удобнее использовать комплексные решения сборки проектов. От сред разработки и до средств непрерывной интеграции.
    Ответ написан
  • Какой вариант оптимальней?

    @MarkusD
    все время мелю чепуху :)
    По первому блоку:
    switch( b )
    {
    case 4:
    	GiveMe();
    case 3:
    	CallMe();
    	break;
    };

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

    Касательно второго блока: предлагаю самостоятельно собрать в дебаге и релизе следующий код, да убедиться в сомнениях самому.
    class A
    {
    public:
    	inline size_t begin()
    	{
    		printf( "begin();\n" );
    		return 0;
    	};
    	
    	inline size_t end()
    	{
    		printf( "end();\n" );
    		return 10;
    	};
    };
    
    A cont;
    for( size_t iter = cont.begin(); cont.end() > iter; ++iter )
    {
    	printf( "%d\n", iter );
    };


    Но тут всегда надо понимать, на какие траты допустимо идти ради удобства. Не стоит увлекаться вторыми переменными в объявлении for если тот же "end()" является легкой и оптимизируемой компилятором конструкцией. В этой связи советую уделить куда большее внимание области инкремента в "for".
    Постфиксная форма всегда приводит к созданию временной переменной, префиксная - нет. Префиксные формы для счетчиков циклов в Google Code Style помечены как хорошая практика.
    Ответ написан
    Комментировать
  • Каким путем развиваться как специалист в геймдеве?

    @MarkusD
    все время мелю чепуху :)
    За плечами 2,5 курса универа, более менее вменяемо освоен язык С++ и есть понимание как пишутся программы.

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

    Так вот, нужен ли я буду с знаниями как программируется сама графика и движки, но без умения работать с готовыми решениями?

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

    Ты молодой человек, дело ясное что руки у тебя чешутся за все сразу взяться. Попробуй решить свой вопрос кратковременными занятиями во всех направлениях. Unity тебе поможет быстро вополтить игру в прототипе. Разные MDK к существующим играм помогут со скриптингом/модингом. Пакеты типа SDL/Cocos2d вполне смогут помочь тебе быстро поднять окружение для изучения невидимых с верхнего уровня фишек, типа вот таких:
    habrahabr.ru/post/241760
    habrahabr.ru/post/248381
    habrahabr.ru/post/250467
    habrahabr.ru/post/238425
    habrahabr.ru/post/248313
    habrahabr.ru/post/244367

    А важны всегда все люди. Точно сказать не получится: важнее ли ипользователи UDK чем его создатели. Каждый просто занял свое место в индустрии.
    Ответ написан
    Комментировать
  • Как парсить структуры с битовыми полями в С?

    @MarkusD
    все время мелю чепуху :)
    Так. Для начала...
    void* p = 02b05e1...

    SectionSyntaxIndicator=00 -Должна быть 1


    Первые 8 бит отходят к "unsigned tableID", все норм. А потом в описании идет однобитовый "unsigned SectionSyntaxIndicator".

    Значение второго байта == 0xB0, его первый бит == 0 и в результате все нормально.
    Первые 4 бита 0xB0 равны 0, поэтому все следующие ("SectionSyntaxIndicator", "Reserved1", "Reserved2") поля будут равны 0 ибо это первые четыре бита этого же самого байта.

    Поле "SectionLength" занимает 12 бит, стало быть оно захватывает вторые 4 бита из 0xB0 и весь байт 0x5E. Поэтому все правильно что оно тебе пишет 0x5EB.

    Теперь. Про битовые поля в структурах.
    habrahabr.ru/post/142662

    После этой ссылки я даже не знаю, что еще от себя писать.
    В целом, программа тут ни в чем не виновата, просто, по видимому, ты сам забыл что в шестнадцатиричном представлении байта пишется сперва старший квартет бит, а потом младший. Весной это бывает. :)
    Ответ написан
    1 комментарий
  • Можно ли перегружать базовые типы в С++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Немного расширю ответ MiiNiPaa.

    Добавить поддержку шаблонных компараторов можно таким образом:
    template< typename item_t, template< typename > class item_traits >
    class item_processor{
    private:
    	typedef item_traits<item_t> traits_t;
    	...
    };


    Изюмина во втором параметре шаблона - это шаблонный параметр шаблона. :)
    У Девида Вандервуда по этому поводу было даже так написано: "Поскольку параметр шаблонного параметра шаблона не используется, его имя можно опустить".
    Такая запись говорит о том, что при инстанцировании или специализации шаблона в его объявлении ожидается только имя шаблона, а не его инстанцирование.

    Используется это дело вот так:
    template< typename target_t >
    class traits_less;
    
    ...
    
    item_processor<char, traits_less> char_processor;


    Все компараторы STL имеют один параметр шаблона. Таким образом, их использование тоже становится допустимо в качестве параметра для item_processor. В то же время, и свой компаратор в такую систему добавить проблемы не составит.
    Ответ написан
    Комментировать
  • Как трактовать определение в двух разных модулях классов с одинаковым именем?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Именно такую ситуацию имел в команде пару месяцев назад. Человек использовал небольшой вспомогательный класс внутри реализации (.cpp) юнит-тестов. И оба раза назвал этот впосогательный класс одинаково. Код в этом случае прекрасно компилируется, только работает как граната.

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

    Обычный линковщик ожидает что на вход к нему будут поданы уже готовые к линейной линковке блоки. Обычный линковщик, если его не попросить, связывает только уже используемые участки кода, начиная с точки входа или точек экспорта. Сортировка блоков (библиотек и модулей) обычно топологическая, но с сохранением алфавитного порядка между модулями в рамках одного ранга. Вот и получается, что "ликовщик увязывает этот код как получится".
    Линковщик в очередном модуле встречает еще не связанное, но уже используемое где-то имя типа и генерирует для этого типа код. Далее, в другом модуле линковщик снова встречает имя этого же уже связанного типа и просто отбрасывает его. Но отбрасывается не весь тип, а только уже связанные его части. Если во втором рассматриваемом типе будет находиться иной набор функций, они будут подвязаны к набору функций уже встроенного типа. И вот с этого места начинается дорога в ад, т.к. у двух таких типов может быть разный размер, разные поля в состоянии, разное выравнивание, разная реализация одинаково названных функций.

    Выход из ситуации:
    • использовать Forward declaration для таких локальных классов (добиться ошибки 'class redefinition' в таких случаях);
    • обертывать описания в локальные неименованные namespace (добиться уникальности пространства для таких классов);
    • описывать такие локальные классы как nested-классы от глобальных (делать хотяб Forward declaration в пространстве глобального класса - тоже упор на уникальность пространства);
    Ответ написан
  • Как загрузить DDS файл в c++ builder?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    DDS - Direct Draw Surface - это формат контейнера, заполненного одним или несколькими изображениями, снабженного специальным DDS заголовком.
    Как правило, в таком формате хранятся сжатые аппаратно-декодируемыми методами текстуры (S3TC, PVR, ATC, ETC). Вместе с тем, контейнер позволяет хранить и несжатые цветовые данные.
    Тип метода сжатия указан в DDS-заголовке, в полле "_fourcc", в виде четырехсимвольной строки.

    Пример описания DDS-заголовка:
    https://github.com/FrankStain/tex-conv/blob/master...

    Пример непосредственной загрузки DDS файла:
    https://github.com/FrankStain/tex-conv/blob/master...

    При загрузке DDS-файла, первым этапом читаем заголовок, все что дальше заголовка - это изображени(е|я).
    Если в заголовке файла указан формат сжатия данных, то перед их ручным использованием данные надо распаковать. Несжатые данные (RGB(1|4|8)(A) форматы) можно использовать прямо из файла.
    Писать распаковщик формата сжатия своими руками - дело затруднительное. Легче взять готовые библиотеки у непосредственных разработчиков или посредников этих форматов.
    ATC распаковывается с помощью Adreno SDK ( https://developer.qualcomm.com/download ).
    PVR распаковывается с помощью PowerVR SDK ( community.imgtec.com/developers/powervr/graphics-sdk ).
    DXT или S3TC обрабатывают как оба вышеперечисленных пакета, так и DirectX SDK. Так же существует набор утилит от nVidia - Texture Tools ( https://developer.nvidia.com/legacy-texture-tools ).
    Ответ написан
    Комментировать
  • Возможно ли использование компилируемых ЯП на Android?

    @MarkusD
    все время мелю чепуху :)
    Не стоит путать трансляцию, интерпретацию и компиляцию. Постановка вопроса неточна в целом.

    Java является транслируемым языком, а не интерпретируемым. Трансляция идет в опкод Java LLVM (в обычном случае) или в опкод Dalvik LLVM (в случае сборки для Android). После трансляции, исполняемый DEX (Dalvik Executable) файл содержит предварительный (сырой) байткод. Это уже не текст, который надо интерпретировать, это уже код операционных команд виртуальной машины низкого уровня. В момент запуска на конкретном устройстве, DEX файл дополнительно обрабатывается (всего один раз, при запуске приложения): из него удаляются элементы совместимости, блоки типа "if( value )", где value - это "final bool == false" (и много еще чего); после чего подается на обработку процессору LLVM.

    Поэтому можно не беспокоиться о медленности интерпретации Java кода, т.к. интерпретации нет. Возможно меня даже анафеме предадут за то, что я яву назвал транслируемым, а не компилируемым языком. :)
    Больше обо всем этом можно узнать в документации по Sun Java Machine и в документации Android Dalvik Machine.

    Однако, возвращаясь к вопросу о компилируемых языках. Сам язык, как моя практика показывает, значения тут не имеет. Любой язык может стать как транслируемым, так и компилируемым или интерпретируемым. Для разработки на Android существует уже много инструментов с поддержкой множества самых разных языком. Кое-кто и python использует в качестве языка разработки проектов.
    Существует так называемая связка поддержки низкого уровня. В нее входят средства компиляции языков в команды непосредственно процессор(а|ов). Для Android этим целям служат языки C и C++.
    И, раз уж в вопросе делается акцент на максимальное быстродействие программы, можно предположить что именно этот момент интересен больше всего.

    Native Development Kit - NDK - позволяет создавать низкоуровневые модули для последующего подключения их в основной код проекта (написанный обычно на Java). Во времена ранних версий (1.6-2.0-2.2) андроида, команда разработчиков рекомендовала минимизировать объем низкоуровневого кода и опираться только на средства Java.
    Однако, со временем стало понятно что низкоуровневый код пользуется большой популярностью и функциональность NDK стала увеличиваться. С каких-то пор стало и вовсе возможно создавать исключительно C/C++ приложения без кода на яве, только это тот еще геморрой.
    Использовать C/C++ для разработки на андроид не сложнее использования оных в разработке для других ОС, но ровно до момента, когда надо пообщаться со стороной на яве. Для налаживания общения между C/C++ и Java кодом в NDK имеется интерфейс JNI. С JNI проще всего познакомиться на сайте Oracle, в разделах документации к Java.
    Ответ написан
    1 комментарий
  • GameDev - куда "шагнуть", и как туда попасть для обучения?

    @MarkusD
    все время мелю чепуху :)
    Новосибирск значит?..
    Почитай про C#, про Unity. Попробуй себя в создании чего-нибудь в этой среде.
    О плюсах даже не думай. Шарпом владеть легче и спокойнее.

    Твоя первая задача - войти в состав группы разработчиков. В центре и Академе есть несколько контор, кто делает недоигры на Unity. Эта цель на ближайшие год-два, начиная с даты приема на работу.
    Математика на этом этапе поднимается сама собой, без нее у тебя просто ничего не выйдет.
    Цель вторая - сбор информации о внутренней кухне. Алгоритмы, методики, парадигмы/идиомы/шаблоны/подходы программирования. Собирай знания и прокачивайся через Unity. Занять может до года, до двух.

    Язык и среда разработки, в целом-то, значения никакого не имеют. Оговорка: они не имеют значения для тебя, если ты действительно тяготеешь к программированию и геймдеву.

    Цель три. Если тебе хочется работать именно в области C/C++, то самое время начинать сползать с шарпа. Это может оказаться болезненным периодом, но иголки в кактусе не бесконечны. Справишься если захочешь.

    Советую прочитать от корки до корки (и гвоздями прибить к мозгу, дабы ни слова не забывалось) книгу Дж. Пойя "Как решать задачу" в редакции от Гайдука. Книга маленькая, но ее надо прочувствовать. В целом-то, с нее и начать свое движение будет полезно.

    После выполнения третьей цели дальнейшее направление тебе амбиции подскажут.
    Всегда стоит помнить, что в программировании и (особенно) в геймдеве немало откровенно слабых и чрезмерно амбициозных людей. Задача номер ноль - постараться не примкнуть к их числу с первых дней и до пенсии.
    Ответ написан
    1 комментарий
  • Есть ли способ посчитать хэшсумму объявления структуры?

    @MarkusD
    все время мелю чепуху :)
    Если я правильно понял суть вопроса, то могу предложить один вариант. Скажем, будем считать CRC32 на этапе компиляции (тема бородатая, делается легко). Предположим, что 11 стандарта на руках нету.
    В первую очередь надо взять строку от определения структуры. Сделать это можно так:

    #define TOSTR( V )  #V
    #define MKSTR( V ) TOSTR( V )
    #define STRINGIFY_BODY( STR_NAME, ... ) \
    static const char* STR_NAME = MKSTR( __VA_ARGS__ ); \
    __VA_ARGS__
    
    STRINGIFY_BODY( MY_STRUCT_STR,
    struct MyStruct {
    	uint32_t	m_field1;
    	int16_t	m_field2;
    	int64_t	m_field3;
    }
    );


    Конечно-же, сама строка нам тут не нужна, а нужна именно CRC32. Да при том, посчитанная в compile-time.
    Для этого вводим в свой код такой вот ужас:
    static const uint32_t CRC32_TABLE[256] = {
    ... // тут много констант.
    };
    
    template< int pos >
    __forceinline uint32_t make_crc32( const char* data ){
    	const uint32_t crc = make_crc32< pos - 1 >( data );
    	return ( crc >> 8 ) ^ CRC32_TABLE[ ( crc ^ data[ pos ] ) & 0x000000FFU ];
    };
    
    template<>
    __forceinline uint32_t make_crc32<-1>( const char* data ){
    	return 0xFFFFFFFFU;
    };
    
    // -1 будет указывать на концевой ноль строки,
    // а -2 - в самую пору для начала подсчета - это последний символ строки.
    #define GET_CRC32( V ) ( make_crc32< sizeof( V ) - 2 >( V ) ^ 0xFFFFFFFFU )


    В самом подсчете суммы я мог наломать дров, точностей на скорую руку не помню. Поправьте меня, если не так.
    Что это за код.
    "__forceinline" - специальное слово для MSVC++ компилятора, указывающее принудительное развертывание этого кода. Работает это слово только при включенной оптимизации.
    Шаблон "make_crc32" будет инстанцирован столько раз, какая в коде встретится максимальная длина строки. В релизе шаблоны "make_crc32" будут схлопнуты в значения, а табличка "CRC32_TABLE" будет вырезана из бинарника за ненадобностью. Контрольная сумма посчитатся при сборке проекта.
    Теперь осталось только дописать макрос стрингификатор.

    #define STRINGIFY_BODY( CRC_NAME, ... ) \
    static const uint32_t CRC_NAME = GET_CRC32( MKSTR( __VA_ARGS__ ) ); \
    __VA_ARGS__


    А использовать теперь этот стрингификатор можно вот так:

    STRINGIFY_BODY( MY_STRUCT_CRC,
    struct MyStruct {
    	static const uint32_t H;
    
    	uint32_t	m_field1;
    	int16_t	m_field2;
    	int64_t	m_field3;
    }
    );
    
    const uint32_t MyStruct::H = MY_STRUCT_CRC;


    ...или как-либо иначе. Идея, в общем, нарисовалась вот такая.
    От __forceinline можно избавиться в пользу constexpr (для случая с 11 стандартом), результат должен быть таким же.
    В релизе breakpoint внутри подсчета CRC32 не сработает, т.к. все уже посчитано, а в дебаге будет работать (если в дебаге оптимизация не включена).
    Ответ написан
    Комментировать