Audio3D

更新頁 :
頁面創建日期 :

提示中的注意事項

此範例基於以下網站上發佈的程式。 我稍微更改了代碼,以便更容易理解,並用日語解釋它。 基本上,我們使用原始代碼,因此,如果您確實希望將其用於遊戲程式,請及時修復它。

參考網站

此外,我解釋的假設是,我有一些關於單遊戲和XNA的基本知識。 有關基本部分,請參閱單 遊戲提示XNA 提示

特別是,要知道向量三角函數矩陣作為數學的必要程度。

環境

平臺
  • Windows 10
  • 代碼可以轉移到其他支援MonoGame的平臺上。
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

關於示例

根據相機的位置,狗的位置貓的位置會分別發出叫聲。 貓繞著原點轉,玩家可以使用鑰匙操作攝像機的位置。 確保每次相機或貓的位置發生變化時,聲音會發生變化。 我認為使用耳機很容易理解。 我在 5.1 頻道上未驗證。

操作方法

操作內容 鍵盤遊戲板 (XInput) 滑鼠觸摸
攝像機前進和後退 ↑↓ 左搖桿(向上和向下) - -
更改攝像機的方向 ←→ 左搖杆(左和右) - -
遊戲結束 Esc Back - -

準備什麼

  • 狗的叫聲檔
  • 三個貓叫聲檔
  • 狗圖片檔
  • 貓圖片檔
  • 地面圖像檔

官方網站的原始示例使用預構建的內容 .xnb 檔。 如果要按原樣使用官方網站示例,請直接將其添加到Visual Studio解決方案中,並在生成時複製它,而無需使用MGCB。

我不知道為什麼原始示例採用這種方法,但這可能是因為我正在遷移專案的舊版本。 當然,使用 MGCB 生成相同的文件沒有問題。 可在此網站上下載的專案已修改為 MGCB 版本。

程式

有關完整代碼,請參閱下載程式。

由於此示例旨在使代碼在運行時更易於理解,但代碼更易於使用,因此代碼有點冗長。 如果僅使用 Audio3D,則編寫代碼要少得多,但下面是一個原始示例。 對於不太重要的部分,請快速沖洗。

專案由以下代碼檔案組成:

  • Audio3DGame
  • AudioManager
  • Cat
  • Dog
  • IAudioEmitter
  • Program
  • QuadDrawer
  • SpriteEntity

四維繪製器類

此類是繪製矩形多邊形的幫助器類。 矩形多邊形主要用於顯示 3D 子畫面(看板)。 此提示還用於貓和狗看板以及地面的矩形顯示。

此類與音訊 3D 無關。

欄位

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);
}

此類是通用的,因此在 Game 類的初始化過程中建立實例並 GraphicsDevice 接收 。

在這裡,您可以設置繪製子畫面所需的效果,以及矩形多邊形所需的四個頂點的位置。 大小在繪製時縮放,因此從 -1 到 1。

繪製四邊形方法

/// <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 指定變數 ,從而更改紋理的座標。 如果將 VertexPosition 文字.文本集合設置為超出 0 到 1 的範圍,則預設情況下將重複繪製紋理。 它可用於重複排列磁貼等。 事實上,地面上的方格圖案似乎排列了多個簡單的黑白圖像。

IAudioEmitter 介面

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

定義為發出聲音的實體。 發出聲音的實體是狗和貓,因此它們在每個類中都繼承了它們。 作為埃米特,您需要的資訊包括:

屬性 用途
Position 實體的位置
Forward 實體指向的方向(向量)
Up 實體的向上。 人說是頭存在的方向
Velocity 實體的移動速度。 用於計算多普勒值。

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);
}

狗和貓的圖像和位置是不同的,但除此之外,繪製機制是完全相同的,所以我在這裡描述。

每個座標變換和紋理都傳遞給四繪圖器以顯示矩形多邊形。 坐標轉換是基本知識,所以我省略了解釋。

Matrix.CreateConstrainedBillboard 使用方法和相機資訊,您可以輕鬆地表示看板,並充分利用它。

狗類

狗畫,玩叫是類。 SpriteEntity 繼承類。 狗在 3D 空間中保持固定位置。

欄位

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

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

_timeDelay 每隔一段時間播放一次。

SoundEffectInstance _activeSound 是播放聲音的實例。 SoundEffectInstance 類在聲音播放中看起來不錯,但您可以在 3D 聲音中使用它。 順便說一下 _activeSound ,它只會從以後的音訊管理器類接收引用,因此不要在 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 類相同,在下一個類發出蜂鳴音之前的剩餘時間使用。

貓的叫聲是隨機選擇的,有三種類型,但與音訊3D無關,因為它是一個補充元素。

更新方法

/// <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 類

最後,這是音訊 3D 的本質。 3D 聲音由聽聲音的人(聽眾)和發出聲音的人(發射器)組成。 由於發射器已由狗或貓定義,因此音訊管理器類定義偵聽器。 通常有多個發射器,而只有一個偵聽器。

此類 GameComponent 繼承 ,並在Game 類中註冊和使用元件。

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 是從遊戲類設置的。

AudioEmitter _emitter 是將 3D 應用於聲音播放時的發射器定義。 在此範例中,每個發射器物件的值 _emitter 在播放聲音時設置為 ,因此實例共用一個。 當然,您可以為每個 AudioEmitter 物件設定 。

_activeSounds 包含有關您剛剛定義的 ActiveSound 類正在播放的聲音的資訊。 發射器資訊會不斷更改,因此用於更新位置等,並丟棄已播放的聲音實例。

構造函數

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

GameComponent 由於類是繼承的,因此 Game 它接收實例並將其傳遞給基類。

Initialize 方法

/// <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.DistanceScaleSoundEffect.DopplerScale 是專用 static 於 3D 聲音的參數。

SoundEffect.DistanceScale 使大,你可以聽到的聲音,即使在遠處。

SoundEffect.DopplerScale 是多普勒效應的影響。 數位越大,多普勒效果越大。

_soundNames 將中定義的資產名稱儲存在循環載入中 _soundEffects

Dispose 方法

/// <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 調用 方法以更新聲音的位置以匹配發射器。 如果播放完任何聲音,則銷毀實例。

播放 3D 聲音方法

/// <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 聲音的基本處理是在設置 3D 資訊后 SoundEffectInstance.Play 開始播放。 之後,您將定期更新 3D 資訊,直到播放完聲音。

應用 3D 方法

/// <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 設置值。 如果已 AudioEmitter 提供 ,則 SoundEffectInstance.Apply3D 只需將偵聽器和發射器傳遞給 方法即可。

Audio3DGame 類

最後,我將解釋遊戲類的內容。

欄位

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();
}

AudioManagerGameComponents 並註冊到 。

您還生成了 Cat 和 Dog 類。

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 調用 和 _dog Update 方法以分別移動和處理振鈴。

繪製方法

/// <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.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 類中編寫一些處理即可。 然而,它變得很長,因為我寫提示的基礎上,原來的故事。