WPF 창에 직접 XNA 뷰 표시

페이지 업데이트 :
페이지 생성 날짜 :

요약

XNA를 사용하여 WPF 창에서 직접 렌더링하는 방법에 대해 설명합니다.

WPF ウインドウ上に直接 XNA のビューを表示する

운영 환경

필수 구성 요소

지원되는 XNA 버전
  • 2.0
  • 3.0
지원되는 플랫폼
  • Windows(XP SP2 이상, Vista)
Windows 필수 버텍스 셰이더 버전 1.1
Windows 필수 픽셀 셰이더 버전 1.1

운영 환경

플랫폼

물질

WPF 응용 프로그램에서 XNA 사용에서는 WindowsFormsHost 컨트롤을 사용하여 뷰를 표시했지만 이제는 WPF 창만 사용하여 다각형을 표시하려고 합니다. "WPF 응용 프로그램에서 XNA 사용"은 약간의 표현이므로 차이점을 설명하겠습니다.

네임스페이스 별칭

// 名前空間エイリアス
using Xna = Microsoft.Xna.Framework;
using XnaGraphics = Microsoft.Xna.Framework.Graphics;

Color 및 Matrix 구조체는 XNA Framework 및 WPF 네임스페이스에서 다루어지므로 이 구조체를 사용하려면 네임스페이스에서 구조체 이름을 지정해야 합니다. 그러나 매번 긴 것 같아서 네임 스페이스 별칭을 만들고 있습니다. 예를 들어 "Microsoft.Xna.Framework.Rectangle"은 이제 "Xna.Rectangle"로 지정할 수 있습니다.

타이머

.NET Framework 3.0부터는 "System.Windows.Threading.DispatcherTimer"라는 타이머 클래스를 사용할 수 있으며 이 타이머 클래스는 모든 프레임을 렌더링하는 데 사용됩니다. WPF는 DispatcherTimer를 사용하여 UI와 동일한 스레드에서 처리할 수 있도록 합니다.

/// <summary>
/// タイマー
/// </summary>
private DispatcherTimer timer = null;

필드에서 DispatcherTimer를 선언합니다.

/// <summary>
/// タイマーイベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
    this.Draw();
}

DispatcherTimer가 일정한 간격으로 호출하는 메서드를 정의합니다.

// タイマー
this.timer = new DispatcherTimer();
this.timer.Tick += new EventHandler(dispatcherTimer_Tick);
this.timer.Interval = new TimeSpan(0, 0, 0, 0, 16);
this.timer.Start();

타이머를 설정하는 과정입니다. DispatcherTimer의 인스턴스를 만들고 호출할 메서드와 시간 간격을 설정한 후 DispatcherTimer.Start 메서드는 타이머를 시작합니다.

창 핸들 가져오기

Direct3D 디바이스를 만들려면 창 핸들이 필요합니다. 그러나 WPF에는 창 핸들의 개념이 없으므로 Window 클래스에서 직접 가져올 수 있는 방법이 없습니다.

그러나 이 경우와 같이 창 핸들이 필요한 경우가 있으므로 Win32 코드에 대한 interop으로 "System.Windows.Interop.WindowInteropHelper" 클래스를 통해 창 핸들을 가져올 수 있습니다. (이것은 Window 클래스입니다)

// ウィンドウハンドルを取得する
IntPtr handle = new WindowInteropHelper(this).Handle;

Direct3D 디바이스를 만들 때 이 핸들을 사용합니다. 그러나 OnInitialized 메서드에서 창 핸들을 가져오려고 하면 창이 아직 만들어지지 않았기 때문에 0이 반환됩니다. 이 샘플에서 장치는 OnSourceInitialized 메서드에서 만들어집니다.

지정된 영역으로 렌더링

대상 영역을 GraphicsDevice.Present의 두 번째 인수로 지정할 수 있습니다. 샘플에서는 오프셋으로 (50, 50) 위치에 그려고합니다. 크기는 백 버퍼의 크기(300, 300)와 동일합니다. 첫 번째 인수는 그릴 영역이며, null을 지정하면 원래 버퍼가 사용됩니다.

// 描画先を指定する
Xna.Rectangle rect = new Xna.Rectangle(
    50, 50,
    (int)this.viewSize.Width, (int)this.viewSize.Height);

this.device.Present(null, rect, this.windowHandle);

장치 파괴

창을 닫으면 장치가 파괴됩니다. 원래는 Dispose 메서드로 처리하려고 했지만 WPF Window 클래스는 기본적으로 IDisposable을 상속하지 않기 때문에 대신 여기에 작성했습니다.

/// <summary>
/// ウインドウが閉じたとき
/// </summary>
/// <param name="e"></param>
protected override void OnClosed(EventArgs e)
{
    if (this.device != null)
    {
        this.device.Dispose();
        this.device = null;
    }

    base.OnClosed(e);
}

실행

샘플 프로그램을 실행하는 경우 이 문서의 시작 부분에 있는 것과 같은 화면 이미지로 실행할 수 있습니다.

언뜻보기에는 잘 렌더링되지만 마우스로 드래그하여 창 크기를 조정하면 크기 조정 중에 보기가 깜박이거나 사라집니다. WPF 렌더링과 충돌하는 것 같고 많은 것을 시도했지만 해결할 수 없었습니다.

XNA를 사용하는 경우 WindowsFormsHost 컨트롤을 사용하는 것이 더 안전해 보입니다.

이 프로그램을 사용하려면 .NET Framework 3.0, 최신 DirectX 런타임Microsoft XNA Framework 재배포 가능 패키지 2.0이 필요합니다. 또, 하드웨어 요구 사항으로서 「Pixel Shader 1.1 이상을 지원하는 그래픽 카드」가 필요합니다.

또한 프로젝트는 "Visual Studio 2008 Professional Edition"에서 생성되었습니다. Visual Studio 2008을 위한 환경을 준비합니다.