Показаны сообщения с ярлыком xna. Показать все сообщения
Показаны сообщения с ярлыком xna. Показать все сообщения

воскресенье, 11 ноября 2012 г.

Распаковка DxtCompressed текстур


Статья актуальна для разработки в XNA Game Studio и MonoGame.

MSDN Library: Opaque and 1-Bit Alpha Textures (Direct3D 9)

В данной статье приведу пример распаковки DxtCompressed текстуры. DxtCompressed текстуры имеют несколько форматов сжатия. Ниже приведенный пример распаковки предназначен для работы с форматом DXT1. В моих проектах это наиболее распространенный применяемый формат. В DXT1 прозрачность кодируется только двумя значениями 0 и 1 т.е. ни какой полу прозрачности, что дает преимущество сжатия в 4 раза. Для сравнения в DXT5 поддерживает полу прозрачность, которая кодируется отдельным блоком, за счет чего текстура жмется всего в 2 раза.

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

Код примера можно разбить на части:
- загрузка DxtCompressed текстуры;
- распаковка и преобразование в массив цветов Color[];
- преобразование массива цветов в обычную текстуру;
- вывод текстуры на экран для визуальной проверки.

Для работы примера необходимо:
- создать новый проект Windows Game (4.0);
- в Content добавить нашу DXT1 текстуру;
- в свойствах этой текстуры переключить параметр "Texture Format" с "Color" в "DxtCompressed"


- добавляем код из примера.

Код:  

 
 using System;
 
 using Microsoft.Xna.Framework;
 using Microsoft.Xna.Framework.Content;
 using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework.Input;
 
 namespace DxtCompressed
 {
     public class Game1 : Microsoft.Xna.Framework.Game
     {
         GraphicsDeviceManager _graphics;
         SpriteBatch _spriteBatch;
 
         Texture2D _dxtCompressedTexture;
         Texture2D _colorTexture;
 
         public Game1()
         {
             _graphics = new GraphicsDeviceManager(this);
             Content.RootDirectory = "Content";
         }
 
         protected override void LoadContent()
         {
             _spriteBatch = new SpriteBatch(GraphicsDevice);
             _dxtCompressedTexture = Content.Load<Texture2D>("512x256");
             _colorTexture = Unpacking(_dxtCompressedTexture);
         }
 
         protected override void Update(GameTime gameTime)
         {
             if(Keyboard.GetState().IsKeyDown(Keys.Escape))
                 Exit();
         }
 
         protected override void Draw(GameTime gameTime)
         {
             GraphicsDevice.Clear(Color.CornflowerBlue);
             _spriteBatch.Begin();
             _spriteBatch.Draw(_colorTexture, Vector2.Zero, Color.White);
             _spriteBatch.End();
         }
 
         public struct DXT1pixel
         {
             public ushort color0;
             public ushort color1;
 
             public byte[,] pixel;
 
             public DXT1pixel(byte[] b)
             {
                 color0 = 0;
                 color0 |= (ushort)(b[1] << 8);
                 color0 |= (ushort)b[0];
                 color1 = 0;
                 color1 |= (ushort)(b[3] << 8);
                 color1 |= (ushort)b[2];
                 pixel = new byte[4, 4];
                 byte[] bbbb;
                 for(int i = 0; i < 4; i++)
                 {
                     bbbb = DecompressByte(b[i + 4]);
                     pixel[0, i] = bbbb[0];
                     pixel[1, i] = bbbb[1];
                     pixel[2, i] = bbbb[2];
                     pixel[3, i] = bbbb[3];
                 }
             }
 
             private byte[] DecompressByte(byte b)
             {
                 byte[] bb = new byte[4];
                 for(int i = 0; i < 4; i++)
                 {
                     bb[i] = (byte)(b & 0x03);
                     b = (byte)(b >> 2);
                 }
                 return bb;
             }
         }
 
         private Color WordToColor(int word)
         {
             int r,g,b;
             b = word & 0x1f;
             word = word >> 5;
             g = word & 0x3f;
             word = word >> 6;
             r = word;
             return new Color(r * 8, g * 4, b * 8);
         }
 
         public Texture2D Unpacking(Texture2D dxtTexture)
         {
             if(dxtTexture.Format != SurfaceFormat.Dxt1)
                 return null;
             int _width = dxtTexture.Width;
             int _height = dxtTexture.Height;
             int w = _width >> 2;
             int h = _height >> 2;
             byte[] color = new byte[w * h * 8];
             dxtTexture.GetData<byte>(color);
             //
             DXT1pixel[,] dxt = new DXT1pixel[w, h];
             int i;
             byte[] b = new byte[8];
             for(int y = 0; y < h; y++)
             {
                 for(int x = 0; x < w; x++)
                 {
                     i = (y * w + x) * 8;
                     Array.Copy(color, i, b, 0, 8);
                     dxt[x, y] = new DXT1pixel(b);
                 }
             }
             i = 0;
             Color[] newColor = new Color[_width * _height];
             for(int y = 0; y < h; y++)
             {
                 for(int x = 0; x < w; x++)
                 {
                     for(int yy = 0; yy < 4; yy++)
                     {
                         for(int xx = 0; xx < 4; xx++)
                         {
                             int ic = (y * 4 + yy) * _width + x * 4 + xx;
                             Color color0 = WordToColor(dxt[x, y].color0);
                             Color color1 = WordToColor(dxt[x, y].color1);
                             if(dxt[x, y].color0 > dxt[x, y].color1) 
                             {
                                 Color color2 = new Color(
                                     (int)(color0.R * 0.66f + color1.R * 0.34f),
                                     (int)(color0.G * 0.66f + color1.G * 0.34f),
                                     (int)(color0.B * 0.66f + color1.B * 0.34f));
                                 Color color3 = new Color(
                                     (int)(color0.R * 0.34f + color1.R * 0.66f),
                                     (int)(color0.G * 0.34f + color1.G * 0.66f),
                                     (int)(color0.B * 0.34f + color1.B * 0.66f));
                                 switch(dxt[x, y].pixel[xx, yy])
                                 {
                                     case 0x00:
                                         newColor[ic] = color0;
                                         break;
                                     case 0x01:
                                         newColor[ic] = color1;
                                         break;
                                     case 0x02:
                                         newColor[ic] = color2;
                                         break;
                                     case 0x03:
                                         newColor[ic] = color3;
                                         break;
                                 }
                             }
                             else 
                             {
                                 
                                 Color color2 = new Color(
                                     (int)(color0.R / 2 + color1.R / 2),
                                     (int)(color0.G / 2 + color1.G / 2),
                                     (int)(color0.B / 2 + color1.B / 2));
                                 Color color3 = Color.Transparent;
                                 
                                 switch(dxt[x, y].pixel[xx, yy])
                                 {
                                     case 0x00:
                                         newColor[ic] = color0;
                                         break;
                                     case 0x01:
                                         newColor[ic] = color1;
                                         break;
                                     case 0x02:
                                         newColor[ic] = color2;
                                         break;
                                     case 0x03:
                                         newColor[ic] = color3;
                                         break;
                                 }
                             }
                             
                         }
                     }
                 }
             }
             Texture2D t = new Texture2D(_graphics.GraphicsDevice, _width, _height);
             t.SetData<Color>(newColor);
             return t;
         }
     }
 }
 
 
 
 
 
 

Как работать с XNA Game Studio в Visual Studio 2012?

Александр Сороколетов подсказал волшебную статью:
How to install XNA game studio on Visual Studio 2012?

суббота, 15 октября 2011 г.

"GB&W Lite" [wp7][game][release]

Выпустил лайт версию игры GB&W.



Вот видео с мастер классом по прохождению 19го уровня:



игру можно найти тут: 


Как видите есть ряд изменений в оформлении, они коснулись и полной версии приложения для которого вышло соответствующее обновление.

Оказалось, при первой публикации лайт версии, GB&W занимает в памяти более 90 МБ (на сколько не скажу). Очень много анимированной растровой рафики. Лайт версию пришлось немного порезать. Получается для GB&W мне в свое время сделали скидку :).

суббота, 2 июля 2011 г.

"GB&W" in "Top WP7 Games : June 2011 [ POLL ]"

Сайт bestwp7games.com предложил голосование за игры для Windows Phone 7 вышедшие в июне. В этом списке на ряду с топовыми играми, имеющими статут xbox live, каким то образом, оказалась и наша игра GB&W. Приглашаю вас присоединиться к нашей группе болельщиков и принять участие в голосовании.

Проголосовать можно тут

Регистрация не нужна. Достаточно выбрать соответствующий пункт и подтвердить кнопкой "vote".

Всем огромное спасибо за отклик и участие!

P.S. Топовым играм, имеющим статус xbox live и команду поддержки, хватает внимания, загрузок и покупок, а вот нам бы все это даже очень не помешало.

понедельник, 13 июня 2011 г.

GB&W - Update v1.4

Это уже 4-е обновление.

Сейчас доступно:
- 40 уровней.
- 14 типов кирпичей
- 14 бонусов

Одна из последних добавленный пакостей это кирпич с кодовым названием "Биомасса". Он не прерывно плодиться с постоянной скоростью, кроме того он подвержен влиянию 2х бонусов:
- катализатор, единовременно ускоряющий рост общего объема биомассы
- заморозка, соответственно временно останавливающий рост биомассы.

Вот новое видео:

суббота, 23 апреля 2011 г.

GB&W - Windows Phone 7 game preview 3

Не пугайтесь обилию выпадающих бонусов. Это один из первых десяти обучающих уровней. Десятка разбита по применению тематических бонусов их комбинаций.
В этом уровне первый раз появляется усиление на пулемет, который в свою очередь превращается в нечто иное :).

Рекомендую смотреть в качестве 720p (сейчас 480p)



Мне часто задают вопрос - как происходит управление в игре и как целиться? Все просто. Кораблик управляется одним касанием и постоянно находится под пальцем. Пулеметы наводятся на ближайшую цель в радиусе поражения. Вторым касанием на кнопки в верхней панели активируется тяжелое вооружение:
ракета I - открыть/закрыть подвеску, для экономии боезапаса. Само наводится и поражает ближайшего бота. При потере цели в процессе подлета (цель уничтожена др. ракетой и т.д.) находит и атакует следующую ближайшую.
ракета II - пуск. Само наводится и поражает/запечатывает ближайшую шахту из которой появляются боты.
ракета III - активация ракеты. Второе нажатие в области экрана помечает область поражения и происходит старт. Так же взрывается от столкновения с кирпичами.

Я потратил какое то время на изменение своего Touch класса. Теперь он понимает второе касание типа Tap при активном первом касании. Т.е. можно не прерываясь контролить кораблик и активировать ракетные бонусы.

среда, 23 марта 2011 г.

"Guns, Balls & Walls" - Windows Phone 7 game preview 2

Делюсь очередным видео. Музыкальное сопровождение отключено по политическим соображениям. Еще нет четкой договоренности с композитором.

понедельник, 7 марта 2011 г.

"Guns, Balls & Walls" - Windows Phone 7 game preview

Игра предназначается для Windows Phone 7 marketplace.
Cуть игры - смешанный коктейль трех направлений:
1) арканоид - основная идея и условия прохождения уровня.
2) аэрохоккей - расширенные правила взаимодействия биты(battleship) и мяча
3) хардкорный 2d шутер - пулеметы, ракеты, враги, кровь, взрывы ... боже дай мне сил остановиться :))).

Отладочный уровень:


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

В целом данный проект является экспериментом, в том числе и в 2d.
Очень много времени уходит на рисование, особенно что касается на первый взгляд не заметных деталей таких как кровь на решетках вентиляторов и т.д. . Примерное соотношение времени = (день рисую) / (час программирую). С самим вопросом рисования проблем нет, но не набита рука, плюс, например, если в карандаше я нарисую все что угодно, то ощущение цвета нужно еще нарабатывать. Радует то, что по мере работы прокачка движется. Мега респект создателям графического редактора Paint.net

Windows Phone 7 радует своими возможностями. По предварительным подсчетам, в данном приложении, запас на частицы порядка 4000 штук. Возможно при желании у меня получится и больше. Время покажет.

пятница, 14 января 2011 г.

XNA, GarbageCollector, SynchronizeWithVerticalRetrace, IsFixedTimeStep – используйте правила и будет вам счастье …

И так по порядку. Что значат эти термины:

SynchronizeWithVerticalRetrace – public properties GraphicsDeviceManager. Gets or sets a value that indicates whether to sync to the vertical trace (vsync) when presenting the back buffer (MSDN). Эта штука синхронизирует переключение back buffers с частотой обновления экрана монитора. Зная, как технически происходит обновление изображения на мониторе, можно сказать, что включение данного параметра нужно для предотвращения разрезания изображения при смене кадров.

IsFixedTimeStep - public properties Microsoft.Xna.Framework.Game. Gets or sets a value indicating whether to use fixed time steps (MSDN). Параметр, отвечающий за включение или отключение режима, который отвечает за ограничение частоты вызовов метода Update.

GarbageCollector – системный сборщик мусора. Конкретно нас будет интересовать класс System.GC .

Как работают SynchronizeWithVerticalRetrace и IsFixedTimeStep. Есть четыре сочетания данных флагов:
1. SynchronizeWithVerticalRetrace = false и IsFixedTimeStep = false
В этом случае графические буферы меняются по мере их рисования, не дожидаясь синхроимпульса, синхронно вызывается метод Update.
Особенности: обычно в приложении не требуется вызывать метод Update с максимально возможной частотой. При SynchronizeWithVerticalRetrace = false мы имеем возможность измерять значение fps превышающее частоту обновления монитора. Активно пользуюсь при отладке приложения.
2. SynchronizeWithVerticalRetrace = false и IsFixedTimeStep = true
В этом случае мы можем дополнительно контролировать частоту вызова методов Update и Draw и устанавливать фиксированный интервал через свойство TargetElapsedTime.
Особенности: сомнительное удовольствие применять данный подход повсеместно. Он подходит, для каких либо исключительных случаев, в которых его применение полностью обоснованно. Не пользуюсь.
3. SynchronizeWithVerticalRetrace = true и IsFixedTimeStep = false
Тут все просто, обновляем, рендерим, ждем синхронизации, меняем back buffers и по новой.
Особенности: невозможно измерить значение fps выше частоты обновления монитора. Пользуюсь.
4. SynchronizeWithVerticalRetrace = true и IsFixedTimeStep = true
Интересный случай. Если время синхронизации меньше IsFixedTimeStep, то ждем следующей синхронизации и только после этого выполняется Update и Draw. Если же время синхронизации больше IsFixedTimeStep, то по факту выполнения синхронизации не медленно выполняется Update и Draw. Эдакий хромающий ослик без одной ноги).
Особенности: теряюсь в догадках где это можно применить, точно не мой профиль). В некоторых случаях при значениях TargetElapsedTime/ vsyncTime = 1.5f (примерно) можно на глаз заметить не равномерное обновления экрана. Не пользуюсь.

Уже теплее. И так, о главном). Если вы написали приложение|игру используя XNA, применили из вышеописанных приемов 1й или 3й и в вашем приложении наблюдаются периодические задержки, а вы к тому же нечайно вспомнили «слова которые нельзя произносить» - Garbage Collector ))). Главное не паникуйте, при этом не обязательно писать в различных форумах Ваше мнение о .net и XNA, крутости и преимуществах C++. Все это от нервов, нервы от не знания, не знание от лени, лень от глупости, но это лечится.

Как пользуется Garbage Collector’ом рядовой «пользователь» Visual Studio? Да ни как. Он полагается на настройки по умолчанию для Garbage Collector’а. Настройки по умолчанию рассчитаны на так называемые бизнес приложения, для динамичной работы с DirectX нужно доработать.

По умолчанию Garbage Collector выполняет освобождение памяти в любом случае не синхронно с работой Update. Мертвый груз копится и освобождается в непредсказуемый, для логики вашего приложения, момент. Самый простой выход – это делать в начале очередного Update:
System.GC.Collect(1);
Где 1 это номер поколения, значение подобрано экспериментально.

С другой стороны может быть не целесообразно перегибать палку и выполнять сборку мусора 60 раз в секунду при включенной синхронизации (если у монитора такая рабочая частота обновления экрана) или более 60 раз при выключенной синхронизации.
По этому можно сделать так:

using System;
using System.ComponentModel;
using Microsoft.Xna.Framework;

namespace GeneralPresentationFoundation.gSystem.gGarbageCollector
{
    public class GarbageCollector
    {

        private BackgroundWorker bw = new BackgroundWorker();

        private int stepNumber = 0;

        private int sleepStep = 1;
        public  int SleepStep { get { return sleepStep; } set { sleepStep = value; } }

        public GarbageCollector()
        {
            Init();
        }

        public GarbageCollector(int sleepStep)
        {
            this.sleepStep = sleepStep;
            Init();
        }

        private void Init()
        {
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        }

        public void Update()
        {
            if(!bw.IsBusy)
            {
                stepNumber++;
                if(stepNumber == sleepStep)
                {
                    stepNumber = 0;
                    bw.RunWorkerAsync();
                }
            }
        }

        private void bw_DoWork(Object sender, EventArgs e)
        {
            GC.Collect(1);
        }
    }
}

Возможно не везде будет полезным применение потока, это просто мой случай.
GarbageCollector gc = new GarbageCollector(20);
т.е. при частоте монитора 60 Гц - 3 раза в секунду.

четверг, 14 октября 2010 г.

GPF - "WPF" for XNA

GPF - на таком рабочем названии библиотеки я остановился.

Сегодня закончил модуль 3d форм - это такой штука, обеспечивающая
функционал добавления в сцену объектов на плоские грани которых можно проецировать формы и оперировать ими указателем мыши.

Пока не стал возиться с наложением форм на не плоские поверхности по
причине того, что это нарушит совместимость с Windows Phone 7 экземпляром библиотеки.

Вот такое видео:



Показал Expander, DataGrid, Table.
Со стилями элементов опять не поиграл. Оставил на потом)

суббота, 9 октября 2010 г.

XNA. gEngine. Forms (GUI)

Вот и созрел я до первого показа своего интерфейса для приложений реализованных на базе 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 движка.
В представленном видео все элементы и формы выводятся с простым базовым стилем. Под базовым стилем подразумевается (если есть) прямоугольная рамка и (если есть) прямоугольник фона. Стиль любого элемента достаточно просто меняется наследованием от него нового нового класса. Я пока не стал перегружать рабочую версию интерфейса надуманным стилем и растровой графикой, бесполезная трата времени на то что в последствии не пригодится.

четверг, 7 октября 2010 г.

Фрагмент истории xnadev.ru

Своим рассказом хочу развеять некоторые предрассудки, заблуждения и прояснить ситуацию.

Как то зашел разговор о том, что с различных ресурсов, пользователей задающих вопросы по XNA, перенаправляют на наш сайт xnadev.ru. Пользователи приходят не (!) регистрируясь осматриваются, чаще всего делаю вывод о том что форум мертв и уходят. Т.е. не задерживаются.

На самом же деле форум не мертв. Собралась достаточно дружная компания, костяк из админов, MVP, активных грамотных пользователей, плюс немного интересных и эксцентричных персонажей, добавляющих в наше общение позитив, и прочие читатели. Каждый занимается своим делом, временами рассказываем, показываем наработки. Ежедневно отвечаем на вопросы (я это делаю по вечерам). Общаемся в меру свободного времени и настроения. 

Но до недавнего времени доступ к самым активным веткам форума был закрыт (!) для не зарегистрированных пользователей. Без злого умысла, данное обстоятельство было продиктовано техническими ограничениями тарифа хостинга сайта (нагрузки, трафик и т.д.), а так как админы платили кровные из своего кармана, то пояса пришлось затянуть по туже. Сейчас мы сменили хостинг, переехали на 1gb.ru. Еще раз спасибо за содействие Евгению Марченкову (Microsoft). Возможности расширились и мы изменили права доступа для всех пользователей (и соответственно поисковых систем). Вот такая вот наша сказка).

Милости просим.

среда, 19 мая 2010 г.

Windows Phone 7 + XNA 4.0 + Test Game

Поигрался с Windows Phone 7, начал с проверки возможностей 2D, вот что получилось:

очень пока не хватает возможности использовать собственные шейдеры, а решать этот вопрос в обход длинным путем не очень хочется. так бы уже можно было поиграть с normal maping и добавить к рельефу освещение).

воскресенье, 21 февраля 2010 г.

#1 XnaDevRuEngine - 3d Max Export Plugin

Заработал мой плагин экспорта из 3d Max. Были сложности с взаимодействием костей, но упорство, блокнот и карандаш все решили.

четверг, 4 февраля 2010 г.

#0 XnaDevRuEngine

Мои коллеги, с которыми меня объединяет деятельность в рамках ресурса xnadev.ru, и я, начали работу над 3D движком XnaDevRuEngine. Если не брать в расчет наработки по модулю интерфейса (forms), которые ранее существовали как самостоятельный проект, то следующее видео можно считать первым, демонстрирующим XnaDevRuEngine:

Сейчас частично готовы модули SceneTree и прототип ResorceManager.

четверг, 10 сентября 2009 г.

С#+XNA. В погоне за fps. I

Краткое содержание:

- Введение;
- Часть 1-я. Новичкам;
- Часть 2-я. Та же песня с Microsoft'ом;
- Часть 3-я философская. Охотник или жертва;
- Заключение.


Введение.

Данная статья является попыткой посмотреть в корень проблемы. Проблемы достижения высоких показателей fps с точки зрения построения C# кода не касаясь при этом XNA раздела 3D, относящегося к непосредственным инструкциям видеокарте.


Часть 1-я. Новичкам.

Мои наблюдения основаны на постоянном присутствии в различных форумах и участии в различных проектах. Соль рассматриваемого мною вопроса заключается в использовании Fields и Properties членов классов и структур.
Для полноты ощущений, в моих изысканиях, меня интересовали следующие ссылочные типы:
- class;
- interface;
- object.
Сейчас мы просмотрим на время выполнения кода при различной организации доступа к членам класса.
Пример:


using System;
namespace TestObjectInterface
{
class Program
{
static Unit[] unit;
static object[] unitobj;
static iUnit[] uniti;

static int count;

static void Main(string[] args)
{
count = 10000000;
unit = new Unit[count];
unitobj = new object[count];
uniti = new iUnit[count];
Init();
double tf = TestFields();
double tp = TestProperties();
double tfo = TestFieldsObject();
double tpo = TestPropertiesObject();
double tpi = TestPropertiesInterface();
Console.WriteLine("testFields = "
+ tf.ToString() + " mc, k = 1");
Console.WriteLine("testProperties = "
+ tp.ToString() + " mc, k = "
+ (tp / tf).ToString());
Console.WriteLine("testFieldsObject = "
+ tfo.ToString() + " mc, k = "
+ (tfo / tf).ToString());
Console.WriteLine("testPropertiesObject = "
+ tpo.ToString() + " mc, k = "
+ (tpo / tf).ToString());
Console.WriteLine("testPropertiesInterface = "
+ tpi.ToString() + " mc, k = "
+ (tpi / tf).ToString());
//Console.ReadKey();
}

static void Init()
{
for (int i = 0; i < count; i++)
{
unit[i] = new Unit(i, i * 2);
uniti[i] = (iUnit)unit[i];
unitobj[i] = (object)unit[i];
}
}

static double TestFields()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = unit[i].x + unit[i].y; // 0
n = unit[i].x + unit[i].y; // 1
n = unit[i].x + unit[i].y; // 2
n = unit[i].x + unit[i].y; // 3
n = unit[i].x + unit[i].y; // 4
n = unit[i].x + unit[i].y; // 5
n = unit[i].x + unit[i].y; // 6
n = unit[i].x + unit[i].y; // 7
n = unit[i].x + unit[i].y; // 8
n = unit[i].x + unit[i].y; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestProperties()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = unit[i].X + unit[i].Y; // 0
n = unit[i].X + unit[i].Y; // 1
n = unit[i].X + unit[i].Y; // 2
n = unit[i].X + unit[i].Y; // 3
n = unit[i].X + unit[i].Y; // 4
n = unit[i].X + unit[i].Y; // 5
n = unit[i].X + unit[i].Y; // 6
n = unit[i].X + unit[i].Y; // 7
n = unit[i].X + unit[i].Y; // 8
n = unit[i].X + unit[i].Y; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestFieldsObject()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 0
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 1
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 2
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 3
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 4
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 5
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 6
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 7
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 8
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestPropertiesObject()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 0
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 1
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 2
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 3
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 4
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 5
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 6
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 7
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 8
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestPropertiesInterface()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = uniti[i].iX + uniti[i].iY; // 0
n = uniti[i].iX + uniti[i].iY; // 1
n = uniti[i].iX + uniti[i].iY; // 2
n = uniti[i].iX + uniti[i].iY; // 3
n = uniti[i].iX + uniti[i].iY; // 4
n = uniti[i].iX + uniti[i].iY; // 5
n = uniti[i].iX + uniti[i].iY; // 6
n = uniti[i].iX + uniti[i].iY; // 7
n = uniti[i].iX + uniti[i].iY; // 8
n = uniti[i].iX + uniti[i].iY; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}
}

interface iUnit
{
int iX {get;}
int iY {get;}
}

public class Unit : iUnit
{
public int x;
public int X { get { return x; } }
int iUnit.iX { get { return x; } }

public int y;
public int Y { get { return y; } }
int iUnit.iY { get { return y; } }

public Unit(int inX, int inY)
{
x = inX;
y = inY;
}
}
}

Я не буду уделять внимание коду, пример достаточно детский. Скажу только то, что по 10 инструкций в каждом цикле сделано для контрастного выделения времени выполнения тела цикла, от самой инструкции цикла.
У меня программа отработала следующим образом:


На мой взгляд все достаточно наглядно. Самый скоростной способ это естественно обращение к полю класса на прямую.
А теперь поясню к чему рассказал и продемонстрировал прописную истину. Всем так же известно, что в разработке 3D, в отличие от бизнес, приложений особенно ярко выражена погоня за скоростью. Но тем не менее, специалисты начинающие(!) работать с XNA, уже как правило, достаточно уверенно себя ощущают в области C# разработки. Вот в этом то и заключается основной казус. Который объясняется тем фактом, что вся справочная и обучающая литература пестрит примерами описывающими доступ к полям классов через свойства. В следствии чего ребята начинают автоматически(!) применять данный подход во всех случаях. И там где это действительно необходимо, и где не очень. Да это хороший тон(!). Удобно перехватывать обращения к полю класса, обрабатывать возникающие при этом исключительные ситуации. Но не подходит для применения в 3D(!!!).
Не всегда можно быстро выдать столько информации по данному вопросу очередному собеседнику. Да и сейчас я описал вопрос не за пять минут, но в дальнейшем могу с легкостью ссылаться на себя :).


Часть 2-я. Та же песня с Microsoft'ом.

Не для кого не секрет, что я люблю поковырять рефлектором все, что меня хотя бы мало-мальски интересует. Добавлю то, что порой это приносит больше информации, чем например чтение непосредственно MSDN. Поэтому в довесок к первой части я решил привести один(!) пример из библиотеки XNA.
Рассмотрим XNA 3.1, виндовую библиотеку Microsoft.Xna.Framework.dll,
одноименный namespace, и пусть подопытным будет структура Vector3.
Вот часть кода данной структуры, в рамках которой мы сейчас и пообщаемся:


public struct Vector3
{
public float X;
public float Y;
public float Z;
...
private static Vector3 _one;
public static Vector3 One
{
get
{
return _one;
}
}
...
public Vector3(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
...
static Vector3()
{
...
_one = new Vector3(1f, 1f, 1f);
...
}
}

К полям X, Y и Z вопросов нет. Но обращаю ваше внимание на свойство One. По логике One возвращает единичный вектор постоянного значения. А вот тут по подробнее. Разум цепляется за формулировку "постоянное значение". Читаем MSDN и выясняем, что переменная или поле с постоянным значением может быть объявлена при помощи двух ключевых слов модификаторов доступа, которыми являются const и readonly. На всякий случай поясню формулировкой, переменная или поле постоянного значения бывает двух видов:
- const -> применяется при объявления полей и локальных переменных, постоянное значение присваивается на этапе компиляции, исключительно(!) при объявлении;
- readonly -> применяется при объявления полей, значение может присваивается как при объявлении, так и в конструкторе данного класса или структуры.
Но речь идет о поле постоянного значения которое не возможно рассчитать при компиляции, что накладывает ряд ограничений и в соответствии с рассматриваемым нами случаем, расширим формулировки:
- Модификатор static не допускается в объявлении константы;
- значение константы должно быть полностью вычислено во время компиляции;
- Единственными возможными значениями для констант ссылочных типов являются string и null;
Ясное дело на static ставим крест, он однозначно не подходит. А вот readonly наш размерчик! И как нельзя лучше вписывается в нашу ситуацию. Потому, заранее уже зная куда нас это приведет, предлагаю по тестировать и сравнить время чтения значения из свойства стандартной структуры с чтением значения из иначе построенной структуры.
Смотрим пример:


using System;
using Microsoft.Xna.Framework;
namespace TestFields
{
class Program
{

static int count;

static void Main(string[] args)
{
count = 10000000;
Console.WriteLine("testStandartVector3 = " +
TestStandartVector3().ToString() +
" mc");
Console.WriteLine("testMyVector3 = " +
TestMyVector3().ToString() +
" mc");
}

static double TestStandartVector3()
{
DateTime dtStart = DateTime.Now;
Vector3 vec;
for (int i = 0; i < count; i++)
{
vec = Vector3.One; // 0
vec = Vector3.One; // 1
vec = Vector3.One; // 2
vec = Vector3.One; // 3
vec = Vector3.One; // 4
vec = Vector3.One; // 5
vec = Vector3.One; // 6
vec = Vector3.One; // 7
vec = Vector3.One; // 8
vec = Vector3.One; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestMyVector3()
{
DateTime dtStart = DateTime.Now;
gVector3 vec;
for (int i = 0; i < count; i++)
{
vec = gVector3.One; // 0
vec = gVector3.One; // 1
vec = gVector3.One; // 2
vec = gVector3.One; // 3
vec = gVector3.One; // 4
vec = gVector3.One; // 5
vec = gVector3.One; // 6
vec = gVector3.One; // 7
vec = gVector3.One; // 8
vec = gVector3.One; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}
}

public struct gVector3
{
public float X;
public float Y;
public float Z;

public static readonly gVector3 One;

public gVector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}

static gVector3()
{
One = new gVector3(1, 1, 1);
}
}
}

Получаем вот такой вот результат:


Внимание вопрос(!): какая была необходимость реализовывать "постоянное значение" через свойство возвращающее значение приватного поля, если в сравнительном отношении скорости, с реализацией через модификатор доступа readonly, составляет разницу практически в(!) два раза? Мне не понятно, а вам? Буду очень признателен тому человеку, который вежливо мне объяснит.
Товарищам, которые планируют достаточно часто применять постоянные вектора:
- Zero;
- One;
- UnitX;
- UnitY;
- UnitZ;
- Up;
- Down;
- Right;
- Left;
- Forward;
- Backward.
Рекомендую не использовать стандартные, а реализовать самостоятельно, как в выше приведенном примере.
Мысль которую я хотел донести, звучит следующим образом:
- изучайте все средства, которые собираетесь применять в своих проектах;
- в результате изучения берите только лучшее и делайте наконец то сказку былью;
- не знание законов не освобождает от ответственности. Это про специалистов, которые по своей беспечности, не разбираясь, применяют все подряд, а в последствии сетуют на Microsoft. Хотя это говорит с плохой стороны не(!) о Microsoft.
- допуская то, что не смотря на "много букв", у читателей данной статьи мнения разойдутся. Я добавил третью часть. Читать рекомендую всем, только одним принять к сведению, а иным просто улыбнуться.


Часть 3-я философская. Охотник или жертва?

Жертвам своей не компетентности, которые любят плохо отзываться о Microsoft, посвящается.
Что, из себя, представляет понятие мода? Мода - в широком смысле этого слова, определяет стремление, того или иного человека, быть похожим на основное большинство. Хм, так то оно так, когда в меру, без фанатизма и крайностей. А так же не идет в разрез с законами эволюции, которые не зависимо от нас хранят приоритетное право "сильных" и "особенно удачных" особей выживать. К примеру обратная крайность это не формальные движения в нашем обществе.
Применяем выше описанное отступление к нашей основной теме. И так у нас есть три основных типа людей. Систематизируем их и результат оформим в виде структуры:
- ярко выражено не(!) довольные Microsoft'ом -> модные "жертвы". Да, уже более десяти лет считается модно и наверное круто так себя вести;
- фанаты Microsoft'а -> назовем их "панки", так веселее смотрится не формальное определение. Обычно эти люди либо являются специалистами, но узкого профиля, либо совсем не являются таковыми. Если кого то обидел, сообщите мне. Я подберу более мягкое определение.
- специалисты -> "охотники". Ни кому не покланяются, ни кого не ругают. Им некогда отвлекаться на разную чушь. Эти люди берут от жизни только лучшее, прекрасно понимая при этом как и где это лучшее применять, и как и где не стоит. Они же становятся MCP, MVP, "черными поясами" Intel, … , и другими почетными товарищами.


Заключение.

Ну вот и все. Всем перца насыпал. Развеялся. Можно дальше "охотиться" ... оговорился :) ... работать и точить свое мастерство.