2.场景中的对象-SceneNode基类
建立了场景类之后,接下来就要处理场景中的所有对象。一般说来,场景中应该包含2D图像、文字、相机、天空盒(球)、地形、模型等。因此应该将这些对象的共性抽象出来作为所有对象的基类,本教程叫做SceneNode类(有些游戏也称作GameObject或BaseEntity),所有对象都包含的属性应该是位置、旋转、缩放,所以在SceneNode命名空间下建立一个SceneNode类:
namespace StunEngine.SceneNodes
{
/// <summary>
/// 这是引擎场景图的基类,引擎中的所有对象都从这个类继承。
/// SceneNode拥有实体的基本属性:位置,缩放,旋转和Engine。
/// </summary>
public abstract class SceneNode
{
构造函数和成员变量
![]()
属性
![]()
/// <summary>
/// 更新控制器
/// </summary>
/// <param name="gameTime"></param>
public virtual void Update(GameTime gameTime)
{
if (controllers.Count > 0)
for (int i = 0; i < controllers.Count; i++)
controllers[i].Update(gameTime);
}
![]()
/// <summary>
/// 初始化
/// </summary>
public abstract void Initialize();
![]()
/// <summary>
/// 返回包含node位置的Vector3
/// </summary>
/// <param name="position"></param>
public void GetPosition(out Vector3 position)
{
position = pose.Position;
}
![]()
/// <summary>
/// 在这个SceneNode上连接一个新Controller
/// </summary>
/// <param name="controller">IController接口</param>
public void AttachController(IController controller)
{
if (!controllers.Contains(controller))
{
controllers.Add(controller);
controller.AttachNode(this);
}
}
![]()
/// <summary>
/// 从这个SceneNode移除一个Controller
/// </summary>
/// <param name="controller">IController接口</param>
public void DetachController(IController controller)
{
if (controllers.Contains(controller))
{
controllers.Remove(controller);
controller.DetachNode();
}
}
}
}
从代码中可见,这个类并没有直接包含对象的Position、Scale和Rotation,而是把这些属性都放在了一个称为WorldPose的新类中,这样做可以更加灵活地处理这些属性,而且以后还可以在WorldPose类中进行BB和AABB碰撞检测。这种做法与《Beginning XNA 3.0 Game Programming From Novice to Professional》(以后简称《BeginningXNA》)一书中第十章(在这本书的XNA2.0版本中是第九章)“Lights, Camera, Transformations!”的方法是一样的,只不过该书中对应的类是Transformation类。
WorldPose类的代码如下所示:
namespace StunEngine
{
/// <summary>
/// 表示一个对象在世界中的位置
/// </summary>
public class WorldPose
{
![]()
/// <summary>
/// 构造函数
/// </summary>
public WorldPose()
{
Rotation = Quaternion.Identity;
Position = Vector3.Zero;
Scale = Vector3.One;
UpdateMatrix();
}
![]()
/// <summary>
/// 缩放
/// 注意这个公有成员必须使用SetScale()方法进行改变
/// </summary>
public Vector3 Scale;
/// <summary>
/// 位置
/// 注意这个公有成员必须使用SetPosition()方法进行改变
/// </summary>
public Vector3 Position;
/// <summary>
/// 旋转
/// 注意这个公有成员必须使用SetRotation()方法进行改变
/// </summary>
public Quaternion Rotation;
/// <summary>
/// 世界变换矩阵
/// 注意这个公有成员不能改变,它是基于SROT因子进行计算的
/// </summary>
public Matrix WorldMatrix;
/// <summary>
/// 对应的节点
/// </summary>
public SceneNode Node;
![]()
internal Matrix ScaleMatrix;
internal Matrix RotationMatrix;
internal Matrix TranslateMatrix;
![]()
/// <summary>
/// 设置一个新位置
/// </summary>
/// <param name="position"></param>
public void SetPosition(ref Vector3 position)
{
Position = position;
TranslateMatrix = Matrix.CreateTranslation(Position);
RebuildWorldMatrix();
}
![]()
/// <summary>
/// 设置一个新旋转
/// </summary>
/// <param name="rot"></param>
public void SetRotation(ref Quaternion rot)
{
Rotation = rot;
RotationMatrix = Matrix.CreateFromQuaternion(Rotation);
RebuildWorldMatrix();
}
![]()
/// <summary>
/// 设置一个新缩放
/// </summary>
/// <param name="scale"></param>
public void SetScale(ref Vector3 scale)
{
Scale = scale;
ScaleMatrix = Matrix.CreateScale(Scale);
RebuildWorldMatrix();
}
![]()
/// <summary>
/// 计算世界矩阵(按SROT的顺序)
/// </summary>
public void UpdateMatrix()
{
ScaleMatrix = Matrix.CreateScale(Scale);
RotationMatrix = Matrix.CreateFromQuaternion(Rotation);
TranslateMatrix = Matrix.CreateTranslation(Position);
RebuildWorldMatrix();
}
![]()
private void RebuildWorldMatrix()
{
Matrix.Multiply(ref ScaleMatrix, ref RotationMatrix, out WorldMatrix);
WorldMatrix.Translation = Position;
}
}
}
在SceneNode类中还可以看到一个类型为IController的集合controllers以及管理这个集合的AttachController和DetachController方法,并且在Update方法中调用了这个集合中所有元素的Update方法。这主要是处理用户输入的,直接在SceneNode类中实现用户输入肯定是不可取的,因为实际游戏的情况非常复杂,有时想控制一个相机的移动,有时想控制场景中一个模型的移动,一种做法是创建一个Player类,在Player类中包含要控制的SceneNode,通过改变ScenNode的位置、旋转或缩放实现用户控制。
本引擎采用的做法是在SceneNode中包含一个Controller类集合,可以随时通过添加或移除其中的Controller实现对不同对象的控制,代码位于Controllers命名空间,包括IController.cs接口、Controller.cs基类,其他控制器类都是从这个基类继承的,还有实际进行用户输入管理的Input.cs。
其中Input.cs直接来自于《ProfessionalXNA》的10.1Input类,比较长这里就不写了,可去看看教程或代码中的注释,我很喜欢这个静态类,可以在引擎的任何地方方便地进行调用,如果说要进行什么改进的话,应该就是实现一个手柄映射,只需编写手柄控制代码,它会自动映射到键盘,这样移植到Xbox360平台更加方便,映射的方法可参见《BeginningXNA》中的12.4.2 管理游戏设置。
下面是IController.cs接口的代码,规定了Controller.cs类中必须要实现的方法:
namespace StunEngine.Controllers
{
/// <summary>
/// 定义ISceneNode控制器
/// </summary>
public interface IController
{
/// <summary>
/// 获取或设置enabled状态
/// </summary>
bool Enabled { get; set; }
![]()
/// <summary>
/// 更新控制器,由ControllerAction()调用
/// </summary>
/// <param name="gameTime"></param>
void Update(GameTime gameTime);
![]()
/// <summary>
/// 链接一个控制器
/// </summary>
/// <param name="node">要链接的节点</param>
void AttachNode(SceneNode node);
![]()
/// <summary>
/// 移除控制器ISceneNode.
/// </summary>
void DetachNode();
![]()
/// <summary>
/// 控制器逻辑
/// </summary>
/// <param name="gameTime"></param>
void ControllerAction(GameTime gameTime);
}
}
之后Controller.cs类实现这个接口:
namespace StunEngine.Controllers
{
/// <summary>
/// SceneNode Controlle抽象类,是所有controllers的基类
/// </summary>
public abstract class Controller : IController
{
/// <summary>
/// 创建一个新Controller对象
/// </summary>
public Controller(StunXnaGE engine)
{
this.engine = engine;
}
![]()
/// <summary>
/// 指向引擎的引用
/// </summary>
protected StunXnaGE engine;
![]()
/// <summary>
/// 此对象控制的节点
/// </summary>
protected SceneNode controlledNode;
![]()
/// <summary>
/// controllers主要控制逻辑
/// 在Controller.Update()中调用,间接由SceneNode.Update()调用
/// 必须被重写!
/// </summary>
/// <param name="gameTime"></param>
public abstract void ControllerAction(GameTime gameTime);
![]()
/// <summary>
/// 设置控制的节点
/// </summary>
/// <param name="node"></param>
public virtual void AttachNode(SceneNode node)
{
if (controlledNode != null)
{
SceneNode tmp = controlledNode;
controlledNode = null;
tmp.DetachController(this);
}
![]()
node.AttachController(this);
controlledNode = node;
}
/// <summary>
/// 移除控制的节点
/// </summary>
public virtual void DetachNode()
{
if (controlledNode != null)
{
SceneNode tmp = controlledNode;
controlledNode = null;
tmp.DetachController(this);
}
}
![]()
private bool isEnabled = true;
![]()
/// <summary>
/// 打开/关闭控制器
/// </summary>
public bool Enabled
{
get { return isEnabled; }
set {isEnabled = value;}
}
![]()
/// <summary>
/// 如果isEnabled则执行ControllerAction方法,这个方法被SceneNode.Update()调用
/// </summary>
/// <param name="gameTime"></param>
public void Update(GameTime gameTime)
{
if (isEnabled && controlledNode!= null && engine.IsActive)
ControllerAction(gameTime);
}
}
![]()
}
以上就是场景中所有对象基类的代码,下一个教程会编写从这个抽象类继承的不同对象类。
发布时间:2009/11/20 下午2:11:51 阅读次数:7784