Как видите есть ряд изменений в оформлении, они коснулись и полной версии приложения для которого вышло соответствующее обновление.
Оказалось, при первой публикации лайт версии,
GB&W занимает в памяти более 90 МБ (на сколько не скажу). Очень много анимированной растровой рафики. Лайт версию пришлось немного порезать. Получается для GB&W мне в свое время сделали скидку :).
Сейчас доступно:
- 40 уровней.
- 14 типов кирпичей
- 14 бонусов
Одна из последних добавленный пакостей это кирпич с кодовым названием "Биомасса". Он не прерывно плодиться с постоянной скоростью, кроме того он подвержен влиянию 2х бонусов:
- катализатор, единовременно ускоряющий рост общего объема биомассы
- заморозка, соответственно временно останавливающий рост биомассы.
Сегодня зарегистрировал свой проект в конкурсе мобильных приложений под Windows Phone 7 на сайте конференции DevCon•11. В конкурсе участвует следующее видео:
Ребят у меня к вам просьба - кому симпатичен данный проект, пожалуйста не пожалейте 5 секунд своего времени, проголосуйте за него, для меня важен каждый Ваш голос. Ниже кнопочка для голосования:
Заранее благодарен.
Наконец то завершена работа над первым релизом игры. Почему первым? Потому что в планах добавлять уровни в следующих обновлениях. Сейчас приложение находится на этапе публикации в Marketplace.
Не пугайтесь обилию выпадающих бонусов. Это один из первых десяти обучающих уровней. Десятка разбита по применению тематических бонусов их комбинаций.
В этом уровне первый раз появляется усиление на пулемет, который в свою очередь превращается в нечто иное :).
Рекомендую смотреть в качестве 720p (сейчас 480p)
Мне часто задают вопрос - как происходит управление в игре и как целиться? Все просто. Кораблик управляется одним касанием и постоянно находится под пальцем. Пулеметы наводятся на ближайшую цель в радиусе поражения. Вторым касанием на кнопки в верхней панели активируется тяжелое вооружение:
ракета I - открыть/закрыть подвеску, для экономии боезапаса. Само наводится и поражает ближайшего бота. При потере цели в процессе подлета (цель уничтожена др. ракетой и т.д.) находит и атакует следующую ближайшую.
ракета II - пуск. Само наводится и поражает/запечатывает ближайшую шахту из которой появляются боты.
ракета III - активация ракеты. Второе нажатие в области экрана помечает область поражения и происходит старт. Так же взрывается от столкновения с кирпичами.
Я потратил какое то время на изменение своего Touch класса. Теперь он понимает второе касание типа Tap при активном первом касании. Т.е. можно не прерываясь контролить кораблик и активировать ракетные бонусы.
Игра предназначается для Windows Phone 7 marketplace.
Cуть игры - смешанный коктейль трех направлений:
1) арканоид - основная идея и условия прохождения уровня.
2) аэрохоккей - расширенные правила взаимодействия биты(battleship) и мяча
3) хардкорный 2d шутер - пулеметы, ракеты, враги, кровь, взрывы ... боже дай мне сил остановиться :))).
Отладочный уровень:
Планируется сменное оружие для обоих типов вооружения,
а так же их прокачка. Возможные параметры: дальность, урон, скорострельность. Пока остановимся на стандартных амбициях.
В целом данный проект является экспериментом, в том числе и в 2d.
Очень много времени уходит на рисование, особенно что касается на первый взгляд не заметных деталей таких как кровь на решетках вентиляторов и т.д. . Примерное соотношение времени = (день рисую) / (час программирую). С самим вопросом рисования проблем нет, но не набита рука, плюс, например, если в карандаше я нарисую все что угодно, то ощущение цвета нужно еще нарабатывать. Радует то, что по мере работы прокачка движется. Мега респект создателям графического редактора Paint.net
Windows Phone 7 радует своими возможностями. По предварительным подсчетам, в данном приложении, запас на частицы порядка 4000 штук. Возможно при желании у меня получится и больше. Время покажет.
GPF - на таком рабочем названии библиотеки я остановился.
Сегодня закончил модуль 3d форм - это такой штука, обеспечивающая
функционал добавления в сцену объектов на плоские грани которых можно проецировать формы и оперировать ими указателем мыши.
Пока не стал возиться с наложением форм на не плоские поверхности по
причине того, что это нарушит совместимость с Windows Phone 7 экземпляром библиотеки.
Вот такое видео:
Показал Expander, DataGrid, Table.
Со стилями элементов опять не поиграл. Оставил на потом)
Вот и созрел я до первого показа своего интерфейса для приложений реализованных на базе XNA. Как говорится лучше один раз увидеть …, так не буду Вас задерживать:
Рекомендую смотреть в HD режиме.
Данное видео не показывает всех элементов и возможностей. Опубликовал по многочисленным заявкам. За продолжением следите в следующих сериях.
Идея архитектуры, данного интерфейса, базируется на архитектуре предложенной в WPF. Но ядро и элементы написаны с нуля и оптимизированы под 3d. Так же данная библиотека работает и на Windows Phone 7.
Список уже реализованных элементов выглядит следующим образом:
- Button
- Canvas
- CheckBox
- CheckElement
- ConsoleOutput
- DataGrid
- Expander
- Image
- ListSelector
- Menu
- ProgressBar
- ScrollBar
- ScrollDiagram2d
- ScrollView
- Slider
- StackPanel
- Switch
- TabControl
- Table
- TextBlock
- TextBox
В этот список попали не характерные для WPF элементы, что было продиктовано первоочередной необходимостью реализации отображения функционала 3d движка.
В представленном видео все элементы и формы выводятся с простым базовым стилем. Под базовым стилем подразумевается (если есть) прямоугольная рамка и (если есть) прямоугольник фона. Стиль любого элемента достаточно просто меняется наследованием от него нового нового класса. Я пока не стал перегружать рабочую версию интерфейса надуманным стилем и растровой графикой, бесполезная трата времени на то что в последствии не пригодится.
Поигрался с Windows Phone 7, начал с проверки возможностей 2D, вот что получилось:
очень пока не хватает возможности использовать собственные шейдеры, а решать этот вопрос в обход длинным путем не очень хочется. так бы уже можно было поиграть с normal maping и добавить к рельефу освещение).
Мои коллеги, с которыми меня объединяет деятельность в рамках ресурса xnadev.ru, и я, начали работу над 3D движком XnaDevRuEngine. Если не брать в расчет наработки по модулю интерфейса (forms), которые ранее существовали как самостоятельный проект, то следующее видео можно считать первым, демонстрирующим XnaDevRuEngine:
Сейчас частично готовы модули SceneTree и прототип ResorceManager.
В данном исполнении нет каустики. Но сейчас я заинтересовался этой темой, нагреб кучу инфы, изучаю ... пробую. Пока мои изыскания выглядят так:
Т.е. простое вычисление в один (сильно тяжелые вычисления для real time) проход на CPU.
Пока делал первое видео, в глаза бросилась вот какая штука. Изначально видео с водичкой весило 2,092 ГБ, конвертировал при помощи Camtasia Studio и как то сильно быстро изменялся прогресс конвертирования. Посмотрел загрузку процессоров ... и о чудо.
Camtasia Studio умеет распределять вычисления на все доступные процессоры. Конвертация прошла в real time режиме :). Очень порадовало!
Читал несколько статей на эту тему. Искал с рабочими примерами (желательно на XNA + HLSL), конкретно с пред расчетом и готовым шейдером. Но нашел только много теории и не для XNA и DirectX, а в общем. Написано умно, ничего не понял. Конкретно озадачив себя, просидел как всегда допоздна. На утро, проснулся с готовым представлением о содержимом шейдера. Мне повезло.
Демонстрация:
И так по порядку. Рассмотрим вот такую картинку:
Здесь изображен процесс перехода между уровнями детализации ландшафта. черным цветом - контур геометрии предыдущего слоя детализации; синим цветом – его вершины; красным цветом – вершины нового уровня детализации. зеленым цветом – выделен контур новой геометрии с учетом достроенных вершин, в следствии разбиения; фиолетовым цветом – вершины середин отрезков между вершинами предыдущего слоя детализации; красные вектора в низ – delta вектора разности между серединами отрезков вершин предыдущего слоя и соответствующими им новыми вершинами. Дальше по тексту разъясню подробнее.
Что из себя представляет «Terrain Geomorphing in the Vertex Shader»? Если посмотреть более пристально на видео пример к моей предыдущей статье "Procedural Landscape on XNA Game Studio 3.0", где нет геоморфинга,то можно увидеть как «выпрыгивает» (новый научный термин) новая геометрия, при наезде друг на друга слоев с различной детализацией.
Это не есть good!
Легко избежать этого можно следующим образом: - для каждой достраиваемой вершине в новом, более детализированном слое рассчитываем ее положение на середине отрезка построенного между соседними вершинами старого, менее детализированного слоя. Эта вершина поможет нам скрыть (замаскировать) первоначальную разницу изменения геометрии. И находим вектор разницу между этой вершиной и вершиной с реальным значением высоты. Вектор должен быть именно такого направления как показано на рисунке. Это даст нам возможность при его сложении с реальной вершиной опускать ее до уровня середины отрезка.
Нам понадобится следующее описание формата вершин для хранения выше описанных данных:
public Vector3 Position; public Vector3 PositionGeomorphing; public Vector3 Normal; public Vector3 NormalGeomorphing; public Vector2 TextureCoordinate; publicfloat IndexDetailedLayer;
Здесь PositionGeomorphing и есть то поле для хранения вектора смещения.
Нечто похожее делаем и с нормалями.
Все волшебство произойдет в шейдере!
Куда мы передадим дополительную информацию о: float3 vEyePosition; - положении камеры float layerRadius[7]; - реальные размеры уровней детализации
Для каждой вершины будем считать удаление от камеры, по ее индексу выбирать данные, из массива размеров уровней детализации, для расчета коэффициента затухания вектора PositionGeomorphing.
На видео я намеренно включаю рисование сетки треугольников и поднимаю камеру над ландшафтом. Для наглядности так же выключил, из расчета, определение видимых слоев детализации по мере взлета, потому видно все. Обращаем внимание на fps (когда камера высоко не в счет, по выше описанной причине)
Закончил эту часть работы давно. Даже успели обсудить на xnadev.ru . Сейчас данный проект уже несколько раз преобразился. Возможно, в скором времени, дойдут руки сделать новое видео. Как раз будет повод рассказать о том, что из себя представляет «Terrain Geomorphing in the Vertex Shader» и с чем его едят. Пока расскажу об этом этапе.
Как это делалось?
Описал представление слоев детализации и данных о вершинах. Карта логики представления слоев детализации выглядит так:
Карта логики связывания вершин слоев детализации в треугольники, с учетом связей между соседними слоями выглядит так:
На обеих картинках цвета слоев не совпадают. Лень переделывать. Просто остались наработанные картинки, которые изначально не предназначались для статьи.
Для каждого слоя детализации происходит проверка изменения положения камеры. Если произошло смещение на расстояние равное шагу данного слоя детализации, значит этот слой участвует в рекурсивной генерации новых данных для индексного буфера. Данный механизм запускается в отдельном потоке каждый раз, когда помечается на обновление слой с самой высокой детализацией. А далее проверяется выше описанное условие для остальных слоев. В потоке выполняются следующие операции:
- разбиение слоев детализации, добавление новых вершин (и корректировка их высот) в единый массив вершин; - инициализация единого массива индексов, связывающих все треугольники (вершины), всех слоев детализации. - генерация нормалей.
На пальцах все это выглядит просто, для оценки реализации приведу пример рекурсивного метода, рассчитывающего интервал значений индексного массива, соответствующего ветке дерева визуализации нулевого слоя детализации. Честное слово, мне проще код писать, чем слова в предложения собирать. Не ругайте сильно, но по другому мне сложно коротко сформулировать ту кучу "каракуль", которыми я исписал свой блокнот, занимаясь данной темой. Нулевым в данном случае является слой наименьшей детализации. Что бы окончательно Вас запутать. Скажу что понятия уровней детализации и дерева детализации тесно сплетены между собой. Дерево это восходящие, из слоя нулевой детализации, ветви связей перпендикулярно пересекающие по иерархии старшинства следующие уровни (плоскости) детализации, разделяясь в точке пересечения на четыре наследника. Потому Quad tree. Зачем все так сложно? Зачем представление в виде дерева и плоскостей? Все просто. Каждое представление для выполнения различных задач. Каждые задачи наиболее быстро выполняются только в одном из представлений. Плоскости для линейных задач, дерево для рекурсивных и отслеживания наследственных связей. Выше упомянутый обещанный рекурсивный метод возвращает все индексы всех треугольников ограниченных габаритами одной ветки нулевого слоя детализации.
Блок схема обещанного метода:
Блок схема автоматически сгенерирована примочкой к Visual Studio под названием «Microsoft Visual Studio Learning Pack 2.0»
Код метода:
Код метода: /// /// Рекурсивная сборка индексов отдельной ветки дерава уровней детализации ландшафта /// privatevoid InitNodeIndex (gplNode inNode, refint inIndex) { // --- уровень детализации --------------------------------------- int l = inNode.Level; // --- индекс активной ветки -------------------------------------- int i = inNode.Index; // --- размеры активного уровня детализации ----------------------- int w = levelInfo[l].W; int h = levelInfo[l].H; // --- признаки соединение со слоем более низкой детализации ------ bool bLeft = false; bool bRight = false; bool bUp = false; bool bDown = false; // --- индексные направления слоя для псевдодвухмерной карты ------ gxplTrends trends = new gxplTrends(); trends.NewTrends(i, w); // --- если ветка не разбита ---------------------------------------------------------------------------------------- if (inNode.NodeChildren == null) { // -------------------------------------------------------------------------------------------------------------- #region --- собираем два треугольника --------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------- // - 1й треугольник ----------------------- indices[inIndex + 0] = trends.Center; indices[inIndex + 1] = trends.Right; indices[inIndex + 2] = trends.Down; // - 2й треугольник ----------------------- indices[inIndex + 3] = trends.Down; indices[inIndex + 4] = trends.Right; indices[inIndex + 5] = trends.DownRight; inIndex += 6; // -------------------------------------------------------------------------------------------------------------- #endregion ------------------------------------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- } // --- если разбита ------------------------------------------------------------------------------------------------- else { // -------------------------------------------------------------------------------------------------------------- #region --- определение режима соединения с соседними ветками --------------------------------------------------- // -------------------------------------------------------------------------------------------------------------- // --- check left join -------------------------------------------- //if (!bNoLeft) bLeft = (inNode.Coord.X > levelInfo[l].SX0) ? map[trends.Left].NodeChildren == null : true; // --- check right join ------------------------------------------- //if (!bNoRight) bRight = (inNode.Coord.X < levelInfo[l].SX0 + levelInfo[l].W - 1) ? map[trends.Right].NodeChildren == null : true; // --- check up join ---------------------------------------------- //if (!bNoUp) bUp = (inNode.Coord.Y > levelInfo[l].SY0) ? map[trends.Up].NodeChildren == null : true; // --- check down join -------------------------------------------- //if (!bNoDown) bDown = (inNode.Coord.Y < levelInfo[l].SY0 + levelInfo[l].H - 1) ? map[trends.Down].NodeChildren == null : true; // -------------------------------------------------------------------------------------------------------------- #endregion ------------------------------------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- #region --- сборка всех треугольников с учетом связей различных слоев детализации ------------------------------- // -------------------------------------------------------------------------------------------------------------- // --- индексные направления слоя +1 для псевдодвухмерной карты ------------------------- gxplTrends trends4x3 = new gxplTrends(inNode.NodeChildren[3].Index, levelInfo[l + 1].W); // -------------------------------------------------------------------------------------------------------------- #region --- если с лева соединение с более высокой детализацией -------------------------------------------------- // -------------------------------------------------------------------------------------------------------------- if (bLeft) { indices[inIndex + 0] = trends.Center; indices[inIndex + 1] = trends4x3.Center; indices[inIndex + 2] = trends.Down; inIndex += 3; // --- если с верху нет! соединения с более высокой детализацией (добавочный треугольник) if (!bUp) { indices[inIndex + 0] = trends.Center; indices[inIndex + 1] = trends4x3.Up; indices[inIndex + 2] = trends4x3.Center; inIndex += 3; } // --- если с низу нет! соединения с более высокой детализацией (добавочный треугольник) if (!bDown) { indices[inIndex + 0] = trends.Down; indices[inIndex + 1] = trends4x3.Center; indices[inIndex + 2] = trends4x3.Down; inIndex += 3; } } else// если нет соединение с более высокой детализацией, то переходим к следующему уровню детализации { if (!bUp) // Х0 InitNodeIndex(inNode.NodeChildren[0], ref inIndex); // 00
if (!bDown) // 00 InitNodeIndex(inNode.NodeChildren[2], ref inIndex); // Х0 } // -------------------------------------------------------------------------------------------------------------- #endregion ------------------------------------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- #region --- если с права соединение с более высокой детализацией ------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- if (bRight) { indices[inIndex + 0] = trends.Right; indices[inIndex + 1] = trends.DownRight; indices[inIndex + 2] = trends4x3.Center; inIndex += 3; // --- если с верху нет! соединения с более высокой детализацией (добавочный треугольник) if (!bUp) { indices[inIndex + 0] = trends4x3.Center; indices[inIndex + 1] = trends4x3.Up; indices[inIndex + 2] = trends.Right; inIndex += 3; } // --- если с низу нет! соединения с более высокой детализацией (добавочный треугольник) if (!bDown) { indices[inIndex + 0] = trends4x3.Center; indices[inIndex + 1] = trends.DownRight; indices[inIndex + 2] = trends4x3.Down; inIndex += 3; } } else// если нет соединение с более высокой детализацией, то переходим к следующему уровню детализации { if (!bUp) // 0Х InitNodeIndex(inNode.NodeChildren[1], ref inIndex); // 00
if (!bDown) // 00 InitNodeIndex(inNode.NodeChildren[3], ref inIndex); // 0Х } // -------------------------------------------------------------------------------------------------------------- #endregion ------------------------------------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- #region --- если с верху соединение с более высокой детализацией ------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- if (bUp) { indices[inIndex + 0] = trends.Center; indices[inIndex + 1] = trends.Right; indices[inIndex + 2] = trends4x3.Center; inIndex += 3; // --- если с лева нет! соединения с более высокой детализацией (добавочный треугольник) if (!bLeft) { indices[inIndex + 0] = trends.Center; indices[inIndex + 1] = trends4x3.Center; indices[inIndex + 2] = trends4x3.Left; inIndex += 3; } // --- если с права нет! соединения с более высокой детализацией (добавочный треугольник) if (!bRight) { indices[inIndex + 0] = trends4x3.Center; indices[inIndex + 1] = trends.Right; indices[inIndex + 2] = trends4x3.Right; inIndex += 3; } } // -------------------------------------------------------------------------------------------------------------- #endregion ------------------------------------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- #region --- если с низу соединение с более высокой детализацией ------------------------------------------------- // -------------------------------------------------------------------------------------------------------------- if (bDown) { indices[inIndex + 0] = trends.Down; indices[inIndex + 1] = trends4x3.Center; indices[inIndex + 2] = trends.DownRight; inIndex += 3; // --- если с лева нет! соединения с более высокой детализацией (добавочный треугольник) if (!bLeft) { indices[inIndex + 0] = trends4x3.Center; indices[inIndex + 1] = trends.Down; indices[inIndex + 2] = trends4x3.Left; inIndex += 3; } // --- если с права нет! соединения с более высокой детализацией (добавочный треугольник) if (!bRight) { indices[inIndex + 0] = trends4x3.Center; indices[inIndex + 1] = trends4x3.Right; indices[inIndex + 2] = trends.DownRight; inIndex += 3; } } //else //{ // //} // -------------------------------------------------------------------------------------------------------------- #endregion ------------------------------------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------- #endregion ------------------------------------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------------- } }
Свои задачи данная разработка уже выполнила. Для себя дальнейшие пути развития данной идеи я вижу два: 1) Растачивать дальше в этом направлении. Реализовать динамическую подгруздку данных о высотах с диска. Есть расчетные таблицы. Разработан алгоритм быстрого обновления кэшей высот к уровням детализации, без смещения (move) данных. Это позволит работать с ландшафтами огромного размера. Приведу пример расчета размеров LOD’ов для карты 1310.72^2 км с максимальной детализацией до 10^2 м:
2) Для игр. Придется переделать исходную коллекцию массивов высот в один. И реализовать новую выборку.