XNA Game Engine教程系列2- Engine, Content和Services

在这一章中,我们要创建游戏引擎的主类。首先新添一个文件“Engine.cs”到文件夹“Components”中,然后加入以下代码以创建一个静态类“Engine”。这个类与前面我们创建的其他类的区别是它不需要实例化(使用“new ObjectType ();”),这里我们使用“Engine.MemberName”获取其成员。以下是是开始代码:

using System.Collections.Generic;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Graphics;

namespace Innovation


public static class Engine { }



// The GraphicsDevice the engine is using

public static GraphicsDevice GraphicsDevice;

// The Engine's SpriteBatch

public static SpriteBatch SpriteBatch;

// The collection of GameScreens we are managinng

public static GameScreenCollection GameScreens = new GameScreenCollection();

// The current GameTime

public static GameTime GameTime;

// Whether the Engine has been initialized yet

public static bool IsInitialized = false;

下一步我们要创建一个方法来初始化引擎。这个方法接受IgraphicsDeviceService对象以帮助我们建立引擎的graphics。如果是从Game类初始化的话,这个graphics就是在默认游戏模板中创建的GraphicsDeviceManager graphics。下面是initialize方法:

// Initializes the engine

public static void SetupEngine(IGraphicsDeviceService GraphicsDeviceService)


// Setup the GraphicsDevice and SpriteBatch

Engine.GraphicsDevice = GraphicsDeviceService.GraphicsDevice;

Engine.SpriteBatch = new SpriteBatch(GraphicsDeviceService.GraphicsDevice);

Engine.IsInitialized = true;



// Update the engine, screens, and components

public static void Update(GameTime gameTime)


// Update the game time

Engine.GameTime = gameTime;

// Create a temporary list

List updating = new List();

// Populate the temp list

foreach (GameScreen screen in GameScreens)


// BlocksUpdate and OverrideUpdateBlocked login

for (int i = GameScreens.Count - 1; i >= 0; i--)

if (GameScreens[i].BlocksUpdate)


if (i > 0)

for (int j = i - 1; j >= 0; j--)

if (!GameScreens[j].OverrideUpdateBlocked)



} // Update remaining components

foreach (GameScreen screen in updating)

if (screen.Initialized)


// Clear list


// Repopulate list

foreach (GameScreen screen in GameScreens)


// BlocksInput and OverrideInputBlocked login

for (int i = GameScreens.Count - 1; i >= 0; i--)

if (GameScreens[i].BlocksInput) { if (i > 0)

for (int j = i - 1; j >= 0; j--)

if (!GameScreens[j].OverrideInputBlocked)




// Set IsInputAllowed for all GameScreens

foreach (GameScreen screen in GameScreens)

if (!screen.InputDisabled)

screen.IsInputAllowed = updating.Contains(screen);


screen.IsInputAllowed = false;



// Draws the current collection of screens and components. Accepts a

// ComponentType to render

public static void Draw(GameTime gameTime, ComponentType RenderType)


// Update the time, create a temp list

Engine.GameTime = gameTime;

List drawing = new List();

// Clear the back buffer


// Populate the temp list if the screen is visible

foreach (GameScreen screen in GameScreens)

if (screen.Visible)


// BlocksDraw and OverrideDrawBlocked logic

for (int i = GameScreens.Count - 1; i >= 0; i--)

if (GameScreens[i].BlocksDraw)


if (i > 0)

for (int j = i - 1; j >= 0; j--)


if (!GameScreens[j].OverrideDrawBlocked)





// Draw the remaining screens

foreach (GameScreen screen in drawing)

if (screen.Initialized)



下一步我们将添加一个IServiceContainer对象。这个对象将追踪叫做Service Providers的对象。我们可以根据类型检索和存储Service Providers。例如,为了获得GraphicsDeviceService,我们将使用“Engine.Services.GetService(typeof (IGraphicsDeviceService));”。或者,为了存储叫做“content”的ContentManager对象,我们使用“Engine.Services.AddService(typeof(ContentManager),content);”。我们将创建一个继承自IserviceContainer的类,其中包含了添加,删除并获得服务的方法。以下是代码:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Xna.Framework;

namespace Innovation


public class IEServiceContainer : IServiceProvider


// Contains the service types and services Dictionary services = new Dictionary(); // Add a new service public void AddService(Type Service, object Provider)

{ // If we already have this type of service provider, throw an

// exception if (services.ContainsKey(Service)) throw new Exception("The service container already has a " + "service provider of type " + Service.Name); // Otherwise, add it to the list this.services.Add(Service, Provider); }

// Get a service from the service container public object GetService(Type Service) {

// If we have this type of service, return it foreach (Type type in services.Keys) if (type == Service) return services[type];

// Otherwise, throw an exception throw new Exception("The service container does not contain " + "a service provider of type " + Service.Name); } // A shortcut way to get a service. The benefit here is that we

// can specify the type in the brackets and also return the // service of that type. For example, instead of

// "Camera cam = (Camera)Services.GetService(typeof(Camera));", // we can use "Camera cam = Services.GetService()" public T GetService() { object result = GetService(typeof(T)); if (result != null) return (T)result; return default(T); } // Removes a service provider from the container public void RemoveService(Type Service) { if (services.ContainsKey(Service)) services.Remove(Service); } // Gets whether or not the container has a provider of this type public bool ContainsService(Type Service) { return services.ContainsKey(Service); } } }


// The engine's service container public static IEServiceContainer Services;


// Setup the service container and add the IGraphicsDeviceService to it Engine.Services = new IEServiceContainer(); Engine.Services.AddService(typeof(IGraphicsDeviceService), GraphicsDeviceService);


using System;

using System.Collections.Generic;

using Microsoft.Xna.Framework.Content;

namespace Innovation


public class IEContentManager : ContentManager


// Do nothing in the constructor except inherit from ContentManager

public IEContentManager(IServiceProvider serviceProvider) : base(serviceProvider)



// Whether or not we should keep objects that have been loaded. This

// way we can avoid loading assets multiple times. However, this may

// lead to problems with multiple objects changing loaded data, such

// as effects on a model

public bool PreserveAssets = true;

// Keep a list of disposable assets and loaded assets

List disposable = new List();

Dictionary loaded = new Dictionary();

// Override loading of assets so we can use our own functionality

public override T Load(string assetName) {

// Create a new instance of the requested asset

T r = this.ReadAsset(assetName, RecordIDisposable);

// If we are holding on to loaded assets, add it to the list of

// loaded assets

if (PreserveAssets && !loaded.ContainsKey(assetName)) l

oaded.Add(assetName, r);

// Return the loaded asset

return r;


// Internal method to record disposable assets

void RecordIDisposable(IDisposable asset) {

// If we are monitoring loaded assets, add it to the list of

// disposable assets

if (PreserveAssets)



// Unload all content

public override void Unload()


// Dispose all disposable assets

foreach (IDisposable disp in disposable)


// Clear all loaded assets loaded.Clear();



// Unload a specific piece of content

public void Unload(string assetName)


// If the asset has been loaded

if (loaded.ContainsKey(assetName))


// If it is disposable, dispose it and take it off the

// list of disposable content

if (loaded[assetName] is IDisposable && disposable.Contains((IDisposable)loaded[assetName]))


IDisposable obj = disposable[ disposable.IndexOf((IDisposable)loaded[assetName])];


disposable.Remove(obj); }

// Take it off the list of loaded content






现在,我们需要添加一个content manager实例到Engine类中。在其他成员声明附近添加以下代码:

// The engine's content manager

public static IEContentManager Content;


// Setup the content manager using the service container

Engine.Content = new IEContentManager(Services);


// GameScreen provided by the engine.

public static GameScreen BackgroundScreen;

// The GameScreen to set to new GameScreens when a screen is not specified

public static GameScreen DefaultScreen;


// Setup the background screen

BackgroundScreen = new GameScreen("Engine.BackgroundScreen");

BackgroundScreen.OverrideUpdateBlocked = true;

BackgroundScreen.OverrideDrawBlocked = true;

BackgroundScreen.OverrideInputBlocked = true;

// Set the default screen to the background screen so new screens will

// use it automatically unless told otherwise

DefaultScreen = BackgroundScreen;

最后我们需要做的是创建GameScreenCollection类。这个类跟踪GameScreens。它继承自KeyedCollection,因此它可以根据GameScreen的名称返回一个GameScreen,例如: “Engine.GameScreens [”GameScreenName“]”。这样做比使用列表好,因为使用列表的话我们必须跟踪screen的IDs。还有一些逻辑是处理重置Engine.DefaultScreen(如果DefaultScreen被移除的时候)。代码如下:

using System.Collections.ObjectModel;

namespace Innovation


public class GameScreenCollection : KeyedCollection

{ // Allow us to get a screen by name like so:

// Engine.GameScreens["ScreenName"]

protected override string GetKeyForItem(GameScreen item)

{ return item.Name; }

protected override void RemoveItem(int index)


// Get the screen to be removed

GameScreen screen = Items[index];

// If this screen is the current default screen, set the

// default to the background screen

if (

Engine.DefaultScreen == screen)

Engine.DefaultScreen = Engine.BackgroundScreen;






发布时间:2008/12/25 13:19:43  阅读次数:6389

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


沪公网安备 31011002002865号