为了允许游戏可以简单的使用追踪摄像机,我们将添加一个新的属性给MatrixCameraObject类,它可以放置目标对象的一个引用。如果这个属性,命名为ChaseObject,默认值为null,相机将完全像以前那样,仅按照它的位置和LookAtTarget坐标放置它本身。如果提供了游戏对象,然而,相机的行为将改变以便它围绕场景来跟随对象。
当操作视角设置模式时,相机有2个责任它必须实现每一个它的Update方法:它必须放置它本身在目标对象后面,并且它必须直接看到目标对象。让我们看下它是如何实现的。
首先,Update代码检查看下是否提供ChaseObject。如果不是,它申请默认的对象转换,不做任何事返回,如下面代码所示。
base.Update(gameTime); // Do we have a chase object? if (ChaseObject == null) { // No, so simply apply the identity matrix // Calculate and apply the standard camera transformations SetIdentity(); ApplyStandardTransformations(); return; }
如果提供追踪对象,我们必须首先计算相机的位置。通过在当前相机位置和对象位置间找到距离来实现。最初相机的位置可能靠近对象本身,但是在一对帧中它将放置到它准备去捕捉的后面。
在相机和对象见的距离通过对象位置减去相机位置可以得到。结果值命名为delta,然后提供一个单元长度矢量来标准化。通过在相机中捕捉到的位置来找到矢量来定位相机的位置。
但是如果相机和对象正好在相同位置上?为了处理这个情况,我们总是在类级变量中存储所有最近距离的矢量,_lastChaseCamDelta将重新再使用这个0矢量的情况。Update代码部分可以在下面的代码中看到。
// Find the vector between the current position and the chase object position delta = Position - ChaseObject.Position; // Normalize the delta vector delta.Normalize(); // If the delta is zero (the camera position is already directly on the chase // object, which will happen if the object stops moving) retain the last used delta if (delta == Vector3.Zero) { delta = _lastChaseCamDelta; } else { // Store the delta for later use _lastChaseCamDelta = delta; }
经过在相机和对象间计算方向,目前代码准备去构建相机的转换矩阵。它通过正确的在对象上部转换开始。从这里它一点点将对象转换回以便我们可以从后面看到它。执行这个转换的方向,我们已经计算到delta变量中。我们想要在相机和飞行间保持的距离来缩小这个方向的矢量。
在公共属性中定义了的距离称为ChaseDistance。设置它到正值将在目标对象后面定位相机。然而,它同样能设置负值,它将放到对象前面,回头看对象(通常向后移动)。在某些情况下,相机位置可以是有用的,但并不意味着,如果玩家控制了对象,他不能看到他正在行进的方向。
我们同样对于ChaseDistance也支持一个特殊的值。如果该值设置为0,我们将它当做“第一人称”模式,这意味着我们将直接从对象视角点看到,而不是从肩膀上看。不幸的是,如果我们告诉XNA在相同位置如相机位置去看,它会感到很困惑,因为它部知道该往哪个方向去实际对准相机。要解决这个问题,我们任然从对象位置减去一段很小的距离,通过乘以delta乘以0.01。
我们也允许相机指定高度来提高对象的位置。通常情况下,你将提供小的高度以便相机看上去朝对象轻微的下降。这个高度将被设置到ChaseElevation属性中。
下面代码将描述相机的Update代码段,用来转换相机的位置
// Transform the camera position to position it relative to the chase object SetIdentity(); // Translate to the chase object's position ApplyTransformation(Matrix.CreateTranslation(ChaseObject.Position)); // Apply the chase distance. Are we in first- or third-person view? if (ChaseDistance != 0) { // Third person view // Translate towards or away from the object based on the ChaseDistance ApplyTransformation(Matrix.CreateTranslation(delta * ChaseDistance)); // Apply the vertical offset ApplyTransformation(Matrix.CreateTranslation(0, ChaseElevation, 0)); } else { // First person view // Translate a tiny distance back from the view point ApplyTransformation(Matrix.CreateTranslation(delta * 0.01f)); }
现在相机放置到相对由距离和高度定义的对象。最后几个步骤是为了确保相机实际上是看上去朝着对象,然后为下一次更新准备更新相机位置。
为了设置相机所看到的方向,我们简单设置LookAtTarget属性到包含位置的追踪对象中。当相机的Draw方法执行时,它将为它的CreateLookAt矩阵使用该值,确保视角对象依旧在场景中央。
然后相机位置通过简单设置正确的在追踪对象上面来更新。下一次Update函数调用后,假设对象已经移动了,通过和相机位置对比它的新位置,它将再次能够决定对象移动的方向。、
相机的更新代码片段:设置相机的方向和位置
// Ensure that we are looking at the chase object LookAtTarget = ChaseObject.Position; // Set the camera position to exactly match the chase object position // so that we can continue to follow it in the next update Position = ChaseObject.Position;