netlib.narod.ru< Назад | Оглавление | Далее >

Космическая камера

Класс SpaceCamera немного сложнее, чем класс SimpleCamera, и даже класс ChaseCamera для гоночной игры выглядит проще. Главная причина сложности кода класса SpaceCamera в том, что это первый класс космической камеры, который я написал, и математика кватернионов не является простой вещью. Класс камеры поддерживает три режима: камера для меню, камера для игры и режим свободной камеры для тестовых модулей. Класс SpaceCamera из Rocket Commander XNA несколько проще, поскольку математика кватернионов убрана, и управление ракетой слегка упрощено, допуская только наклон и рыскание. Вращение ракеты вокруг оси Z теперь автоматически обрабатывается движком, что упрощает полет и не дает потерять ориентацию. Главная причина такого изменения — позволить игроку легко управлять ракетой с игрового пульта Xbox 360, что сложнее в оригинальной игре Rocket Commander, которая также поддерживает игровой пульт Xbox 360.

Киньте взгляд на наиболее важный метод в SpaceCamera, который обрабатывает весь пользовательский ввод и обновляет позицию камеры в каждом кадре:

/// <summary>
/// Обработка пользовательского ввода для игры
/// Это место, где происходит весь ввод в игре.
/// </summary>
private void HandlePlayerInput()
{
  if (Player.lifeTimeMs < Player.LifeTimeZoomAndAccelerateMs)
  {
    float speedPercentage =
         Player.lifeTimeMs / (float)Player.LifeTimeZoomAndAccelerateMs;
    // Используем квадрат для лучшего эффекта ускорения
    Player.SetStartingSpeed(speedPercentage * speedPercentage);

    // Всегда перемещаемся вперед
    Translate(Player.Speed * BaseGame.MoveFactorPerSecond *
              Player.MovementSpeedPerSecond, MoveDirections.Z);
    if (Player.gameTimeMs < 100)
    {
      yawRotation = 0;
      pitchRotation = 0;
      pos = Vector3.Zero;
    } // if
  } // if

Показанный код в Rocket Commander увеличивает скорость ракеты и также сбрасывает значения вращения и местоположения, если игра только что запущена. Затем вы выполняете несколько проверок, закончена ли игра, и обрабатываете специальные случаи, чтобы позволить перемещение. Далее обрабатывается ввод с мыши и клавиатуры. В значительной степени это оригинальный код из самой первой, реализованной мной версии класса SpaceCamera; весь остальной код был добавлен позже для поддержки большего числа устройств ввода:

#region Поддержка мыши/клавиатуры
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)

// Используем asdw (клавиатура qwerty), aoew (клавиатура Дворака)
// или клавиши курсора (все клавиатуры?) для перемещения вокруг.
// Примечание: Если вы хотите изменить какие-либо клавиши,
// используйте пункт меню 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;
    // Ограничиваем до максимально возможной скорости
    if (Player.Speed > Player.MaxSpeedWithoutItem)
      Player.Speed = Player.MaxSpeedWithoutItem;
  } // if
} // if

// Подстраиваем текущую скорость под текущую скорость игрока
float moveFactor = Player.Speed * maxMoveFactor;
float slideFactor = maxSlideFactor;

// Всегда перемещаемся вперед
Translate(+moveFactor, MoveDirections.Z);

// Сдвиг
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

// Вверх/вниз
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 для всех классов ввода. Очень хороша общая идея XNA объединить все устройства ввода в одном пространстве имен; единственную проблему создает отсутствие классов мыши для среды времени выполнения Xbox 360. Даже если класс не поддерживается, Microsoft должна была реализовать класс-заглушку, чтобы код, по крайней мере, компилировался без необходимости менять весь код ввода, который как вариант может использовать мышь. Ладно, все равно вы исправите это в вашем собственном классе Input.

Вот код для игрового пульта Xbox 360 из Rocket Commander XNA:

#region Поддержка ввода для пульта XBox360 controller
// 2006-03-09: Добавлена поддержка Input
rotationFactor = 3.0f * BaseGame.MoveFactorPerSecond;

// Меняем поворот камеры, когда используется правый джойстик
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)

// Используем левый джойстик для перемещения вокруг
if (Input.GamePad.ThumbSticks.Left.Y != 0)
{
  float oldPlayerSpeed = Player.Speed;
  Player.Speed += 0.75f * Input.GamePad.ThumbSticks.Left.Y *
                  BaseGame.MoveFactorPerSecond;

  // Если произошло изменение, только уменьшаем топливо
  if (oldPlayerSpeed != Player.Speed)
    consumedAdditionalFuel = true;
} // if

// Сдвиг
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>
/// Перемещение по осям X, Y или Z на заданную дистанцию
/// </summary>
/// <param name="amount">Дистанция</param>
/// <param name="direction">Направление</param>
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 используется для вращения камеры. Рыскание поворачивает вас влево и вправо, а наклон — вверх и вниз. В оригинальной игре Rocket Commander здесь использовались кватернионы и они позволяли вам также вращаться вокруг ракеты. В Rocket Commander XNA ракета автоматически выравнивается для вас, чтобы упростить игру на Xbox 360.

/// <summary>
/// Повороты вокруг осей наклона, рыскания и вращения
/// </summary>
/// <param name="axis">Ось</param>
/// <param name="angle">Угол</param>
private void Rotate(RotationAxis axis, float angle)
{
  if (axis == RotationAxis.Yaw)
    yawRotation -= angle;
  else
    pitchRotation -= angle;
} // Rotate(axis, angle)

Во всех классах камеры есть тестовые модули для их проверки. Если вы пробуете создать собственный класс камеры, удостоверьтесь, что тестируете его, или столкнетесь с проблемами.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz