Audio3D

Halaman Diperbarui :
Tanggal pembuatan halaman :

Catatan tentang Tips ini

Sampel ini didasarkan pada program yang dipublikasikan di situs berikut. Saya mengubah kode sedikit untuk membuatnya lebih mudah untuk memahami dan menjelaskannya dalam bahasa Jepang. Pada dasarnya, kami menggunakan kode asli apa adanya, jadi jika Anda benar-benar mengadopsinya dalam program permainan Anda, perbaiki tepat waktu dan gunakan.

Situs referensi

Selain itu, dijelaskan dengan asumsi bahwa Anda memiliki beberapa pengetahuan dasar tentang MonoGame dan XNA. Lihat Tips MonoGame dan Tips XNA untuk kemerahan.

Secara khusus, karena matematika, vektor, fungsi trigonometri, matriks, dll. sangat penting, jadi ketahuilah apa itu sampai batas tertentu.

lingkungan

balei-balei
  • Windows 10
  • Kode dapat digunakan pada platform berkemampuan MonoGame lainnya
Visual Studio
  • Studio Visual 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

Tentang sampel

Berdasarkan posisi kamera, Anda dapat mendengar panggilan dari posisi anjing dan posisi kucing. Kucing berputar dan melingkari asalnya, dan pemain dapat mengontrol posisi kamera dengan kunci. Pastikan bahwa cara Anda mendengar suara berubah dengan setiap perubahan posisi kamera atau posisi kucing. Saya pikir mudah dimengerti menggunakan earphone. Saya belum dikonfirmasi di saluran 5.1.

Cara mengoperasikannya

Apa yang Harus Dilakukan Keyboard Gamepad (XInput) Mouse Touch
Kamera maju dan mundur ↑↓ Tongkat Kiri (Atas dan Bawah) - -
Mengubah orientasi kamera ←→ Tongkat Kiri (Kiri dan Kanan) - -
Akhir dari permainan Esc Belakang - -

Apa yang harus dipersiapkan

  • File audio jeritan anjing
  • Tiga file audio jeritan kucing
  • Berkas Gambar Anjing
  • Berkas gambar kucing
  • File gambar tanah

Sampel asli di situs resmi menggunakan file .xnb konten bawaan. Jika Anda ingin menyimpan sampel di situs resmi, jangan gunakan mgcbs, tambahkan langsung ke solusi Visual Studio Anda, dan salin pada waktu pembuatan.

Saya tidak tahu mengapa sampel asli mengambil metode ini, tetapi saya pikir itu mungkin karena kami memigrasikan versi proyek yang lebih lama apa adanya. Tentu saja, Anda dapat menggunakan mgcb untuk menghasilkan file yang sama. Proyek yang dapat diunduh di situs ini telah dimodifikasi ke versi MGCB.

rencana

Unduh program untuk semua kode.

Sampel ini dirancang untuk membuat kode terlihat sedikit lebih umum sambil membuat pemutaran suara terlihat bagus saat runtime. Menggunakan Audio3D hanya akan jauh lebih sedikit kode, tetapi dijelaskan di sepanjang sampel aslinya. Saya akan menyimpan bagian-bagian yang kurang penting.

Proyek ini terdiri dari file kode berikut:

  • Audio3DGame
  • AudioManager
  • Kucing
  • Anjing
  • IAudioEmitter
  • Rencana
  • QuadDrawer
  • SpriteEntity

Kelas QuadDrawer

Kelas ini adalah kelas pembantu untuk menggambar poligon persegi panjang. Poligon persegi panjang sering digunakan terutama untuk menampilkan sprite 3D (papan reklame). Tips ini digunakan untuk papan reklame untuk kucing dan anjing, serta untuk tampilan persegi panjang tanah.

Kelas ini tidak ada hubungannya dengan Audio3D.

kebun

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

Informasi minimum yang diperlukan untuk menggambar poligon. Gambar poligon dari data simpul tanpa menyiapkan data model.

pembangun

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

Karena kelas ini dapat digunakan secara universal, Anda GraphicsDevice menerima instans yang dibuat dalam proses inisialisasi kelas game.

Di sini, kami mengatur efek yang diperlukan untuk menggambar sprite dan posisi 4 simpul yang diperlukan untuk poligon persegi panjang. Ukurannya harus diskalakan saat menggambar, jadi -1 hingga 1.

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

Mengatur tekstur yang diteruskan ke persegi panjang dan menggambarnya. Karena ini adalah proses dasar, tidak ada yang perlu diperhatikan.

Jika ada satu titik, variabel yang dipanggil dapat ditentukan dalam textureRepeats argumen, sehingga koordinat tekstur dapat diubah. Jika vertexPositionTexture.TextureCoordinate diatur di luar kisaran 0 hingga 1, teksturnya digambar berulang kali secara default. Dengan menggunakannya, dimungkinkan untuk mengekspresikan bahwa ubin dapat diatur berulang kali. Bahkan, pola kotak-kotak di tanah tampaknya memiliki beberapa gambar hitam dan putih sederhana berbaris.

Antarmuka IAudioEmitter

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

Didefinisikan sebagai entitas yang memancarkan suara. Karena entitas yang memancarkan suara kali ini adalah anjing dan kucing, mereka diwarisi di kelas masing-masing. Emitor membutuhkan informasi berikut:

Penggunaan properti
Posisi Posisi entitas
Maju Arah yang dihadapi entitas (vektor)
Ke atas atas entitas. Arah di mana kepala ada dalam diri seseorang
Kecepatan Kecepatan di mana entitas bergerak. Digunakan untuk menghitung nilai Doppler.

SpriteEntity class

Kelas untuk mewakili sprite 3D (papan reklame). Ini juga merupakan penghasil emisi, dan mewarisi IAudioEmitter . Anjing dan kucing mewarisi kelas ini.

harta benda

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

Ini memiliki informasi lokasi entitas, dll. Pada dasarnya saya memiliki informasi sebagai emitor. Ini juga merupakan entitas gambar, sehingga juga memiliki gambar (Tekstur) untuk ditampilkan.

Metode pembaruan

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

Menjelaskan posisi entitas dan proses bermain suara, tetapi menerapkannya di kelas turunan.

Metode menggambar

/// <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 memiliki gambar dan posisi yang berbeda, tetapi mekanisme menggambar lainnya persis sama, jadi saya menggambarkannya di sini.

Setiap transformasi dan tekstur diteruskan ke QuadDrawer untuk menampilkan poligon persegi panjang. Karena transformasi koordinat adalah pengetahuan dasar, penjelasan tidak tercakup.

Matrix.CreateConstrainedBillboard Gunakan informasi metode dan kamera untuk dengan mudah mewakili papan reklame, jadi manfaatkan secara efektif.

Kelas anjing

Ini adalah kelas yang melakukan pemutaran gambar dan nyanyian anjing. SpriteEntity Mewarisi kelas. Anjing tetap dalam posisi tetap di ruang 3D.

kebun

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

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

_timeDelay Digunakan pada interval untuk memainkan tangisan.

SoundEffectInstance _activeSound Adalah contoh untuk memainkan suara. SoundEffectInstance Seperti yang sering Anda lihat di kelas pemutaran suara, Anda dapat menggunakan ini seperti dalam suara 3D. Ngomong-ngomong _activeSound , saya hanya menerima referensi dari kelas AudioManager nanti, jadi saya tidak akan membuangnya di sisi kelas Dot.

Metode pembaruan

/// <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 tetap berada di posisi yang sama untuk waktu yang lama, sehingga empat parameter pertama ditentukan oleh serangan yang menentukan. Menggambar dilakukan di kelas dasar.

_timeDelay kemudian digunakan sebagai waktu yang tersisa untuk berhenti, bermain menangis.

AudioManager akan direproduksiPlay3DSound dalam ruang 3D dengan meneruskan nama teriakan, informasi loop, dan informasi Anda sendiri sebagai penghasil emisi ke .

Kelas Anjing dirancang untuk memutar teriakan, jadi Anda tidak perlu berpikir terlalu dalam karena itu hanya penyesuaian, seperti bermain atau berhenti bercabang.

Kelas kucing

Ini adalah kelas yang melakukan menggambar kucing dan pemutaran bernyanyi. SpriteEntity Mewarisi kelas. Kucing bergerak di sekitar asalnya.

kebun

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

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

_timeDelay digunakan dengan cara yang sama seperti kelas Anjing, dengan sisa waktu sebelum jeritan berikutnya.

Ada tiga jenis panggilan kucing yang dipilih dan diputar secara acak, tetapi tidak ada hubungannya dengan Audio3D karena mereka adalah elemen bonus.

Metode pembaruan

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

Karena kucing berbalik asal dalam gerakan melingkar, ia memprosesnya sehingga menggerakkan lintasan lingkaran menggunakan fungsi trigonometri. Akibatnya, posisi, maju, dan Kecepatan telah berubah satu demi satu. Oleh karena itu, ketika Anda menjalankan program ini, Anda dapat melihat bahwa tangisan kucing berputar-putar bahkan jika Anda tidak menggerakkan kamera.

_timeDelay Kode berikut adalah proses di mana tangisan kucing ditugaskan dan dimainkan secara acak secara berkala. audioManager.Play3DSound Saya menyerahkan diri pada metode yang merupakan emitor. Anda dapat melihat bahwa posisi pemutaran suara berubah satu per satu.

Kelas AudioManager

Ini akhirnya menjadi inti dari Audio3D. Suara 3D terdiri dari pendengar dan emitor. Emitor sudah mendefinisikan anjing dan kucing, sehingga kelas AudioManager mendefinisikan pendengar. Biasanya ada beberapa emitor, sementara hanya ada satu pendengar.

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

Kelas ActiveSound

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

Ini didefinisikan di bagian bawah kode, tetapi ini adalah kelas untuk mempertahankan keadaan yang sedang dimainkan. Ini memiliki informasi tentang contoh suara yang dimainkan dan emitor sedang dimainkan.

kebun

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
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 menentukan nama file audio (nama aset) untuk dimuat. Karena ini adalah program sampel, itu didefinisikan untuk memuat file audio secara massal terlebih dahulu. Data _soundEffects suara yang diimpor disimpan dalam .

AudioListener Listener Adalah definisi dari pendengar. Informasi pendengar didefinisikan karena public diatur dari kelas Game.

AudioEmitter _emitter adalah definisi emitor saat menerapkan 3D ke pemutaran suara. Dalam sampel ini, saat memutar suara, nilai setiap objek emitor diatur ke _emitter , jadi ini adalah bentuk yang membagikan satu sebagai contoh. Tentu saja, Anda dapat memiliki untuk setiap AudioEmitter objek.

_activeSounds memiliki informasi tentang suara yang Anda mainkan di kelas yang Anda tentukan ActiveSound sebelumnya. Karena informasi emitor berubah sepanjang waktu, itu digunakan untuk memperbarui informasi lokasi, dll., Dan untuk membuang instans suara yang telah selesai diputar.

pembangun

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

GameComponent Karena mewarisi kelas, ia Game menerima instance dan meneruskannya ke kelas dasar.

Metode inisialisasi

/// <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 Karena mewarisi kelas, secara otomatis dipanggil pada inisialisasi.

SoundEffect.DistanceScale dan SoundEffect.DopplerScale merupakan parameter yang didedikasikan untuk static suara 3D.

SoundEffect.DistanceScale untuk mendengar suara bahkan di kejauhan.

SoundEffect.DopplerScale Adalah pengaruh dari efek Doppler. Semakin tinggi jumlahnya, semakin besar efek Doppler.

_soundNames Simpan nama aset yang ditentukan dalam loop untuk memuat _soundEffects .

Metode buang

/// <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 Ini secara otomatis disebut di akhir permainan karena mewarisi kelas.

Menghancurkan semua aset suara yang diimpor.

Metode pembaruan

/// <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 Karena mewarisi kelas, secara otomatis dipanggil dalam proses pembaruan.

Periksa suara yang sedang diputar dan, jika Apply3D sedang diputar, panggil metode untuk memperbarui lokasi suara agar sesuai dengan emitor. Jika ada suara yang selesai diputar, instans akan dibuang.

Metode 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 memutar suara 3D dari luar. Ini memiliki nama suara untuk dimainkan, apakah untuk loop, dan informasi emitor.

Jika Anda ingin memutar suara, buat instans suara baru dari sumber daya suara yang Anda simpan di _soundEffects. SoundEffectInstance dapat juga memiliki loop atau tidak, jadi atur informasi loop.

Karena saya membuat sebagai ActiveSound status bermain, saya mengatur informasi emitor yang merupakan objek pemutaran suara di sana. Jika Anda ingin menerapkan informasi lokasi 3D ke suara, Anda akan Emitter mendapatkan nilai yang Anda butuhkan.

Apply3D Metode ini dijelaskan di bawah ini, tetapi ini adalah proses penerapan informasi 3D dari emitor ke suara.

Proses dasar suara 3D adalah memulai pemutaran dengan SoundEffectInstance.Play kumpulan informasi 3D. Sisanya adalah memperbarui informasi 3D secara berkala sampai suara selesai diputar.

Terapkan metode3D

/// <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 menerapkan efek 3D ke instans suara tertentu menggunakan pendengar dan emitor.

Kami menetapkan nilai ke _emitter instans umum setiap kali. Jika Anda SoundEffectInstance.Apply3D sudah AudioEmitter memilikinya, cukup berikan pendengar dan penghasil emisi ke metode.

Kelas Audio3DGame

Akhirnya, mari kita lihat tentang apa kelas Game.

kebun

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 Mendaftar di , tetapi memilikinya sebagai bidang karena mengaksesnya secara individual.

Yang lain mendefinisikan entitas anjing, kucing, tekstur untuk gambar tanah, quaddrawer, informasi kamera, dan informasi input.

pembangun

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 Hasilkan dan GameComponents daftar dengan .

Anda juga telah menghasilkan kelas Kucing dan Anjing.

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

Muat tekstur yang diperlukan untuk setiap gambar.

QuadDrawer dihasilkan GraphicsDevice di sini karena membutuhkan.

Metode pembaruan

/// <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 Metode dan UpdateCamera metode adalah proses pengambilan input perangkat dan penanganan kamera, yang akan dibahas nanti.

Karena posisi kamera dan posisi pendengar hampir selalu sama, pendengar diatur ke nilai yang sama setelah proses pembaruan kamera.

_cat Panggil metode dan _dog Perbarui untuk bergerak dan menjerit, masing-masing.

Metode menggambar

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

Menghasilkan transformasi tampilan dan proyeksi dari informasi kamera.

Untuk tanah, persegi panjang yang digambar di QuadDrawer ditampilkan secara vertikal, sehingga diputar menjadi horizontal dan diperbesar secara moderat. _quadDrawer.DrawQuad Argumen kedua dari metode ini menentukan jumlah pengulangan tekstur, yang menentukan bahwa 32x32 lembar muncul berdampingan.

_cat dan _dog di-billboard secara internal, sehingga informasi lokasi kamera diteruskan dan digambar.

Metode HandleInput

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

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

Informasi tentang keyboard dan gamepad diperoleh, dan akhir permainan ditentukan.

UpdateCamera method

/// <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 dikendalikan untuk bergerak.

Ini juga mencakup perhitungan seperti akselerasi dan gesekan, tapi saya pikir itu mungkin dengan mempertimbangkan efek Doppler pada suara 3D. Karena saya tidak melakukan sesuatu yang istimewa tentang perhitungan itu sendiri, saya tidak akan membahasnya.

Ringkasan

Saya telah membahasnya panjang lebar, tetapi biasanya cukup untuk merujuk ke kelas untuk AudioManager cara memutar suara 3D. Bahkan, sebagian besar kesulitan dilakukan oleh kerangka kerja, karena apa yang SoundEffectInstance kita lakukan adalah menetapkan informasi 3D ke dalam . Jika Anda hanya mencicipi bagian-bagian suara 3D, Anda akan menulis sedikit pekerjaan di kelas Game. Namun, saya menulis Tips berdasarkan cerita aslinya, jadi sudah jauh lebih lama.