重用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 阅读次数:8966