重用XNA的代码

我得承认我是微软的忠实拥趸,它的优点是入门容易,参考资料多,现阶段比较吸引我的另一个优点是“一次编写,处处可用”(当然这个处处通常只局限于微软自家的产品),在上一篇文章Sliverlight3D Shader教程1——环境光照中,我使用Silverlight Toolkit的3D应用程序模板将原来的XNA程序移植到了Silverlight平台,期间的代码还是有较大的修改的。能否将XNA的代码原封不动地粘贴到Silverlight项目中呢?答案是肯定的,解决问题的关键就在于自己实现Microsoft.Xna.Framework命名空间下的Game类。

程序主要参考(其实是照抄)跨平台3D游戏引擎Engine Nine(http://nine.codeplex.com/)的对应Silverlight的代码,不知这个引擎的此部分代码是否参考自XNA官网上的Silverlight XNA Game Components(http://xbox.create.msdn.com/en-US/education/catalog/sample/silverlight_xna_game_components),因为事实上代码是几乎完全一样的。 为了代码的重用,也为了今后有空(其实不太会有空了)实现我的Silverlight平台上的StunEngine,首先创建一个Silverlight 3D Library项目,我命名为StuEngine。然后新建一个文件夹,我命名为Framework,在这个新文件夹中新建一个名为Game的类,代码如下:

#region Using Directives
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion

namespace Microsoft.Xna.Framework
{
    /// <summary>
    /// Game类,提供基本的图形设备初始化、游戏逻辑和绘制的代码。
    /// </summary>
    public class Game : UserControl
    {
        private bool surfaceLoaded = false;
        private bool suppressDraw = false;
        private DrawingSurface Surface;
        private GameTime gameTime = new GameTime();

        public GraphicsDevice GraphicsDevice { get; private set; }
        public GameWindow Window { get; private set; }        
        public GameComponentCollection Components { get; private set; }
        public new ContentManager Content { get; private set; }
        
        public TimeSpan InactiveSleepTime { get; set; }
        public bool IsActive { get { return true; } }
        public bool IsFixedTimeStep { get; set; }
        public bool IsMouseVisible { get; set; }

        public TimeSpan TargetElapsedTime { get; set; }

        public float AspectRatio { get; private set;}
        
        public Game()
        {
            //Width = 800;
            //Height = 600;
            //AspectRatio = (float)(Width / Height);
            AspectRatio = 4.0f / 3;

            Mouse.RootControl = this;
            Keyboard.RootControl = this;

            Window = new GameWindow(this);
            Components = new GameComponentCollection();
            Content = new ContentManager(null);
            base.Content = (Surface = new DrawingSurface());

            Loaded += new RoutedEventHandler(Game_Loaded);
            Surface.Draw += new EventHandler<DrawEventArgs>(Surface_Draw);
        }

        /// <summary>
        /// 如果没有正确的显卡或声卡则显示错误信息。
        /// </summary>
        protected virtual bool ShowMissingRequirementMessage(Exception exception)
        {
            MessageBox.Show(exception.Message, "框架出错", MessageBoxButton.OK);
            return false;
        }

        void Game_Loaded(object sender, RoutedEventArgs e)
        {
            surfaceLoaded = true;            

            try
            {
                GraphicsDevice = GraphicsDeviceManager.Current.GraphicsDevice;
                if (GraphicsDevice == null)
                    throw new InvalidOperationException("无法创建图形设备。");
                if (GraphicsDeviceManager.Current.RenderMode != RenderMode.Hardware)
                {

                    string message;
                    switch (GraphicsDeviceManager.Current.RenderModeReason)
                    {
                        case RenderModeReason.GPUAccelerationDisabled:
                            message = "显示异常描述:3D加速被禁用!\n\n解决方法:请联系站长启用本页面的显卡3D加速参数。";
                            break;
                        case RenderModeReason.SecurityBlocked:
                            message = "显示异常描述:Silverlight默认安全选项阻止了该站点的3D显示!\n\n解决方法:"
                                + "\n1.在本页面点击鼠标右键;"
                                + "\n2.选择“Silverlight”;"
                                + "\n3.在弹出的“Microsoft Silverlight 配置”对话框中选择“权限”选项卡;"
                                + "\n4.找到本站点域名后允许3D安全策略;"
                                + "\n5.点击“确认”按钮;"
                                + "\n6.刷新本页面";
                            break;
                        case RenderModeReason.Not3DCapable:
                            message = "显示异常描述:没有找到3D显卡!\n\n解决方法:请更换显卡或安装合适的驱动程序。";
                            break;
                        case RenderModeReason.TemporarilyUnavailable:
                            message = "显示异常描述:您的显卡暂时无法使用!\n\n解决方法:请重启浏览器后刷新本页面。";
                            break;
                        default:
                            message = "显示异常描述:未知错误!";
                            break;
                    }
                    MessageBox.Show(message, "3D显示异常", MessageBoxButton.OK);
                }
            }
            catch (Exception exception)
            {
                if (!ShowMissingRequirementMessage(exception))
                    throw;
            }            

            Initialize();
            LoadContent();
            Components.Initialize();
        }

        private void Surface_Draw(object sender, DrawEventArgs e)
        {
            if (surfaceLoaded)
            {
                gameTime.ElapsedGameTime = e.DeltaTime;
                gameTime.TotalGameTime = e.TotalTime;
                
                Update(gameTime);
                Components.Update(gameTime);
                
                if (!suppressDraw && BeginDraw())
                {
                    Draw(gameTime);
                    Components.Draw(gameTime);
                    EndDraw();
                }
                suppressDraw = false;

                e.InvalidateSurface();
            }
        }

        protected virtual void Initialize() { }
        protected virtual void LoadContent() { }

        protected virtual bool BeginDraw() { return true; }
        protected virtual void Update(GameTime gameTime) { }
        protected virtual void Draw(GameTime gameTime) { }
        protected virtual void EndDraw() { }

        public void SuppressDraw() { suppressDraw = true; }
    }
}

Game类从UseControl继承,你要新建的游戏类从Game继承,也就是一个UseControl了。 从Game类中你还可以看到额外还需自己编写GameTime类、GameWindow类,比较简单就不贴出了,请看源代码,而GameComponent的实现需要IGameComponent.cs等8个文件,比较长,也请自己研究源代码吧。

建立好引擎类库之后,要编写自己的游戏,需要在解决方案中再添加一个Silverlight 3D Application项目,首先将它添加的Scene.cs等文件都删除,只保留MainPage.xaml,在MainPage.xaml.cs中删去所有其他代码,只保留如下代码:

public partial class MainPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

在项目中新添一个类,我命名为DiffuseColorGame.cs,这个类就是游戏的主类,代码如下:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Silverlight3dApp
{
    public class DiffuseColorGame:Game
    {
        Model theMesh;
        // The object that will contain our shader
        SilverlightEffect effect;

        // Parameters for our shader object
        SilverlightEffectParameter projectionParameter;
        SilverlightEffectParameter viewParameter;
        SilverlightEffectParameter worldParameter;
        SilverlightEffectParameter ambientIntensityParameter;
        SilverlightEffectParameter ambientColorParameter;
        // new parameters for diffuse light
        SilverlightEffectParameter diffuseIntensityParameter;
        SilverlightEffectParameter diffuseColorParameter;
        SilverlightEffectParameter diffuseDirectionParameter;

        Matrix world, view, projection;
        float ambientLightIntensity;
        Vector4 ambientLightColor;

        double rotateCamera = 0.0f;
        
        public DiffuseColorGame()
        {
            Content.RootDirectory = "Content";
        }

        public void SetupShaderParameters()
        {
            // Bind the parameters with the shader.
            worldParameter = effect.Parameters["World"];
            viewParameter = effect.Parameters["View"];
            projectionParameter = effect.Parameters["Projection"];

            ambientColorParameter = effect.Parameters["AmbientColor"];
            ambientIntensityParameter = effect.Parameters["AmbientIntensity"];
            diffuseColorParameter = effect.Parameters["DiffuseColor"];
            diffuseIntensityParameter = effect.Parameters["DiffuseIntensity"];
            diffuseDirectionParameter = effect.Parameters["DiffuseDirection"];
        }

        protected override void LoadContent()
        {
            theMesh = Content.Load<Model>("Object");

            // Load the shader
            effect = Content.Load<SilverlightEffect>("Shader");

            // Set up the parameters
            SetupShaderParameters();

            // calculate matrixes
            //float aspectRatio = (float)GraphicsDevice.Viewport.Width / (float)GraphicsDevice.Viewport.Height;
            float fov = MathHelper.PiOver4 * AspectRatio * 3 / 4;
            projection = Matrix.CreatePerspectiveFieldOfView(fov, AspectRatio, 0.1f, 1000.0f);

            //create a default world matrix
            world = Matrix.Identity;

            SpriteBatch sp = new SpriteBatch(GraphicsDevice);
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {      

            ambientLightIntensity = 1.0f;
            ambientLightColor = new Color(0,100,0,255).ToVector4();

            rotateCamera += gameTime.ElapsedGameTime.Milliseconds / 1000.0;
            view = Matrix.CreateLookAt(new Vector3(5.0f * (float)Math.Cos(rotateCamera), 2, 5.0f * (float)Math.Sin(rotateCamera)), new Vector3(0, 2, 0), Vector3.Up);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        protected override void Draw(GameTime gameTime)
        {           

            GraphicsDevice.Clear(new Color(47, 79, 79, 255));

            ModelMesh mesh = theMesh.Meshes[0];
            ModelMeshPart meshPart = mesh.MeshParts[0];

            // Set parameters
            projectionParameter.SetValue(projection);
            viewParameter.SetValue(view);
            worldParameter.SetValue(world);
            ambientIntensityParameter.SetValue(ambientLightIntensity);
            ambientColorParameter.SetValue(ambientLightColor);
            diffuseColorParameter.SetValue(Color.White.ToVector4());
            diffuseIntensityParameter.SetValue(0.5f);

            Vector3 diffuseLightDirection = new Vector3(0, -1, -1);

            //ensure the light direction is normalized, or
            //the shader will give some weird results
            diffuseLightDirection.Normalize();
            diffuseDirectionParameter.SetValue(diffuseLightDirection);

            //set the vertex source to the mesh's vertex buffer
            GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset);

            //set the current index buffer to the sample mesh's index buffer
            GraphicsDevice.Indices = meshPart.IndexBuffer;

            effect.CurrentTechnique = effect.Techniques[0];

            for (int i = 0; i < effect.CurrentTechnique.Passes.Count; i++)
            {
                //EffectPass.Apply will update the device to
                //begin using the state information defined in the current pass
                effect.CurrentTechnique.Passes[i].Apply();

                //theMesh contains all of the information required to draw
                //the current mesh
                GraphicsDevice.DrawIndexedPrimitives(
                    PrimitiveType.TriangleList, 0, 0,
                    meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);
            }

            base.Draw(gameTime);
        }

    }
}

这个类的代码直接来自于XNA Shader编程教程2-漫反射光照,唯一的区别就是使用SilverlightEffect代替Effect,silverlightEffectParameter代替EffectParameter。这样就达到了本文想要达到的目的——以尽可能的小的变化将XNA代码一直到Silverlight平台上。

最后,因为DiffuseColorGame.cs是一个UseControl,所以你可以通过拖动的方法将它放置在Grid中,代码如下:

<Grid x:Name="LayoutRoot" Background="White">
    <my:DiffuseColorGame />
</Grid>

拖动控件

使用这个框架,你可以很容易地将XNA代码移植到Silverlight平台上,而且从这个框架出发,你还可以实现自己的游戏引擎。

源代码下载:DiffuseColorGame.zip

文件下载(已下载 2387 次)

发布时间:2012/9/16 下午10:34:42  阅读次数:8877

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号