WPF アプリケーションで XNA を使用する

Page updated :

The page you are currently viewing does not support the selected display language.

概要

WPF アプリケーションで XNA Framework を使用する方法について説明しています。

WPF アプリケーションで XNA を使用する

動作環境

必須環境

対応 XNA バージョン
  • 2.0
  • 3.0
対応プラットフォーム
  • Windows (XP SP2 以降, Vista)
Windows 必須頂点シェーダ バージョン 1.1
Windows 必須ピクセルシェーダ バージョン 1.1

動作確認環境

プラットフォーム

内容

DirectX を使用して特定のコントロール上にレンダリングを行うには、そのコントロールのウインドウハンドルを取得する必要があります。しかし、WPF のコントロールは Windows Form のコントロールと違い、ウインドウハンドルというものがありません(WPF では単にコントロールを「描画」しているだけに過ぎません)。

しかし、WPF には「WindowsFormsHost」というコントロールがあり、これを使うことによって Windows Form コントロールを使用することができます。

今回は上図のようにポリゴンが回転されながら描画するサンプルを作成しますが、XNA 自体に関しては説明を入れると長くなってしまうので詳細は省きます。WPF と XNA の関連性だけ説明していきます。

まず、WPF のウインドウ(ここでは Window1.xaml)を開き、ツールバーから「WindowsFormHost」コントロールを配置します。右にあるスライダーはおまけです。

WPF ウインドウデザイン

本当は XAML の方にもう少し手を加えなければいけないのですが、XNA を使うためのコントロールを先に作成しなければいけないので後に回します。

プロジェクトに「Windows Forms」の「カスタムコントロール」を追加してください。名前は「GraphicsDeviceControl」としておきます。

Windows Forms のカスタムコントロールを追加

事前に「Microsoft.Xna.Framework」の参照を忘れずに追加しておいてください。「Microsoft.Xna.Framework.Game」もありますが、こちらはゲーム専用プロジェクトで使うものなので入れる必要はありません。

Managed DirectX の参照

サンプルコードを手短にするために、XNA に関係するプログラムはすべて GraphicsDeviceControl.cs にまとめています。あまり汎用性がある書き方ではないので、各自応用して書き換えてください。

GraphicsDeviceControl の全コードは下のようにしました(デザイナ部分は除く )。XNA を使ったことがある人なら、特に変わった事はしていないことがわかるかと思います。常にポリゴンが回転するように見せるために Timer コンポーネントを使用しています。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace XNAOnWPF
{
    /// <summary>
    /// グラフィックデバイスコントロール
    /// </summary>
    public partial class GraphicsDeviceControl : Control
    {
        /// <summary>
        /// グラフィックデバイス
        /// </summary>
        private GraphicsDevice device = null;

        /// <summary>
        /// エフェクト
        /// </summary>
        private BasicEffect effect = null;

        /// <summary>
        /// 頂点データ
        /// </summary>
        private VertexPositionColor[] vertices = new VertexPositionColor[3];
        /// <summary>
        /// 頂点データ
        /// </summary>
        public VertexPositionColor[] Vertices
        {
            get { return this.vertices; }
        }


        /// <summary>
        /// コンストラクタ
        /// </summary>
        public GraphicsDeviceControl()
        {
            InitializeComponent();
        }

        /// <summary>
        /// コントロールが作成されるとき
        /// </summary>
        protected override void OnCreateControl()
        {
            if (this.DesignMode == false)
            {
                try
                {
                    // デバイス作成
                    PresentationParameters pp = new PresentationParameters();
                    pp.SwapEffect = SwapEffect.Discard;
                    pp.BackBufferWidth = 300;
                    pp.BackBufferHeight = 300;
                    pp.EnableAutoDepthStencil = true;
                    pp.AutoDepthStencilFormat = DepthFormat.Depth16;
                    this.device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter,
                        DeviceType.Hardware, this.Handle, pp);

                    // 頂点データの設定
                    this.vertices[0] = new VertexPositionColor(
                        new Vector3(0.0f, -2.0f + (float)Math.Sqrt(3) * 3.0f, 0.0f),
                        new Microsoft.Xna.Framework.Graphics.Color(255, 0, 0));
                    this.vertices[1] = new VertexPositionColor(
                        new Vector3(3.0f, -2.0f, 0.0f),
                        new Microsoft.Xna.Framework.Graphics.Color(0, 255, 0));
                    this.vertices[2] = new VertexPositionColor(
                        new Vector3(-3.0f, -2.0f, 0.0f),
                        new Microsoft.Xna.Framework.Graphics.Color(0, 0, 255));

                    // 頂点定義
                    this.device.VertexDeclaration =
                        new VertexDeclaration(this.device, VertexPositionColor.VertexElements);

                    // エフェクト
                    this.effect = new BasicEffect(this.device, null);
                    this.effect.VertexColorEnabled = true;

                    // ビュー変換行列を設定
                    this.effect.View = Matrix.CreateLookAt(
                        new Vector3(0.0f, 0.0f, -10.0f),
                        new Vector3(0.0f, 0.0f, 0.0f),
                        Vector3.Up);

                    // 射影変換を設定
                    this.effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                        MathHelper.ToRadians(45.0f), 1.0f, 1.0f, 100.0f);

                    // レンダリングステート設定
                    this.device.RenderState.CullMode = CullMode.None;
                    this.device.RenderState.AlphaBlendEnable = true;
                    this.device.RenderState.SourceBlend = Blend.SourceAlpha;
                    this.device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex.ToString());
                }
            }

            base.OnCreateControl();
        }

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }

            if (disposing)
            {
                if (this.device != null)
                {
                    this.device.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        /// <summary>
        /// 描画イベント
        /// </summary>
        /// <param name="pe"></param>
        protected override void OnPaint(PaintEventArgs pe)
        {
            this.Draw();

            base.OnPaint(pe);
        }

        /// <summary>
        /// 描画
        /// </summary>
        private void Draw()
        {
            if (this.device == null)
            {
                return;
            }

            this.device.Clear(Microsoft.Xna.Framework.Graphics.Color.DarkBlue);

            // ポリゴンを描画する
            this.effect.Begin();
            this.effect.Techniques[0].Passes[0].Begin();

            this.effect.World = Matrix.CreateRotationY((float)Environment.TickCount / 1000.0f);
            this.device.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList, vertices, 0, 1);

            this.effect.Techniques[0].Passes[0].End();
            this.effect.End();

            this.device.Present();
        }

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

コントロールを作成したら、XAML の方を見てみます。作成したコントロールを配置するのでルートタグに名前空間を追加します。ここでは「xw」と定義しています。

<Window&nbsp;x:Class="ManagedDirectXOnWPF.Window1"
&nbsp;&nbsp;&nbsp;&nbsp;xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
&nbsp;&nbsp;&nbsp;&nbsp;xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
&nbsp;&nbsp;&nbsp;&nbsp;Title="WPFウインドウ上でManaged DirectXを使用してポリゴン描画"
&nbsp;&nbsp;&nbsp;&nbsp;Height="338"&nbsp;Width="422"
&nbsp;&nbsp;&nbsp;&nbsp;xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xw="clr-namespace:ManagedDirectXOnWPF">
    <!-- 省略 -->
</Window>

続いて配置した WindowsFormsHost コントロールのタグを展開し、下のように GraphicsDeviceControl を追加します。

<my:WindowsFormsHost&nbsp;Name="windowsFormsHostManagedDirectX"&nbsp;Width="300"&nbsp;Height="300"
                    HorizontalAlignment="Left"&nbsp;VerticalAlignment="Top">
  <xw:GraphicsDeviceControl&nbsp;x:Name="GraphicsDeviceControl"&nbsp;/>
</my:WindowsFormsHost>

「x:Name」はなくてもかまいませんが、サンプルではスライダーで頂点データにアクセスするために使用しています。

これを実行すれば、サンプルみたいにポリゴンが回転して描画されるシーンを WPF 上で実行することができます。スライダーによるアクセスはおまけなのでサンプルデータをダウンロードして確認してみてください。

プログラムを実行させるには「.NET Framework 3.0」と「最新の DirectX ランタイム」「Microsoft XNA Framework Redistributable 2.0」が必要です。また、ハードウェア要件として「ピクセルシェーダ 1.1 以上に対応したグラフィックカード」が必要です。

また、プロジェクトは「Visual Studio 2008 Professional Edition」で作成しています。Visual Studio 2008 の環境を用意してください。