Audio3D

Strona zaktualizowana :
Data utworzenia strony :

Uwagi na ten temat Wskazówki

Ten przykład jest oparty na programach opublikowanych w następujących witrynach. Zmieniam trochę kod, aby ułatwić zrozumienie i wyjaśnienie go po japońsku. Zasadniczo używamy oryginalnego kodu takim, jaki jest, więc jeśli faktycznie zaadaptujesz go w swoim programie gry, popraw go w odpowiednim czasie i użyj go.

Strona referencyjna

Ponadto jest to wyjaśnione przy założeniu, że masz podstawową wiedzę na temat MonoGame i XNA. Zobacz MonoGame Tips i XNA Tips dla rumianych.

W szczególności, ponieważ matematyka, wektory, funkcje trygonometryczne, macierze itp. są niezbędne, więc proszę wiedzieć, czym one są do pewnego stopnia.

środowisko

podest
  • System Windows 10
  • Kod może być używany na innych platformach obsługujących MonoGame
Visual Studio
  • Program Visual Studio 2019
Platforma .NET Core
  • 3.1
MonoGame
  • 3.8

Informacje o próbkach

Na podstawie położenia kamery można usłyszeć połączenie z pozycji psa i pozycji kota. Kot krąży i okrąża pochodzenie, a gracz może kontrolować pozycję kamery za pomocą. Upewnij się, że sposób, w jaki słyszysz dźwięk, zmienia się z każdą zmianą położenia kamery lub pozycji kota. Myślę, że łatwo zrozumieć używanie słuchawek. Jestem niepotwierdzony na kanale 5.1.

Jak obsługiwać

What's Do Keyboard Gamepad (XInput) Mouse Touch
Aparat do przodu i do tyłu ↑↓ Lewy drążek (górny i dolny) - -
Zmiana orientacji kamery ←→ Lewy drążek (lewy i prawy) - -
Koniec gry Esc Wstecz - -

Co przygotować

  • Plik audio pisku psa
  • Trzy pliki audio piszczące koty
  • Plik obrazu psa
  • Plik obrazu kota
  • Plik obrazu podłoża

Oryginalna próbka na oficjalnej stronie wykorzystuje wstępnie skompilowaną zawartość plików .xnb . Jeśli chcesz zachować próbki na oficjalnej stronie, nie używaj mgcbs, dodaj je bezpośrednio do rozwiązania Visual Studio i skopiuj je w czasie kompilacji.

Nie wiem, dlaczego oryginalna próbka przyjmuje tę metodę, ale myślę, że to prawdopodobnie dlatego, że migrujemy starsze wersje projektu. Oczywiście możesz użyć mgcb do wygenerowania tego samego pliku. Projekty, które można pobrać na tej stronie, zostały zmodyfikowane do wersji MGCB.

program

Pobierz program dla całego kodu.

Ten przykład został zaprojektowany, aby kod wyglądał nieco bardziej ogólnie, a jednocześnie sprawiał, że odtwarzanie dźwięku wyglądało dobrze w czasie wykonywania. Używanie tylko Audio3D byłoby znacznie mniejszym kodem, ale jest opisane w oryginalnym przykładzie. Zachowam mniej ważne części.

Projekt składa się z następujących plików kodu:

  • Audio3DGame
  • AudioManager
  • Kot
  • Pies
  • IAudioEmitter
  • Program
  • QuadDrawer
  • SpriteEntity

QuadDrawer klasa

Ta klasa jest klasą pomocniczą do rysowania prostokątnych wielokątów. Prostokątne wielokąty są często używane głównie do wyświetlania sprite'ów 3D (billboardów). Ta wskazówka jest używana do billboardów dla kotów i psów, a także do prostokątnych wyświetlaczy ziemi.

Ta klasa nie ma nic wspólnego z Audio3D.

pole

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

Minimum informacji wymaganych do rysowania wielokątów. Rysuj wielokąty z danych wierzchołków bez przygotowywania danych modelu.

konstruktor

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

Ponieważ ta klasa jest uniwersalnie użyteczna, otrzymujesz GraphicsDevice instancję utworzoną w procesie inicjowania klasy gry.

Tutaj ustawiamy efekt niezbędny do narysowania sprite'a i położenie 4 wierzchołków wymaganych dla prostokątnego wielokąta. Rozmiar powinien być skalowany podczas rysowania, więc wynosi od -1 do 1.

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

Ustawia przekazywaną teksturę na prostokąt i rysuje ją. Ponieważ jest to podstawowy proces, nie ma nic do odnotowania.

Jeśli istnieje jeden punkt, w textureRepeats argumencie można określić wywołaną zmienną, dzięki czemu można zmienić współrzędne tekstury. Jeśli vertexPositionTexture.TextureCoordinate jest ustawiona poza zakresem od 0 do 1, tekstura jest domyślnie rysowana wielokrotnie. Za jego pomocą można wyrazić, że płytki można układać wielokrotnie. W rzeczywistości wzory w kratkę na ziemi wydają się mieć wiele prostych czarno-białych obrazów ustawionych w linii.

Interfejs IAudioEmitter

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

Zdefiniowany jako jednostka emitująca dźwięk. Ponieważ jednostki, które emitują dźwięk tym razem, to psy i koty, są one dziedziczone w swoich klasach. Emiter potrzebuje następujących informacji:

Wykorzystanie nieruchomości
Pozycja Pozycja jednostki
Naprzód Kierunek, w którym skierowana jest encja (wektor)
W górę Up podmiotu. Kierunek, w którym głowa istnieje w osobie
Prędkość Prędkość, z jaką porusza się jednostka. Służy do obliczania wartości dopplerowskich.

SpriteEntity, klasa

Klasa do reprezentowania sprite'ów 3D (billboardów). Jest również emiterem i dziedziczy IAudioEmitter . Psy i koty dziedziczą tę klasę.

własność

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

Ma informacje o lokalizacji jednostki itp. Zasadniczo mam informacje jako emiter. Jest to również jednostka rysunkowa, więc ma również obraz (Teksturę) do wyświetlenia.

Metoda aktualizacji

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

Opisuje pozycję encji i proces odtwarzania dźwięku, ale implementuje go w klasie pochodnej.

Metoda rysowania

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

Psy i koty mają różne obrazy i pozycje, ale drugi mechanizm rysowania jest dokładnie taki sam, więc opisuję je tutaj.

Każda transformacja i tekstura są przekazywane do QuadDrawer w celu wyświetlenia prostokątnego wielokąta. Ponieważ transformacja współrzędnych jest podstawową wiedzą, wyjaśnienia nie są uwzględnione.

Matrix.CreateConstrainedBillboard Użyj informacji o metodzie i kamerze, aby łatwo reprezentować billboardy, więc efektywnie je wykorzystaj.

Klasa psa

Jest to klasa, która rysuje i śpiewa psy. SpriteEntity Dziedziczenie klasy. Pies pozostaje w ustalonej pozycji w przestrzeni 3D.

pole

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

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

_timeDelay jest używany w odstępach czasu do odtwarzania płaczu.

SoundEffectInstance _activeSound jest instancją do odtwarzania dźwięku. SoundEffectInstance Jak często widzisz w klasie odtwarzania dźwięku, możesz użyć tego tak, jak w dźwięku 3D. Nawiasem _activeSound mówiąc, później otrzymuję tylko referencję z klasy AudioManager, więc nie odrzucę jej po stronie klasy Dot.

Metoda aktualizacji

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

W tej próbce pies pozostaje w tej samej pozycji przez długi czas, więc pierwsze cztery parametry są określone przez decydujące uderzenie. Rysowanie odbywa się w klasie podstawowej.

_timeDelay jest następnie używany jako pozostały czas na zatrzymanie się, grając płacz.

AudioManager zostanie odtworzonyPlay3DSound w przestrzeni 3D poprzez przekazanie nazwy krzyku, informacji o pętli i własnych informacji jako emitera do .

Klasa Pies została zaprojektowana tak, aby zapętlić krzyk, więc nie musisz się zbytnio zastanawiać, ponieważ jest to tylko dostosowanie, takie jak gra lub zatrzymanie rozgałęzienia.

Klasa Cat

Jest to klasa, która rysuje kota i śpiewa. SpriteEntity Dziedziczenie klasy. Kot porusza się po pochodzeniu.

pole

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

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

_timeDelay jest używany w taki sam sposób, jak klasa Pies, z resztą czasu przed następnym piskiem.

Istnieją trzy rodzaje wywołań kotów, które są losowo wybierane i odtwarzane, ale nie mają nic wspólnego z Audio3D, ponieważ są elementem bonusowym.

Metoda aktualizacji

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

Ponieważ kot obraca się wokół początku ruchem okrężnym, przetwarza go tak, że porusza trajektorią okręgu za pomocą funkcji trygonometrycznej. W rezultacie pozycja, przód i prędkość zmieniały się jeden po drugim. Dlatego po uruchomieniu programu widać, że płacz kota krąży wokół, nawet jeśli nie poruszasz kamerą.

_timeDelay Poniższy kod to proces, w którym płacz kota jest losowo przydzielany i odtwarzany w regularnych odstępach czasu. audioManager.Play3DSound Przekazuję się metodzie, która jest emiterem. Widać, że pozycja odtwarzania dźwięku zmienia się jeden po drugim.

Klasa AudioManager

To w końcu esencja Audio3D. Dźwięk 3D składa się ze słuchacza i emitera. Emiter definiuje już psy i koty, więc klasa AudioManager definiuje detektory. Zwykle istnieje wiele emiterów, podczas gdy jest tylko jeden słuchacz.

Ta klasa dziedziczy,Game rejestruje GameComponent i używa składnika w klasie.

ActiveSound klasa

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

Jest zdefiniowany na dole kodu, ale jest to klasa do zachowania stanu, który jest odtwarzany. Zawiera informacje o odtwarzanej instancji dźwiękowej i odtwarzanym emiterze.

pole

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
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 definiuje nazwę pliku audio (nazwę zasobu) do załadowania. Ponieważ jest to przykładowy program, zdefiniowano, aby najpierw załadować plik audio zbiorczo. Zaimportowane dane _soundEffects dźwiękowe są przechowywane w formacie .

AudioListener Listener to definicja słuchacza. Informacje o odbiorniku są definiowane, public ponieważ są ustawiane z klasy Game.

AudioEmitter _emitter to definicja emitera podczas stosowania 3D do odtwarzania dźwięku. W tym przykładzie podczas odtwarzania dźwięku wartość każdego obiektu emitera jest ustawiona na _emitter , więc jest to forma, która współdzieli jeden jako instancję. Oczywiście możesz mieć dla każdego AudioEmitter obiektu.

_activeSounds zawiera informacje o dźwięku, który odtwarzasz w klasie, którą zdefiniowałeś ActiveSound wcześniej. Ponieważ informacje o emiterze zmieniają się przez cały czas, są używane do aktualizowania informacji o lokalizacji itp. oraz do odrzucania instancji dźwiękowych, które zakończyły odtwarzanie.

konstruktor

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

GameComponent Ponieważ dziedziczy klasę, Game otrzymuje instancję i przekazuje ją do klasy bazowej.

Metoda inicjowania

/// <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 Ponieważ dziedziczy klasę, jest automatycznie wywoływana podczas inicjowania.

SoundEffect.DistanceScale i SoundEffect.DopplerScale są parametrami dedykowanymi dźwiękowi static 3D.

SoundEffect.DistanceScale , aby usłyszeć dźwięk nawet w oddali.

SoundEffect.DopplerScale to wpływ efektu Dopplera. Im wyższa liczba, tym większy efekt Dopplera.

_soundNames Zapisz nazwę zasobu zdefiniowaną w pętli, aby załadować _soundEffects .

Metoda utylizowania

/// <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 Jest automatycznie wywoływany na końcu gry, ponieważ dziedziczy klasę.

Niszczenie wszystkich zaimportowanych zasobów dźwiękowych.

Metoda aktualizacji

/// <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 Ponieważ dziedziczy klasę, jest automatycznie wywoływana w procesie aktualizacji.

Sprawdź aktualnie odtwarzany dźwięk i, jeśli Apply3D jest odtwarzany, wywołaj metodę, aby zaktualizować lokalizację dźwięku, aby pasowała do emitera. Jeśli odtwarzanie jakiegokolwiek dźwięku zostało zakończone, instancja zostanie odrzucona.

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

Proces odtwarzania dźwięku 3D z zewnątrz. Ma nazwę dźwięku do odtworzenia, czy zapętlać, i emiter informacji.

Jeśli chcesz odtworzyć dźwięk, utwórz nowe wystąpienie dźwięku z zasobu dźwiękowego przechowywanego w _soundEffects. SoundEffectInstance może również mieć pętlę lub nie, więc ustaw informacje o pętli.

Ponieważ tworzę jako ActiveSound stan odtwarzania, ustawiam informacje o emiterze, które są obiektem odtwarzającym dźwięk. Jeśli chcesz zastosować informacje o lokalizacji 3D do dźwięku, otrzymasz Emitter potrzebną wartość.

Apply3D Metoda jest opisana poniżej, ale jest to proces nakładania informacji 3D z emitera do dźwięku.

Podstawowym procesem dźwięku 3D jest rozpoczęcie odtwarzania z ustawionymi informacjami SoundEffectInstance.Play 3D. Reszta polega na okresowym aktualizowaniu informacji 3D, aż dźwięk zostanie ukończony.

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

Proces stosowania efektu 3D do określonej instancji dźwiękowej przy użyciu detektorów i emiterów.

Za każdym razem ustawiamy wartość na _emitter wspólnego wystąpienia. SoundEffectInstance.Apply3D Jeśli już AudioEmitter go masz, po prostu przekaż słuchacza i emiter do metody.

Audio3DGame Klasa

Na koniec przyjrzyjmy się, o co chodzi w klasie Game.

pole

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 Rejestruje się GameComponents w programie , ale ma go jako pole, ponieważ uzyskuje do niego dostęp indywidualnie.

Inne definiują psy, elementy kotów, tekstury do rysowania podłoża, quaddrawer, informacje o kamerze i informacje wejściowe.

konstruktor

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 Wygeneruj i GameComponents zarejestruj się za pomocą programu .

Wygenerowałeś także klasy Kota i Psa.

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

Załaduj tekstury potrzebne dla każdego rysunku.

QuadDrawer jest GraphicsDevice generowany tutaj, ponieważ wymaga .

Metoda aktualizacji

/// <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 Metody i UpdateCamera metody to proces pobierania danych wejściowych urządzenia i obsługi kamery, który zostanie omówiony później.

Ponieważ pozycja kamery i pozycja słuchacza są prawie zawsze takie same, słuchacz jest ustawiany na tę samą wartość po procesie aktualizacji kamery.

_cat Wywołaj metody i _dog Update, aby odpowiednio przesuwać i piszczeć.

Metoda rysowania

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

Generuje przekształcenia widoku i projekcji na podstawie informacji z kamery.

W przypadku podłoża prostokąt narysowany w QuadDrawer jest wyświetlany pionowo, więc jest obracany w poziomie i umiarkowanie powiększony. _quadDrawer.DrawQuad Drugi argument metody określa liczbę powtórzeń tekstury, co określa, że arkusze 32x32 pojawiają się obok siebie.

_cat i _dog są umieszczane wewnętrznie, więc informacje o lokalizacji kamery są przekazywane i rysowane.

Metoda HandleInput —

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

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

Informacje na klawiaturze i gamepadzie są pozyskiwane, a koniec gry jest określany.

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

Kamera jest sterowana ruchem.

Zawiera również obliczenia takie jak przyspieszenie i tarcie, ale myślę, że prawdopodobnie bierze pod uwagę efekt Dopplera na dźwięk 3D. Ponieważ nie robię nic specjalnego z samymi obliczeniami, nie będę o tym dyskutował.

Streszczenie

Omówiłem to obszernie, ale zwykle wystarczy odnieść się do klasy AudioManager , aby dowiedzieć się, jak odtwarzać dźwięk 3D. W rzeczywistości większość trudności jest wykonywana przez ramy, ponieważ to, co SoundEffectInstance robimy, to ustawianie informacji 3D w . Jeśli samplowałeś tylko części dźwięku 3D, skończysz pisząc trochę pracy w klasie Game. Piszę jednak Porady w oparciu o oryginalną historię, więc minęło dużo więcej czasu.