Аудио3D

Страница обновлена :
Дата создания страницы :

Примечания к этим советам

Этот пример основан на программах, опубликованных на следующих сайтах. Я немного изменяю код, чтобы было легче понять и объяснить его на японском языке. В принципе, мы используем исходный код как есть, поэтому, если вы действительно принимаете его в свою игровую программу, своевременно исправьте его и используйте.

Справочный сайт

Кроме того, это объясняется предположением, что у вас есть некоторые базовые знания о MonoGame и XNA. Смотрите Советы по MonoGame и XNA для мелочей.

В частности, поскольку математика, векторы, тригонометрические функции, матрицы и т. Д. Являются важными, поэтому, пожалуйста, знайте, что это такое в некоторой степени.

окружающая среда

платформа
  • Окна 10
  • Код можно использовать на других платформах с поддержкой MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
МоноИгра
  • 3.8

О примерах

Исходя из положения камеры, можно услышать зов с позиции собаки и положения кошки. Кот кружит и обводит начало, а игрок может управлять положением камеры с помощью ключа. Убедитесь, что способ, которым вы слышите звук, меняется с каждым изменением положения камеры или положения кошки. Я думаю, что это легко понять с помощью наушников. Я не подтвержден на канале 5.1.

Как работать

Что делать Клавиатура Геймпад (XInput) Мышь Сенсорный
Камера вперед и назад ↑↓ Левый джойстик (сверху и снизу) - -
Изменение ориентации камеры ←→ Левый джойстик (левый и правый) - -
Конец игры Эск Назад - -

Что приготовить

  • Собака визжит аудио файл
  • Три кошачьих визжащих аудиофайла
  • Файл изображения собаки
  • Файл изображения кошки
  • Файл изображения земли

Оригинальный образец на официальном сайте использует готовый контент .xnb файлов. Если вы хотите сохранить примеры на официальном сайте, не используйте mgcbs, добавьте их непосредственно в решение Visual Studio и скопируйте их во время сборки.

Я не знаю, почему в оригинальном примере используется этот метод, но я думаю, что это, вероятно, потому, что мы переносим старые версии проекта как есть. Конечно, вы можете использовать mgcb для создания того же файла. Проекты, которые можно скачать на этом сайте, были изменены на версию MGCB.

программа

Скачайте программу для всего кода.

Этот пример предназначен для того, чтобы код выглядел немного более общим, а воспроизведение звука выглядело хорошо во время выполнения. Использование только Audio3D было бы гораздо меньшим кодом, но это описано в оригинальном примере. Я оставлю менее важные части.

Проект состоит из следующих файлов кода:

  • Аудио3DИгра
  • АудиоМенеджер
  • Кошка
  • Собака
  • IAudioEmitter
  • Программа
  • Квадровер
  • СпрайтЭнтитити

Класс Квадровер

Этот класс является вспомогательным классом для рисования прямоугольных многоугольников. Прямоугольные полигоны часто используются в основном для отображения 3D-спрайтов (рекламных щитов). Эти наконечники используются для рекламных щитов для кошек и собак, а также для прямоугольных отображений земли.

Этот класс не имеет ничего общего с Audio3D.

поле

readonly GraphicsDevice _graphicsDevice;
readonly AlphaTestEffect _effect;
readonly VertexPositionTexture[] _vertices;

Минимальная информация, необходимая для рисования полигонов. Рисование полигонов из данных вершин без подготовки данных модели.

конструктор

/// <summary>
/// 新しい四辺形の描画ワーカーを作成します。
/// </summary>
public QuadDrawer(GraphicsDevice device)
{
  _graphicsDevice = device;

  _effect = new AlphaTestEffect(device);

  _effect.AlphaFunction = CompareFunction.Greater;
  _effect.ReferenceAlpha = 128;

  // 4つの頂点の配列を事前に割り当てます。
  _vertices = new VertexPositionTexture[4];

  _vertices[0].Position = new Vector3(1, 1, 0);
  _vertices[1].Position = new Vector3(-1, 1, 0);
  _vertices[2].Position = new Vector3(1, -1, 0);
  _vertices[3].Position = new Vector3(-1, -1, 0);
}

Поскольку этот класс является универсально используемым, вы получаете GraphicsDevice экземпляр, созданный в процессе инициализации игрового класса.

Здесь мы устанавливаем эффект, необходимый для рисования спрайта и положения 4 вершин, необходимых для прямоугольного многоугольника. Размер должен быть масштабирован при рисовании, поэтому он составляет -1 к 1.

Метод DrawQuad

/// <summary>
/// 3Dワールドの一部として四角形を描画します。
/// </summary>
public void DrawQuad(Texture2D texture, float textureRepeats, Matrix world, Matrix view, Matrix projection)
{
  // 指定されたテクスチャとカメラマトリックスを使用するようにエフェクトを設定します。
  _effect.Texture = texture;

  _effect.World = world;
  _effect.View = view;
  _effect.Projection = projection;

  // 指定された数のテクスチャの繰り返しを使用するように頂点配列を更新します。
  _vertices[0].TextureCoordinate = new Vector2(0, 0);
  _vertices[1].TextureCoordinate = new Vector2(textureRepeats, 0);
  _vertices[2].TextureCoordinate = new Vector2(0, textureRepeats);
  _vertices[3].TextureCoordinate = new Vector2(textureRepeats, textureRepeats);

  // 矩形を描画します。
  _effect.CurrentTechnique.Passes[0].Apply();

  _graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _vertices, 0, 2);
}

Задает переданную текстуру в прямоугольник и рисует его. Поскольку это базовый процесс, то отметить нечего.

Если есть одна точка, в textureRepeats аргументе может быть указана вызываемая переменная, так что координаты текстуры могут быть изменены. Если параметр vertexPositionTexture.TextureCoordinate установлен за пределами диапазона от 0 до 1, текстура по умолчанию рисуется неоднократно. Используя его, можно выразить, что плитку можно расположить многократно. На самом деле, клетчатые узоры на земле, по-видимому, имеют несколько простых черно-белых изображений, выстроенных в линию.

Интерфейс IAudioEmitter

/// <summary>
/// 3D サウンドを再生するエンティティの位置と速度を検索するために AudioManager が使用するインターフェイス。
/// </summary>
public interface IAudioEmitter
{
  Vector3 Position { get; }
  Vector3 Forward { get; }
  Vector3 Up { get; }
  Vector3 Velocity { get; }
}

Определяется как сущность, излучающая звук. Поскольку сущности, которые излучают звук на этот раз, являются собаками и кошками, они наследуются в своих соответствующих классах. Излучателю необходима следующая информация:

Использование недвижимости
Позиция Позиция юридического лица
Вперёд Направление, в котором находится сущность (вектор)
Вверх Верх сущности. Направление, в котором голова существует в человеке
Скорость Скорость, с которой движется сущность. Используется для вычисления доплеровских значений.

Класс SpriteEntity

Класс для представления 3D спрайтов (рекламных щитов). Он также является излучателем и наследует IAudioEmitter . Собаки и кошки наследуют этот класс.

свойство

/// <summary>エンティティの3D位置を取得または設定します。</summary>
public Vector3 Position { get; set; }

/// <summary>エンティティが向いている方向を取得または設定します。</summary>
public Vector3 Forward { get; set; }

/// <summary>このエンティティの上方向を取得または設定します。</summary>
public Vector3 Up { get; set; }

/// <summary>このエンティティの移動速度を取得または設定します。</summary>
public Vector3 Velocity { get; protected set; }

/// <summary>このエンティティの表示に使用されるテクスチャを取得または設定します。</summary>
public Texture2D Texture { get; set; }

Он имеет информацию о местоположении объекта и т. Д. В основном у меня есть информация как излучатель. Это также сущность рисования, поэтому у нее также есть изображение (текстура) для отображения.

Метод обновления

/// <summary>
/// エンティティの位置を更新し、サウンドを再生できるようにします。
/// </summary>
public abstract void Update(GameTime gameTime, AudioManager audioManager);

Описывает положение сущности и процесс воспроизведения звука, но реализует его в производном классе.

Метод рисования

/// <summary>
/// エンティティをビルボードスプライトとして描画します。
/// </summary>
public void Draw(QuadDrawer quadDrawer, Vector3 cameraPosition, Matrix view, Matrix projection)
{
  Matrix world = Matrix.CreateTranslation(0, 1, 0) *
                 Matrix.CreateScale(800) *
                 Matrix.CreateConstrainedBillboard(Position, cameraPosition, Up, null, null);

  quadDrawer.DrawQuad(Texture, 1, world, view, projection);
}

Собаки и кошки имеют разные изображения и позиции, но другой механизм рисования точно такой же, поэтому я описываю их здесь.

Каждое преобразование и текстура передаются в QuadDrawer для отображения прямоугольного многоугольника. Поскольку преобразование координат является базовым знанием, объяснения не охватываются.

Matrix.CreateConstrainedBillboard Используйте информацию о методе и камере, чтобы легко представлять рекламные щиты, поэтому эффективно используйте их.

Класс собак

Это класс, который занимается рисованием собак и пением. SpriteEntity Наследование класса. Собака остается в фиксированном положении в 3D-пространстве.

поле

/// <summary>サウンドを開始または停止するまでの時間。</summary>
TimeSpan _timeDelay = TimeSpan.Zero;

/// <summary>現在再生中のサウンド(ある場合)。</summary>
SoundEffectInstance _activeSound = null;

_timeDelay используется через определенные промежутки времени для воспроизведения крика.

SoundEffectInstance _activeSound — экземпляр для воспроизведения звука. SoundEffectInstance Как вы часто увидите в классе воспроизведения звука, вы можете использовать это как в 3D-звуке. Кстати _activeSound , я получаю ссылку только от класса AudioManager позже, поэтому я не буду отбрасывать ее на стороне класса Dot.

Метод обновления

/// <summary>
/// 犬の位置を更新し、音を鳴らします。
/// </summary>
public override void Update(GameTime gameTime, AudioManager audioManager)
{
  // エンティティを固定位置に設定します。
  Position = new Vector3(0, 0, -4000);
  Forward = Vector3.Forward;
  Up = Vector3.Up;
  Velocity = Vector3.Zero;

  // 時間遅延がなくなった場合は、ループ音を開始または停止します。 これは通常は永久に続きますが、6秒の遅延後に停止し、さらに4秒後に再起動します。
  _timeDelay -= gameTime.ElapsedGameTime;

  if (_timeDelay < TimeSpan.Zero)
  {
    if (_activeSound == null)
    {
      // 現在再生中のサウンドがない場合は、トリガーします。
      _activeSound = audioManager.Play3DSound("DogSound", true, this);

      _timeDelay += TimeSpan.FromSeconds(6);
    }
    else
    {
      // それ以外の場合は、現在のサウンドを停止します。
      _activeSound.Stop(false);
      _activeSound = null;

      _timeDelay += TimeSpan.FromSeconds(4);
    }
  }
}

В этом образце собака долгое время остается в одном и том же положении, поэтому первые четыре параметра задаются решающим ударом. Рисование выполняется в базовом классе.

_timeDelay затем используется в качестве оставшегося времени, чтобы остановиться, играя крик.

AudioManager будет воспроизводитьсяPlay3DSound в 3D-пространстве путем передачи имени крика, информации о цикле и вашей собственной информации в качестве излучателя в .

Класс Dog предназначен для того, чтобы зацикливать крик, поэтому вам не нужно думать слишком глубоко, потому что это просто корректировка, такая как игра или прекращение ветвления.

Класс кошек

Это класс, который занимается рисованием кошек и пением. SpriteEntity Наследование класса. Кошка движется вокруг начала.

поле

/// <summary>次の音を鳴らすまでの時間。</summary>
TimeSpan _timeDelay = TimeSpan.Zero;

/// <summary>サウンドバリエーションから選択するための乱数ジェネレータ。</summary>
static readonly Random _random = new Random();

_timeDelay используется так же, как и класс Dog, в остальное время до следующего визга.

Существует три типа кошачьих звонков, которые случайным образом выбираются и воспроизводятся, но они не имеют ничего общего с Audio3D, потому что они являются бонусным элементом.

Метод обновления

/// <summary>
/// 猫の位置を更新し、音を鳴らします。
/// </summary>
public override void Update(GameTime gameTime, AudioManager audioManager)
{
  // 猫を大きな円で動かします。
  double time = gameTime.TotalGameTime.TotalSeconds;

  float dx = (float)-Math.Cos(time);
  float dz = (float)-Math.Sin(time);

  Vector3 newPosition = new Vector3(dx, 0, dz) * 6000;

  // エンティティの位置と速度を更新します。
  Velocity = newPosition - Position;
  Position = newPosition;
  if (Velocity == Vector3.Zero)
  {
    Forward = Vector3.Forward;
  }
  else
  {
    Forward = Vector3.Normalize(Velocity);
  }

  Up = Vector3.Up;

  // 時間遅延がなくなった場合は、別の単発音をトリガーします。
  _timeDelay -= gameTime.ElapsedGameTime;

  if (_timeDelay < TimeSpan.Zero)
  {
    // 異なる3つのサウンドバリエーション(CatSound0、CatSound1、CatSound2)からランダムに選択します。
    string soundName = "CatSound" + _random.Next(3);

    audioManager.Play3DSound(soundName, false, this);

    _timeDelay += TimeSpan.FromSeconds(1.25f);
  }
}

Поскольку кошка вращается вокруг начала в круговом движении, она обрабатывает его так, что перемещает траекторию круга с помощью тригонометрической функции. В результате позиция, вперед и скорость меняются одна за другой. Поэтому, когда вы запускаете программу, вы можете видеть, что кошачий крик кружит вокруг, даже если вы не двигаете камерой.

_timeDelay Следующий код представляет собой процесс, в котором крик кошки случайным образом назначается и воспроизводится через равные промежутки времени. audioManager.Play3DSound Я перехожу к методу, который является излучателем. Вы можете видеть, что положение воспроизведения звука меняется один за другим.

Класс AudioManager

В этом, наконец, суть Audio3D. 3D-звук состоит из слушателя и излучателя. Излучатель уже определяет собак и кошек, поэтому класс AudioManager определяет прослушивателей. Обычно существует несколько излучателей, в то время как есть только один слушатель.

Этот класс наследует иGame регистрирует GameComponent и использует компонент в классе.

Класс ActiveSound

/// <summary>
/// アクティブな3Dサウンドを追跡し、アタッチされているエミッターオブジェクトを記憶するための内部ヘルパークラス。
/// </summary>
private class ActiveSound
{
  public SoundEffectInstance Instance;
  public IAudioEmitter Emitter;
}

Он определен в нижней части кода, но это класс для сохранения воспроизводимого состояния. Он содержит информацию о воспроизводимом звуковом экземпляре и воспроизводимом излучателе.

поле

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
static string[] _soundNames =
  {
    "CatSound0",
    "CatSound1",
    "CatSound2",
    "DogSound",
  };

/// <summary>音を聞くリスナーの情報です。これは通常カメラに一致するように設定されます。</summary>
public AudioListener Listener { get; } = new AudioListener();

/// <summary>AudioEmitter は、3Dサウンドを生成しているエンティティを表します。</summary>
readonly AudioEmitter _emitter = new AudioEmitter();

/// <summary>再生可能なすべての効果音を保存します。</summary>
readonly Dictionary<string, SoundEffect> _soundEffects = new Dictionary<string, SoundEffect>();

/// <summary>現在再生中のすべての3Dサウンドを追跡します。また、再生が終わったインスタンスの破棄にも使用します。</summary>
readonly List<ActiveSound> _activeSounds = new List<ActiveSound>();

_soundNames определяет имя загружаемого аудиофайла (имя ресурса). Поскольку это пример программы, определено, что сначала загружается аудиофайл массово. Импортированные звуковые данные хранятся _soundEffects в .

AudioListener Listener — определение слушателя. Сведения о прослушивателе определяются, поскольку они public задаются из класса Game.

AudioEmitter _emitter — определение излучателя при применении 3D к воспроизведению звука. В этом примере при воспроизведении звука значение каждого объекта излучателя устанавливается равным _emitter , поэтому это форма, которая разделяет один из них в качестве экземпляра. Конечно, можно иметь для каждого AudioEmitter предмета.

_activeSounds содержит сведения о звуке, который вы воспроизводите в классе, определенном ActiveSound ранее. Поскольку информация об излучателе постоянно меняется, она используется для обновления информации о местоположении и т. Д., А также для отбрасывания звуковых экземпляров, которые закончили воспроизведение.

конструктор

public AudioManager(Game game) : base(game) { }

GameComponent Поскольку он наследует класс, Game он получает экземпляр и передает его базовому классу.

Метод инициализации

/// <summary>
/// オーディオマネージャを初期化します。
/// </summary>
public override void Initialize()
{
  // ゲームの世界のスケールと一致するように、3Dオーディオのスケールを設定します。
  // DistanceScale は、離れるにつれて音量が変化する音の量を制御します。
  // DopplerScale は、サウンドを通過するときにピッチが変化するサウンドの量を制御します。
  SoundEffect.DistanceScale = 2000;
  SoundEffect.DopplerScale = 0.1f;

  // すべての効果音をロードします。
  foreach (string soundName in _soundNames)
  {
    _soundEffects.Add(soundName, Game.Content.Load<SoundEffect>(soundName));
  }

  base.Initialize();
}

GameComponent Поскольку он наследует класс, он автоматически вызывается при инициализации.

SoundEffect.DistanceScale и SoundEffect.DopplerScale являются параметрами, предназначенными для static 3D-звука.

SoundEffect.DistanceScale слышать звук даже на расстоянии.

SoundEffect.DopplerScale является влиянием эффекта Доплера. Чем выше число, тем больше эффект Доплера.

_soundNames Сохраните имя ресурса, определенное в цикле для загрузки _soundEffects .

Метод удаления

/// <summary>
/// 効果音データをアンロードします。
/// GameComponent として登録すればゲーム終了時に自動的に呼ばれます。
/// </summary>
protected override void Dispose(bool disposing)
{
  try
  {
    if (disposing)
    {
      foreach (SoundEffect soundEffect in _soundEffects.Values)
      {
        soundEffect.Dispose();
      }

      _soundEffects.Clear();
    }
  }
  finally
  {
    base.Dispose(disposing);
  }
}

GameComponent Он автоматически вызывается в конце игры, поскольку наследует класс.

Уничтожение всех импортируемых надежных активов.

Метод обновления

/// <summary>
/// 3Dオーディオシステムの状態を更新します。
/// </summary>
public override void Update(GameTime gameTime)
{
  // 現在再生中のすべての3Dサウンドをループします。
  int index = 0;

  while (index < _activeSounds.Count)
  {
    ActiveSound activeSound = _activeSounds[index];

    if (activeSound.Instance.State == SoundState.Stopped)
    {
      // 音の再生が終了した場合は廃棄してください。
      activeSound.Instance.Dispose();

      // アクティブリストから削除します。
      _activeSounds.RemoveAt(index);
    }
    else
    {
      // サウンドがまだ再生されている場合は、3D設定を更新します。
      Apply3D(activeSound);

      index++;
    }
  }

  base.Update(gameTime);
}

GameComponent Поскольку он наследует класс, он автоматически вызывается в процессе обновления.

Проверьте воспроизводимый в данный момент звук и, если он Apply3D воспроизводится, вызовите метод для обновления местоположения звука в соответствии с излучателем. Если воспроизведение какого-либо звука завершено, экземпляр отбрасывается.

Метод Play3DSound

/// <summary>
/// 新しい3Dサウンドを設定し再生します。
/// </summary>
public SoundEffectInstance Play3DSound(string soundName, bool isLooped, IAudioEmitter emitter)
{
  ActiveSound activeSound = new ActiveSound();

  // インスタンスを生成し、インスタンス、エミッターを設定します。
  activeSound.Instance = _soundEffects[soundName].CreateInstance();
  activeSound.Instance.IsLooped = isLooped;

  activeSound.Emitter = emitter;

  // 3D 位置を設定します。
  Apply3D(activeSound);

  activeSound.Instance.Play();

  // このサウンドがアクティブであることを保存します。
  _activeSounds.Add(activeSound);

  return activeSound.Instance;
}

Процесс воспроизведения 3D звука извне. Он имеет название звука для воспроизведения, петлю ли и информацию об излучателе.

Если вы хотите воспроизвести звук, создайте новый звуковой экземпляр из звукового ресурса, который вы храните в _soundEffects. SoundEffectInstance также может иметь цикл или нет, поэтому установите информацию о цикле.

Поскольку я создаю как ActiveSound воспроизводимое состояние, я устанавливаю информацию об излучателе, которая является объектом воспроизведения звука. Если вы хотите применить 3D-информацию о местоположении к звуку, вы получите Emitter необходимое значение.

Apply3D Способ описан ниже, но это процесс нанесения 3D-информации от излучателя к звуку.

Основной процесс 3D-звука заключается в том, чтобы начать воспроизведение с SoundEffectInstance.Play набором 3D-информации. Остальное заключается в периодическом обновлении 3D-информации до тех пор, пока звук не закончит воспроизведение.

Метод Apply3D

/// <summary>
/// 3Dサウンドの位置と速度の設定を更新します。
/// </summary>
private void Apply3D(ActiveSound activeSound)
{
  _emitter.Position = activeSound.Emitter.Position;
  _emitter.Forward = activeSound.Emitter.Forward;
  _emitter.Up = activeSound.Emitter.Up;
  _emitter.Velocity = activeSound.Emitter.Velocity;

  activeSound.Instance.Apply3D(Listener, _emitter);
}

Процесс применения 3D-эффекта к заданному звуковому экземпляру с использованием прослушивателей и излучателей.

Каждый раз мы устанавливаем значение _emitter общего экземпляра. Если он у вас SoundEffectInstance.Apply3D уже AudioEmitter есть, просто передайте прослушиватель и излучатель методу.

Класс Аудио3DGame

Наконец, давайте посмотрим, что такое класс Game.

поле

readonly GraphicsDeviceManager _graphics;
readonly AudioManager _audioManager;
readonly SpriteEntity _cat;
readonly SpriteEntity _dog;

/// <summary>地面の描画に使用するテクスチャーです。</summary>
Texture2D _checkerTexture;

QuadDrawer _quadDrawer;

Vector3 _cameraPosition = new Vector3(0, 512, 0);
Vector3 _cameraForward = Vector3.Forward;
Vector3 _cameraUp = Vector3.Up;
Vector3 _cameraVelocity = Vector3.Zero;

KeyboardState _currentKeyboardState;
GamePadState _currentGamePadState;

AudioManager Регистрируется GameComponents в , но имеет его как поле, потому что он обращается к нему индивидуально.

Другие определяют собаку, кошачьи сущности, текстуры для наземного рисования, квадривер, информацию о камере и входную информацию.

конструктор

public Audio3DGame()
{
  Content.RootDirectory = "Content";

  _graphics = new GraphicsDeviceManager(this);

  _audioManager = new AudioManager(this);

  // AudioManager を Components に追加して自動的に Update メソッドが呼ばれるようにします。
  Components.Add(_audioManager);

  _cat = new Cat();
  _dog = new Dog();
}

AudioManager Создание и GameComponents регистрация в .

Вы также создали классы Кошки и Собаки.

Метод LoadContent

/// <summary>
/// グラフィックコンテンツをロードします。
/// </summary>
protected override void LoadContent()
{
  _cat.Texture = Content.Load<Texture2D>("CatTexture");
  _dog.Texture = Content.Load<Texture2D>("DogTexture");

  _checkerTexture = Content.Load<Texture2D>("checker");

  // 四角形ポリゴンを描画するためのクラス
  _quadDrawer = new QuadDrawer(_graphics.GraphicsDevice);
}

Загрузите текстуры, необходимые для каждого чертежа.

QuadDrawer генерируется GraphicsDevice здесь, потому что требует .

Метод обновления

/// <summary>
/// ゲームがロジックを実行できるようにします。
/// </summary>
protected override void Update(GameTime gameTime)
{
  HandleInput();

  UpdateCamera();

  // 新しいカメラの位置について AudioManager に伝えます。
  _audioManager.Listener.Position = _cameraPosition;
  _audioManager.Listener.Forward = _cameraForward;
  _audioManager.Listener.Up = _cameraUp;
  _audioManager.Listener.Velocity = _cameraVelocity;

  // ゲームエンティティに動き回って音を鳴らすように伝えます。
  _cat.Update(gameTime, _audioManager);
  _dog.Update(gameTime, _audioManager);

  base.Update(gameTime);
}

HandleInput Методы и UpdateCamera методы представляют собой процесс получения ввода устройства и обработки камеры, который будет рассмотрен позже.

Поскольку положение камеры и положение прослушивателя почти всегда одинаковы, прослушиватель устанавливается на одно и то же значение после процесса обновления камеры.

_cat Вызовите методы Update _dog для перемещения и визга соответственно.

Метод рисования

/// <summary>
/// ゲームが描画する必要があるときに呼び出されます。
/// </summary>
protected override void Draw(GameTime gameTime)
{
  var device = _graphics.GraphicsDevice;

  device.Clear(Color.CornflowerBlue);

  device.BlendState = BlendState.AlphaBlend;

  // カメラ行列を計算します。
  var view = Matrix.CreateLookAt(_cameraPosition, _cameraPosition + _cameraForward, _cameraUp);
  var projection = Matrix.CreatePerspectiveFieldOfView(1, device.Viewport.AspectRatio, 1, 100000);

  // チェッカーグラウンドポリゴンを描画します。
  var groundTransform = Matrix.CreateScale(20000) * Matrix.CreateRotationX(MathHelper.PiOver2);

  _quadDrawer.DrawQuad(_checkerTexture, 32, groundTransform, view, projection);

  // ゲームエンティティを描画します。
  _cat.Draw(_quadDrawer, _cameraPosition, view, projection);
  _dog.Draw(_quadDrawer, _cameraPosition, view, projection);

  base.Draw(gameTime);
}

Создает преобразования зрения и проекции на основе информации камеры.

Для земли прямоугольник, нарисованный в QuadDrawer, отображается вертикально, поэтому он поворачивается горизонтально и увеличивается умеренно. _quadDrawer.DrawQuad Второй аргумент метода указывает количество повторений текстуры, которое указывает, что 32x32 листа отображаются рядом.

_cat и _dog размещаются внутри, поэтому информация о местоположении камеры передается и рисуется.

Метод HandleInput

/// <summary>
/// ゲームを終了するための入力を処理します。
/// </summary>
void HandleInput()
{
  _currentKeyboardState = Keyboard.GetState();
  _currentGamePadState = GamePad.GetState(PlayerIndex.One);

  // 終了を確認します。
  if (_currentKeyboardState.IsKeyDown(Keys.Escape) ||
      _currentGamePadState.Buttons.Back == ButtonState.Pressed)
  {
    Exit();
  }
}

Информация о клавиатуре и геймпаде приобретается, и определяется окончание игры.

Метод UpdateCamera

/// <summary>
/// カメラを動かすための入力を処理します。
/// </summary>
void UpdateCamera()
{
  const float turnSpeed = 0.05f;
  const float accelerationSpeed = 4;
  const float frictionAmount = 0.98f;

  // 左または右に曲がります。
  float turn = -_currentGamePadState.ThumbSticks.Left.X * turnSpeed;

  if (_currentKeyboardState.IsKeyDown(Keys.Left)) turn += turnSpeed;
  if (_currentKeyboardState.IsKeyDown(Keys.Right)) turn -= turnSpeed;

  _cameraForward = Vector3.TransformNormal(_cameraForward, Matrix.CreateRotationY(turn));

  // 前方または後方に加速します。
  float accel = _currentGamePadState.ThumbSticks.Left.Y * accelerationSpeed;

  if (_currentKeyboardState.IsKeyDown(Keys.Up)) accel += accelerationSpeed;
  if (_currentKeyboardState.IsKeyDown(Keys.Down)) accel -= accelerationSpeed;

  _cameraVelocity += _cameraForward * accel;

  // 現在の位置に速度を追加します。
  _cameraPosition += _cameraVelocity;

  // 摩擦力を加えます。
  _cameraVelocity *= frictionAmount;
}

Камера управляется движением.

Он также включает в себя такие вычисления, как ускорение и трение, но я думаю, что он, вероятно, учитывает эффект Доплера на 3D-звук. Поскольку я не делаю ничего особенного с самим расчетом, я не буду его обсуждать.

Сводка

Я подробно рассмотрел это, но обычно этого достаточно, чтобы обратиться к классу, AudioManager чтобы узнать, как воспроизводить 3D-звук. На самом деле, большинство трудностей выполняется фреймворком, потому что то, что SoundEffectInstance мы делаем, это настройка 3D-информации в . Если вы сэмплировали только части 3D-звука, вы в конечном итоге напишете небольшую работу в классе Game. Тем не менее, я пишу «Советы», основанные на оригинальной истории, так что это было намного дольше.