1.7 使用GameComponents
问题
你想将应用程序分离为GameComponent 类,实现组件的复用。
解决方案
程序的特定部分可以从程序中分离出来。在一个XNA程序中,大多数此种类型的部分都需要被更新和绘制,例如粒子系统和billboard(通常翻译为公告板或广告牌)系统(见教程3-11和3-12)。
正确的做法是为这些特定部分创建一个单独的类。在XNA Game 类中,创建这个类的实例,对它进行初始化,更新和绘制。所以你想让你的新类拥有自己的Initialize, (Un) LoadContent,Update和Draw方法,这让你就可以很容易地在Game类中调用它们了。
如果你想在这个新类中定义这些方法,最好的方法就是从GameComponent类继承。你可以将这个类添加到Game类的Components集合中,这样当Game类初始化完成后就会调用GameComponent类的Initialize方法(如果你已经定义了一个)。 而且,每次Game类的Update方法调用后,GameComponent的Update方法也会自动被调用。
如果你的组件还有绘制某些东西,你可以从DrawableGameComponent类继承而不是从GameComponent类,这样在组件类中还会包含一个Draw method,它会在Game类的Draw方法后被调用。
注意在Game类的Initialize方法最后调用了base. Initialize,就是这个代码行调用了所有GameComponent类的Initialize方法,在其他方法中你也能看到类似的base.Update(gameTime),base.Draw(gameTime)。
工作原理
例如教程3-11中的billboarding代码封装在了一个GameComponent中。而且因为这个代码还需要绘制一些东西,所以使用的是DrawableGameComponent类。
创建一个新的 (Drawable)GameComponent
通过右击项目并选择Add →New File可以添加一个新的类文件,接着选择Class,我把它命名为BillboardGC。在这个新文件中添加XNA using代码行,只需将Game类的using复制到这个新类即可。
下一步保证这个新类从GameComponent类或DrawableGameComponent类继承,如下面的代码所示。代码显示了如何创建教程3-11的billboarding。其中有些代码如CreateBBVertices 并没有写出,因为这个教程我们关注的是Initialize,LoadContent,Update和Draw方法。
class BillboardGC : DrawableGameComponent { private GraphicsDevice device; private BasicEffect basicEffect; private Texture2D myTexture; private VertexPositionTexture[] billboardVertices; private VertexDeclaration myVertexDeclaration; private List<Vector4> billboardList = new List<Vector4>(); public Vector3 camPosition; public Vector3 camForward; public Matrix viewMatrix; public Matrix projectionMatrix; public BillboardGC(Game game) : base(game) { } public override void Initialize() { device = Game.GraphicsDevice; base.Initialize(); } protected override void LoadContent() { basicEffect = new BasicEffect(device, null); myTexture = Game.Content.Load<Texture2D>("billboardtexture"); AddBillboards(); myVertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements); } public override void Update(GameTime gameTime) { CreateBBVertices(); base.Update(gameTime); } . . . public override void Draw(GameTime gameTime) { //draw billboards . . . } }
注意在Initialize方法中,你的component可以获取Game类,这样component就能获取Game类的公共字段,例如Game. GraphicsDevice和Game.Content。
使用GameComponent
定义完GameComponent类后,你就可以将它添加到Game类的GameComponent集合中,一旦添加后,GameComponent的注意方法就会自动被调用。最简单的方法就是创建一个GameComponent的实例并立即添加到Components集合中,最好的位置是在Game类的构造函数中。
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; Components.Add(new BillboardGC(this)); }
这样一开始就能调用Initialize和LoadContent方法,并且在Game类的Update和Draw方法完成后调用这个新类的Update和Draw方法。有些情况下,你需要更新一些组件的公有变量。例如在billboarding组件中你需要更新camPosition和camForward变量调整View和Projection矩阵使之能正确地绘制到屏幕。因此,你应在Game类中添加一个组件变量。
BillboardGC billboardGC;
然后存储这个组件并将它添加到Components集合中:
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; billboardGC = new BillboardGC(this); Components.Add(billboardGC); }
在Game类的Update方法中,你可以更新组件中的四个变量,在Update方法最后调用所有组件的Update方法,让billboarding组件进行更新:
protected override void Update(GameTime gameTime) { . . . billboardGC.camForward = quatCam.Forward; billboardGC.camPosition = quatCam.Position; billboardGC.viewMatrix = quatCam.ViewMatrix; billboardGC.projectionMatrix = quatCam.ProjectionMatrix; base.Update(gameTime); }
Game类的Draw方法更简单,只是在调用所有组件的Draw方法前清除背景:
protected override void Draw(GameTime gameTime) { device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,Color.CornflowerBlue, 1, 0); base.Draw(gameTime); }
最后一行代码会调用billboarding 组件的Draw方法,将billboards绘制到屏幕。
代码
GameComponent和Game类的Initialize,LoadContent,Update和Draw方法的代码已经在前面写过了。