เสียง3D

ปรับปรุงหน้า :
วันที่สร้างเพจ :

หมายเหตุเกี่ยวกับเคล็ดลับนี้

ตัวอย่างนี้อ้างอิงจากโปรแกรมที่ประกาศบนเว็บไซต์ต่อไปนี้ ฉันเปลี่ยนรหัสเล็กน้อยเพื่อให้ง่ายต่อการเข้าใจและอธิบายเป็นภาษาญี่ปุ่น โดยทั่วไปเราใช้รหัสเดิมตามที่เป็นอยู่ดังนั้นหากคุณนํามาใช้ในโปรแกรมเกมของคุณให้แก้ไขในเวลาที่เหมาะสมและใช้งานได้

ไซต์อ้างอิง

นอกจากนี้ยังมีการอธิบายบนสมมติฐานว่าคุณมีความรู้พื้นฐานเกี่ยวกับ MonoGame และ XNA ดู เคล็ดลับ MonoGame และ เคล็ดลับ XNA สําหรับ ruddly

โดยเฉพาะอย่างยิ่ง, คณิตศาสตร์, เวกเตอร์, ฟังก์ชันตรีโกณมิติ, เมทริกซ์, ฯลฯ เป็นสิ่งจําเป็น, ดังนั้นโปรดรู้ว่ามันคืออะไรในระดับหนึ่ง

สิ่งแวดล้อม

แท่น
  • วินโดวส์ 10
  • สามารถใช้รหัสบนแพลตฟอร์มอื่น ๆ ที่เปิดใช้งาน MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
โมโนเกม
  • 3.8

เกี่ยวกับตัวอย่าง

ขึ้นอยู่กับตําแหน่งของกล้องคุณสามารถได้ยินเสียงเรียกจากตําแหน่งของสุนัขและตําแหน่งของแมว แมววงกลมและวงกลมแหล่งกําเนิดและผู้เล่นสามารถควบคุมตําแหน่งของกล้องด้วยกุญแจ ตรวจสอบให้แน่ใจว่าวิธีที่คุณได้ยินเสียงเปลี่ยนไปตามการเปลี่ยนแปลงแต่ละครั้งในตําแหน่งของกล้องหรือตําแหน่งของแมว ฉันคิดว่ามันง่ายที่จะเข้าใจโดยใช้หูฟัง ฉันยังไม่ได้รับการยืนยันทางช่อง 5.1

วิธีการใช้งาน

เกมคืออะไร
คีย์บอร์ดแพด (XInput) เมาส์สัมผัส
กล้องไปข้างหน้าและย้อนกลับ ↑↓ ติดซ้าย (บนและล่าง) - -
เปลี่ยนการวางแนวกล้อง ←→ ติดซ้าย (ซ้ายและขวา) - -
จบเกม Esc ย้อนกลับ - -

สิ่งที่ต้องเตรียม

  • ไฟล์เสียงส่งเสียงสุนัข
  • ไฟล์เสียงร้องแมวสามไฟล์
  • แฟ้มรูปสุนัข
  • แฟ้มรูปภาพแมว
  • ไฟล์ภาพภาคพื้นดิน

ตัวอย่างต้นฉบับในเว็บไซต์อย่างเป็นทางการใช้ไฟล์ .xnb เนื้อหาที่สร้างไว้ล่วงหน้า หากคุณต้องการเก็บตัวอย่างไว้ในเว็บไซต์อย่างเป็นทางการอย่าใช้ mgcbs เพิ่มลงในโซลูชัน Visual Studio ของคุณโดยตรงและคัดลอกในเวลาที่สร้าง

ฉันไม่รู้ว่าทําไมตัวอย่างดั้งเดิมถึงใช้วิธีนี้ แต่ฉันคิดว่าอาจเป็นเพราะเรากําลังย้ายโครงการเวอร์ชันเก่าๆ ตามที่เป็นอยู่ แน่นอนคุณสามารถใช้ mgcb เพื่อสร้างไฟล์เดียวกัน โครงการที่สามารถดาวน์โหลดได้บนเว็บไซต์นี้ได้รับการแก้ไขเป็นรุ่น MGCB

โปรแกรม

ดาวน์โหลดโปรแกรมสําหรับรหัสทั้งหมด

ตัวอย่างนี้ถูกออกแบบมาเพื่อทําให้โค้ดดูทั่วไปขึ้นเล็กน้อยในขณะที่ทําให้การเล่นเสียงดูดีในขณะทํางาน การใช้ Audio3D เท่านั้นจะเป็นรหัสที่น้อยกว่ามาก แต่อธิบายไว้ตามตัวอย่างดั้งเดิม ฉันจะเก็บส่วนที่สําคัญน้อยกว่าไว้

โครงการประกอบด้วยไฟล์รหัสต่อไปนี้:

  • ออดิโอ3DGame
  • ออดิโอแมนเนจเกอร์
  • แมว
  • หมา
  • IAudioEmitter
  • โปรแกรม
  • ควอดดราเวอร์
  • สไปรท์เอนทิตี้

คลาส QuadDrawer

คลาสนี้เป็นคลาสผู้ช่วยสําหรับการวาดรูปหลายเหลี่ยมสี่เหลี่ยม รูปหลายเหลี่ยมสี่เหลี่ยมมักใช้เป็นหลักเพื่อแสดงสไปรท์ 3 มิติ (ป้ายโฆษณา) เคล็ดลับนี้ใช้สําหรับป้ายโฆษณาสําหรับแมวและสุนัขเช่นเดียวกับการแสดงรูปสี่เหลี่ยมผืนผ้าของพื้นดิน

คลาสนี้ไม่มีส่วนเกี่ยวข้องกับ 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 interface

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

กําหนดเป็นเอนทิตีที่ปล่อยเสียง เนื่องจากหน่วยงานที่เปล่งเสียงในครั้งนี้คือสุนัขและแมวพวกเขาจึงได้รับมรดกในชั้นเรียนของตน ตัวปล่อยต้องการข้อมูลต่อไปนี้:

การใช้คุณสมบัติ
ฐานะ ตําแหน่งเอนทิตี้
ข้างหน้า ทิศทางที่เอนทิตีกําลังเผชิญอยู่ (เวกเตอร์)
ขึ้น การขึ้นของเอนทิตี้ ทิศทางที่ศีรษะมีอยู่ในบุคคล
ความเร็ว ความเร็วที่เอนทิตีย้าย ใช้เพื่อคํานวณค่า Doppler

คลาสสไปรท์เอนทิตี้

คลาสสําหรับแสดงสไปรท์ 3 มิติ (ป้ายโฆษณา) นอกจากนี้ยังเป็นตัวปล่อยและสืบทอด 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 กําลังสืบทอดคลาส สุนัขยังคงอยู่ในตําแหน่งคงที่ในพื้นที่ 3 มิติ

สนาม

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

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

_timeDelay ใช้เป็นระยะ ๆ เพื่อเล่นเสียงร้อง

SoundEffectInstance _activeSound เป็นตัวอย่างสําหรับการเล่นเสียง SoundEffectInstance ดังที่คุณเห็นบ่อยๆ ในคลาสของการเล่นเสียง คุณสามารถใช้สิ่งนี้ได้เช่นเดียวกับเสียง 3 มิติ โดยวิธีการ _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 ในพื้นที่ 3 มิติโดยส่งผ่านชื่อของเสียงร้องข้อมูลลูปและข้อมูลของคุณเองเป็นตัวปล่อยไปยัง

คลาส Dog ได้รับการออกแบบมาเพื่อวนรอบเสียงร้องดังนั้นคุณจึงไม่ต้องคิดลึกเกินไปเพราะเป็นเพียงการปรับเช่นการเล่นหรือหยุดการแตกแขนง

คลาสแมว

มันเป็นชั้นเรียนที่วาดภาพแมวและเล่นร้องเพลง 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 ในที่สุด เสียง 3 มิติประกอบด้วยผู้ฟังและตัวปล่อย ตัวปล่อยกําหนดสุนัขและแมวแล้วดังนั้นคลาส 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 คือคําจํากัดความของตัวปล่อยเมื่อใช้ 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 เสียง 3 มิติ

SoundEffect.DistanceScale ที่จะได้ยินเสียงแม้ในระยะไกลของ

SoundEffect.DopplerScale เป็นอิทธิพลของผล Doppler ยิ่งตัวเลขสูงเท่าใด เอฟเฟ็กต์ Doppler ก็จะยิ่งมากขึ้นเท่านั้น

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

กระบวนการเล่นเสียง 3 มิติจากภายนอก มันมีชื่อของเสียงที่จะเล่นไม่ว่าจะวนซ้ําและข้อมูลตัวปล่อย

ถ้าคุณต้องการเล่นเสียง ให้สร้างอินสแตนซ์เสียงใหม่จากทรัพยากรเสียงที่คุณเก็บไว้ใน_soundEffects SoundEffectInstance นอกจากนี้ยังสามารถมีลูปได้หรือไม่ดังนั้นตั้งค่าข้อมูลลูป

เนื่องจากฉันกําลังสร้างเป็น ActiveSound สถานะการเล่นฉันจึงตั้งค่าข้อมูลตัวปล่อยที่เป็นวัตถุการเล่นของเสียงที่นั่น หากคุณต้องการใช้ข้อมูลตําแหน่ง 3 มิติกับเสียง Emitter คุณจะได้รับค่าที่คุณต้องการ

Apply3D วิธีการนี้อธิบายไว้ด้านล่าง แต่เป็นกระบวนการของการใช้ข้อมูล 3 มิติจากตัวปล่อยกับเสียง

กระบวนการพื้นฐานของเสียง 3 มิติคือการเริ่มเล่นด้วย SoundEffectInstance.Play ชุดข้อมูล 3 มิติ ส่วนที่เหลือคือการอัปเดตข้อมูล 3 มิติเป็นระยะ ๆ จนกว่าเสียงจะเล่นเสร็จ

วิธีการใช้3D

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

กระบวนการใช้เอฟเฟกต์ 3 มิติกับอินสแตนซ์เสียงที่ระบุโดยใช้ตัวฟังและตัวปล่อย

เราตั้งค่าเป็น_emitterของอินสแตนซ์ทั่วไปในแต่ละครั้ง หากคุณมีSoundEffectInstance.Apply3Dอยู่แล้วAudioEmitterเพียงส่งผู้ฟังและตัวปล่อยไปยังวิธีการ

คลาสเกมเสียง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ลงทะเบียนใน แต่มีเป็นฟิลด์เนื่องจากจะเข้าถึงเป็นรายบุคคล

คนอื่น ๆ กําหนดสุนัขหน่วยงานแมวพื้นผิวสําหรับการวาดภาพภาคพื้นดิน quaddrawer ข้อมูลกล้องและข้อมูลการป้อนข้อมูล

ผู้สร้าง

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จะถูกป้ายโฆษณาภายใน ดังนั้นข้อมูลตําแหน่งของกล้องจะถูกส่งผ่านและวาด

วิธีจัดการอินพุท

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

กล้องถูกควบคุมให้เคลื่อนที่

นอกจากนี้ยังมีการคํานวณเช่นการเร่งความเร็วและแรงเสียดทาน แต่ฉันคิดว่ามันอาจคํานึงถึงเอฟเฟกต์ Doppler กับเสียง 3 มิติ เนื่องจากฉันไม่ได้ทําอะไรพิเศษเกี่ยวกับการคํานวณเองฉันจะไม่พูดถึงมัน

สรุป

ฉันครอบคลุมมันที่ความยาว แต่มักจะเพียงพอที่จะอ้างถึงชั้นเรียนสําหรับ AudioManager วิธีการเล่นเสียง 3 มิติ ในความเป็นจริงปัญหาส่วนใหญ่ทําโดยกรอบเพราะสิ่งที่เรา SoundEffectInstance กําลังทําคือการตั้งค่าข้อมูล 3 มิติเป็น . หากคุณสุ่มตัวอย่างเฉพาะส่วนของเสียง 3 มิติคุณจะเขียนงานเล็ก ๆ น้อย ๆ ในคลาสเกม อย่างไรก็ตามฉันกําลังเขียนเคล็ดลับตามเรื่องราวดั้งเดิมดังนั้นมันจึงใช้เวลานานกว่ามาก