Задать вопрос
  • Как overcockers узнают про правленный hosts?

    @Mercury13
    Программист на «си с крестами» и не только
    Ещё и с ошибкой написали, правильно «правленый».

    А вот так. Из кода страницы.
    var script = document.createElement('script');
    script.onerror = function(){
       // тут пишем сообщение об ошибке
    }
    script.src = "https://c.amazon-adsystem.com/aax2/amzn_ads.js";
    document.body.appendChild(script);

    То есть всё просто: если при вызове рекламы произошла ошибка, выводим сообщение.

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

    Да ещё и опечатка у вас под стать этому гадству: «overcockers».
    Ответ написан
    Комментировать
  • Для чего нужны замыкания в C++ и как вы их используете?

    @Mercury13
    Программист на «си с крестами» и не только
    Замыкание — это способ передать в callback, из какого контекста он запустился.
    struct Row {
      int data[10];
    };
    struct Table {
      Row rows[10];
    }

    Нам нужно отсортировать таблицу по j-му столбцу. Номер j заранее неизвестен.

    int sortJ;
    
    int myCompare(const void* a,const void* b) {
      int ia = reinterpret_cast<Row*>(a)->data[sortJ];
      int ib = reinterpret_cast<Row*>(b)->data[sortJ];
      if (ia < ib) return -1;
      if (ia == ib) return 0;
      return 1;
    }
    
    int someJ = 5;
    sortJ = someJ;
    qsort(table.rows, 10, sizeof(Row), myCompare);

    Вот эта переменная sortJ — по какому столбцу сортировать — это и есть замыкание. Но, как известно, «избегай незнакомых женщин и глобальных переменных». Поэтому на STL мы делаем функтор (объект-функцию) и эту информацию кидаем в него.

    class MyCompare {
    public:
      MyCompare(int aJ) : j(aJ) {}
      bool operator () (const Row& a, const Row& b) const
        { return (a.data[j] < b.data[j]); }
    private:
      const int j;
    }
    
    int someJ = 5;
    std::sort(table.rows, table.rows + 10, MyCompare(someJ));

    Вот мы и избавились от глобальной переменной, закинув наше замыкание в private-поля объекта.

    Что плохо? Не будем говорить про технические тонкости. С точки зрения красоты и лаконичности кода: код слишком разлапистый. И тут пришёл C++11.
    int someJ = 5;
    std::sort(table.rows, table.rows + 10,
      [someJ](const Row& a, const Row& b) -> bool { return (a.data[someJ] < b.data[someJ]); } );

    Корявовато, но таков синтаксис Си++. Автоматически создаётся объект-функтор, и someJ становится его полем. Вот оно, замыкание — [someJ] — то есть те вещи, которые надо протащить внутрь функтора.

    Из реального проекта. Отбегал поток автоматического поиска нового регистрационного ключа; если что-то получилось — синхронно вызываем лямбду через Qt’шный механизм «сигнал-слот». Чтобы всё было синхронно, нужен объект, живущий в главном потоке (и он в интерфейсе MainControl — управление главной формой — тоже есть). Но тогда придётся вызывать не слот, а лямбду. Этой лямбде нужны два поля: fReregKey (новый ключ защиты от копирования) и fMainControl. Оба они в this, его и замыкаем.
    connect(this, &RenewThread::needUpdate, fMainControl.maincQobj(),
                [this]() {
            drm::createFile(fReregKey);
            fMainControl.maincLayoutOnRegister();
        });


    А теперь посмотрим в WinApi. Первая попавшаяся функция из DirectInput.
    HRESULT EnumObjects(
             LPDIENUMDEVICEOBJECTSCALLBACK lpCallback,
             LPVOID pvRef,
             DWORD dwFlags
    );
    
    BOOL DIEnumDeviceObjectsCallback(
             LPCDIDEVICEOBJECTINSTANCE lpddoi,
             LPVOID pvRef
    );

    Про pvRef говорится: функции EnumObjects он ни на что не нужен; что функция приняла, то и даст в callback. Тоже форма замыкания: можно передать указатель на любые данные, которые нужны callback’у.
    Ответ написан
    4 комментария
  • Почему ошибка в netbeans c++?

    @Mercury13
    Программист на «си с крестами» и не только
    В одном проекте два CPP-файла: int_float.cpp и hello.cpp. В обоих по main’у.
    Я не знаю, как NB объединяет файлы в проект, но если всё, что в каталоге и подкаталогах — значит, не держи где-то в подкаталогах другой проект.

    Второй вариант — очистить временный каталог с .o-файлами; там, возможно, завалялось что-то старое.
    Ответ написан
    Комментировать
  • Как определить число инверсий для перестановки?

    @Mercury13
    Программист на «си с крестами» и не только
    Инверсия — это когда a < b, но стоят они наоборот.
    В пределах блока (например, 1…3n−2) инверсий, разумеется, нет.

    Между первым и вторым блоком.
    • С 2-кой: 4, 7,… То есть n−1 шт.
    • С 5-кой: начиная с 7 — то есть n−2 шт.
    • Итого 0 + 1 + 2 +…+ (n−1) = n(n−1)/2 шт.

    Между первым и третьим, и между вторым и третьим сам инверсии посчитаешь? И чётное оно или нет — тоже, надеюсь, посчитать несложно.
    Ответ написан
    Комментировать
  • Как разнести класс по файлам?

    @Mercury13
    Программист на «си с крестами» и не только
    Принцип прост. В .h можно ставить только то, что не производит кода. Как только в проекте появится второй CPP и задействует этот хедер, код будет произведён дважды, и компоновщик (cl/ld/ilink) будет ругаться, что переменная или функция в двух экземплярах. Что именно не производит кода…
    • Определения макросов. Они в принципе кода не производят.
    • Объявление любого типа. Оно лишь говорит об устройстве этого самого типа; код же производят те, кто этим типом пользуются.
    • Шаблоны. Код производит не сам шаблон, а факт расшаблонивания. Разумеется, шаблон может расшаблониться в двух единицах компиляции, но с этим автоматически бороться научились.
    • inline—  код производит не сам inline, а факт включения. inline бывает как явный ключевым словом, так и неявный — в теле класса.
    • Прототипы и extern — они говорят: код есть, но где-то не здесь.
    • Constexpr C++11. Они подставляют значение.
    • Некоторые const в зависимости от компилятора. Например, на Borland const double производит код, а const int — нет.

    Производят код и в хедерах запрещены.
    • Переменная без extern, даже const.
    • Функция, которая не inline.
    • Полностью специализированный шаблон, в котором не осталось шаблонных параметров (template<>).

    Не производят кода, но и лучше закинуть в CPP.
    • Некоторые скрытые (private) inline и шаблоны, если они не используются из хедера.
    Ответ написан
    3 комментария
  • Как изменить класс компонента?

    @Mercury13
    Программист на «си с крестами» и не только
    Самый простой способ. Открываем DFM (ПКМ на форме, View as Text) и проводим Search-Replace.
    После этого вносим любые изменения в модуль (да хоть пробел добавляем и удаляем), и сохраняем. Автоматика будет спрашивать: заменить? — соглашаемся.

    Я изменял в Unit1.pas - type TPanel на sPanel и в Unit1.dfm TPanel на sPanel.
    Но при открытии пишет, что класс sPanel не найден, хотя в uses прописано sPanel.

    Если модуль называется sPanel, то сам компонент должен немного по-другому. TsPanel?

    UPD. Да, TsPanel, если это он.
    Ответ написан
    Комментировать
  • Как можно реализовать?

    @Mercury13
    Программист на «си с крестами» и не только
    Подсчитать кол-во нулей (единиц, двоек, троек). Если неодинаково — нельзя.
    Иначе — подсчитаем такую матрицу (4×4). Если в 1-й строке на i-м месте 1, а во 2-й 3, прибавляем единицу к a[1, 3].
    a[i, i] не учитываем: они уже на своих местах.
    a[i, j] и a[j, i] взаимокоменсируем: каждая такая парочка даёт min(a[i, j], a[j, i]) перестановок.
    Точно так же пересматриваем все тройки a[i, j], a[j, k] и a[k, i]. Как всегда, взаимокомпенсируем их; каждая такая тройка даёт 2·min(•, •, •) пререстановок.
    Остаётся просуммировать всё, что осталось (кроме диагонали, разумеется), и помножить на 3/4.
    Ответ написан
    Комментировать
  • На чем сейчас разрабатывают игры?

    @Mercury13
    Программист на «си с крестами» и не только
    Почему скриптовой язык идет вместе с компилируемыми? Разные задачи?

    1. Для упрощения внутренней логики игры. Например, программа — монолитное однопоточное приложение, но каждый объект ведёт себя как сопрограмма. Качающийся маятник можно реализовать на состоянии и на сопрограммах…
    // На состоянии
    функ Маятник.ПровестиТакт
      ++кадр;
      если (кадр >= 10)
        кадр = 0;
      УстановитьКадр(кадр);
    
    // На сопрограммах
    функ Маятник.Жизнь
      для i=[0..10)
        УстановитьКадр(i)
        НовыйТакт


    2. Чтобы геймдизайнеры и прочие непрофессионалы (в программировании, естественно, непрофессионалы) могли свободно корректировать код.

    3. Часто игру пишут на готовом движке. А в нём зашит какой-то скриптовый язык — это самый простой способ написать игровой движок, законченный и пригодный к применению программистом куда меньшей квалификации. Так, например, устроен Wintermute Engine (старый, но хорошо известный движок для квестов).

    Читал, что плюса, C#, Lua.

    • C++: язык с давними традициями в геймдеве. Выразительный код (при достаточном профессионализме) плюс контроль за деструкторами — так что не будет просадок FPS из-за мусорщика. Выбор №1, если нужно написать игру с нуля.
    • C#: очень простой в изучении язык общего назначения. Библиотеки для Windows, скорее всего, у пользователя есть изначально. Куча движков, библиотек и фреймворков. Даром, что мусорный — если игра не слишком сложная, просадок FPS не будет (хотя последнего босса Hyper Light Drifter я долго громил — в критические моменты игра тормозила).
    • Lua: наиболее известный из скриптовых языков, которые можно реально присоединять к Си-программе, и скрипт будет вызывать функции, написанные на обычном компилируемом языке.
    Ответ написан
    Комментировать
  • Как правильно организовать структуру в классе?

    @Mercury13
    Программист на «си с крестами» и не только
    Помимо того, что я написал…

    1. Как устроить данные?
    struct CheckLine {
    public:
       int itemCode;    // код товара
       int qty;         // количество
       int price;       // цена, по которой всё это продано в копейках
       const CheckLine* next() const { return _next; }
    private:
       friend class Check;   // я тут ошибся с const-корректностью и заconst’ив всё, что можно, не дал Check’у писать
       CheckLine* _next;
    }
    
    class Check {
    public:
        Check() : _firstLine(NULL), _lastLine(NULL) {}
        void addLine(int itemCode, int qty, int price);   // пиши реализацию сам.
        const CheckLine* firstLine() const { return _firstLine; }
        ~Check();  // не забудь про деструктор…
        Check(const Check&)  // …конструктор копирования…
        Check& operator = (const Check&);  // и операцию «присвоить».
    private:
        CheckLine *_firstLine, *_lastLine;
    }

    В общем, я тут для простоты (мы же пока не знаем, что такое vector) наладил хранение данных связанным списком.

    2. Во внутренней кухне класса ты обращаешься к консоли. Зачем? Обычно это делают как можно ближе к main.

    Вернусь — буду писать дальше.
    Ответ написан
    3 комментария
  • Как удалить файлы по маске в подкаталогах Delphi?

    @Mercury13
    Программист на «си с крестами» и не только
    Ну ясен перец.
    Каталоги надо искать по *, а файлы удалять по *.log.
    Ответ написан
    Комментировать
  • Как расставить скобки в выражении всеми возможными способами? какова сложность оптимального алгоритма?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут чёткое динамическое программирование. У нас N+1 операнд, и N операций.
    Создаём кэш: для всех L и R, 0<=L<=R<=N, мы храним:
    • Операцию: null, +, ×, many.
    • Ассоциативный массив:
    •• Ключ — число (целое/рациональное/плавающее/фиксированное — с какими хотите работать).
    ̃•• Значение — тройка:
    ••• Номер операции с макс. приоритетом.
    ••• Значение слева (такое же число).
    ••• Значение справа (такое же число).
    Для всех L=R кэш заполнен такой величиной:
    • Операция — null.
    • В массиве один элемент: ключ — операнд, значение — любая затычка.
    Для кэша годится, например, матрица (N+1)×(N+1).

    После этого проходимся по диагоналям нашего кэша, обрабатывая сначала L=0..N−1, R=L+1, затем L=0..N−2, R=L+2, и т.д., пока на дойдём до диагонали из одного элемента: L=0, R=N. Как именно обрабатываем?
    1. Смотрим операцию, записанную в кэше (L+1, R), и L-ю операцию в выражении. Если вторая — «сложить» или «умножить», а первая — null или совпадает, записываем эту операцию в кэш на место (L, R). Иначе — пишем many.
    2. Вычисляем R1: если в кэше на месте (L, R) есть какая-то операция, то L; если many — то R−1 (оптимизация по ассоциативности).
    3. Проходимся по всем M=L…R1.
    3.1. Двойным циклом проходимся по содержимому кэша в (L,M) и (M+1, R). Обозначим переменные u и v.
    3.1.1. Записываем в кэш на место (L,R), если таковое отсутствует:
           • ключ u (+−×/) v
           • номер операции — M
           • значения слева и справа — u и v.
    Рассмотрим, например 1 + 2 + 3 − 4.
    (0, 0) — null; (1 → ? ? ?)
    (1, 1) — null, (2 → ? ? ?)
    (2, 2) — null, (3 → ? ? ?)
    (3, 3) — null, (4 → ? ? ?)

    UPD: Операнды у наc { 1 2 3 4 }, орерации { + + − }. Ну разумеется.

    Теперь смотрим, что будет в (0, 1). В (1, 1) в кэше null, нулевая операция + — операция +. Одна итерация, R1=0.
    Прокрутив эту итерацию, получаем…
    (0, 1) — +; (3 → 0 1 2). Это означает: получили 3, сложив 0-м плюсом 1 и 2.
    Аналогично…
    (1, 2) — +; (5 → 1 2 3)
    (2, 3) — many; (−1 → 2 3 4)

    Вторая диагональ. (0, 2). В позиции (1, 2): в кэше +, 0-я операция +. Записываем + и прокручиваем одну итерацию, от 0 до 0. Т.е. складываем (0, 0) и (1, 2).
    (0, 2) — +; (6 → 0 1 5).
    Теперь (1, 3). Операция будет many; прокручиваем и получаем:
    для M=1 — складываем (1, 1) и (2, 3), и получаем (1 → 1, 2, −1).
    для M=2 — вычитаем (1, 2) и (3, 3), и получаем (1 → 2, 5, 4).
    Если без дублей…
    (1,3) — many; (1 → 1 2 -1)

    Ну и, наконец, пошли (0, 3). Операция будет many…
    для M=0 — складываем (0, 0) и (1, 3), и будет (2 → 0 1 1).
    для M=1 — складываем (0, 1) и (2, 3), и будет (2 → 1 3 −1).
    для M=2 — вычитаем (0, 2) и (3, 3), и будет (2 → 2 6 −4). Повторим, что это значит: получилось 2, из-за того, что вычли 2-м минусом 6 и −4.
    Итого, без дублей, (0, 3) — many, (2 → 0 1 1).

    Так «повезло», что у нас вышел один результат. Но в результате объединения может быть и множество.

    А теперь вопрос: откуда мы получили 2-ку? Смотрим в (0, 3) и получаем: 2 = 1 +0 1. Левую единицу раскрываем через (0, 0), правую — через (1, 3): 2 = 1 +0 2 +1 (−1).
    Остаётся раскрыть −1, и получаем 2 = 1 + 2 + 3 − 4.
    Это можно сделать рекурсивно.

    Если точный путь вычислений не нужен, ассоциативный массив превращается в множество без повторов. Не (2 → 0 1 1), а просто 2.
    Во многих вариантах динамического программирования, если прямой ход не нужен, двухмерный кэш можно превратить в одномерный. Тут я не вижу способа. Написал, да понял, что ошибся.

    UPD. А сложность какая? — у меня выходит 2n·n². Почти уверен, что реально цифра меньше, просто думать над этим облом.
    Ответ написан
    Комментировать
  • Your CPU does not support required features (VT-x or SVM)?

    @Mercury13
    Программист на «си с крестами» и не только
    Ну очевидно.
    Если у вас Windows/OSX: либо использовать процессор Intel с функцией VT-x (виртуализация), либо выбрать из списка другой эмулятор (правда, медленно будет).
    Если у вас Linux — также годятся процессоры AMD с функцией SVM.
    Ну и, разумеется, можно заливать программу не на эмулятор, а на настоящий телефон.

    Процессор-то у вас какой? Вижу, требования жёсткие, и может случиться, что Hyper-V работает, а AS — нет.
    Ответ написан
  • Как решить уравнение х*х-2=3y в целых числах?

    @Mercury13
    Программист на «си с крестами» и не только
    x делится на 3: 9z² − 2 = 3y. Справа делится на 3, слева нет.
    Иначе: (x−1)(x+1) = 3y + 1. Хотя бы один из множителей кратен трём. Всё наоборот: слева делится на 3, справа нет.
    Дальше уже, по-моему, доказательство не упростить.
    Ответ написан
    Комментировать
  • Как правильно распределить память?

    @Mercury13
    Программист на «си с крестами» и не только
    Что у нас такое cur_dir?
    У функции strcat_s три параметра…
    errno_t strcat_s(
       char *strDestination,
       size_t numberOfElements,
       const char *strSource 
    );

    В C++ есть также шаблонная перегрузка с двумя параметрами.
    template <size_t size>
    errno_t strcat_s(
       char (&strDestination)[size],
       const char *strSource 
    ); // C++ only

    Вывод такой. Перегрузка с двумя параметрами принимает первым параметром только массив, но не char*.

    P.S. А лучше, конечно, будет использовать контейнеры наподобие std::string.
    Ответ написан
    3 комментария
  • Как объяснить кусок кода C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Весь этот код (за исключением Close) — автогенерируемый.

    ///// Защита от повторного включения
    #ifndef Unit1H
    #define Unit1H
    
    ///// Хедеры VCL. Причём всё это сделано так, чтобы упростить написание ценой удлинения
    ///// компиляции. Более громоздкий, но и более удачный вариант.
    ///// В H:
    /////   namespace Controls { class TLabel; }
    /////   using namespace Controls;
    ///// В CPP:
    /////   #include <Controls.hpp>
    ///// Вот таким образом можно (было) избавиться от каскадного подключения
    ///// хедера Controls. А то каждый, кто использует главной форму,
    ///// автоматически подключает эти хедеры.
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    ///// Только от Forms.hpp избавиться таким макаром нельзя:
    ///// мы наследуемся от TForm.
    #include <Forms.hpp>
    
    ///// Класс формы. Все формы наследуются от TForm.
    class TForm1 : public TForm
    {
       ///// Особое право доступа Borland, для совместимости с Delphi.
       ///// Поля и свойства published не просто public, но включаются
       ///// в структуру рефлексии (aka reflection или introspection)
       ///// и программа о них знает при выполнении.
       ///// Применительно к формам — published-поля доступны
       ///// загрузчику.
    __published: // IDE-managed Components
       ///// Компоненты, которые мы установили на форме редактором.
    TLabel *Label1;
    TButton *Button1;
       ///// События, которые мы прописали в редакторе.
       ///// __fastcall — модель вызова, аналогичная Delphi.
       ///// Именно такая модель вызова принята в обработчиках
       ///// событий.
    void __fastcall Button1Click(TObject *Sender);
       ///// Пользователь пока не прописал никаких своих
       ///// полей и функций.
    private: // User declarations
    public: // User declarations
       ///// Конструктор. Раз уж у формы нетривиальный конструктор —
       ///// по правилам Си++ его надо повторить в подклассе.
       ///// Снова-таки, модель вызова __fastcall: в формах Delphi
       ///// используются т.н. виртуальные конструкторы, 
       ///// когда по имени класса можно создать объект этого класса.
       ///// Фабричный метод, только немного лучше.
       ///// Но это значит: у всех подчинённых классов
       ///// должен быть один и тот же набор параметров
       ///// и модель вызова.
    __fastcall TForm1(TComponent* Owner);
    };
    //---------------------------------------------------------------------------
    ///// Как известно, переменная объявляется один раз.
    ///// Поскольку хедер может подключаться к огромному числу CPP,
    ///// её объявляют как extern (она есть, но в другом месте).
    ///// Макрос PACKAGE раскрывается в __declspec(package),
    ///// чтобы эту штуку можно было собрать как пакет.
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif

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

    @Mercury13
    Программист на «си с крестами» и не только
    Нужно постольку, поскольку художник понимает ваши почеркушки. В смысле, какие-то знания не помешают, но не самоцель.
    Куда важнее (из художественного)…
    • Понимание стилей искусства, как исторических, так и современных.
    • Умение быстро сфотошопить концепт-рисунок, который и будет техзаданием художнику.
    • Умение внятно, словами и зарисовками, объяснить художнику, где он не прав.
    • По портфолио понять, брать художника в проект или не стоит.
    • Умение собрать из «всякого хлама в интернете» временную картинку, чтобы понять, не будет ли лучше другая метафора или другой стиль.

    Бывают игры, вышедшие из графического стиля. Например: Myst, Syberia, Transistor. В них геймдизайнер — в первую очередь художник. Но важно и сделать надлежащий геймплей — то, чего, по-моему, не хватает Transistor.
    Ответ написан
    Комментировать
  • Почему видеокарта работает только без драйверов?

    @Mercury13
    Программист на «си с крестами» и не только
    У видеоплаты очень много блоков, в том числе…
    • Графические конвейеры.
    • Видеопамять.
    • RAMDAC и его аналог, дающий интерфейс DVI.
    • Блок аппаратной отрисовки примитивов в 2D (не так быстро, как графическими конвейерами, но до пикселя точно).
    • Блок совместимости с VGA и VESA (это очень старые стандарты, но надо же как-то показывать, пока нет драйверов?)
    • Интерфейс PCI-Express, который и даёт взаимодействие видяхи с шиной.
    • Схема питания.
    Вариант 1. Слетел один или несколько видеоконвейеров; видяха работает только в совместимости с VESA, где видеоконвейеры не нужны.
    Вариант 2. Слетела часть памяти или её дешифратор; драйверы Microsoft и nVidia держат видеобуфер в разных местах.
    Вариант 3. Слетел один из преобразователей питания (у хорошей видяхи их может быть до 4). Какой-то простейший набор блоков работает, а переходим на полное ускорение — нет.
    Вариант 4. Слетела часть выводов PCI-Express, из-за чего в новом PCI-Express не получается раскочегарить видяху.

    Реальный случай. ThinkPad T-series — хорошие ноутбуки, но T40 известен своим ненадёжным Radeon Mobility. Чип отвалился и работал только в совместимости с VESA.
    Ответ написан
    1 комментарий
  • Как в Linux через raw sockets подключиться к tcp-серверу, используя Qt?

    @Mercury13
    Программист на «си с крестами» и не только
    ::connect
    Ответ написан
    Комментировать
  • Как организована многопоточность в играх жанра RTS?

    @Mercury13
    Программист на «си с крестами» и не только
    Без параллельности можно обойтись — и поначалу лучше обойдитесь.
    Если уж хочется помногопоточить — я бы разбил на два потока, интеллект и графику.

    У каждого юнита три одинаковых структуры, renderInfo, bufferedInfo и aiInfo. Там могут быть координаты, курс, HP, ссылка на следующего — всё, что угодно. Ссылка на следующего — это важно, ведь юниты могут появляться и исчезать.

    Поток интеллекта работает с aiInfo; где-то здесь есть и код мультиплеера. Закончив шаг, поток захватывает мьютекс и для всех юнитов (понятие «все» определяется по aiInfo) даёт bufferedInfo = aiInfo.

    Поток прорисовки захватывает тот же мьютекс и для всех юнитов («все» по bufferedInfo) даёт renderInfo = bufferedInfo. А затем, освободив мьютекс, делает с этим renderInfo что хочет.
    Под мьютексом будем сидеть очень мало: ни прорисовки, ни мультиплеера там нет.

    Поскольку юнит исчезает из памяти только тогда, когда на него пропадают ссылки во всех трёх структурах, надо делать или объектный пул (т.е. юнит, будучи созданным, остаётся навсегда, но впоследствии этот блок памяти можно будет задействовать на другой юнит), или какие-нибудь «мусорные» указатели наподобие std::shared_ptr.

    Такая система аналогична тройной буферизации в прорисовке. Только в видяшной тройной буферизации асинхронно действуют кадры монитора и кадры рендерера; у нас — такты игры и кадры рендерера.

    Разбивать интеллект на потоки откровенно тяжело, и в игре с мультиплеером я просто не знаю, как. RTS обычно передают по сети короткие пакеты наподобие «выделить юнитов в квадрате (X,Y) — (X,Y)», «добавить к выделению юнита 1234» и «отправиться в точку (X, Y)». Эти команды, будучи выполненными на разных компьютерах, выполняются одинаково.

    Если объединить эти две конструкции — повторяемые расчёты и асинхронный рендеринг — возможны неточности, и, надеюсь, они будут приемлемыми — на экране (renderInfo) юнит попадает в отмеченный квадрат, а в aiInfo уже нет, посколько сдвинулся.

    Отдельный вопрос — компенсация пинга в играх по интернету. Не думал пока над этим. Наверно, придётся иметь officialAiInfo и lagCompensatedAiInfo…

    ЗЫ. Подсказывают, что такт игры в такой ситуёвине длится порядка 0,1 с, т.е. может продлиться несколько кадров. Тогда во всех этих …info надо описать движение так, чтобы в любой момент получить промежуточное x(t), y(t), yaw(t)… Для танка — bodyYaw(t) и turretYaw(t), для человечка — фазу анимации…

    ЗЗЫ. Если игра трёхмерная и вид хоть как-то настраивается, одна из команд игры будет выглядеть «выделить юнитов в квадрате (X,Y) — (X,Y) с матрицей преобразования A».

    ЗЗЗЫ. Чтобы игра была повторяемой (важно для мультиплеера), все расчёты, влияющие на течение игры, проводить в фиксированной запятой. Все переменные, с этим связанные, чем угодно, хоть нулём, но инициализировать.
    Ответ написан
    Комментировать
  • Какие есть книги про учёных для детей?

    @Mercury13
    Программист на «си с крестами» и не только
    Ирина (Ирэн) Радунская. Очень красиво писала, хоть и староваты книги.
    Ответ написан
    Комментировать