XNA Game Engine教程系列#4–DrawOrder
此教程较短。我本来准备把这部分内容放到下面的,但这样就太长了,所以我分成了两部分。在这一章中我们将在Component类中增加一个DrawOrder属性。这个属性将修改GameScreen绘制Component的顺序。我们需要修改Componnet,GameScreen,ComponentCollection,并创建一个叫做ComponentEnumerator的自定义枚举器,这将使我们能够根据Component的DrawOrder设置组件。
首先我们将在Component类中添加一个drawOrder属性。我们还将创建一个叫做DrawOrder的共有成员,它能够在值发生变化时调用DrawOrderChanged事件,当绘制顺序改变时其他物体可以通过“hook”收到信息。例如,ComponentCollection需要知道这一点,使其能够以更新绘制顺序并返回到绘制的componennts。以下是DrawOrder属性、DrawOrderChanged、ComponentDrawOrderChangedEventArgs和ComponentDrawOrderChangedEventHandler的代码,后两个在组件类外部,而前两个在组件类内部。
// The draw order of the component. Lower values draw first
int drawOrder = 1;
// Draw order changed event
public event ComponentDrawOrderChangedEventHandler DrawOrderChanged;
// Public draw order. If the value is changed, we fire the draw
// order change event public int DrawOrder
{
get { return drawOrder;}
set {
// Save a copy of the old value and set the new one
int last = drawOrder;
drawOrder = value;
// Fire DrawOrderChanged
if (DrawOrderChanged != null)
DrawOrderChanged(this, new ComponentDrawOrderChangedEventArgs( this, last, this.Parent.Components));
}
}
// Event arguments for draw order change on a component
public class ComponentDrawOrderChangedEventArgs : EventArgs {
// Component that was modified public Component Component;
// The old draw order public int LastDrawOrder;
// The collection that owns the component
public ComponentCollection ParentCollection;
public ComponentDrawOrderChangedEventArgs(Component Component, int LastDrawOrder, ComponentCollection ParentCollection)
{
this.Component = Component;
this.LastDrawOrder = LastDrawOrder;
this.ParentCollection = ParentCollection;
}
}
// Event handler for draw order change on a component
public delegate void ComponentDrawOrderChangedEventHandler( object sender, ComponentDrawOrderChangedEventArgs e);
现在,我们将创建ComponentEnumerator。这个类是负责告诉游戏循环按什么顺序绘制物体。举例来说,我们要绘制的顺序不一定和更新是相同的。因此,我们将创建一个自定义的枚举器并返回ComponentCollection,即:“foreach(Component component in Components.InDrawOrder)”。这个枚举器包含组件列表和ints列表。第二个列表确定返回组件的顺序,这是因为列表中的每个数字都指向组件列表的一个索引。代码如下:
using System.Collections;
using System.Collections.Generic; Generic;
namespace Innovation
{
public class ComponentEnumerator : IEnumerator
{
// The current position int position = -1;
// The collection we are enumerating ComponentCollection collection;
// The order in which to return items from 'collection'
List
// The current item, which is the item who's index is
// at the current position in the order list
public object Current { get { return collection[ordered[position]]; } }
// Constructor sets the local component list and order
public ComponentEnumerator(ComponentCollection Collection, List
{
this.collection = Collection;
this.opublic bool MoveNext()
{
position++;
// If we have reached the end of the list, stop
// enumerating
if (position == ordereturn false;
// Otherwise keep going
return true;
}
public IEnumerator GetEnumerator()
{
return this;
}
// Resets to the beginning of the list
public void Reset()
{
position = -1;
}
}
}
下一步,我们需要建立ComponentCollection使用此枚举器。下面是代码:
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Innovation
{
// A custom collection for managing components in a GameScreen
public class ComponentCollection : Collection
{
// The GameScreen to manage components for GameScreen owner;
// The list containing each component's index in the
// component list, in the order we want them to draw
List
// Constructor
public ComponentCollection(GameScreen Owner) { owner = Owner; }
// Override InsertItem so we can set the parent of the
// component to the owner and manage the DrawOrder
protected override void InsertItem(int index, Component item)
{
// Remove component from it's parent's list
if (item.Parent != null && item.Parent != owner)
item.Parent.Components.Remove(item);
// Set the new parent item.Parent = owner;
// Tell what to do when the item's draw order changes item.
DrawOrderChanged += new ComponentDrawOrderChangedEventHandler(ComponentDrawOrderChangeEventHandler);
base.InsertItem(index, item);
// Update its position in the draw order list UpdateDrawPosition(item);
}
// Draw order changed event handler
void ComponentDrawOrderChangeEventHandler(object sender, ComponentDrawOrderChangedEventArgs e)
{
// We simply update the component's position using the
// existing method
UpdateDrawPosition(e.Component);
}
// Updates the position of the component in the draw order list
void UpdateDrawPosition(Component Component)
{
// Save the draw order and index location in the component list
int ord = Component.DrawOrder;
int loc = Items.IndexOf(Component);
// Remove the index from the in order list
if (inDrawOrder.Contains(loc))
inDrawOrder.Remove(loc);
// Create our index variable int i = 0;
// Search through the ordered list until we find a component of
// lesser or equal draw order value
if (ord > 0)
{
while (i < inDrawOrder.Count)
// If the current item's draw order is greator or
// equal to the one we are working with...
if (Items[inDrawOrder[i]].DrawOrder >= ord)
{
// If it is greator, decrement it so it is
// above the component we are moving's draw order...
if (Items[inDrawOrder[i]].DrawOrder > ord) i--;
// And stop looping
break;
}
// Otherwise, keep going (until we reach the end of the
// list)
else i++;
}
// Insert the location of the component in the component list
// into the ordered list
inDrawOrder.Insert(i, Items.IndexOf(Component));
}
// Tells what enumerator to use when we want to loop through
// components by draw order
public ComponentEnumerator InDrawOrder { get { return new ComponentEnumerator(this, inDrawOrder); }
}
// Override RemoveItem so we can set the parent of
// the component to null (no parent)
protected override void RemoveItem(int index)
{
Items[index].Parent = null;
// Unhook the draw order change event
Items[index].DrawOrderChanged -= ComponentDrawOrderChangeEventHandler;
// Remove the component from the collection
base.RemoveItem(index);
// Rebuild inDrawOrder
inDrawOrder.Clear();
foreach (Component component in Items)
UpdateDrawPosition(component);
}
}
}
最后,我们将改变GameScreen以使用这个新枚举器,在Draw方法中将这行代码:
foreach (Component component in Components)
变为
foreach (Component component in Components.InDrawOrder)
现在,我们可以进行测试了。如果在建立了actor后,才在LoadContent方法中创建一个新的ClearScreen并运行游戏,我们应该看到一个红色的屏幕。
ClearScreen clr = new ClearScreen();
这是因为ClearScreen在actor之后绘制,它会覆盖前面绘制的东西。但是,如果我们告诉actor在ClearScreen之后绘制,那么只有背景是红色的,因为actor绘制在其上。
ClearScreen clr = new ClearScreen();
actor.DrawOrder = 2;
下次我们将在引擎中添加一个物理系统!
发布时间:2008/12/26 上午8:04:54 阅读次数:6617