Áudio3D

Página atualizada :
Data de criação de página :

Notas sobre estas Dicas

Esta amostra é baseada nos programas publicados nos seguintes sites. Eu mudo um pouco o código para facilitar a compreensão e explicação em japonês. Basicamente, usamos o código original como está, então se você realmente adotá-lo em seu programa de jogo, corrigi-lo em tempo hábil e usá-lo.

Site de referência

Além disso, explica-se no pressuposto de que você tem algum conhecimento básico sobre MonoGame e XNA. Veja dicas de MonoGame e XNA Dicas para o ruddly.

Em particular, como matemática, vetores, funções trigonométricas, matrizes, etc. são essenciais, então saiba o que são até certo ponto.

ambiente

plataforma
  • Windows 10
  • O código pode ser usado em outras plataformas habilitadas para MonoGame
Estúdio Visual
  • Visual Studio 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

Sobre amostras

Com base na posição da câmera, você pode ouvir a chamada da posição do cão e a posição do gato. O gato circula e circula a origem, e o jogador pode controlar a posição da câmera com a chave. Certifique-se de que a maneira como você ouve o som muda a cada mudança na posição da câmera ou na posição do gato. Acho que é fácil de entender usando fones de ouvido. Não estou confirmado no canal 5.1.

Como operar

O que fazer teclado Gamepad (XInput) Mouse Touch
Câmera para frente e para trás ↑↓ Vara esquerda (superior e inferior) - -
Mude a orientação da câmera ←→ Vara esquerda (esquerda e direita) - -
Fim do jogo Esc Voltar - -

O que preparar

  • Arquivo de áudio squeal de cachorro
  • Três arquivos de áudio de guincho de gato
  • Arquivo de imagem de cachorro
  • Arquivo de imagem de gato
  • Arquivo de imagem de terra

A amostra original no site oficial usa arquivos prebuilt content .xnb . Se você quiser manter as amostras no site oficial, não use mgcbs, adicione-as diretamente à sua solução Visual Studio e copie-as na hora da compilação.

Eu não sei por que a amostra original está tomando este método, mas eu acho que é provavelmente porque estamos migrando versões mais antigas do projeto como é. Claro, você pode usar mgcb para gerar o mesmo arquivo. Os projetos que podem ser baixados neste site foram modificados para a versão MGCB.

programa

Baixe o programa para obter todo o código.

Esta amostra foi projetada para fazer o código parecer um pouco mais geral, ao mesmo tempo em que faz a reprodução de som parecer boa no tempo de execução. Usar Audio3D seria muito menos código, mas é descrito ao longo da amostra original. Vou ficar com as partes menos importantes.

O projeto consiste nos seguintes arquivos de código:

  • Audio3DGame
  • AudioManager
  • Gato
  • Cão
  • IAudioEmitter
  • Programa
  • QuadDrawer
  • Entidade Sprite

Classe QuadDrawer

Esta classe é uma classe de ajudante para desenhar polígonos retangulares. Polígonos retangulares são frequentemente usados principalmente para exibir sprites 3D (outdoors). Esta dica é usada para outdoors para cães e gatos, bem como para exibições retangulares do chão.

Esta aula não tem nada a ver com audio3D.

campo

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

Informações mínimas necessárias para desenhar polígonos. Desenhe polígonos a partir de dados de vértices sem preparar dados do modelo.

construtor

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

Como esta classe é universalmente utilizável, você GraphicsDevice recebe uma instância criada no processo de inicialização da classe de jogo.

Aqui, definimos o efeito necessário para desenhar o sprite e a posição dos 4 vértices necessários para o polígono retangular. O tamanho deve ser dimensionado ao desenhar, por isso é -1 a 1.

Método 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);
}

Define a textura passada em um retângulo e desenha-a. Como este é um processo básico, não há nada a notar.

Se houver um ponto, uma variável chamada pode ser especificada no textureRepeats argumento, para que as coordenadas da textura possam ser alteradas. Se o vtexPositionTexture.TextureCoordinate for definido além da faixa 0 a 1, a textura é desenhada repetidamente por padrão. Usando-o, é possível expressar que as telhas podem ser arranjadas repetidamente. Na verdade, os padrões quadrimestrados no chão parecem ter várias imagens simples em preto e branco alinhadas.

Interface iAudioEmitter

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

Definida como uma entidade que emite som. Como as entidades que emitem som desta vez são cães e gatos, elas são herdadas em suas respectivas classes. O emissor precisa das seguintes informações:

Uso de propriedade
Posição Posição da entidade
Encaminhar A direção que a entidade está enfrentando (vetor)
Em cima O up da entidade. A direção em que a cabeça existe em uma pessoa
Velocidade A velocidade com que a entidade se move. Usado para calcular valores doppler.

Classe SpriteEntity

Uma classe para representar sprites 3D (outdoors). É também um emissor, e herda IAudioEmitter . Cães e gatos herdam essa classe.

propriedade

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

Tem as informações de localização da entidade, etc. Basicamente eu tenho informações como emissor. É também uma entidade de desenho, por isso também tem uma imagem (Textura) para exibir.

Método de atualização

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

Descreve a posição da entidade e o processo de reprodução de som, mas implementa-o na classe derivada.

Método de desenho

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

Cães e gatos têm imagens e posições diferentes, mas o outro mecanismo de desenho é exatamente o mesmo, então eu os descrevo aqui.

Cada transformação e textura é passada para QuadDrawer para exibir um polígono retangular. Como a transformação coordenada é o conhecimento básico, as explicações não são cobertas.

Matrix.CreateConstrainedBillboard Use informações sobre métodos e câmeras para representar facilmente outdoors, por isso faça uso efetivo deles.

Aula de cachorro

É uma aula que faz desenho de cachorro e reprodução de canto. SpriteEntity Herdando a classe. O cão permanece em posição fixa no espaço 3D.

campo

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

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

_timeDelay é usado em intervalos para tocar o grito.

SoundEffectInstance _activeSound é um exemplo para tocar um som. SoundEffectInstance Como você verá frequentemente na classe de reprodução de som, você pode usar isso como está no som 3D. A propósito _activeSound , só recebo uma referência da classe AudioManager mais tarde, então não vou descartá-la no lado da classe Dot.

Método de atualização

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

Nesta amostra, o cão permanece na mesma posição por um longo tempo, de modo que os quatro primeiros parâmetros são especificados por um ataque decisivo. O desenho é feito na classe básica.

_timeDelay é então usado como o tempo restante para parar, tocando o grito.

AudioManager será reproduzidoPlay3DSound em espaço 3D passando o nome do grito, informações em loop e suas próprias informações como emissor para .

A aula de Dog foi projetada para dar o loop do choro, para que você não tenha que pensar muito profundamente porque é apenas um ajuste, como jogar ou parar de ramificar.

Aula de gato

É uma aula que faz desenho de gato e reprodução de canto. SpriteEntity Herdando a classe. O gato está se movendo em torno da origem.

campo

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

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

_timeDelay é usado da mesma forma que a classe Dog, com o resto do tempo antes do próximo guincho.

Existem três tipos de chamadas de gato que são selecionadas aleatoriamente e reproduzidas, mas não têm nada a ver com Audio3D porque são um elemento bônus.

Método de atualização

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

Uma vez que o gato gira em torno da origem em um movimento circular, ele processa-o de modo que ele move a trajetória do círculo usando uma função trigonométrica. Como resultado, posição, frente e Velocity mudaram um após o outro. Portanto, quando você executa o programa, você pode ver que o choro do gato está circulando ao redor mesmo se você não mover a câmera.

_timeDelay O código a seguir é um processo no qual o choro do gato é atribuído aleatoriamente e jogado em intervalos regulares. audioManager.Play3DSound Estou me passando para o método que é um emissor. Você pode ver que a posição de reprodução do som muda uma a uma.

AudioManager classe

Esta é finalmente a essência do Audio3D. Um som 3D consiste em um ouvinte e um emissor. O emissor já define cães e gatos, então a classe AudioManager define os ouvintes. Geralmente há vários emissores, enquanto há apenas um ouvinte.

Esta classe herda eGame registra GameComponent e usa o componente na classe.

Classe ActiveSound

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

É definido na parte inferior do código, mas é uma classe para preservar o estado que está jogando. Tem informações sobre a ocorrência de som sendo reproduzida e o emissor sendo tocado.

campo

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
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 define o nome do arquivo de áudio (nome do ativo) para carregar. Por ser um programa de amostra, é definido para carregar o arquivo de áudio em massa primeiro. Os dados de som importados são _soundEffects armazenados em .

AudioListener Listener é a definição do ouvinte. As informações do ouvinte são definidas porque public são definidas a partir da classe Jogo.

AudioEmitter _emitter é a definição do emissor ao aplicar 3D à reprodução de som. Nesta amostra, ao reproduzir um som, o valor de cada objeto emissor é definido, por _emitter isso é uma forma que compartilha um como uma instância. Claro, você pode ter para cada AudioEmitter objeto.

_activeSounds tem informações sobre o som que você está tocando na classe que você definiu ActiveSound anteriormente. Como as informações do emissor mudam o tempo todo, ela é usada para atualizar informações de localização, etc., e descartar instâncias de som que terminaram de reproduzir.

construtor

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

GameComponent Como herda a classe, Game recebe a instância e passa para a classe base.

Método de inicialização

/// <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 Por herdar a classe, é automaticamente chamada na inicialização.

SoundEffect.DistanceScale e SoundEffect.DopplerScale são parâmetros dedicados ao static som 3D.

SoundEffect.DistanceScale para ouvir o som mesmo à distância.

SoundEffect.DopplerScale é a influência do efeito Doppler. Quanto maior o número, maior o efeito Doppler.

_soundNames Salve o nome do ativo definido em um loop para carregar _soundEffects .

Método de descarte

/// <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 É automaticamente chamado no final do jogo porque herda a classe.

Destruindo todos os ativos de som importados.

Método de atualização

/// <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 Por herdar a classe, é automaticamente chamada no processo de atualização.

Verifique o som que está sendo reproduzido no momento e, se estiver Apply3D sendo reproduzido, ligue para o método para atualizar a localização do som para combinar com o emissor. Se algum som tiver terminado de tocar, a instância será descartada.

Método 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;
}

O processo de reprodução de som 3D de fora. Ele tem o nome do som para tocar, seja para loop, e informações de emissor.

Se você quiser reproduzir um som, crie uma nova instância de som a partir do recurso de som que você mantém no _soundEffects. SoundEffectInstance também pode ter um loop ou não, então defina as informações do loop.

Como estou criando como ActiveSound um estado de reprodução, eu definai informações emitter que é o objeto de reprodução do som lá. Se você quiser aplicar informações de localização 3D a um som, você terá Emitter o valor necessário.

Apply3D O método é descrito abaixo, mas é o processo de aplicação de informações 3D do emissor ao som.

O processo básico de som 3D é iniciar a reprodução com SoundEffectInstance.Play conjunto de informações 3D. O resto é atualizar as informações 3D periodicamente até que o som termine de ser reproduzido.

Aplicar método3D

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

O processo de aplicação de um efeito 3D a uma instância de som especificada usando ouvintes e emissores.

Definimos o valor para o _emitter da instância comum cada vez. Se você SoundEffectInstance.Apply3DAudioEmitter tem um, basta passar o ouvinte e emissor para o método.

Audio3DGame Class

Finalmente, vamos ver do que se trata a classe Game.

campo

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 Registra-se, mas tem como campo porque o acessa individualmente.

Outros definem cães, entidades de gatos, texturas para desenho de terra, quadriciclo, informações da câmera e informações de entrada.

construtor

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 Gerar e GameComponents registrar com .

Você também gerou as aulas de Gato e Cachorro.

Método 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);
}

Carregue as texturas necessárias para cada desenho.

QuadDrawer é GraphicsDevice gerado aqui porque requer .

Método de atualização

/// <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 Os métodos e UpdateCamera métodos são o processo de recuperação da entrada do dispositivo e manuseio da câmera, que será discutido posteriormente.

Como a posição da câmera e a posição do ouvinte são quase sempre as mesmas, o ouvinte está definido para o mesmo valor após o processo de atualização da câmera.

_cat Ligue para os métodos e _dog atualizar para mover e gritar, respectivamente.

Método de desenho

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

Gera transformações de visualização e projeção a partir de informações da câmera.

Para o chão, o retângulo desenhado em QuadDrawer é exibido verticalmente, por isso é girado para ser horizontal e ampliado moderadamente. _quadDrawer.DrawQuad O segundo argumento do método especifica o número de repetições da textura, que especifica que as folhas 32x32 aparecem lado a lado.

_cat e _dog são espalhados internamente, de modo que as informações de localização da câmera são passadas e desenhadas.

Método HandleInput

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

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

Informações sobre o teclado e gamepad são adquiridas, e o final do jogo é determinado.

Atualizar métodoCamera

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

A câmera é controlada para se mover.

Também inclui cálculos como aceleração e atrito, mas acho que provavelmente está levando em conta o efeito Doppler no som 3D. Como não faço nada de especial sobre o cálculo em si, não vou discutir isso.

Resumo

Eu cobri-o longamente, mas geralmente é o suficiente para se referir à classe para AudioManager como reproduzir som 3D. Na verdade, a maioria das dificuldades são feitas pelo framework, porque o que SoundEffectInstance estamos fazendo é definir informações 3D em . Se você experimentou apenas as partes do som 3D, você vai acabar escrevendo um pouco de trabalho na classe Jogo. No entanto, estou escrevendo dicas baseadas na história original, então tem sido muito mais tempo.