الصوت3D

تحديث الصفحة :
تاريخ إنشاء الصفحة :

ملاحظات حول هذه النصائح

يعتمد هذا النموذج على البرامج المنشورة على المواقع التالية. أقوم بتغيير الرمز قليلا لتسهيل فهمه وشرحه باللغة اليابانية. في الأساس ، نستخدم الرمز الأصلي كما هو ، لذلك إذا كنت تعتمده بالفعل في برنامج اللعبة الخاص بك ، فقم بتصحيحه في الوقت المناسب واستخدامه.

الموقع المرجعي

بالإضافة إلى ذلك ، يتم شرحه على افتراض أن لديك بعض المعرفة الأساسية حول MonoGame و XNA. راجع نصائح MonoGame ونصائح XNA للروضة.

على وجه الخصوص ، بما أن الرياضيات والمتجهات والدوال المثلثية والمصفوفات وما إلى ذلك ضرورية ، لذا يرجى معرفة ما هي إلى حد ما.

وسط

رصيف
  • ويندوز ١٠
  • يمكن استخدام التعليمات البرمجية على الأنظمة الأساسية الأخرى التي تدعم MonoGame
استوديو مرئي
  • فيسوال ستوديو 2019
.NET Core
  • 3.1
لعبة أحادية
  • 3.8

حول العينات

بناء على موضع الكاميرا ، يمكنك سماع المكالمة من موقع الكلب وموضع القطة. تدور القطة حول الأصل وتدور حوله ، ويمكن للاعب التحكم في موضع الكاميرا باستخدام المفتاح. تأكد من أن الطريقة التي تسمع بها الصوت تتغير مع كل تغيير في موضع الكاميرا أو موضع القطة. أعتقد أنه من السهل فهم استخدام سماعات الأذن. أنا غير مؤكد على القناة 5.1.

كيفية العمل

ماذا تفعل لوحة المفاتيح لوحة الألعاب (XInput) الماوس باللمس
الكاميرا للأمام والخلف ↑↓ العصا اليسرى (أعلى وأسفل) - -
تغيير اتجاه الكاميرا ←→ العصا اليسرى (اليسار واليمين) - -
نهاية اللعبة إسك ظهر - -

ما يجب تحضيره

  • الكلب صرير ملف صوتي
  • ثلاثة ملفات صوتية لصراخ القطط
  • ملف صورة الكلب
  • ملف صورة القط
  • ملف صورة أرضية

يستخدم النموذج الأصلي على الموقع الرسمي ملفات . xnb للمحتوى الذي تم إنشاؤه مسبقا. إذا كنت ترغب في الاحتفاظ بالعينات على الموقع الرسمي ، فلا تستخدم mgcbs ، وأضفها مباشرة إلى حل Visual Studio الخاص بك ، وانسخها في وقت الإنشاء.

لا أعرف لماذا تأخذ العينة الأصلية هذه الطريقة ، لكنني أعتقد أنه ربما لأننا نقوم بترحيل الإصدارات القديمة من المشروع كما هي. بالطبع ، يمكنك استخدام mgcb لإنشاء نفس الملف. تم تعديل المشاريع التي يمكن تنزيلها على هذا الموقع إلى إصدار MGCB.

برنامج

قم بتنزيل البرنامج لجميع التعليمات البرمجية.

تم تصميم هذه العينة لجعل التعليمات البرمجية تبدو أكثر عمومية قليلا مع جعل تشغيل الصوت يبدو جيدا في وقت التشغيل. استخدام Audio3D فقط سيكون رمزا أقل بكثير ، ولكن يتم وصفه على طول العينة الأصلية. سأحتفظ بالأجزاء الأقل أهمية.

يتكون المشروع من ملفات التعليمات البرمجية التالية:

  • الصوت3DGame
  • أوديو مانجر
  • قط
  • هالباعث
  • برنامج
  • رباعي الأدراج
  • سبريت كيان

فئة الأدراج الرباعية

هذه الفئة هي فئة مساعدة لرسم المضلعات المستطيلة. غالبا ما تستخدم المضلعات المستطيلة في المقام الأول لعرض العفاريت 3D (اللوحات الإعلانية). تستخدم هذه النصائح في اللوحات الإعلانية للقطط والكلاب ، وكذلك للعروض المستطيلة للأرض.

هذا الفصل لا علاقة له ب Audio3D.

ميدان

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

الحد الأدنى من المعلومات المطلوبة لرسم المضلعات. ارسم مضلعات من بيانات القمة دون إعداد بيانات النموذج.

منشئ

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

  _effect = new AlphaTestEffect(device);

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

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

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

نظرا لأن هذه الفئة قابلة للاستخدام عالميا، تتلقى GraphicsDevice مثيلا تم إنشاؤه في عملية تهيئة فئة اللعبة.

هنا ، قمنا بتعيين التأثير اللازم لرسم العفريت وموضع الرؤوس ال 4 المطلوبة للمضلع المستطيل. يجب تغيير الحجم عند الرسم ، لذلك فهو -1 إلى 1.

طريقة درو كواد

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

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

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

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

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

يضبط النسيج الذي تم تمريره على مستطيل ويرسمه. نظرا لأن هذه عملية أساسية ، فلا يوجد شيء يمكن ملاحظته.

إذا كانت هناك نقطة واحدة ، فيمكن تحديد متغير يسمى في textureRepeats الوسيطة ، بحيث يمكن تغيير إحداثيات النسيج. إذا تم تعيين vertexPositionTexture.TextureCoordinate خارج النطاق من 0 إلى 1، رسم النسيج بشكل متكرر بشكل افتراضي. باستخدامه ، من الممكن التعبير عن أنه يمكن ترتيب البلاط بشكل متكرر. في الواقع ، يبدو أن الأنماط المتقلبة على الأرض تحتوي على العديد من الصور البسيطة بالأبيض والأسود.

IAudioEmitter واجهة

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

يعرف بأنه كيان يصدر صوتا. نظرا لأن الكيانات التي تنبعث منها الأصوات هذه المرة هي وقطط ، فهي موروثة في فئاتها الخاصة. يحتاج الباعث إلى المعلومات التالية:

استخدام العقار
موضع موقف الكيان
أمامي الاتجاه الذي يواجهه الكيان (متجه)
فَوْق ما يصل الكيان. الاتجاه الذي يوجد فيه الرأس في الشخص
السرعه السرعة التي يتحرك بها الكيان. تستخدم لحساب قيم دوبلر.

فئة SpriteEntity

فئة لتمثيل العفاريت 3D (اللوحات الإعلانية). وهو أيضا باعث ، ويرث IAudioEmitter . الكلاب والقطط ترث هذه الفئة.

مال

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

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

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

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

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

يحتوي على معلومات موقع الكيان ، إلخ. أساسا لدي معلومات كباعث. إنه أيضا كيان رسم ، لذلك يحتوي أيضا على صورة (نسيج) لعرضها.

طريقة التحديث

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

يصف موقع الكيان وعملية تشغيل الصوت، ولكنه ينفذه في الفئة المشتقة.

طريقة الرسم

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

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

الكلاب والقطط لديها صور ومواقع مختلفة ، ولكن آلية الرسم الأخرى هي نفسها تماما ، لذلك أصفها هنا.

يتم تمرير كل تحويل وملمس إلى QuadDrawer لعرض مضلع مستطيل. نظرا لأن تحويل الإحداثيات هو معرفة أساسية ، فلا يتم تغطية التفسيرات.

Matrix.CreateConstrainedBillboard استخدم معلومات الأسلوب والكاميرا لتمثيل اللوحات الإعلانية بسهولة ، لذا استفد منها بفعالية.

فئة الكلاب

إنها فئة تقوم برسم الكلاب وتشغيل الغناء. SpriteEntity وراثة الفصل. يبقى الكلب في وضع ثابت في مساحة 3D.

ميدان

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

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

_timeDelay يستخدم على فترات للعب البكاء.

SoundEffectInstance _activeSound هو مثال لتشغيل صوت. SoundEffectInstance كما سترى غالبا في فئة تشغيل الصوت ، يمكنك استخدام هذا كما هو الحال في صوت 3D. بالمناسبة _activeSound ، أتلقى فقط مرجعا من فئة AudioManager لاحقا ، لذلك لن أتجاهله على جانب فئة Dot.

طريقة التحديث

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

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

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

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

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

في هذه العينة ، يبقى الكلب في نفس الموضع لفترة طويلة ، لذلك يتم تحديد المعلمات الأربعة الأولى بضربة حاسمة. يتم الرسم في الفصل الأساسي.

ثم يتم استخدام _timeDelay كوقت متبقي للتوقف ، ولعب البكاء.

AudioManager سيتم استنساخهاPlay3DSound في مساحة 3D عن طريق تمرير اسم البكاء ، ومعلومات الحلقة ، والمعلومات الخاصة بك كباعث ل .

تم تصميم فئة الكلب لتكرار البكاء ، لذلك لا يتعين عليك التفكير بعمق لأنه مجرد تعديل ، مثل اللعب أو إيقاف التفرع.

فئة القط

إنها فئة تقوم برسم القطط وتشغيل الغناء. SpriteEntity وراثة الفصل. القط يتحرك حول الأصل.

ميدان

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

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

_timeDelay يستخدم بنفس طريقة فئة الكلب ، مع بقية الوقت قبل الصراخ التالي.

هناك ثلاثة أنواع من مكالمات القطط التي يتم اختيارها وتشغيلها عشوائيا ، ولكن لا علاقة لها ب Audio3D لأنها عنصر مكافأة.

طريقة التحديث

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

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

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

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

  Up = Vector3.Up;

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

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

    audioManager.Play3DSound(soundName, false, this);

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

نظرا لأن القط يدور حول الأصل في حركة دائرية ، فإنه يعالجه بحيث يحرك مسار الدائرة باستخدام دالة مثلثية. ونتيجة لذلك، تغير الموضع والأمام والسرعة واحدة تلو الأخرى. لذلك ، عند تشغيل البرنامج ، يمكنك أن ترى أن صرخة القطة تدور حولها حتى لو لم تقم بتحريك الكاميرا.

_timeDelay الرمز التالي هو عملية يتم فيها تعيين صرخة القط بشكل عشوائي وتشغيلها على فترات منتظمة. audioManager.Play3DSound أنا أمرر إلى الطريقة التي هي باعث. يمكنك أن ترى أن موضع تشغيل الصوت يتغير واحدا تلو الآخر.

فئة أوديو مانجر

هذا هو أخيرا جوهر Audio3D. يتكون صوت 3D من مستمع وباعث. يحدد باعث بالفعل الكلاب والقطط ، لذلك تحدد فئة AudioManager المستمعين. عادة ما يكون هناك العديد من بواعث ، في حين أن هناك مستمعا واحدا فقط.

ترث هذه الفئة المكون في الفئة وتقومGame بتسجيله GameComponent واستخدامه.

فئة أكتيف ساوند

/// <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 فإنه يتلقى المثيل ويمرره إلى الفئة الأساسية.

تهيئة الأسلوب

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

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

  base.Initialize();
}

GameComponent نظرا لأنه يرث الفئة ، يتم استدعاؤها تلقائيا عند التهيئة.

SoundEffect.DistanceScale وهي SoundEffect.DopplerScale معلمات مخصصة لصوت static 3D.

SoundEffect.DistanceScale لسماع الصوت حتى في المسافة.

SoundEffect.DopplerScale هو تأثير تأثير دوبلر. كلما زاد العدد ، زاد تأثير دوبلر.

_soundNames احفظ اسم الأصل المعرف في حلقة لتحميله _soundEffects .

طريقة التخلص

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

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

GameComponent يتم استدعاؤه تلقائيا في نهاية اللعبة لأنه يرث الفصل.

تدمير جميع الأصول السليمة المستوردة.

طريقة التحديث

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

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

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

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

      index++;
    }
  }

  base.Update(gameTime);
}

GameComponent نظرا لأنه يرث الفئة ، يتم استدعاؤها تلقائيا في عملية التحديث.

تحقق من الصوت الذي يتم تشغيله حاليا ، وإذا كان Apply3D قيد التشغيل ، فاتصل بالطريقة لتحديث موقع الصوت لمطابقة الباعث. إذا انتهى تشغيل أي صوت، تجاهل المثيل.

طريقة Play3DSound

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

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

  activeSound.Emitter = emitter;

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

  activeSound.Instance.Play();

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

  return activeSound.Instance;
}

عملية لعب الصوت 3D من الخارج. يحتوي على اسم الصوت المطلوب تشغيله ، سواء لحلقة ، وإصدار المعلومات.

إذا كنت تريد تشغيل صوت، فقم بإنشاء مثيل صوت جديد من مورد الصوت الذي تحتفظ به في _soundEffects. SoundEffectInstance يمكن أن يكون لها أيضا حلقة أم لا ، لذا قم بتعيين معلومات الحلقة.

نظرا لأنني أقوم بإنشاء ActiveSound كحالة تشغيل ، أقوم بتعيين معلومات باعث تكون كائن تشغيل الصوت هناك. إذا كنت ترغب في تطبيق معلومات موقع 3D على صوت ، فستحصل Emitter على القيمة التي تحتاجها.

Apply3D يتم وصف الطريقة أدناه ، ولكنها عملية تطبيق معلومات 3D من الباعث إلى الصوت.

العملية الأساسية للصوت 3D هي بدء التشغيل مع SoundEffectInstance.Play مجموعة معلومات 3D. الباقي هو تحديث معلومات 3D بشكل دوري حتى يتم الانتهاء من تشغيل الصوت.

طريقة Apply3D

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

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

عملية تطبيق تأثير 3D على مثيل صوت محدد باستخدام المستمعين والباعثين.

نقوم بتعيين القيمة إلى _emitter المثيل المشترك في كل مرة. إذا كان لديك بالفعل 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();
}

AudioManager إنشاء والتسجيل GameComponents مع .

لقد قمت أيضا بإنشاء فصول القط والكلب.

طريقة تحميل المحتوى

/// <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 الأساليب للتحرك والصراخ ، على التوالي.

طريقة الرسم

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

  device.Clear(Color.CornflowerBlue);

  device.BlendState = BlendState.AlphaBlend;

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

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

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

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

  base.Draw(gameTime);
}

يولد تحويلات العرض والإسقاط من معلومات الكاميرا.

بالنسبة للأرض ، يتم عرض المستطيل المرسوم في QuadDrawer عموديا ، بحيث يتم تدويره ليكون أفقيا ومكبرا بشكل معتدل. _quadDrawer.DrawQuad تحدد الوسيطة الثانية للطريقة عدد مرات تكرار النسيج ، والتي تحدد أن أوراق 32x32 تظهر جنبا إلى جنب.

يتم وضع _cat _dog على لوحات إعلانية داخليا، بحيث يتم تمرير معلومات موقع الكاميرا ورسمها.

طريقة HandleInput

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

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

يتم الحصول على معلومات حول لوحة المفاتيح ولوحة الألعاب ، ويتم تحديد نهاية اللعبة.

طريقة UpdateCamera

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

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

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

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

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

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

  _cameraVelocity += _cameraForward * accel;

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

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

يتم التحكم في الكاميرا للتحرك.

ويشمل أيضا حسابات مثل التسارع والاحتكاك ، ولكن أعتقد أنه ربما يأخذ في الاعتبار تأثير دوبلر على الصوت 3D. بما أنني لا أفعل أي شيء خاص حول الحساب نفسه ، فلن أناقشه.

ملخص

لقد غطيتها مطولا ، ولكن عادة ما يكون ذلك كافيا للإشارة إلى الفصل الدراسي لكيفية AudioManager تشغيل صوت 3D. في الواقع ، يتم تنفيذ معظم الصعوبات بواسطة الإطار ، لأن ما SoundEffectInstance نقوم به هو تعيين معلومات 3D في . إذا كنت قد أخذت عينات فقط من أجزاء صوت 3D ، فسينتهي بك الأمر إلى كتابة القليل من العمل في فصل اللعبة. ومع ذلك ، أنا أكتب نصائح استنادا إلى القصة الأصلية ، لذلك كان أطول بكثير.