Audio3D

Laman dikemaskini :
Tarikh penciptaan halaman :

Nota mengenai Petua ini

Sampel ini berdasarkan program yang diterbitkan di laman web berikut. Saya menukar kod sedikit untuk menjadikannya lebih mudah difahami dan menerangkannya dalam bahasa Jepun. Pada asasnya, kami menggunakan kod asal seperti sedia ada, jadi jika anda benar-benar menggunakannya dalam program permainan anda, betulkannya tepat pada masanya dan gunakannya.

Laman rujukan

Di samping itu, ia dijelaskan dengan andaian bahawa anda mempunyai pengetahuan asas mengenai MonoGame dan XNA. Lihat Petua MonoGame dan Petua XNA untuk ruddly.

Khususnya, sebagai matematik, vektor, fungsi trigonometri, matriks, dan lain-lain adalah penting, jadi sila tahu apa yang mereka sedikit sebanyak.

Persekitaran

Platform
  • Windows 10
  • Kod boleh digunakan pada platform didayakan MonoGame yang lain
Visual Studio
  • Studio Visual 2019
Teras .NET
  • 3.1
MonoGame
  • 3.8

Mengenai sampel

Berdasarkan kedudukan kamera, anda boleh mendengar panggilan dari kedudukan anjing dan kedudukan kucing. Kucing bulatan dan bulatan asal, dan pemain boleh mengawal kedudukan kamera dengan kunci. Pastikan cara anda mendengar bunyi berubah dengan setiap perubahan dalam kedudukan kamera atau kedudukan kucing. Saya fikir ia mudah difahami menggunakan fon telinga. Aku belum dikonfirmasi di saluran 5.1.

Bagaimana untuk beroperasi

Sentuhan
Apa yang Perlu Dilakukan Tetikus Gamepad Papan Kekunci (XInput)
Kamera ke hadapan dan ke belakang ↑↓ Batang Kiri (Atas dan Bawah) - -
Tukar orientasi kamera ←→ Batang Kiri (Kiri dan Kanan) - -
Akhir permainan Esc Kembali - -

Apa yang perlu disediakan

  • Fail audio squeal anjing
  • Tiga fail audio squeal kucing
  • Fail Imej Anjing
  • Fail gambar kucing
  • Fail imej tanah

Sampel asal di laman web rasmi menggunakan fail .xnb kandungan prabina. Jika anda ingin menyimpan sampel di laman web rasmi, jangan gunakan mgcbs, tambahkannya terus ke penyelesaian Visual Studio anda, dan salinnya pada masa binaan.

Saya tidak tahu mengapa sampel asal mengambil kaedah ini, tetapi saya fikir ia mungkin kerana kita memindahkan versi lama projek seperti yang ada. Sudah tentu, anda boleh menggunakan mgcb untuk menjana fail yang sama. Projek-projek yang boleh dimuat turun di laman web ini telah diubah suai kepada versi MGCB.

Program

Muat turun program untuk semua kod.

Sampel ini direka untuk menjadikan kod kelihatan sedikit lebih umum sambil menjadikan main balik bunyi kelihatan baik pada masa jalan. Menggunakan Audio3D sahaja akan menjadi lebih kurang kod, tetapi ia diterangkan di sepanjang sampel asal. Aku akan menyimpan bagian yang kurang penting.

Projek ini terdiri daripada fail kod berikut:

  • Permainan Audio3D
  • Pengurus Audio
  • Kucing
  • Anjing
  • IAudioEmitter
  • Program
  • QuadDrawer
  • SpriteEntity

Kelas QuadDrawer

Kelas ini adalah kelas pembantu untuk melukis poligon segi empat tepat. Poligon segi empat tepat sering digunakan terutamanya untuk memaparkan sprites 3D (papan iklan). Petua ini digunakan untuk papan iklan untuk kucing dan anjing, serta untuk paparan segi empat tepat tanah.

Kelas ini tidak ada hubungannya dengan Audio3D.

Bidang

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

Maklumat minimum yang diperlukan untuk melukis poligon. Lukis poligon daripada data bucu tanpa menyediakan data model.

Constructor

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

Oleh kerana kelas ini boleh digunakan secara universal, anda GraphicsDevice menerima contoh yang dibuat dalam proses permulaan kelas permainan.

Di sini, kami menetapkan kesan yang diperlukan untuk menarik sprite dan kedudukan 4 bucu yang diperlukan untuk poligon segi empat tepat. Saiznya harus diskalakan semasa melukis, jadi ia adalah -1 hingga 1.

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

Tetapkan tekstur yang diluluskan ke segi empat tepat dan lukiskannya. Oleh kerana ini adalah proses asas, tidak ada yang perlu diperhatikan.

Sekiranya terdapat satu titik, pembolehubah yang dipanggil boleh ditentukan dalam textureRepeats hujah, supaya koordinat tekstur dapat diubah. Jika vertexPositionTexture.TextureCoordinate ditetapkan di luar julat 0 hingga 1, tekstur dilukis berulang kali secara lalai. Menggunakannya, adalah mungkin untuk menyatakan bahawa jubin boleh diatur berulang kali. Malah, corak berkotak-kotak di atas tanah nampaknya mempunyai pelbagai imej hitam dan putih yang mudah dibarisi.

Antara muka IAudioEmitter

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

Ditakrifkan sebagai entiti yang mengeluarkan bunyi. Oleh kerana entiti yang mengeluarkan bunyi kali ini adalah anjing dan kucing, mereka diwarisi dalam kelas masing-masing. Emitter memerlukan maklumat berikut:

Penggunaan hartanah
Kedudukan Kedudukan entiti
Ke hadapan Arah yang dihadapi oleh entiti (vektor)
Naik Naik entiti. Arah di mana kepala wujud pada seseorang
Halaju Kelajuan di mana entiti bergerak. Digunakan untuk mengira nilai Doppler.

Kelas SpriteEntity

Kelas untuk mewakili sprites 3D (papan iklan). Ia juga merupakan pemancar, dan mewarisi IAudioEmitter . Anjing dan kucing mewarisi kelas ini.

Hartanah

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

Ia mempunyai maklumat lokasi entiti, dsb. Pada dasarnya aku punya informasi sebagai pemancar. Ia juga merupakan entiti lukisan, jadi ia juga mempunyai imej (Tekstur) untuk dipaparkan.

Kaedah kemas kini

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

Menerangkan kedudukan entiti dan proses bermain bunyi, tetapi melaksanakannya dalam kelas yang diperolehi.

Kaedah cabutan

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

Anjing dan kucing mempunyai imej dan kedudukan yang berbeza, tetapi mekanisme lukisan lain adalah sama, jadi saya menerangkannya di sini.

Setiap transformasi dan tekstur diserahkan kepada QuadDrawer untuk memaparkan poligon segi empat tepat. Oleh kerana transformasi koordinat adalah pengetahuan asas, penjelasan tidak dilindungi.

Matrix.CreateConstrainedBillboard Gunakan maklumat kaedah dan kamera untuk mewakili papan iklan dengan mudah, jadi gunakannya dengan berkesan.

Kelas anjing

Ia adalah kelas yang melakukan main balik lukisan dan nyanyian anjing. SpriteEntity Mewarisi kelas. Anjing itu kekal dalam kedudukan tetap dalam ruang 3D.

Bidang

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

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

_timeDelay digunakan pada selang waktu untuk memainkan tangisan.

SoundEffectInstance _activeSound adalah contoh untuk memainkan bunyi. SoundEffectInstance Seperti yang sering anda lihat dalam kelas main balik bunyi, anda boleh menggunakannya seperti dalam bunyi 3D. Dengan cara _activeSound ini, saya hanya menerima rujukan dari kelas AudioManager kemudian, jadi saya tidak akan membuangnya di bahagian kelas Dot.

Kaedah kemas kini

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

Dalam sampel ini, anjing itu kekal dalam kedudukan yang sama untuk masa yang lama, jadi empat parameter pertama ditentukan oleh mogok yang menentukan. Lukisan dilakukan di kelas asas.

_timeDelay kemudian digunakan sebagai masa yang tinggal untuk berhenti, bermain menangis.

AudioManager akan diterbitkan semulaPlay3DSound dalam ruang 3D dengan menghantar nama tangisan, maklumat gelung, dan maklumat anda sendiri sebagai pemancar kepada .

Kelas Anjing direka untuk melingkarkan tangisan, jadi anda tidak perlu berfikir terlalu mendalam kerana ia hanya pelarasan, seperti bermain atau berhenti bercabang.

Kelas kucing

Ia adalah kelas yang melukis kucing dan menyanyi main balik. SpriteEntity Mewarisi kelas. Kucing bergerak di sekitar asal usulnya.

Bidang

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

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

_timeDelay digunakan dengan cara yang sama seperti kelas Anjing, dengan selebihnya sebelum squeal seterusnya.

Terdapat tiga jenis panggilan kucing yang dipilih dan dimainkan secara rawak, tetapi ia tidak ada kaitan dengan Audio3D kerana ia adalah elemen bonus.

Kaedah kemas kini

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

Oleh kerana kucing berpusing asal dalam gerakan bulat, ia memprosesnya supaya ia menggerakkan trajektori bulatan menggunakan fungsi trigonometri. Akibatnya, kedudukan, ke hadapan, dan Halaju telah berubah satu demi satu. Oleh itu, apabila anda menjalankan program ini, anda dapat melihat bahawa tangisan kucing berputar-putar walaupun anda tidak menggerakkan kamera.

_timeDelay Kod berikut ialah proses di mana tangisan kucing diberikan secara rawak dan dimainkan pada selang masa yang tetap. audioManager.Play3DSound Saya lulus diri saya kepada kaedah yang merupakan pemancar. Anda dapat melihat bahawa kedudukan main balik bunyi berubah satu demi satu.

Kelas Pengurus Audio

Ini akhirnya intipati Audio3D. Bunyi 3D terdiri daripada pendengar dan pemancar. Pemancar sudah mentakrifkan anjing dan kucing, jadi kelas AudioManager mentakrifkan pendengar. Biasanya terdapat berbilang pemancar, sementara hanya ada satu pendengar.

Kelas ini mewarisi danGame mendaftar GameComponent dan menggunakan komponen dalam kelas.

Kelas ActiveSound

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

Ia ditakrifkan di bahagian bawah kod, tetapi ia adalah kelas untuk memelihara keadaan yang sedang dimainkan. Ia mempunyai maklumat mengenai contoh bunyi yang dimainkan dan pemancar dimainkan.

Bidang

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
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 mentakrifkan nama fail audio (nama aset) untuk dimuatkan. Oleh kerana ia adalah program sampel, ia ditakrifkan untuk memuatkan fail audio secara pukal terlebih dahulu. Data bunyi yang diimport disimpan _soundEffects dalam .

AudioListener Listener adalah definisi pendengar. Maklumat pendengar ditakrifkan kerana ia public ditetapkan dari kelas Permainan.

AudioEmitter _emitter ialah definisi pemancar apabila menggunakan 3D pada main balik bunyi. Dalam sampel ini, apabila memainkan bunyi, nilai setiap objek pemancar ditetapkan kepada _emitter , jadi ia adalah bentuk yang berkongsi satu sebagai contoh. Sudah tentu, anda boleh mempunyai setiap AudioEmitter objek.

_activeSounds mempunyai maklumat tentang bunyi yang anda mainkan dalam kelas yang anda tentukan ActiveSound sebelum ini. Oleh kerana maklumat pemancar berubah sepanjang masa, ia digunakan untuk mengemas kini maklumat lokasi, dan lain-lain, dan untuk membuang kejadian bunyi yang telah selesai dimainkan.

Constructor

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

GameComponent Oleh kerana ia mewarisi kelas, Game ia menerima contoh dan lulus ke kelas asas.

Memulakan kaedah

/// <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 Kerana ia mewarisi kelas, ia secara automatik dipanggil pada permulaan.

SoundEffect.DistanceScale dan SoundEffect.DopplerScale parameter yang dikhaskan untuk static bunyi 3D.

SoundEffect.DistanceScale untuk mendengar bunyi walaupun di kejauhan.

SoundEffect.DopplerScale adalah pengaruh kesan Doppler. Semakin tinggi bilangannya, semakin besar kesan Doppler.

_soundNames Simpan nama aset yang ditakrifkan dalam gelung untuk memuatkan _soundEffects .

Kaedah pelupusan

/// <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 Ia secara automatik dipanggil pada akhir permainan kerana ia mewarisi kelas.

Memusnahkan semua aset bunyi yang diimport.

Kaedah kemas kini

/// <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 Kerana ia mewarisi kelas, ia secara automatik dipanggil dalam proses kemas kini.

Semak bunyi yang sedang dimainkan dan, jika ia Apply3D sedang dimainkan, hubungi kaedah untuk mengemas kini lokasi bunyi untuk dipadankan dengan pemancar. Jika sebarang bunyi telah selesai dimainkan, contoh itu dibuang.

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

Proses memainkan bunyi 3D dari luar. Ia mempunyai nama bunyi untuk dimainkan, sama ada untuk gelung, dan maklumat pemancar.

Jika anda ingin memainkan bunyi, cipta tika bunyi baharu daripada sumber bunyi yang anda simpan dalam _soundEffects. SoundEffectInstance juga boleh mempunyai gelung atau tidak, jadi tetapkan maklumat gelung.

Oleh kerana saya mencipta sebagai ActiveSound keadaan bermain, saya menetapkan maklumat pemancar yang merupakan objek main balik bunyi di sana. Jika anda ingin menggunakan maklumat lokasi 3D pada bunyi, anda akan Emitter mendapat nilai yang anda perlukan.

Apply3D Kaedah ini diterangkan di bawah, tetapi ia adalah proses memohon maklumat 3D dari pemancar ke bunyi.

Proses asas bunyi 3D adalah untuk memulakan main balik dengan SoundEffectInstance.Play set maklumat 3D. Selebihnya adalah untuk mengemas kini maklumat 3D secara berkala sehingga bunyi selesai dimainkan.

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

Proses menggunakan kesan 3D pada contoh bunyi yang ditentukan menggunakan pendengar dan pemancar.

Kami menetapkan nilai kepada _emitter contoh biasa setiap kali. Jika anda SoundEffectInstance.Apply3D sudah AudioEmitter mempunyai satu, hanya lulus pendengar dan pemancar kepada kaedah.

Kelas Audio3DGame

Akhirnya, mari kita lihat apa yang kelas Permainan adalah kira-kira.

Bidang

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;

AudioManagerGameComponents Daftar dalam , tetapi memilikinya sebagai medan kerana ia mengaksesnya secara individu.

Yang lain menentukan anjing, entiti kucing, tekstur untuk lukisan tanah, quaddrawer, maklumat kamera, dan maklumat input.

Constructor

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 Menjana dan GameComponents mendaftar dengan .

Anda juga telah menjana kelas Kucing dan Anjing.

LoadContent method

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

Muatkan tekstur yang diperlukan untuk setiap lukisan.

QuadDrawerGraphicsDevice dijana di sini kerana memerlukan .

Kaedah kemas kini

/// <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 Kaedah dan UpdateCamera kaedah adalah proses mendapatkan input peranti dan mengendalikan kamera, yang akan dibincangkan kemudian.

Oleh kerana kedudukan kamera dan kedudukan pendengar hampir selalu sama, pendengar ditetapkan kepada nilai yang sama selepas proses kemas kini kamera.

_cat Panggil kaedah dan _dog Kemas kini untuk bergerak dan menjerit, masing-masing.

Kaedah cabutan

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

Menjana transformasi pandangan dan unjuran daripada maklumat kamera.

Untuk tanah, segi empat tepat yang dilukis dalam QuadDrawer dipaparkan secara menegak, jadi ia diputar menjadi mendatar dan diperbesarkan secara sederhana. _quadDrawer.DrawQuad Hujah kedua kaedah menentukan bilangan pengulangan tekstur, yang menentukan bahawa helaian 32x32 muncul bersebelahan.

_cat dan _dog papan iklan di papan iklan secara dalaman, jadi maklumat lokasi kamera diluluskan dan dilukis.

Kaedah HandleInput

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

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

Maklumat mengenai papan kekunci dan gamepad diperolehi, dan akhir permainan ditentukan.

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

Kamera dikawal untuk bergerak.

Ia juga termasuk pengiraan seperti pecutan dan geseran, tetapi saya fikir ia mungkin mengambil kira kesan Doppler pada bunyi 3D. Oleh kerana saya tidak melakukan apa-apa yang istimewa mengenai pengiraan itu sendiri, saya tidak akan membincangkannya.

Ringkasan

Saya telah menutupnya dengan panjang lebar, tetapi biasanya cukup untuk merujuk kepada kelas untuk AudioManager cara memainkan bunyi 3D. Sebenarnya, kebanyakan kesukaran dilakukan oleh rangka kerja, kerana apa yang SoundEffectInstance kita lakukan adalah menetapkan maklumat 3D ke dalam . Jika anda telah mencuba hanya bahagian bunyi 3D, anda akan menulis sedikit kerja dalam kelas Permainan. Walau bagaimanapun, saya menulis Petua berdasarkan cerita asal, jadi sudah lebih lama.