§10.4Space Camera类
SpaceCamera类比SimpleCamera类复杂得多,赛车游戏的ChaseCamera看起来倒简单点。主要的原因这是我写的第一个空间相机类,其中使用到的四元数的数学知识并不那么容易。该相机支持三种模式:菜单画面的相机,游戏中的相机,单元测试中的自由相机。Rocket Command游戏的SpaceCamera有点简单,因为四元数被删除了,只允许俯仰和偏航火箭,而火箭绕Z轴旋转被引擎自动处理,这使得飞行起来容易些,不容易迷失方向。这个变化的主要原因是为了使用Xbox 360手柄也能很好地控制火箭,难度比初版Rocket Commander大。
快速浏览一下SpaceCamera中的重要方法,它们处理所有输入并更新相机位置:
/// <summary>
/// Handle player input for the game.
/// This is where all the input happens in the game.
///</summary>
private void HandlePlayerInput()
if (Player.lifeTimeMs < Player.LifeTimeZoomAndAccelerateMs) /p>
{
float speedPercentage = Player.lifeTimeMs / (float)Player.LifeTimeZoomAndAccelerateMs;
// Use quadradric product for better speed up effect
Player.SetStartingSpeed(speedPercentage * speedPercentage);
// Always move forward
Translate(Player.Speed * BaseGame.MoveFactorPerSecond * Player.MovementSpeedPerSecond, MoveDirections.Z);
if (Player.gameTimeMs < 100)
{
yawRotation = 0; pitchRotation = 0; pos = Vector3.Zero;
} // if
} // if
前面的代码增加火箭速度,如果游戏刚刚开始,同时也重置旋转角度和位置。接下来,您检查游戏是否结束和处理特殊情况使之能移动。然后处理鼠标和键盘输入。这些是我初次实现SpaceCamera类的原始代码,更多代码在以后添加以支持更多的输入设备:
#region Mouse/keyboard support
if (Input.MouseXMovement != 0.0f || Input.MouseYMovement != 0.0f)
{
float xMovement = Input.MouseXMovement;
float yMovement = Input.MouseYMovement;
Rotate(RotationAxis.Yaw, -xMovement * rotationFactor);
Rotate(RotationAxis.Pitch, -yMovement * rotationFactor);
} // if (Mouse.left.Pressed)
// Use asdw (qwerty keyboard), aoew (dvorak keyboard) or
// cursor keys (all keyboards?) to move around.
// Note: If you want to change any keys, use Settings!
if (Input.Keyboard.IsKeyDown(moveForwardKey) || Input.Keyboard.IsKeyDown(Keys.Up) || Input.Keyboard.IsKeyDown(Keys.NumPad8))
{
float oldPlayerSpeed = Player.Speed;
Player.Speed += 0.75f * BaseGame.MoveFactorPerSecond;
} // if
if (Input.Keyboard.IsKeyDown(moveBackwardKey) || Input.Keyboard.IsKeyDown(Keys.Down) || Input.Keyboard.IsKeyDown(Keys.NumPad2))
{
float oldPlayerSpeed = Player.Speed;
Player.Speed -= 0.75f * BaseGame.MoveFactorPerSecond;
} // if
if (Player.speedItemTimeout > 0)
{
Player.speedItemTimeout -= BaseGame.ElapsedTimeThisFrameInMs;
if (Player.speedItemTimeout < 0)
{
Player.speedItemTimeout = 0;
// Reduce to max. possible speed
if (Player.Speed > Player.MaxSpeedWithoutItem)
Player.Speed = Player.MaxSpeedWithoutItem;
} // if
} // if
// Adjust current speed by the current player speed.
float moveFactor = Player.Speed * maxMoveFactor;
float slideFactor = maxSlideFactor;
// Always move forward
Translate(+moveFactor, MoveDirections.Z);
// Slide if (Input.Keyboard.IsKeyDown(moveLeftKey) || Input.Keyboard.IsKeyDown(Keys.Left) || Input.Keyboard.IsKeyDown(Keys.NumPad4))
{
consumedAdditionalFuel = true;
Translate(-slideFactor, MoveDirections.X);
} // if
if (Input.Keyboard.IsKeyDown(moveRightKey) || Input.Keyboard.IsKeyDown(Keys.Right) || Input.Keyboard.IsKeyDown(Keys.NumPad6))
{
consumedAdditionalFuel = true;
Translate(+slideFactor, MoveDirections.X);
} // if
// Up/down
if (Input.Keyboard.IsKeyDown(Keys.F))
{
Translate(+slideFactor, MoveDirections.Y);
} // if
if (Input.Keyboard.IsKeyDown(Keys.V))
{
Translate(-slideFactor, MoveDirections.Y);
} // if
#endregion
为了支持Xbox 360下面的代码是在2006年初,这个游戏发布前的一天被添加的。实现Xinput很简单,我很高兴XNA在所有的输入类中都使用Xinput。把所有输入设备都放在一个命名空间中,这个主意很好,唯一的问题是Xbox 360运行时中没有鼠标类。就算这个类不支持,微软也应实现一个虚拟类,至少可以实现当鼠标可选时,用不着改变所有的输入代码。幸好,你以通过自己的Input类解决了这个问题。
#region Input support for the XBox360 controller
// 2006-03-09: Added Input support
rotationFactor = 3.0f * BaseGame.MoveFactorPerSecond;
// Change camera rotation when right thumb is used.
if (Input.GamePad.ThumbSticks.Right.X != 0.0f || Input.GamePad.ThumbSticks.Right.Y != 0.0f)
{
float xMovement = Input.GamePad.ThumbSticks.Right.X;
float yMovement = Input.GamePad.ThumbSticks.Right.Y;
Rotate(RotationAxis.Yaw, -xMovement * rotationFactor);
Rotate(RotationAxis.Pitch, yMovement * rotationFactor);
} // if (Mouse.left.Pressed)
// Use left thumb for moving around
if (Input.GamePad.ThumbSticks.Left.Y != 0)
{
float oldPlayerSpeed = Player.Speed;
Player.Speed += 0.75f * Input.GamePad.ThumbSticks.Left.Y * BaseGame.MoveFactorPerSecond;
// Only decrease fuel if change happened
if (oldPlayerSpeed != Player.Speed)
consumedAdditionalFuel = true;
} // if
// Slide
if (Input.GamePad.ThumbSticks.Left.X != 0)
{
consumedAdditionalFuel = true;
Translate(slideFactor * Input.GamePad.ThumbSticks.Left.X * 2, MoveDirections.X);
} // if
#endregion
} // HandlePlayerInput()
你可你可以看到代码使用了大量的辅助方法,像Rotate,Translate、Input类中频繁使用的属性和从BaseGame类获得的有用的数值,如MoveFactorPerSecond。 Translate方法沿着x , y ,或z轴移动目前的相机。 x轴是用来左右移动,在Rocket Commander中使用A和D键或方向键实现。 y轴用来上下移动而z轴用来前进后退。在Rocket Commander中z轴是最重要的,你沿着这根轴以极高的速度移动。
/// <summary>
/// Translate into x, y or z axis with a specfic amount.
/// </summary>
/// <param name="amount" /> Amount
/// <param name="direction" /> Direction
private void Translate(float amount, MoveDirections direction)
{
Vector3 dir = direction == MoveDirections.X ? XAxis : direction == MoveDirections.Y ? YAxis : ZAxis; pos += dir * amount;
}
// Translate(amount, direction)
最后,Rotate旋转相机。Yaw是左右转动和Picth让你上下转动。原版本的Rocket Commander使用四元数,它还允许你翻滚火箭。在XNA版本的Rocket Commander中火箭自动调整,这样在Xbox 360上更容控制。
/// <summary>
/// Rotate around pitch, roll or yaw axis.
/// </summary>
/// <param name="axis" /> Axis
/// <param name="angle" /> Angle
private void Rotate(RotationAxis axis, float angle)
{
if (axis == RotationAxis.Yaw)
yawRotation -= angle; else pitchRotation -= angle;
} // Rotate(axis, angle)
所有相机类都要通过单元测试进行测试。如果您尝试创建自己的相机类请务必对其进行测试,避免出错。
发布时间:2008/9/28 上午8:06:59 阅读次数:5493