3.场景中的对象-RenderableSceneNode和UISceneNode
SceneNode并没有实现绘制对象的功能,这个功能是由RenderableSceneNode实现的,两者的关系与XNA自带的GameComponent和DrawableGameComponent是类似的。
在实现RenderableSceneNode类之前,先看一下本引擎要实现的对象的层次结构:
由此图可见,RenderableSceneNode类从SceneNode类继承,而相机无需绘制,所以CameraSceneNode类也从SceneNode类继承,有时在游戏中还要实现一个触发器,比方说玩家移动到一定区域,敌人就会采取行动,这个类TriggerSceneNode也应该从SceneNode类继承。
从RenderableSceneNode类继承的是绘制模型的ModelSceneNode,绘制天空球的SkyDomeSceneNode,绘制地形的TerrainSceneNode和绘制2D控件的UISceneNode,从UISceneNode类还可以派生出按钮类UIButton,文本标签类UILabel和图像类UIImage。
现在看一下RenderableSceneNode类:
namespace StunEngine.SceneNodes
...{
/**//// <summary>
/// 场景中的所有可视对象必须从这个类继承。
/// Draw()必须被绘制的组件重写
/// </summary>
public abstract class RenderableSceneNode : SceneNode
...{
构造函数和成员变量#region 构造函数和成员变量
/**//// <summary>
/// 纹理的名称
/// </summary>
protected string colorTextureName;
/**//// <summary>
/// 纹理
/// </summary>
private Texture2D colorTexture;
protected BasicEffect effect;
/**//// <summary>
/// 创建一个新RenderableSceneNode对象
/// </summary>
/// <param name="engine">引擎</param>
/// <param name="setScene">所属场景</param>
/// <param name="setColorTextureName">纹理名称</param>
/// <param name="colorTexture">颜色纹理</param>
public RenderableSceneNode(StunXnaGE engine, Scene setScene,string setColorTextureName) :this(engine,setScene )
...{
colorTextureName = setColorTextureName;
if (colorTextureName != null)
...{
colorTexture = engine.Content.Load<Texture2D>(colorTextureName);
}
}
/**//// <summary>
/// 创建一个新RenderableSceneNode对象
/// </summary>
/// <param name="engine"></param>
protected RenderableSceneNode(StunXnaGE engine,Scene setScene)
: base(engine,setScene )
...{
}
#endregion
/**//// <summary>
/// 获取纹理的名称
/// </summary>
protected string ColorTextureName
...{
get ...{ return colorTextureName; }
}
/**//// <summary>
/// 获取纹理
/// </summary>
protected Texture2D ColorTexture
...{
get ...{ return colorTexture; }
}
internal virtual void LoadContent()
...{
}
public override void Initialize()
...{
if (Engine.GraphicsDevice.IsDisposed)
return;
effect= new BasicEffect(engine.GraphicsDevice, null);
LoadContent();
isInitialized = true;
}
public virtual void Draw(GameTime gameTime)
...{
}
}
}
因为几乎所有可绘制对象都会包含一张2D纹理,所以有一个叫做colorTexture的Texture2D对象,而BasicEffect对象effect是用来绘制模型的。以后还会碰到更加复杂的情况,比如使用法线映射时,还需要用到另一张法线贴图,BasicEffect也无法满足它的需要,以后会将代码进行重构的,让我们一开始保持简单。
这个引擎的0.1版本并没有实现3D功能,首先实现的是UISceneNode类,这样就可以实现用户菜单和制作简单的2D游戏了。
下面是从RenderableSceneNode继承的UISceneNode类代码:
namespace StunEngine.UI
...{
/**//// <summary>
/// 所有UI Scene nodes的抽象基类
/// </summary>
public abstract class UISceneNode : RenderableSceneNode
...{
成员变量和构造函数#region 成员变量和构造函数
/**//// <summary>
/// 这个node所属的UI manager
/// </summary>
protected UIManager uiManager;
/**//// <summary>
/// 2D屏幕坐标
/// </summary>
protected Vector2 position;
/**//// <summary>
/// Sprite的旋转初始点
/// </summary>
protected Vector2 origin;
/**//// <summary>
/// Sprite的旋转量
/// </summary>
protected float rotation;
/**//// <summary>
/// 缩放
/// </summary>
protected Vector2 scale;
/**//// <summary>
/// 控制水平翻转还是垂直翻转的SpriteEffect
/// </summary>
protected SpriteEffects spriteEffect;
/**//// <summary>
/// Sprite的层深度
/// </summary>
protected float layerDepth;
/**//// <summary>
/// 定义ui元素的tab顺序
/// </summary>
private int tabindex;
/**//// <summary>
/// 定义ui元素的zOrder顺序
/// </summary>
private int zOrder;
/**//// <summary>
/// 如果鼠标在控件内部则返回true
/// </summary>
protected bool isMouseInside;
public UISceneNode(StunXnaGE engine, Scene setScene, string setTextureName, Vector2 setPosition)
: this(engine, setScene, setTextureName, setPosition,0, Vector2.Zero ,Vector2 .One ,SpriteEffects.None, 0)
...{ }
/**//// <summary>
/// UISceneNode基类.
/// </summary>
/// <param name="engine"></param>
/// <param name="setPosition">Top left position of the control in pixels</param>
/// <param name="size">Width and height in pixels</param>
/// <param name="textureName">Texture for the control</param>
/// <param name="highLightTexture">Texture for the control while the mouse hoovers over it</param>
public UISceneNode(StunXnaGE engine, Scene setScene, string setTextureName, Vector2 setPosition,float setRotation, Vector2 setOrigin,Vector2 setScale,SpriteEffects setEffect, float setLayerDepth)
: base(engine, setScene, setTextureName)
...{
this.colorTextureName = setTextureName;
this.rotation = setRotation;
this.origin = setOrigin;
this.scale = setScale;
this.spriteEffect = setEffect;
this.layerDepth = setLayerDepth;
this.uiManager = scene.UiManager ;
this.position = setPosition;
this.TabIndex = -1;
this.layerDepth = 0;
}
#endregion
属性#region 属性
/**//// <summary>
/// 获取或设置控件的2D屏幕位置
/// </summary>
public Vector2 Position ...{ get ...{ return position; } set ...{ position = value; } }
/**//// <summary>
/// 获取或设置Sprite的旋转值
/// </summary>
public float Rotation
...{
get ...{ return rotation; }
set ...{ rotation = value; }
}
/**//// <summary>
/// 获取或设置Sprite的旋转起始点
/// </summary>
public Vector2 Origin
...{
get ...{ return origin; }
set ...{ origin = value; }
}
/**//// <summary>
/// 获取或设置缩放
/// </summary>
public Vector2 Scale
...{
get ...{ return scale; }
set ...{ scale = value; }
}
/**//// <summary>
/// 获取或设置层深度
/// </summary>
public float LayerDepth
...{
get ...{ return layerDepth; }
set ...{ layerDepth = value; }
}
/**//// <summary>
/// 获取或设置控制水平翻转还是垂直翻转的SpriteEffect
/// </summary>
public SpriteEffects SpriteEffect
...{
get ...{ return spriteEffect; }
set ...{ spriteEffect = value; }
}
/**//// <summary>
/// 获取或设置用户是否可以使用TAB使这个控件获取焦点
/// </summary>
public bool TabStop ...{ get; set; }
/**//// <summary>
/// 获取或设置ui元素的tab顺序
/// </summary>
public int TabIndex
...{
get
...{
if (TabStop)
return tabindex;
else
return -1;
}
set ...{ tabindex = value; }
}
/**//// <summary>
/// 定义节点的Z顺序。ZOrder小的节点会绘制在大的之上
/// </summary>
public int ZOrder
...{
get ...{ return zOrder; }
set
...{
zOrder = value;
if (uiManager != null)
uiManager.Sort();
}
}
/**//// <summary>
/// 设置UI管理器
/// </summary>
internal UIManager UIManager ...{ set ...{ this.uiManager = value; } }
#endregion
事件#region 事件
/**//// <summary>
/// 当控件接受焦点时发生
/// </summary>
public event EventHandler GotFocus;
/**//// <summary>
/// 引发GotFocus事件。
/// OnGotFocus方法允许继承的类处理这个事件。
/// 注意:当在继承类中覆写OnGotFocus时,一定要调用基类的OnGotFocus方法才可以注册委托
/// </summary>
/// <param name="e"></param>
protected virtual void OnGotFocus(EventArgs e)
...{
if (GotFocus != null)
GotFocus(this, e);
}
internal void OnGotFocus() ...{ OnGotFocus(EventArgs.Empty); }
/**//// <summary>
/// 当控件失去焦点时发生
/// </summary>
public event EventHandler LostFocus;
/**//// <summary>
/// 引发LostFocus。
/// OnLostFocus方法允许继承的类处理这个事件。
/// 注意:当在继承类中覆写OnLostFocus时,一定要调用基类的OnLostFocus方法才可以注册委托
/// </summary>
/// <param name="e"></param>
protected virtual void OnLostFocus(EventArgs e)
...{
if (LostFocus != null)
LostFocus(this, e);
}
/**//// <summary>
/// 只用于内部的UIManager
/// </summary>
internal void OnLostFocus() ...{ OnLostFocus(EventArgs.Empty); }
/**//// <summary>
/// 当鼠标点击控件时发生
/// </summary>
public event MouseEventHandler MouseClick;
/**//// <summary>
/// 引发MouseClick事件
/// OnMouseClick方法允许继承的类处理这个事件。
/// 注意:当在继承类中覆写OnMouseClick时,一定要调用基类的OnMouseClick方法才可以注册委托
/// </summary>
/// <param name="e"></param>
protected virtual void OnMouseClick(MouseEventArgs e)
...{
if (MouseClick != null)
MouseClick(this, e);
}
/**//// <summary>
/// 只用于内部的UIManager
/// </summary>
internal void OnMouseClick(MouseState ms)
...{
MouseButtons buttons = MouseButtons.None;
if (ms.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
buttons = buttons | MouseButtons.Left;
if (ms.RightButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
buttons = buttons | MouseButtons.Right;
if (ms.MiddleButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
buttons = buttons | MouseButtons.Middle;
MouseEventArgs mea = new MouseEventArgs(buttons, 1, ms.X, ms.Y, ms.ScrollWheelValue);
OnMouseClick(mea);
}
/**//// <summary>
/// 当鼠标进入控件时发生
/// </summary>
public event EventHandler MouseEnter;
/**//// <summary>
/// 引发MouseEnter事件。
/// OnMouseEnter方法允许继承的类处理这个事件。
/// 注意:当在继承类中覆写OnMouseEnter时,一定要调用基类的OnMouseEnter方法才可以注册委托
/// </summary>
/// <param name="e"></param>
protected virtual void OnMouseEnter(EventArgs e)
...{
if (MouseEnter != null)
...{
MouseEnter(this, e);
}
}
/**//// <summary>
/// 只用于内部的UIManager
/// </summary>
internal void OnMouseEnter() ...{ isMouseInside = true; OnMouseEnter(EventArgs.Empty); }
/**//// <summary>
/// 当鼠标离开控件时发生
/// </summary>
public event EventHandler MouseLeave;
/**//// <summary>
/// 引发MouseLeave事件
/// OnMouseLeave方法允许继承的类处理这个事件。
/// 注意:当在继承类中覆写OnMouseLeave时,一定要调用基类的OnMouseLeave方法才可以注册委托
/// </summary>
/// <param name="e"></param>
protected virtual void OnMouseLeave(EventArgs e)
...{
isMouseInside = false;
if (MouseLeave != null)
MouseLeave(this, e);
}
/**//// <summary>
/// 只用于内部的UIManager
/// </summary>
internal void OnMouseLeave() ...{ OnMouseLeave(EventArgs.Empty); }
#endregion
重载方法#region 重载方法
public override void Initialize()
...{
base.Initialize();
}
public override void Draw(GameTime gameTime)
...{
}
#endregion
/**//// <summary>
/// 如果点(x,y)位于控件内部则返回true
/// </summary>
/// <param name="x">2D屏幕x坐标</param>
/// <param name="y">2D屏幕y坐标</param>
/// <returns></returns>
public abstract bool IsPointInside(int x, int y);
}
}
控件本质上就是绘制2D图像和文字,所以看一下SpriteBatch.Draw()和SpriteBatch.DrawString()的最复杂的重载方法,这样可以应付最复杂的情况:
public void Draw (Texture2D texture,Vector2 position,Nullable<Rectangle> sourceRectangle,
Color color,float rotation,Vector2 origin,Vector2 scale,SpriteEffects effects, float layerDepth)
和
public void DrawString (SpriteFont spriteFont,StringBuilder text, Vector2 position,Color color,
float rotation, Vector2 origin,Vector2 scale,SpriteEffects effects,float layerDepth)
比较这两个方法,发现相同的参数是position,rotation,origin,scale,effects,layerDepth,因此把这些变量封装在UISceneNode类中,注意看上去相同的还有color,但是这两个color意义是不同的,绘制图像时的color指调制颜色,绘制文本时指文字颜色。
参考一下WinForm程序中的控件,发现还要实现一些鼠标事件。tabindex用于实现按Tab键可以切换控件,诸如文本标签、图像、进度条之类的控件不接受焦点,这可以用TabStop属性控制,具体的实现是在UIManager中进行的,会在下面的教程中讲到。
发布时间:2009/11/20 下午4:33:25 阅读次数:6183