Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / Введение в программирование для магазина Windows

Введение в программирование для магазина Windows

Внимание! Этот документ ещё не опубликован.

Автор:

Приветствую Вас на продолжение статьи про разработку приложений для магазина Windows! Начало стать находится здесь.

Графика

Теперь не мешало бы заняться графической частью. Как я уже говорил, для простоты мы воспользуемся Direct2D, который работает через Direct3D. Для этого заведём новый класс, который назовём Graphics. Нам будут нужны следующие заголовочные файлы:

#include <agile.h>
#include <wrl.h>
#include <d2d1.h>
#include <d2d1_1.h>
#include <d2d1_1helper.h>
#include <dwrite_1.h>
#include <wincodec.h>
#include <d3d11_1.h>

ВНИМАНИЕ: Обязательно не забудьте компоновщику указать список подключаемых библиотек. Например у меня там указано: d2d1.lib, d3d11.lib, dxgi.lib, ole32.lib, windowscodecs.lib, dwrite.lib, Xaudio2.lib

Думаю что рассказывать что за что отвечает нет смысла, так как это совсем не относится к магазину Windows, приведу сразу код. Объявление:

+ Показать

Реализация:

+ Показать

Теперь всё это дело нужно подключить. Возвращаемся в View и объявляем в классе:

Graphics* graphics;

Создание у нас будет в момент установки окна, в самом конце функции (после всех подписок):

graphics=new Graphics();
graphics->init(_window,Windows::Graphics::Display::DisplayProperties::LogicalDpi);

Для краткости кода я проверки сюда не буду писать (но Вам стоит про них не забывать). Подправим приостановку программы:

void View::onSuspending(Object^ _object,SuspendingEventArgs^ _args)
{
  delete graphics;
}

Не забудем про изменение размеров:

void View::onSizeChanged(CoreWindow^ _window,WindowSizeChangedEventArgs^ _args)
{
  graphics->updateForWindowSizeChange();
}

А также про изменение dpi:

void View::onLogicalDpiChanged(Platform::Object^ _object)
{
  graphics->setDpi(_dpi);
}

Теперь объявим в классе пару переменных:

bool isExit;
bool isVisible;

Одна активируется когда окно закроется, другая - когда скроется. Их начальные значение можно инициализировать в момент инициализации:

void View::Initialize(CoreApplicationView^ _applicationView)
{
  isExit=false;
  isVisible=true;

  _applicationView->Activated+=ref new Windows::Foundation::TypedEventHandler<Windows::ApplicationModel::Core::CoreApplicationView^,Windows::ApplicationModel::Activation::IActivatedEventArgs^>(this,&View::onActivated);
  Windows::ApplicationModel::Core::CoreApplication::Suspending+=ref new Windows::Foundation::EventHandler<Windows::ApplicationModel::SuspendingEventArgs^>(this,&View::onSuspending);
  Windows::ApplicationModel::Core::CoreApplication::Resuming+=ref new Windows::Foundation::EventHandler<Platform::Object^>(this,&View::onResuming);
}

Теперь немного добавим кода:

void View::onVisibilityChanged(CoreWindow^ _window,VisibilityChangedEventArgs^ _args)
{
  isVisible=_args->Visible;
}

void View::onWindowClosed(CoreWindow^ _window,CoreWindowEventArgs^ _args)
{
  isExit=true;
}

И приступим к написанию главного цикла:

void View::Run(void)
{
  while(!isExit)
  {
    if(isVisible)
    {
      Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
      graphics->draw();
      graphics->present();
    }
    else
    {
      Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending);
    }
  }
}

Как видно, в зависимости от того, видим ли мы окно, так поток и обрабатывает события.


ВАЖНО: Никогда не выполняйте долгих операций в главном потоке программы. Главный поток должен всегда оставаться свободным для мгновенной реакции на взаимодействия пользователя с приложением. Иначе приложение могут забраковать на сертификации. Идеальный вариант: впустую крутить главный цикл, когда в этот же момент другой поток (или другие потоки) что-то грузят. Не используйте для работы с потоками std::thread. Из-за этого программа может лечь. Для работы с потоками Windows API предоставляет свои средства. Не используйте для работы с файлами стандартную библиотеку C, либо файловый ввод из STL. Это может привести к краху программы. Для работы с файлами есть средства в Windows API.

Покажу как загрузить картинку и вывести её на экран. Для начала нам нужен интерфейс (объявлять в классе, который будет предоставлять изображение):

Microsoft::WRL::ComPtr<ID2D1Bitmap> bitmap;

Затем нам нужно загрузить картинку:

bool ImageResource::load(void)
{
  Graphics* graphics=getGraphics();
  ComPtr<IWICBitmapDecoder> wicBitmapDecoder;
  IWICImagingFactory2* imgF=graphics->getImageFactory();
  ID2D1DeviceContext* d3dC=graphics->getContext();
  if(imgF && d3dC)
  {
    HRESULT result=imgF->CreateDecoderFromFilename(fileName.c_str(),nullptr,GENERIC_READ,WICDecodeMetadataCacheOnDemand,&wicBitmapDecoder);
    if(FAILED(result)) return false;
    ComPtr<IWICBitmapFrameDecode> wicBitmapFrame;
    result=wicBitmapDecoder->GetFrame(0,&wicBitmapFrame);
    if(FAILED(result)) return false;
    ComPtr<IWICFormatConverter> wicFormatConverter;
    result=imgF->CreateFormatConverter(&wicFormatConverter);
    if(FAILED(result)) return false;
    result=wicFormatConverter->Initialize(wicBitmapFrame.Get(),GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,nullptr,0.0,WICBitmapPaletteTypeCustom);
    if(FAILED(result)) return false;
    result=d3dC->CreateBitmapFromWicBitmap(wicFormatConverter.Get(),BitmapProperties(PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),&bitmap);
    if(FAILED(result)) return false;
  }
  return true;
}

Правда для этого нам надо будет реализовать несколько методов в классе Graphics (последний нужен для создания шрифтов):

ID2D1DeviceContext* Graphics::getContext(void)
{
  return d2dContext.Get();
}

IWICImagingFactory2* Graphics::getImageFactory(void)
{
  return wicFactory.Get();
}

IDWriteFactory1* Graphics::getWriteFactory(void)
{
  return dwriteFactory.Get();
}

А теперь покажу как вывести картинку:

bool Graphics::drawImage(ImageResource* _image,float _x,float _y,float _width,float _height,float _alpha)
{
  d2dContext->SetTransform(Matrix3x2F::Identity());
  d2dContext->DrawBitmap((ID2D1Bitmap*)_image->get(),&RectF((FLOAT)_x,(FLOAT)_y,(FLOAT)(_x+_width),(FLOAT)(_y+_height)),(FLOAT)_alpha);
  return true;
}

А теперь с поворотом:

bool Graphics::drawImage(ImageResource* _image,float _x,float _y,float _width,float _height,float _alpha,float _angle)
{
  d2dContext->SetTransform(Matrix3x2F::Rotation((FLOAT)_angle,Point2F((FLOAT)(_x+_width/2.0F),(FLOAT)(_y+_height/2.0F))));
  d2dContext->DrawBitmap((ID2D1Bitmap*)_image->get(),&RectF((FLOAT)_x,(FLOAT)_y,(FLOAT)(_x+_width),(FLOAT)(_y+_height)),(FLOAT)_alpha);
  return true;
}

Для вывода шрифта нужно объявить (в классе, который будет предоставлять шрифт):

Microsoft::WRL::ComPtr<IDWriteTextFormat> font;

Вот сама загрузка:

bool FontResource::load(void)
{
  Graphics* graphics=getGraphics();
  IDWriteFactory1* factory=graphics->getWriteFactory();
  if(!factory) return false;
  HRESULT result=factory->CreateTextFormat(fontName.c_str(),0,isBold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_REGULAR,DWRITE_FONT_STYLE_NORMAL,DWRITE_FONT_STRETCH_NORMAL,(FLOAT)size,L"en-us",&font);
  if(FAILED(result)) return false;
  return true;
}

А вот вывод:

bool Graphics::drawText(FontResource* _font,const std::wstring& _text,float _x,float _y,float _width,float _height,float _red,float _green,float _blue,float _alpha)
{
  textBrush->SetColor(ColorF((FLOAT)_red,(FLOAT)_green,(FLOAT)_blue));
  textBrush->SetOpacity((FLOAT)_alpha);
  d2dContext->DrawText(_text.c_str(),(UINT32)_text.size(),(IDWriteTextFormat*)_font->get(),&RectF((FLOAT)_x,(FLOAT)_y,(FLOAT)(_x+_width+10.0f),(FLOAT)(_y+_height)),textBrush.Get());
  return true;
}

Предварительно в Graphics нужно объявить:

Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> textBrush;

И изменить инициализацию (дополнить её):

bool Graphics::init(CoreWindow^ _window,float _dpi)
{
  window=_window;
  if(!сreateIndependentResources()) return false;
  if(!сreateDeviceResources()) return false;
  if(!setDpi(_dpi)) return false;
  HRESULT result=d2dContext->CreateSolidColorBrush(ColorF(1.0F,1.0F,1.0F,1.0F),&textBrush);
  if(FAILED(result)) return false;
  return true;
}

У ImageResource и FontResource методы get() возвращают ID2D1Bitmap* и IDWriteTextFormat* соответственно.

Для некоторых может быть полезно уметь определять необходимый размер для текста при заданном шрифте:

bool Graphics::getTextSize(FontResource* _font,const std::wstring& _text,float& _width,float& _height)
{
  IDWriteTextLayout* textLayout=nullptr;
  HRESULT result=dwriteFactory->CreateTextLayout(_text.c_str(),(UINT32)_text.size(),(IDWriteTextFormat*)_font->get(),_width,_height,&textLayout);
  if(FAILED(result)) return false;
  DWRITE_TEXT_METRICS tm;
  result=textLayout->GetMetrics(&tm);
  if(FAILED(result)) return false;
  _width=(float)tm.width;
  _height=(float)tm.height;
  textLayout->Release();
  return true;
}

Тут стоит отметить, что в качестве _width и _height нужно передать максимально разрешённый размер, а в ответ вернёт какой он смог дать.

Заключение

Вот и подошла к концу моя вторая статья, которая показывает основные принципы разработки приложений для магазина Windows. В заключении хочу сказать, что разрабатывать приложения для магазина Windows ничем не сложнее чем для любой другой платформы - стоит всего лишь познакомиться с предоставляемым API, а дальше уже как ножом по маслу. А когда Вы напишите своё первое приложение, то его можно будет отправить на сертификацию через личный кабинет.

Желаю успехов!

22 февраля 2013

#windows, #Windows 8, #Windows 8 Store, #Windows Store, #WinRT, #магазин Windows, #магазин Windows 8

2001—2018 © GameDev.ru — Разработка игр