Audio3D

Page mise à jour :
Date de création de la page :

Notes sur ces conseils

Cet exemple est basé sur les programmes publiés sur les sites suivants. Je change un peu le code pour le rendre plus facile à comprendre et à expliquer en japonais. Fondamentalement, nous utilisons le code original tel quel, donc si vous l’adoptez réellement dans votre programme de jeu, corrigez-le en temps opportun et utilisez-le.

Site de référence

En outre, il est expliqué en supposant que vous avez quelques connaissances de base sur MonoGame et XNA. Voir Conseils MonoGame et Conseils XNA pour le ruddly.

En particulier, comme les mathématiques, les vecteurs, les fonctions trigonométriques, les matrices, etc. sont essentiels, sachez donc ce qu’ils sont dans une certaine mesure.

environnement

plateforme
  • Fenêtres 10
  • Le code peut être utilisé sur d’autres plates-formes compatibles MonoGame
Visual Studio
  • Visual Studio 2019
.NET Core
  • 3.1
MonoGame
  • 3.8

À propos des exemples

En fonction de la position de la caméra, vous pouvez entendre l’appel depuis la position du chien et la position du chat. Le chat tourne et entoure l’origine, et le joueur peut contrôler la position de la caméra avec la touche. Assurez-vous que la façon dont vous entendez le son change à chaque changement de position de la caméra ou de la position du chat. Je pense que c’est facile à comprendre en utilisant des écouteurs. Je ne suis pas confirmé sur le canal 5.1.

Comment opérer

Que faire Clavier Gamepad (XInput) Souris Touch
Caméra avant et arrière ↑↓ Bâton gauche (haut et bas) - -
Modifier l’orientation de la caméra ←→ Stick gauche (gauche et droite) - -
Fin de partie Esc Précédent - -

Ce qu’il faut préparer

  • Fichier audio de crissement de chien
  • Trois fichiers audio de cris de chat
  • Fichier image de chien
  • Fichier image cat
  • Fichier image au sol

L’exemple original sur le site officiel utilise des fichiers .xnb de contenu prédéfinis. Si vous souhaitez conserver les exemples sur le site officiel, n’utilisez pas mgcbs, ajoutez-les directement à votre solution Visual Studio et copiez-les au moment de la génération.

Je ne sais pas pourquoi l’échantillon original prend cette méthode, mais je pense que c’est probablement parce que nous migrons les anciennes versions du projet telles quelles. Bien sûr, vous pouvez utiliser mgcb pour générer le même fichier. Les projets qui peuvent être téléchargés sur ce site ont été modifiés à la version MGCB.

programme

Téléchargez le programme pour tout le code.

Cet exemple est conçu pour rendre le code un peu plus général tout en rendant la lecture du son correcte au moment de l’exécution. L’utilisation d’Audio3D uniquement serait beaucoup moins de code, mais elle est décrite le long de l’échantillon original. Je garderai les parties les moins importantes.

Le projet se compose des fichiers de code suivants :

  • Audio3DGame
  • AudioManager
  • Chat
  • Chien
  • IAudioEmitter
  • Programme
  • QuadDrawer
  • SpriteEntity

QuadDrawer, classe

Cette classe est une classe d’assistance pour dessiner des polygones rectangulaires. Les polygones rectangulaires sont souvent utilisés principalement pour afficher des sprites 3D (panneaux d’affichage). Ces conseils sont utilisés pour les panneaux d’affichage pour chats et chiens, ainsi que pour les affichages rectangulaires du sol.

Cette classe n’a rien à voir avec Audio3D.

champ

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

Informations minimales requises pour dessiner des polygones. Dessinez des polygones à partir de données de sommet sans préparer de données de modèle.

constructeur

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

Étant donné que cette classe est universellement utilisable, vous GraphicsDevice recevez une instance créée dans le processus d’initialisation de la classe de jeu.

Ici, nous définissons l’effet nécessaire pour dessiner le sprite et la position des 4 sommets requis pour le polygone rectangulaire. La taille doit être mise à l’échelle lors du dessin, elle est donc de -1 à 1.

DrawQuad, méthode

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

Définit la texture passée sur un rectangle et la dessine. Comme il s’agit d’un processus de base, il n’y a rien à noter.

S’il y a un point, une variable appelée peut être spécifiée dans textureRepeats l’argument, de sorte que les coordonnées de la texture peuvent être modifiées. Si vertexPositionTexture.TextureCoordinate est défini au-delà de la plage 0 à 1, la texture est dessinée à plusieurs reprises par défaut. En l’utilisant, il est possible d’exprimer que les carreaux peuvent être disposés à plusieurs reprises. En fait, les motifs à carreaux sur le sol semblent avoir plusieurs images simples en noir et blanc alignées.

Interface IAudioEmitter

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

Défini comme une entité qui émet du son. Étant donné que les entités qui émettent du son cette fois sont les chiens et les chats, ils sont hérités dans leurs classes respectives. L’émetteur a besoin des informations suivantes :

Utilisation de la propriété
Position Position de l’entité
En avant Direction vers laquelle l’entité fait face (vecteur)
En haut Le haut de l’entité. La direction dans laquelle la tête existe chez une personne
Vitesse Vitesse à laquelle l’entité se déplace. Utilisé pour calculer les valeurs Doppler.

SpriteEntity, classe

Classe de représentation de sprites 3D (panneaux d’affichage). C’est aussi un émetteur, et hérite IAudioEmitter de . Les chiens et les chats héritent de cette classe.

propriété

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

Il contient les informations de localisation de l’entité, etc. Fondamentalement, j’ai des informations en tant qu’émetteur. C’est aussi une entité de dessin, donc il a aussi une image (Texture) à afficher.

Méthode Update

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

Décrit la position de l’entité et le processus de lecture du son, mais l’implémente dans la classe dérivée.

Draw, méthode

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

Les chiens et les chats ont des images et des positions différentes, mais l’autre mécanisme de dessin est exactement le même, alors je les décris ici.

Chaque transformation et texture est transmise à QuadDrawer pour afficher un polygone rectangulaire. Étant donné que la transformation des coordonnées est une connaissance de base, les explications ne sont pas couvertes.

Matrix.CreateConstrainedBillboard Utilisez la méthode et les informations de la caméra pour représenter facilement les panneaux d’affichage, alors faites-en un usage efficace.

Classe de chien

C’est une classe qui fait du dessin de chien et de la lecture de chant. SpriteEntity Hériter de la classe. Le chien reste dans une position fixe dans l’espace 3D.

champ

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

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

_timeDelay est utilisé à intervalles réguliers pour jouer le cri.

SoundEffectInstance _activeSound est une instance pour jouer un son. SoundEffectInstance Comme vous le verrez souvent dans la classe de lecture sonore, vous pouvez l’utiliser tel quel dans le son 3D. Soit dit en passant _activeSound , je ne reçois qu’une référence de la classe AudioManager plus tard, donc je ne la jetterai pas du côté de la classe Dot.

Méthode Update

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

Dans cet échantillon, le chien reste longtemps dans la même position, de sorte que les quatre premiers paramètres sont spécifiés par une frappe décisive. Le dessin se fait dans la classe de base.

_timeDelay est ensuite utilisé comme le temps restant pour s’arrêter, en jouant le cri.

AudioManager sera reproduit dans l’espacePlay3DSound 3D en transmettant le nom du cri, l’information de boucle et vos propres informations en tant qu’émetteur à .

La classe Dog est conçue pour boucler le cri, vous n’avez donc pas à réfléchir trop profondément car il ne s’agit que d’un ajustement, comme jouer ou arrêter de ramifier.

Classe Cat

C’est une classe qui fait du dessin de chat et de la lecture de chant. SpriteEntity Hériter de la classe. Le chat se déplace autour de l’origine.

champ

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

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

_timeDelay est utilisé de la même manière que la classe Chien, le reste du temps avant le prochain cri.

Il existe trois types d’appels de chat qui sont sélectionnés et lus au hasard, mais ils n’ont rien à voir avec Audio3D car ils sont un élément bonus.

Méthode Update

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

Puisque le chat tourne autour de l’origine dans un mouvement circulaire, il le traite de sorte qu’il déplace la trajectoire du cercle à l’aide d’une fonction trigonométrique. En conséquence, la position, l’avant et la vitesse ont changé l’un après l’autre. Par conséquent, lorsque vous exécutez le programme, vous pouvez voir que le cri du chat tourne en rond même si vous ne déplacez pas la caméra.

_timeDelay Le code suivant est un processus dans lequel le cri du chat est assigné au hasard et joué à intervalles réguliers. audioManager.Play3DSound Je me passe à la méthode qu’est un émetteur. Vous pouvez voir que la position de lecture du son change un par un.

AudioManager, classe

C’est enfin l’essence d’Audio3D. Un son 3D se compose d’un auditeur et d’un émetteur. L’émetteur définit déjà les chiens et les chats, de sorte que la classe AudioManager définit les auditeurs. Il y a généralement plusieurs émetteurs, alors qu’il n’y a qu’un seul auditeur.

Cette classe hérite, enregistre GameComponent etGame utilise le composant dans la classe.

ActiveSound, classe

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

Il est défini au bas du code, mais c’est une classe pour préserver l’état qui joue. Il contient des informations sur l’instance sonore en cours de lecture et l’émetteur en cours de lecture.

champ

// このマネージャーにロードされるすべてのサウンドエフェクトのリスト。
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 définit le nom du fichier audio (nom de la ressource) à charger. Comme il s’agit d’un exemple de programme, il est défini pour charger d’abord le fichier audio en vrac. Les données _soundEffects audio importées sont stockées dans .

AudioListener Listener est la définition de l’auditeur. Les informations de l’écouteur sont définies dans car elles public sont définies à partir de la classe Game.

AudioEmitter _emitter est la définition de l’émetteur lors de l’application de la 3D à la lecture sonore. Dans cet exemple, lors de la lecture d’un son, la valeur de chaque objet émetteur est définie sur _emitter , il s’agit donc d’un formulaire qui en partage un en tant qu’instance. Bien sûr, vous pouvez avoir pour chaque AudioEmitter objet.

_activeSounds contient des informations sur le son que vous jouez dans la classe que vous avez définie ActiveSound précédemment. Étant donné que les informations de l’émetteur changent tout le temps, elles sont utilisées pour mettre à jour les informations de localisation, etc., et pour ignorer les instances sonores qui ont terminé la lecture.

constructeur

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

GameComponent Comme il hérite de la classe, Game il reçoit l’instance et la transmet à la classe de base.

Initialize, méthode

/// <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 Parce qu’il hérite de la classe, il est automatiquement appelé lors de l’initialisation.

SoundEffect.DistanceScale et SoundEffect.DopplerScale sont des paramètres dédiés au static son 3D.

SoundEffect.DistanceScale pour entendre le son même au loin.

SoundEffect.DopplerScale est l’influence de l’effet Doppler. Plus le nombre est élevé, plus l’effet Doppler est important.

_soundNames Enregistrez le nom de la ressource défini dans une boucle pour charger _soundEffects .

Méthode d’élimination

/// <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 Il est automatiquement appelé à la fin du jeu car il hérite de la classe.

Destruction de toutes les ressources sonores importées.

Méthode Update

/// <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 Comme il hérite de la classe, il est automatiquement appelé dans le processus de mise à jour.

Vérifiez le son en cours de lecture et, s’il est en cours de lecture, appelez la méthode pour mettre à jour l’emplacement du son afin qu’il Apply3D corresponde à l’émetteur. Si la lecture d’un son est terminée, l’instance est ignorée.

Play3DSound, méthode

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

Le processus de lecture de son 3D de l’extérieur. Il a le nom du son à jouer, s’il faut boucler, et les informations de l’émetteur.

Si vous souhaitez lire un son, créez une nouvelle instance sonore à partir de la ressource sonore que vous conservez dans le _soundEffects. SoundEffectInstance peut également avoir une boucle ou non, alors définissez les informations de boucle.

Comme je crée en tant qu’état ActiveSound de lecture, j’y définis les informations de l’émetteur qui sont l’objet de lecture du son. Si vous souhaitez appliquer des informations de localisation 3D à un son, vous obtiendrez Emitter la valeur dont vous avez besoin.

Apply3D La méthode est décrite ci-dessous, mais il s’agit du processus d’application des informations 3D de l’émetteur au son.

Le processus de base du son 3D consiste à démarrer la lecture avec SoundEffectInstance.Play un ensemble d’informations 3D. Le reste consiste à mettre à jour les informations 3D périodiquement jusqu’à ce que la lecture du son soit terminée.

Apply3D, méthode

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

Processus d’application d’un effet 3D à une instance sonore spécifiée à l’aide d’écouteurs et d’émetteurs.

Nous définissons la valeur sur la _emitter de l’instance commune à chaque fois. Si vous SoundEffectInstance.Apply3D en avez déjà AudioEmitter un, passez simplement l’auditeur et l’émetteur à la méthode.

Audio3DGame, classe

Enfin, regardons ce qu’est la classe Game.

champ

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 S’inscrit GameComponents dans , mais l’a comme champ car il y accède individuellement.

D’autres définissent des entités de chien, de chat, des textures pour le dessin au sol, un quadri-dessinateur, des informations de caméra et des informations d’entrée.

constructeur

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 Générez et GameComponents enregistrez-vous avec .

Vous avez également généré les classes Chat et Chien.

LoadContent, méthode

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

Chargez les textures nécessaires pour chaque dessin.

QuadDrawer est GraphicsDevice généré ici car nécessite .

Méthode Update

/// <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 Les méthodes et UpdateCamera les méthodes sont le processus de récupération de l’entrée de l’appareil et de manipulation de la caméra, qui sera discuté plus tard.

Étant donné que la position de la caméra et la position de l’écouteur sont presque toujours les mêmes, l’écouteur est réglé sur la même valeur après le processus de mise à jour de la caméra.

_cat Appelez les méthodes et _dog Update pour vous déplacer et crier, respectivement.

Draw, méthode

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

Génère des transformations de vue et de projection à partir des informations de la caméra.

Pour le sol, le rectangle dessiné dans QuadDrawer est affiché verticalement, il est donc tourné pour être horizontal et agrandi modérément. _quadDrawer.DrawQuad Le deuxième argument de la méthode spécifie le nombre de répétitions de la texture, qui spécifie que les feuilles 32x32 apparaissent côte à côte.

_cat et _dog sont affichés en interne, de sorte que les informations de localisation de la caméra sont transmises et dessinées.

HandleInput, méthode

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

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

Les informations sur le clavier et la manette de jeu sont acquises et la fin du jeu est déterminée.

UpdateCamera, méthode

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

La caméra est contrôlée pour se déplacer.

Il inclut également des calculs tels que l’accélération et le frottement, mais je pense que cela prend probablement en compte l’effet Doppler sur le son 3D. Comme je ne fais rien de spécial au sujet du calcul lui-même, je n’en discuterai pas.

Résumé

Je l’ai longuement couvert, mais il suffit généralement de se référer à la classe pour AudioManager savoir comment jouer du son 3D. En fait, la plupart des difficultés sont faites par le framework, car ce SoundEffectInstance que nous faisons est de définir des informations 3D dans . Si vous n’avez échantillonné que les parties du son 3D, vous finirez par écrire un peu de travail dans la classe Jeu. Cependant, j’écris des conseils basés sur l’histoire originale, donc cela fait beaucoup plus longtemps.