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