Ses3D

Sayfa güncel :
Sayfa oluşturma tarihi :

Bu İpuçları hakkında notlar

Bu örnek, aşağıdaki sitelerde yayımlanan programları temel almaktadır. Japonca'da anlaşılmasını ve açıklanmasını kolaylaştırmak için kodu biraz değiştiriyorum. Temel olarak, orijinal kodu olduğu gibi kullanırız, bu nedenle oyun programınızda gerçekten benimsiyorsanız, zamanında düzeltin ve kullanın.

Başvuru sitesi

Ek olarak, MonoGame ve XNA hakkında bazı temel bilgilere sahip olduğunuz varsayımıyla açıklanmaktadır. Dümen için MonoGame İpuçları ve XNA İpuçları'na bakın.

Özellikle, matematik, vektörler, trigonometrik fonksiyonlar, matrisler vb.

çevre

peron
  • Windows 10
  • Kod diğer MonoGame özellikli platformlarda kullanılabilir
Visual Studio
  • Visual Studio 2019
.NET Çekirdek
  • 3.1
MonoGame
  • 3.8

Örnekler hakkında

Kameranın konumuna bağlı olarak, çağrıyı köpeğin konumundan ve kedinin konumundan duyabilirsiniz. Kedi menşei daire içine alır ve daire içine alır ve oynatıcı kameranın konumunu anahtarla kontrol edebilir. Kameranın veya kedinin konumundaki her değişiklikte sesi duyma şeklinizi değiştirdiğinden emin olun. Kulaklık kullanarak anlamanın kolay olduğunu düşünüyorum. Kanal 5.1'de onaylanmadım.

Nasıl çalıştırılır

?
Klavye Gamepad (XInput) Mouse Touch Nasıl Yapılır
Kamera ileri ve geri ↑↓ Sol Çubuk (Üst ve Alt) - -
Kamera yönünü değiştirme ←→ Sol Çubuk (Sol ve Sağ) - -
Oyun sonu Esc Geri - -

Ne hazırlanmalı

  • Köpek öterek ses dosyası
  • Üç kedi gıcırtı ses dosyaları
  • Köpek Resim Dosyası
  • Kedi resim dosyası
  • Zemin görüntü dosyası

Resmi sitedeki orijinal örnek önceden oluşturulmuş içerik .xnb dosyalarını kullanır. Örnekleri resmi sitede tutmak istiyorsanız, mgcb kullanmayın, doğrudan Visual Studio çözümünüze ekleyin ve oluşturma zamanında kopyalayın.

Orijinal örneğin neden bu yöntemi aldığını bilmiyorum, ama sanırım projenin eski sürümlerini olduğu gibi geçiriyoruz. Tabii ki, aynı dosyayı oluşturmak için mgcb kullanabilirsiniz. Bu siteye indirilebilecek projeler MGCB sürümüne değiştirilmiştir.

program

Tüm kod için programı indirin.

Bu örnek, ses çalmanın çalışma zamanında iyi görünmesini sağlarken kodun biraz daha genel görünmesini sağlamak için tasarlanmıştır. Audio3D kullanmak sadece çok daha az kod olurdu, ancak orijinal örnek boyunca açıklanmıştır. Daha az önemli kısımları saklayacağım.

Proje aşağıdaki kod dosyalarından oluşur:

  • Audio3DGame
  • AudioManager
  • Kedi
  • Köpek
  • IAudioEmitter
  • Program
  • QuadDrawer
  • SpriteEntity

QuadDrawer sınıfı

Bu sınıf dikdörtgen çokgenler çizmek için bir yardımcı sınıftır. Dikdörtgen çokgenler genellikle öncelikle 3D spritleri (reklam panoları) görüntülemek için kullanılır. Bu ipuçları, kediler ve köpekler için reklam panolarının yanı sıra zeminin dikdörtgen ekranları için kullanılır.

Bu sınıfın Audio3D ile hiçbir ilgisi yoktur.

alan

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

Çokgen çizmek için gereken minimum bilgi. Model verilerini hazırlamadan köşe verilerinden çokgenler çizin.

Oluşturucu

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

Bu sınıf evrensel olarak kullanılabilir olduğundan, oyun sınıfı başlatma işleminde oluşturulan bir örnek alırsınız GraphicsDevice .

Burada, dikdörtgen çokgen için gereken 4 köşenin sprite ve konumunu çizmek için gerekli etkiyi ayarlıyoruz. Çizim sırasında boyut ölçeklendirilmelidir, bu nedenle -1'e 1'dir.

DrawQuad yöntemi

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

Geçirilen dokuyu bir dikdörtgene ayarlar ve çizer. Bu temel bir süreç olduğundan, dikkat edilecek bir şey yoktur.

Bir nokta varsa, bağımsız değişkende textureRepeats çağrılan bir değişken belirtilebilir, böylece dokunun koordinatları değiştirilebilir. vertexPositionTexture.TextureCoordinate 0 ile 1 aralığının ötesine ayarlanırsa, doku varsayılan olarak art arda çizilir. Bunu kullanarak, fayansların tekrar tekrar düzenlenebileceğini ifade etmek mümkündür. Aslında, yerdeki damalı desenlerde birden fazla basit siyah beyaz görüntü sıralanmış gibi görünüyor.

IAudioEmitter arabirimi

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

Ses yayan bir varlık olarak tanımlanır. Bu kez ses yayan varlıklar köpekler ve kediler olduğundan, kendi sınıflarında mirastırlar. Yayıcının aşağıdaki bilgilere ihtiyacı vardır:

Özellik kullanımı
Konum Varlık konumu
İletmek Varlığın karşı karşıya olduğu yön (vektör)
Yukarı Varlığın yukarısı. Bir kişide başın var olduğu yön
Hız Varlığın hareket hızı. Doppler değerlerini hesaplamak için kullanılır.

SpriteEntity sınıfı

3D sprites (reklam panoları) temsil etmek için bir sınıf. Aynı zamanda bir yayıcıdır ve devralır IAudioEmitter . Köpekler ve kediler bu sınıfa miras kalıyor.

mülk

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

Varlığın konum bilgilerine vb. Temel olarak yayıcı olarak bilgim var. Aynı zamanda bir çizim objesi olduğundan, görüntülenecek bir görüntüye (Doku) da sahiptir.

Güncelleştirme yöntemi

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

Varlığın konumunu ve ses çalma işlemini açıklar, ancak türetilmiş sınıfta uygular.

Çizim yöntemi

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

Köpeklerin ve kedilerin farklı görüntüleri ve konumları vardır, ancak diğer çizim mekanizması tamamen aynıdır, bu yüzden onları burada tarif ediyorum.

Her dönüştürme ve doku, dikdörtgen bir çokgen görüntülemek için QuadDrawer'a geçirilir. Koordinat dönüşümü temel bilgi olduğundan, açıklamalar ele alınmaz.

Matrix.CreateConstrainedBillboard Reklam panolarını kolayca temsil etmek için yöntem ve kamera bilgilerini kullanın, bu nedenle bunları etkili bir şekilde kullanın.

Köpek sınıfı

Köpek çizimi ve şarkı çalma yapan bir sınıftır. SpriteEntity Sınıfı devralıyor. Köpek 3D alanda sabit bir konumda kalır.

alan

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

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

_timeDelay ağlatmak için aralıklarla kullanılır.

SoundEffectInstance _activeSound ses çalmak için bir örnektir. SoundEffectInstance Ses çalma sınıfında sık sık göreceğiniz gibi, bunu 3D seste olduğu gibi kullanabilirsiniz. Bu arada _activeSound , daha sonra yalnızca AudioManager sınıfından bir referans alıyorum, bu yüzden Dot sınıf tarafında atmayacağım.

Güncelleştirme yöntemi

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

Bu örnekte, köpek uzun süre aynı pozisyonda kalır, bu nedenle ilk dört parametre kararlı bir vuruşla belirtilir. Çizim temel sınıfta yapılır.

_timeDelay daha sonra ağlamayı çalarak durmak için kalan süre olarak kullanılır.

AudioManager çığlığınınPlay3DSound adını, döngü bilgilerini ve kendi bilgilerinizi yayıcı olarak ileterek 3D alanda çoğaltılacaktır.

Dog sınıfı ağlamayı döngüye almak için tasarlanmıştır, bu nedenle çok derin düşünmenize gerek yoktur, çünkü bu sadece dallanmayı oynamak veya durdurmak gibi bir ayarlamadır.

Kedi sınıfı

Kedi çizimi ve şarkı çalma yapan bir sınıftır. SpriteEntity Sınıfı devralıyor. Kedi kökenin etrafında hareket ediyor.

alan

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

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

_timeDelay Bir sonraki cıyaklamadan önceki zamanın geri kalanıyla birlikte Dog sınıfıyla aynı şekilde kullanılır.

Rastgele seçilen ve oynatılan üç tür kedi çağrısı vardır, ancak bir bonus öğe oldukları için Audio3D ile hiçbir ilgisi yoktur.

Güncelleştirme yöntemi

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

Kedi dairesel bir hareketle kökenin etrafında döndüğünden, trigonometrik bir işlev kullanarak dairenin yörüngesini hareket ettirebilecek şekilde işler. Sonuç olarak, konum, ileri ve Hız birbiri ardına değişti. Bu nedenle, programı çalıştırdığınızda, kamerayı hareket ettirmeseniz bile kedinin çığlığının etrafında döndüğünü görebilirsiniz.

_timeDelay Aşağıdaki kod, kedinin ağlamasının rastgele atandığı ve düzenli aralıklarla oynandığı bir işlemdir. audioManager.Play3DSound Kendimi yayıcı olan yönteme aktarıyorum. Sesin çalma konumunun birer birer değiştiğini görebilirsiniz.

AudioManager sınıfı

Bu nihayet Audio3D'nin özüdür. 3D ses bir dinleyici ve bir yayıcıdan oluşur. Yayıcı zaten köpekleri ve kedileri tanımlar, bu nedenle AudioManager sınıfı dinleyicileri tanımlar. Genellikle birden çok yayıcı vardır, ancak yalnızca bir dinleyici vardır.

Bu sınıf devralır veGame kaydeder GameComponent ve sınıftaki bileşeni kullanır.

ActiveSound sınıfı

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

Kodun altında tanımlanır, ancak oynatan durumu korumak için bir sınıftır. Çalınan ses örneği ve oynatılmakta olan yayıcı hakkında bilgiye sahiptir.

alan

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
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 yüklenecek ses dosyasının adını (varlık adı) tanımlar. Örnek bir program olduğundan, önce ses dosyasını toplu olarak yüklemek tanımlanır. alınan ses verileri 'de depolanır _soundEffects .

AudioListener Listener dinleyicinin tanımıdır. Game sınıfından ayarlandığı için public dinleyici bilgileri tanımlanır.

AudioEmitter _emitter ses çalma için 3D uygularken yayıcının tanımıdır. Bu örnekte, bir ses çalarken, her yayıcı nesnesinin değeri olarak ayarlanır _emitter , bu nedenle bir örneği örnek olarak paylaşan bir formdur. Tabii ki, her AudioEmitter nesne için sahip olabilirsiniz.

_activeSounds daha önce tanımladığınız ActiveSound sınıfta çaldığınız ses hakkında bilgiye sahiptir. Yayıcı bilgileri her zaman değiştiğinden, konum bilgilerini vb. güncelleştirmek ve çalmayı bitiren ses örneklerini atmak için kullanılır.

Oluşturucu

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

GameComponent Sınıfı devraldığından, Game örneği alır ve temel sınıfa geçirir.

Yöntemi başlat

/// <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 Sınıfı devraldığı için, başlatma sırasında otomatik olarak çağrılır.

SoundEffect.DistanceScale ve SoundEffect.DopplerScale 3D sese static adanmış parametrelerdir.

SoundEffect.DistanceScale uzaktan bile ses duymak için.

SoundEffect.DopplerScale Doppler etkisinin etkisidir. Sayı ne kadar yüksek olursa, Doppler etkisi de o kadar büyük olacaktır.

_soundNames 'de tanımlanan varlık adını yüklenmiş _soundEffects bir döngüye kaydedin.

Dispose yöntemi

/// <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 Sınıfı devraldığı için oyunun sonunda otomatik olarak çağrılır.

Tüm ithal ses varlıklarını yok etme.

Güncelleştirme yöntemi

/// <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 Sınıfı devraldığı için, güncelleştirme işleminde otomatik olarak çağrılır.

Şu anda çalan sesi kontrol edin ve çalıyorsa Apply3D , sesin konumunu yayıcıyla eşleşecek şekilde güncelleştirme yöntemini çağırın. Herhangi bir ses çalmayı bitirdiyse, örnek atılır.

Play3DSound yöntemi

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

Dışarıdan 3D ses çalma işlemi. Çalacak sesin, döngünün yapılıp yapılmayacağının ve yayıcı bilgilerinin adına sahiptir.

Ses çalmak istiyorsanız, _soundEffects tuttuğunuz ses kaynağından yeni bir ses örneği oluşturun. SoundEffectInstance bir döngüye sahip olabilir veya olmayabilir, bu nedenle döngü bilgilerini ayarlayın.

Çalma durumu olarak ActiveSound oluşturduğum için, yayıcı bilgilerini oradaki sesin kayıttan yürütme nesnesi olan ayarlıyorum. Bir sese 3D konum bilgileri uygulamak istiyorsanız, ihtiyacınız olan değeri alırsınız Emitter .

Apply3D Yöntem aşağıda açıklanmıştır, ancak yayıcıdan sese 3D bilgi uygulama işlemidir.

3D sesin temel işlemi, 3D bilgi kümesiyle SoundEffectInstance.Play oynatmaya başlamaktır. Gerisi, ses çalmayı bitirene kadar 3D bilgileri periyodik olarak güncellemektir.

Apply3D yöntemi

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

Dinleyicileri ve yayıcıları kullanarak belirli bir ses örneğine 3D efekti uygulama işlemi.

Değeri her seferinde ortak örneğin _emitter olarak ayarladık. SoundEffectInstance.Apply3D Zaten AudioEmitter varsa, dinleyiciyi ve yayıcıyı yönteme geçirmeniz yeterlidir.

Audio3DGame Sınıfı

Son olarak, Oyun sınıfının ne hakkında olduğuna bakalım.

alan

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 'de kaydolur GameComponents , ancak tek tek eriştüğü için alan olarak vardır.

Diğerleri köpek, kedi varlıkları, zemin çizimi için dokular, dört çizer, kamera bilgileri ve giriş bilgilerini tanımlar.

Oluşturucu

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 'ye oluşturun ve GameComponents kaydolun.

Ayrıca Kedi ve Köpek sınıflarını da oluşturmuşsunuz.

LoadContent yöntemi

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

Her çizim için gereken dokuları yükleyin.

QuadDrawer , GraphicsDevice gerektirdiğinden burada oluşturulur.

Güncelleştirme yöntemi

/// <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 Yöntem ve UpdateCamera yöntemler, cihaz girişini alma ve kamerayı işleme sürecidir ve daha sonra tartışılacaktır.

Kamera konumu ve dinleyici konumu hemen hemen her zaman aynı olduğundan, dinleyici kamera güncelleme işleminden sonra aynı değere ayarlanır.

_cat Sırasıyla taşımak ve _dog ötmek için ve Update yöntemlerini çağırın.

Çizim yöntemi

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

Kamera bilgilerinden görünüm ve projeksiyon dönüşümleri oluşturur.

Zemin için, QuadDrawer'da çizilen dikdörtgen dikey olarak görüntülenir, böylece yatay olacak şekilde döndürülür ve orta derecede büyütülür. _quadDrawer.DrawQuad Yöntemin ikinci bağımsız değişkeni, 32x32 sayfanın yan yana göründüğünü belirten dokunun yineleme sayısını belirtir.

_cat ve _dog dahili olarak reklam panosuna konur, böylece kameranın konum bilgileri geçirilir ve çizilir.

HandleInput yöntemi

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

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

Klavye ve gamepad ile ilgili bilgiler edinilir ve oyunun sonu belirlenir.

UpdateCamera yöntemi

/// <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 hareket etmek için kontrol edilir.

Ayrıca hızlanma ve sürtünme gibi hesaplamaları da içerir, ancak muhtemelen 3D ses üzerindeki Doppler etkisini dikkate aldığını düşünüyorum. Hesaplamanın kendisi hakkında özel bir şey yapmadığım için, bunu tartışmam.

Özet

Uzun uzadıya kapladım, ancak genellikle 3D sesin nasıl çalınabileceğine AudioManager dair sınıfa başvurmak yeterlidir. Aslında, zorlukların çoğu çerçeve tarafından yapılır, çünkü yaptığımız şey SoundEffectInstance 3D bilgileri . Yalnızca 3D sesin parçalarını örneklemişseniz, Oyun sınıfında küçük bir çalışma yazarsınız. Ancak, orijinal hikayeye dayanarak İpuçları yazıyorum, bu yüzden çok daha uzun zaman oldu.