Usando XNA em aplicativos WPF

Página atualizada :
Data de criação de página :

resumo

Descreve como usar o XNA Framework em aplicativos WPF.

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

Ambiente operacional

Pré-requisitos

Versões do XNA suportadas
  • 2.0
  • 3.0
Plataformas suportadas
  • Windows (XP SP2 ou posterior, Vista)
Versão do sombreador de vértice necessária para Windows 1.1
Versão do sombreador de pixel necessária para Windows 1.1

Ambiente operacional

plataforma

substância

Para renderizar em um controle específico usando DirectX, você precisa obter o identificador de janela para esse controle. No entanto, ao contrário dos controles Windows Form, os controles WPF não têm identificadores de janela (no WPF, eles simplesmente "desenham" o controle).

No entanto, o WPF fornece um controle chamado "WindowsFormsHost" que permite usar controles do Windows Forms.

Neste artigo, criaremos uma amostra de desenho de um polígono à medida que ele é girado conforme mostrado acima, mas vou omitir os detalhes do próprio XNA porque seria muito longo explicá-lo. Vou apenas falar sobre a relação entre WPF e XNA.

Primeiro, abra uma janela do WPF (nesse caso, Window1.xaml) e coloque um controle WindowsFormHost na barra de ferramentas. O controle deslizante à direita é um bônus.

WPF ウインドウデザイン

Eu realmente preciso trabalhar um pouco mais no XAML, mas vou deixar isso para mais tarde porque preciso criar os controles para usar o XNA primeiro.

Adicione um "Controle Personalizado" do "Windows Forms" ao seu projeto. Deixe o nome GraphicsDeviceControl.

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

Não se esqueça de adicionar a referência "Microsoft.Xna.Framework" com antecedência. Há também "Microsoft.Xna.Framework.Game", mas isso é usado em um projeto somente de jogo, portanto, não há necessidade de incluí-lo.

Managed DirectX の参照

Para manter o código de exemplo curto, todos os programas relacionados ao XNA são resumidos em GraphicsDeviceControl.cs. Não é um estilo de escrita muito versátil, portanto, aplique-o e reescreva-o.

O código completo para o GraphicsDeviceControl é mostrado abaixo (exceto para a parte do designer). Se você já usou o XNA, saberá que ele não faz nada fora do comum. Estou usando um componente Timer para fazer o polígono parecer girar o tempo todo.

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();
        }
    }
}

Agora que você criou o controle, vamos examinar o XAML. Como vamos colocar o controle que criamos, adicionaremos um namespace à tag raiz. Aqui, é definido como "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>

Em seguida, expanda a marca do controle WindowsFormsHost que você acabou de colocar e adicione um GraphicsDeviceControl, conforme mostrado abaixo.

<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>

Você não precisa ter um "x:Name", mas o exemplo o usa para acessar dados de vértice com um controle deslizante.

Se você fizer isso, poderá executar uma cena no WPF em que os polígonos são desenhados com um polígono giratório, conforme mostrado no exemplo. O acesso por controle deslizante é um bônus, portanto, baixe os dados de amostra e verifique-os.

O programa requer o .NET Framework 3.0, o tempo de execução DirectX mais recente e o Microsoft XNA Framework Redistributable 2.0. Além disso, como requisito de hardware, é necessária "uma placa gráfica compatível com Pixel Shader 1.1 ou superior".

Além disso, o projeto foi criado no "Visual Studio 2008 Professional Edition". Prepare um ambiente para o Visual Studio 2008.