[ AmberSkyNet VR ]Трёхмерные предметы у нас вертятся и летают, и это, конечно, хорошо.. Но например, если мы захотим написать изометрическую игру, или даже совсем двумерную, то не обязательно в ней использовать трёхмерные модельки.. Можно вполне обойтись плоскими картинками, т.е. спрайтами. Реализацией класса, который бы отрисовывал на сцене спрайты мы сейчас и займёмся. Возможно, для двумерной или изометрической игры понадобится специализированный класс мира (интерфейс IWorld), но им мы займёмся несколько позже, если надо будет... CNodeSpriteСделаем класс узла сцены, который бы отрисовывал нам спрайт. Наследовать его, конечно, будем от базового класса CNode, который реализует основные функции, общие для всех узлов сцены... Сразу предусмотрим возможность анимации, чтобы не писать отдельный класс NodeSpriteAnimate,
т.к. в общем-то отрисовка анимации делается простым смещением текстурных координат. class CNodeSprite: public CNode { public: CNodeSprite(IEngine* engine); ~CNodeSprite(); virtual bool LoadResource(); // загружаем ресурсы virtual bool Draw(); // рисуем virtual char Update(float tms);// изменяем состояние спрайта на время tms // "перехватываем" установку параметров, характерных только для спрайта virtual void SetParam(const char* param_name,const char* param_value); protected: UINT current_frame; //текущий анимационный кадр UINT Repeat; // число повторений анимации, 0 - бесконечно UINT Anim_Count_X; // количество кадров, умещающихся в текстуре по X UINT Anim_Count_Y; // количество кадров, умещающихся в текстуре по Y UINT Start_Anim; // номер кадра, с которого начинается анимация UINT Count; // число кадров в анимации bool Billbord; // если true - всегда повёрнут к камере float lifeTime; // "время жизни" (используется для анимации) float *dataBuffer; // буфер точек и текстурных координат IDrawObject *DrawSprite; // объект для отрисовки спрайта граф.менеджером IMaterial *DrawMaterial; // "материал" спрайта }; Функция LoadResource предназначена для загрузки ресурсов, которые использует класс спрайта
для отрисовки. Первым делом проверяется значение параметра "Texture" (которое устанавливается
извне через функцию INode::SetParam() ). Если параметр не установлен - функция возвращает true,
т.к. попытка загрузки ресурсов закончилась ошибкой. Функция Draw устанавливает матрицу вида и отрисовывает спрайт. Если установлен признак Billbord, то координаты точек в буфере dataBuffer пересчитываются так, чтобы отрисовываемый прямоугольник был всегда перпендикулярен лучу обзора, исходящему из камеры. Функция Update увеличивает переменную LifeTime на величину tms, и если результат
больше параметра "Speed", то уменьшает величиную LifeTime на Speed (т.е. возвращаемся на начало анимации)
и производит пересчёт текстурных координат с учётом заданных параметров
анимации и номера текущего кадра. Базовые объектыВ предыдущем шаге мы научили класс мира загружать и сохранять элементы сцены в
виде XML-файлов. Доработаем его немного - введём возможность описания базовых объектов. Добавим функции для поддержки базовых объектов в наш интерфейс мира IWorld и, соответственно, в его реализацию CWorldSimple:
bool SetBaseObject(const char *ObjName, const char *InitString); Функция SetBaseObject задаёт классу IWorld имя базового объекта и его характеристики. Пока в качестве характеристики я использовал строку инициализации, но никто не мешает подвесить XML-парсер и туда.. Это позволит нам создавать группу базовых объектов вызовом одной функци createNode. Но это возможно в будущем, если возникнет такая потребность. Пока же - строка инициализации, которая выглядит так: Параметр=Значение;Параметр=Значение;...Параметр=Значение; Функцию LoadWorld доработаем так, чтобы она различала секции описания базовых объектов и описания самого мира. В XML-файле может быть такое содержимое: <?xml version="1.0" ?> <file Name="FileName" /> ... <BaseObject > <BaseName Type="BaseType" ParamName="ParamValue" ... > ... </BaseObject > <World> <NodeType ParamName="ParamValue" ... > ... </World> Общий список базовых обьектов мира мы можем вынести в отдельный XML-файле, который будем подключать
к разным сценам. Не обязательно подключать этот список "вручную",
можно прописать в XML-файле описания сцены строчку <file Name="FileName" />,
где FileName - имя файла, в котором описаны наши базовые объекты. При разборе XML-файла
нашей карты парсер обнаружит эту строчку и подгрузит в мир данные из файла с именем FileName.
А некоторые базовые объекты будут общими для всех наших миров (например - логотипы SDL, OpenGL
и AmberSkyNetVR ). BaseName - имя базового объекта, при помощи которого на него будут ссылаться в файле сцены Type - название класса базового объекта. Экземпляр класса с таким именем попытается создать наш менеджер плагинов. Имена остальных параметров различны для разных классов... Например вот так выглядит описание логотипа AmberSkyNet в базовой секции: <LogoAmberSkyNet Type="NodeSprite" Texture="AmberSkyNet.png" Size="5 5 5" Billbord="1" Repeat="0" /> NodeType - либо название базового объекта, либо название экземпляра базового класса, который будет создан менеджером плагинов. При разборе XML-файла парсер сначала ищет данное имя в списке своих базовых обьектов. Если в списке базовых объектов такое название отсутствует, то парсер воспримет NodeTypr как имя класса узла сцены и вызовет у менеджера плагинов функцию создания класса с таким именем. Например, если мы захотим в сцене увидеть логотип AmberSkyNet в точке 100,100,100 мы можем написать в секции описания мира строчку: <LogoAmberSkyNet Pos="10 10 10" /> Интерфейс менеджер звуков ISound и его реализация CSoundSDL_mixerАнимационные взрывы нам надо озвучивать, а наш движок пока молчит.. Пора делать менеджер звука. Повременим пока с трёхмерным звуком, пусть у нас менеджер делает 2 вещи - играет постоянно фоновую музыку и однократно играет эффект. Интерфейс будет простой: class ISound { public: virtual ~ISound(){} virtual bool PlayFX(const char *FxName)=0; virtual bool PlayMusic(const char *FxName)=0; virtual bool StopMusic()=0; }; Функция PlayFX запускает воспроизведение звукового эффекта. Функция PlayMusic начинает воспроизведение фоновой музыки. Функция StopMisuc останавливает воспроизведение фоновой музыки. Класс CSoundSDL_mixer, как ясно из его названия, реализует функции этого интерфейса при помощи библиотеки SDL_mixer. Но ничего не мешает написать свой менеджер звука, использующий совсем другую звуковую библиотеку. Изменения в исходникахinclude/: Убран интерфейс менеджера плагинов, который теперь скрыт в недрах IEngine.
Создание объектов теперь производится через вызов IEngine->CreateObject. src/asnCSoundSDL_mixer/: Новый плагин - менеджер звука. src/asnDeviceGl/: В плагин добавлена поддержка текстурных шрифтов,
а так же отслеживание событий выхода/входа курсора мышки в пределы окна, сворачивание окна, итп...
А еще функция MakeScreenshot заработала - теперь можно делать скриншоты
формата bmp. src/asnWorld/: Доработаны процедуры загрузки/сохранения сцены с учётом базовых объектов. src/asnMain/: Небольшая аркадная демка с падающими предметами,
на которые надо быстренько кликнуть мышкой пока они не упали.
Модели по-прежнему позаимствованными из
проекта S.C.O.U.R.G.E. Исходники этого шага выложены в SVN. Скачать их можно набрав команду: svn co https://svn.sourceforge.net/svnroot/ambernet/tags/AmberSkyNet-0.10 ambernet_0.10 |