אודיו3D

עודכן דף :
תאריך יצירת דף :

הערות על עצות אלה

דוגמה זו מבוססת על התוכניות המתפרסמות באתרים הבאים. אני משנה את הקוד קצת כדי להקל על ההבנה ולהסביר את זה ביפנית. בעיקרון, אנו משתמשים בקוד המקורי כפי שהוא, כך שאם אתה באמת לאמץ אותו בתוכנית המשחק שלך, לתקן אותו בזמן ולהשתמש בו.

אתר הפניה

בנוסף, הוא מוסבר על ההנחה כי יש לך קצת ידע בסיסי על MonoGame ו XNA. ראה טיפים MonoGame וטיפים XNA עבור ruddly.

בפרט, כמו מתמטיקה, וקטורים, פונקציות טריגונומטריות, מטריצות, וכו 'הם חיוניים, אז בבקשה לדעת מה הם במידה מסוימת.

סביבה

פלטפורמה
  • Windows 10
  • ניתן להשתמש בקוד בפלטפורמות אחרות התומכות ב- MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
מונוג-משחק
  • 3.8

אודות דוגמאות

בהתבסס על מיקום המצלמה, אתה יכול לשמוע את השיחה ממיקום הכלב ואת המיקום של החתול. החתול מקיף ומקיף את המקור, והשחקן יכול לשלוט במיקום המצלמה עם המפתח. ודאו שהאופן שבו אתם שומעים את הצליל משתנה עם כל שינוי במיקום המצלמה או במיקום החתול. אני חושב שזה קל להבין באמצעות אוזניות. אני לא מאושר בערוץ 5.1.

כיצד לפעול

מה ניתן לעשות לוח המשחקים של לוח המשחקים של המקלדת (XInput) מגע עכבר
מצלמה קדימה ואחורה ↑↓ מקל שמאלי (עליון ותחתון) - -
שינוי כיוון המצלמה ←→ מקל שמאלי (שמאל וימין) - -
סוף המשחק Esc גב - -

מה להכין

  • קובץ שמע צווחני כלבים
  • שלושה קבצי שמע צווחניים של חתולים
  • קובץ תמונת כלב
  • קובץ תמונת חתול
  • קובץ תמונת קרקע

המדגם המקורי באתר הרשמי משתמש בקבצי .xnb של תוכן שנבנה מראש. אם ברצונך לשמור את הדגימות באתר הרשמי, אל תשתמש ב- mgcbs, הוסף אותן ישירות לפתרון Visual Studio שלך והעתק אותן בזמן הבנייה.

אני לא יודע למה המדגם המקורי לוקח את השיטה הזאת, אבל אני חושב שזה כנראה בגלל שאנחנו מעבירים גרסאות ישנות יותר של הפרויקט כפי שהוא. כמובן, אתה יכול להשתמש mgcb כדי ליצור את אותו קובץ. הפרוייקטים שניתן להוריד באתר זה שונו לגירסת MGCB.

תוכנית

הורד את התוכנית עבור כל הקוד.

דוגמה זו נועדה לגרום לקוד להיראות קצת יותר כללי תוך הפיכת השמעת הצליל להיראות טוב בזמן ריצה. שימוש ב- Audio3D בלבד יהיה הרבה פחות קוד, אך הוא מתואר לאורך המדגם המקורי. אני אשמור את החלקים הפחות חשובים.

הפרוייקט מורכב מקבצי הקוד הבאים:

  • Audio3DGame
  • AudioManager
  • חתול
  • כלב
  • IAudioEmitter
  • תוכנית
  • QuadDrawer
  • SpriteEntity

כיתת QuadDrawer

מחלקה זו היא מחלקה מסייעת לציור מצולעים מלבניים. מצולעים מלבניים משמשים לעתים קרובות בעיקר להצגת גבעולים תלת-ממדיים (שלטי חוצות). טיפים אלה משמשים עבור שלטי חוצות עבור חתולים וכלבים, כמו גם עבור תצוגות מלבניות של הקרקע.

לכיתה זו אין שום קשר ל- 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.

שיטת 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);
}

הגדרת המרקם שהועבר למלבן וציורו. מכיוון שמדובר בתהליך בסיסי, אין מה לציין.

אם קיימת נקודה אחת, ניתן לציין משתנה שנקרא בארגומנט textureRepeats , כך שניתן יהיה לשנות את הקואורדינטות של המרקם. אם קודקודPositionTexture.TextureCoordinate מוגדר מעבר לטווח 0 עד 1, המרקם נמשך שוב ושוב כברירת מחדל. באמצעותו, ניתן לבטא כי אריחים ניתן לסדר שוב ושוב. למעשה, נראה כי הדפוסים המשובצים על הקרקע כוללים מספר תמונות פשוטות בשחור-לבן בשורה.

ממשק IAudioEmitter

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

מוגדר כישות הפולטת צליל. מכיוון שהישויות הפולטות צליל הפעם הן כלבים וחתולים, הן עוברות בירושה בכיתות שלהם. המקרן זקוק למידע הבא:

שימוש במאפיין
מיקום מיקום ישות
קדימה הכיוון שאליו פונה הישות (וקטור)
למעלה למעלה של הישות. הכיוון שבו הראש קיים באדם
מהירות המהירות שבה הישות נעה. משמש לחישוב ערכי דופלר.

מחלקת שפיותיות

מחלקה לייצוג שדונים תלת-ממדיים (שלטי חוצות). זה גם פולט, ויורש 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 יורש את הכיתה. הכלב נשאר במצב קבוע בחלל התלת-ממדי.

שדה

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

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

_timeDelay משמש במרווחי זמן כדי לשחק את הבכי.

SoundEffectInstance _activeSound הוא מופע להשמעת צליל. SoundEffectInstance כפי שתראו לעתים קרובות בסוג של השמעת צליל, תוכלו להשתמש באפשרות זו כפי שהיא בצליל תלת-ממדי. אגב _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 תלת-ממדי על-ידי העברת שם הקריאה, מידע הלולאה והמידע שלך כפליטה ל- .

כיתת הכלב נועדה לולאה הבכי, כך שאתה לא צריך לחשוב עמוק מדי כי זה רק התאמה, כגון משחק או הפסקת הסתעפות.

כיתת חתולים

זהו שיעור שעושה ציור חתול ושירת השמעה. 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. צליל תלת-ממדי מורכב ממאזין ופולט. המקרן כבר מגדיר כלבים וחתולים, כך שכיתת AudioManager מגדירה מאזינים. בדרך כלל יש פולטים מרובים, בעוד שיש רק מאזין אחד.

מחלקה זו יורשת ואוגרתGame GameComponent ומשתמשת ברכיב במחלקה.

מחלקת ActiveSound

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

הוא מוגדר בתחתית הקוד, אך הוא מחלקה לשמירה על המצב המופעל. יש לו מידע על מופע הצליל המושמע והפולט המושמע.

שדה

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
static string[] _soundNames =
  {
    "CatSound0",
    "CatSound1",
    "CatSound2",
    "DogSound",
  };

/// <summary>音を聞くリスナーの情報です。これは通常カメラに一致するように設定されます。</summary>
public AudioListener Listener { get; } = new AudioListener();

/// <summary>AudioEmitter は、3Dサウンドを生成しているエンティティを表します。</summary>
readonly AudioEmitter _emitter = new AudioEmitter();

/// <summary>再生可能なすべての効果音を保存します。</summary>
readonly Dictionary<string, SoundEffect> _soundEffects = new Dictionary<string, SoundEffect>();

/// <summary>現在再生中のすべての3Dサウンドを追跡します。また、再生が終わったインスタンスの破棄にも使用します。</summary>
readonly List<ActiveSound> _activeSounds = new List<ActiveSound>();

_soundNames מגדיר את שם קובץ השמע (שם הנכס) שיש לטעון. מכיוון שמדובר בתוכנית לדוגמה, היא מוגדרת לטעון את קובץ השמע בצובר תחילה. נתוני _soundEffects הצליל המיובאים מאוחסנים ב- .

AudioListener Listener היא ההגדרה של המאזין. פרטי המאזין מוגדרים ב- מכיוון שהם public מוגדרים ממחלקת המשחק.

AudioEmitter _emitter הוא ההגדרה של הפולט בעת החלת תלת-ממד על השמעת צליל. בדוגמה זו, בעת השמעת צליל, הערך של כל אובייקט פולט מוגדר כ _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 תלת-ממדי.

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

תהליך השמעת צליל תלת-ממדי מבחוץ. יש לו את שם הצליל להשמעה, אם לולאה, ופולט מידע.

אם ברצונך להשמיע צליל, צור מופע צליל חדש ממשאב הצליל שאתה שומר _soundEffects. SoundEffectInstance יכול להיות גם לולאה או לא, אז הגדר את מידע הלולאה.

מכיוון שאני יוצר כמצב ActiveSound הפעלה, אני מגדיר מידע פולט שהוא אובייקט ההשמעה של הצליל שם. אם ברצונכם להחיל מידע מיקום תלת-ממדי על צליל, Emitter תקבלו את הערך הדרוש לכם.

Apply3D השיטה מתוארת להלן, אך זהו תהליך של החלת מידע תלת-ממדי מהפולט על הצליל.

התהליך הבסיסי של צליל תלת-ממדי הוא להתחיל לנגן עם SoundEffectInstance.Play ערכת מידע תלת-ממדית. השאר הוא לעדכן את המידע התלת-ממדי מעת לעת עד שהצליל יסיים את ההשמעה.

החלת שיטת תלת-ממד

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

תהליך החלת אפקט תלת-ממדי על מופע צליל שצוין באמצעות מאזינים ופולטים.

הגדרנו את הערך _emitter של המופע המשותף בכל פעם. אם כבר AudioEmitter יש לך SoundEffectInstance.Apply3D אחד, פשוט להעביר את המאזין ופולט לשיטה.

מחלקת משחק אודיו3D

לבסוף, בואו נראה על מה שיעור המשחק.

שדה

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 נרשם ב- , אך כולל אותו כשדה מכיוון שהוא ניגש אליו בנפרד.

אחרים מגדירים ישויות כלבים, חתולים, מרקמים לציור קרקע, טרקטורה מרובעת, מידע על מצלמה ומידע קלט.

בנאי

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 באמצעות .

יצרת גם את שיעורי החתול והכלב.

שיטת LoadContent

/// <summary>
/// グラフィックコンテンツをロードします。
/// </summary>
protected override void LoadContent()
{
  _cat.Texture = Content.Load<Texture2D>("CatTexture");
  _dog.Texture = Content.Load<Texture2D>("DogTexture");

  _checkerTexture = Content.Load<Texture2D>("checker");

  // 四角形ポリゴンを描画するためのクラス
  _quadDrawer = new QuadDrawer(_graphics.GraphicsDevice);
}

טען את המרקמים הדרושים עבור כל ציור.

QuadDrawer נוצר GraphicsDevice כאן מכיוון שדורש .

שיטת עדכון

/// <summary>
/// ゲームがロジックを実行できるようにします。
/// </summary>
protected override void Update(GameTime gameTime)
{
  HandleInput();

  UpdateCamera();

  // 新しいカメラの位置について AudioManager に伝えます。
  _audioManager.Listener.Position = _cameraPosition;
  _audioManager.Listener.Forward = _cameraForward;
  _audioManager.Listener.Up = _cameraUp;
  _audioManager.Listener.Velocity = _cameraVelocity;

  // ゲームエンティティに動き回って音を鳴らすように伝えます。
  _cat.Update(gameTime, _audioManager);
  _dog.Update(gameTime, _audioManager);

  base.Update(gameTime);
}

HandleInput השיטות והשיטות UpdateCamera הן תהליך אחזור קלט ההתקן וטיפול במצלמה, אשר יידונו בהמשך.

מכיוון שמיקום המצלמה ומיקום המאזין כמעט תמיד זהים, המאזין מוגדר לאותו ערך לאחר תהליך עדכון המצלמה.

_cat התקשר לשיטות ועדכון _dog כדי להזיז ולצווח, בהתאמה.

שיטת ציור

/// <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 להשמיע צליל תלת-ממדי. למעשה, רוב הקשיים נעשים על ידי המסגרת, כי מה SoundEffectInstance שאנחנו עושים הוא הגדרת מידע 3D לתוך . אם דגמתם רק את החלקים של הצליל התלת-ממדי, בסופו של דבר תכתבו קצת עבודה בכיתת 'משחק'. עם זאת, אני כותב טיפים המבוססים על הסיפור המקורי, אז זה היה הרבה יותר זמן.