вторник, 21 февраля 2012 г.

Редактор логики. Логические блоки.

При маштабировании, все дисплейные объекты увеличиваются переменной Zoom. При сохранении разности положений между, объектами и курсором, мы сам курсор
пропорционально зуму - уменьшаем. И при перемещении объектов, курсор тоже уменьшен. Затем увеличенные дисплейные объекты, мы смещаем относительно смещения мира. Которое происходит с помощью зажатой средней кнопки мышки.

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

Смещение мира при маштабе происходит так:
  1. Выбираем точку на дисплее, относительно которой будет маштаб. Это средина дисплея.
  2. Узнаём смещение мира относительно центра вьювера
    ScriptEditor.zWorldPosX := ScriptEditor.WorldPosX - Form1.GLSceneViewer1.Width/2;
    ScriptEditor.zWorldPosY := ScriptEditor.WorldPosY - Form1.GLSceneViewer1.Height/2;
  3. Вычисляем скорость (маштабирования смещения)
    ScriptEditor.WorldZoom := ScriptEditor.Zoom / 1;
Дело сдесь в том, что скорость маштабирования разная. И растояние от нуля до текущего зума, не всегда равняется единице, а всегда больше или меньше. А при вычислении правильного положения мира:
   ScriptEditor.WorldPosX := Form1.GLSceneViewer1.Width/2  + ScriptEditor.zWorldPosX * ZoomIndex;
   ScriptEditor.WorldPosY := Form1.GLSceneViewer1.Height/2 + ScriptEditor.zWorldPosY * ZoomIndex;
в котором ZoomIndex должен быть максимум единицей ПРИ ОБНОВЛЕНИИ СМЕЩЕНИЯ МИРА ВРУЧНУЮ. Значит нужно сравнить пропорциональность маштабов, что мы и сделали:
   ScriptEditor.WorldZoom := ScriptEditor.Zoom / 1;
Теперь зная с каким ускорением будет увеличиваться мир, мы вычисляем ZoomIndex
  ZoomIndex := ScriptEditor.Zoom / ScriptEditor.WorldZoom;
Тоесть мир увеличивается и смещается с одной скоростью, хотя увеличение и смещение определяются разными методами. Здесь я расматривал метод смещения мира. Так как с маштабом всё и так было в порядке.


Теперь о логическом блоке.
Логический блок это объект который состоит из:
  1. Заголовок
  2. Тело, в которм хранятся контакты ввода и вывода информации

Контакт ввода и вывода это объекты имеющие название, активность и принадлежность к логическому блоку. Алгоритм рисования блока, генерируется в визуализаторе. Тоесть по базовым данным визуализатор строит блок:
 1.Определяемся с шириной всего блока. В этом помогла одна замечательная сценовская функция
  vNameWidth := ScriptEditor.HUDText.BitmapFont.CalcStringWidth(vName);
  Хоть она хоть и возвращает Integer, что мне непонятно почему не Single, но она даёт ширину текста.
  Потом узнаём размер самого длинного имени Ввода и Вывода.
  // - Финальная Ширена блока
  if (vNameWidth > (vNameInputWidth + vNameOutputWidth)) then
    vWidth := 10 + vNameWidth
  else
    vWidth := 10 + vNameInputWidth + 10 + vNameOutputWidth;

  2. Устанавливаем высоту заголовка
  vHeightTitleBar := 20;

  3. Рисуем текст - название заголовка блока
  4. Нужно нарисовать рамку. Рамка это довольно не простая вещь в визуализации. Потому что у меня реализовано увеличение и уменьшение маштаба. Поэтому более широкий спрайт нарисованный споднизу основного спрайта, может подойти только при увеличении картинки. Но при уменьшении, когда размер рамки один пиксель, то на дисплее эта рамка рисуется через раз, потому что её перекрывает верхний главный спрайт. Поэтому для уменьшенной картинки я использую линии.
  Рисую их средствами OpenGL. Это LoopLine.
  Рамка (при увеличении) - спрайт
  Рамка (при уменьшении) - линия

  5. Нужно определиться с высотой тела блока. Для этого сравниваем количество вводов и выводов в блоке, и устанавливаем то что больше.
  6. Визуализация рамки при увеличении и спрайта тела.
  7. Рисуем названия вводов. Это цикл в котором считывается названия с вводов и ХУДТекстом визуализируется со смешением по высоте.
  Каждый шаг это 20 пикселей.
  8. Такой же подход и к визуализиции выводов.

Добавление вводов и выводов ничего нового в код не добавило, кроме подстраховочных обнулений выделеностей всех объектов которые можно выделить. Невозможно выделить одновременно несколько контактов. Это важное правило для соединения.

В основном всё работает по приципам заложеным в фундамент.


четверг, 16 февраля 2012 г.

Редактор логики. Самое начало.

  Редактор логики  это инструмент создания и редактирования логических схем, которые применяются в программировании везде. Логические схемы обычно пишутся вручную, если это не очень сложная, но при создании игр, а я столкнулся с этой проблемой именно при в игрострое, логические схемы бывают очень путанные.
  О чём я говорю? Это может быть всё что угодно, но меня прежде всего интересует функционал персонажа. Его сложные логические, древовидные структуры, у которых проихсходит переплетение ветвей, просто не реально создавать вслепую. А именно так я и создавал всё, поэтому то в этом и проблема. Нет инструмента которым можно было бы эти ветви создавать и САМОЕ ГЛАВНОЕ видеть эту схему. Ведь основная проблема в том, что не получается предусмотреть все возможные ошибки, когда не видишь схемы. Такую схему просто не реально держать в голове. Поэтому я начал писать редактор логики.
  На данный момент готова базовая работа с условными объектами. Я их назвал "дисплейные объекты"
  // - Перемещение дисплейного объекта.
  Перемещение дисплейного объекта происходит с позволения его свойства Moving.
Для нормального перемещения объекта, ему нужно закрепиться за курсором.
Объект всегда смещён относительно курсора, особенно это касается выделенных объектов,
когда курсор наведён не на них.
Поэтому в процедуре нажатия клавиш мышки, всегда сохраняем разность всех дис.объектов
относительно курсора. Это свойство (rPositionX, rPositionY).

  // - Каким методом мы добиваемся включения Moving?
  Первый метод - это прямое наведение курсора на дисплейный объект. Выделен только
Один объект (Selected).
  Второй метод - это выделение нескольких объектов, тот же (Selected).
  Третий метод - комбинированное выделение, это значит, что при зажатой кнопки TAB,
объекты не теряют свой выделености. При комбинированнном выделении, важном параметром является
(Selected) и (Moving). Все (Selected) объекты становятся (Moving) только при нажатии ЛКМ.
Тоесть в случае повторного выделения объекта вторым способом, но с зажатой клавишей TAB,
при обнулении выделености, мы сможем отличить Ранее выделенный объект от толькочто выделенного.
А значит сможем адекватно обнулиться.

  // - Все выделенные объекты сохраняют свою выделенность в двух случаях:
  1. Если мы выбрали один или несколько объектов и нажимаем ЛКМ только на них.
Тоесть в пустоту нажимать нельзя.
  2. Если мы зажали кнопку TAB, то мы можем нажимать сколько угодно и куда угодно ЛКМ.
Выделенность не потеряется, но перемещать объекты мы не сможем. Поэтому выделив объекты используя
первый или второй способ, нужно пользоватся первым способом сохранения выделености,
а значит - навести курсор на уже выделеные объекты и тащить их.

  // - Потерять выделенность можно несколькими способами:
  1. навести курсор на не выделеный объект и нажать ЛКМ,
выделеность всех остальных объектов пропадёт.               - "Не выбраный объект"
  2. навести курсор на выделеный объект и нажать ЛКМ.       - "Повторное нажатие"
  3. не навести курсор ни на один из объектов и нажать ЛКМ. - "Пустота"
  4. делать первый и второй способ без зажатой клавиши TAB. - "Не комбинированное выделение"


//----------------------------------------------------------------------------//
  Существует массив Дисплейных объектов и шаги.
  Шаг это запись которая имеет массив образов. Количество объектов в массиве, равно количеству
Дисплейных объектов при сохранении шага. Тоесть в разных шагах хранится разное количество объектов.
Переключение по шагам произходит по одному принципу:
  Удаляем все дисплейные объекты и на основе шага, как по инструкции, создаём новые дисплейные объекты
Которые заполняем пораметрами считанные с образов. Так мы и получем илюзию того,
что действия отменяются или возвращаются.

  // - Сохранение шага происходит по принципу:
  1. Счётчик повышаем на единицу.
  2. Выделяем память на массив образов в текущем шаге.
  3. Заполняем массив параметрами.
  4. Если сделанный шаг - последний в массиве,
то от счётчика отнимаем единицу и сдвигаем все шаги в сторону нуля на единицу.
  5. И указываем количество шагов сделанных от нуля. Если мы отменяли какието шаги, то
количество шагов должно быть уменьшено.

  // - Считываение шага с контроллера происходит так:
  1. Полностью удаляем все объекты со сцены
  2. Создаём новые на основе образов текущего шага

  // - Переход к Предыдущиму шагу происходит так:
  1. В счётчике текущего шага Отнимаем единицу, упор - не меньше нуля
  2. Считываем шаг с контроллера

  // - Переход к Следующему шагу происходит так:
  1. В счётчике текущего шага Прибавляем единицу, упор - не больше уже сделанных шагов
  2. Считываем шаг с контроллера

  // - Удаление объекта происходит про принципу:
  1. Выделенные объекты удаляем.
  2. Сохраняем шаг.

  // - Копирование объектов происходит так:
  1. Проверим наличие объектов вообще, иначе ничего не делаем
  2. Узнаем количество выбранных объектов
  3. Выделим память на образы копируемых объектов
  4. Заполним образы
  5. Снимим все выделения с объектов
  6. Создадим требуемые копии объектов
  7. Сделаем эти объекты выделеными
  8. Сохраняем шаг

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