XNAの紹介と最初の音楽ゲームの作成

初心者のゲーム開発者のみなさん、そして素敵な人々のみなさん、こんにちは。 今日は、すばらしいXNAフレームワーク(dotNetランタイムランタイムツールキット)を紹介します。 C#でプログラムします。

XNAをより詳しく紹介するために、単純な「音楽的な」2Dおもちゃを書くことをお勧めします。 残りはカットの下にあります。





ウィキペディアの簡単な説明


Microsoft XNA (English XNA's Acronymed-Microsoftによって作成され、コンピューターゲームの開発と管理を容易にする、制御されたランタイム(.NET)を備えたツールのセット。 XNAは「ボイラープレートコードの繰り返し」の記述からゲーム開発を解放するよう努めています



これには何が必要ですか?


1)Fresh DirectX( 例:2010年6月

2)Microsoft Visual C#2010 EXPRESS( 無料ライセンス

3) Microsoft XNA Game Studio 4.0



このレッスンで分解して実行するものは何ですか?






どのゲームを実装しますか?


ゲームの仕組みは狂気に単純です。 このゲームの場合、意味は音楽に基づいて構築され、Isaac Shepard-Leaves in the Windの構成が使用されます。 マウスで「ノート」をキャッチする必要があります。マウスの速度と数は、おおよそゲームの「ビジュアライザー」と言えば、音楽の現在の位置に依存します。 変更には、 通常 (敵)、 (力)、 点滅 (すべてが黄色に変わる)、 黄色 (スコア付けとサイズの速度を上げる)の5種類のノートがあります。



空のプロジェクトをまとめる


まず、必要なすべてのコンポーネントを順番に配置してから、Microsoft Visual C#2010 EXPRESSを起動し、Windows Game(4.0)プロジェクトを作成してmusic_catchと呼びます。







空のプロジェクトが作成されます。これは、コンパイル時にアプリケーションの「画面」をクリアするだけなので、新しいプロジェクトの構造を詳しく見てみましょう。









music_catchプロジェクトは、アプリケーションの「ロジック」です。

Game1.cs-アプリケーションのメインクラス。Microsoft.Xna.Framework.Gameから継承されます。

Program.csは、アプリケーションへの「エントリポイント」であり、私たちにとって興味深いものではありません。

music_catchContentプロジェクトはアプリケーションの「コンテンツ」であり、リソースをそこに配置します。



Game1.csを詳しく見てみましょ

その中で、次のような主な機能を強調表示できます。

Game1()は、クラスのコンストラクターです。

Initialize() -アプリケーションの初期化。

LoadContent() -コンテンツのロード。

UnloadContent() -コンテンツをアップロードします。

更新(GameTime gameTime) -アプリケーションロジック(物理など)を更新します

Draw(GameTime gameTime) -ゲームをレンダリングします。 注意、描画の操作はここでのみ実行する必要があります。



空のプロジェクトが組み立てられました。先に進み、アプリケーションにリソースを追加し、必要なすべてのリソースをmusic_catch \ music_catchContentフォルダーに「スロー」します。 この場合、5つのPNGファイルと1つの音楽伴奏です。 これをすべてプロジェクトに追加します。











同じ場所でフォントを作成し、SpriteFont1.spritefontの本体で名前とサイズを指定します。

<FontName>Segoe UI Mono</FontName> <Size>14</Size>
      
      















将来のコンテンツの変数を作成します。

 private List<Texture2D> MelList; private Texture2D mouse; private Song song; private SpriteFont font;
      
      







そして、それをLoadContent()にロードします:



 MelList = new List<Texture2D>(); for(int a = 1; a <= 5; a++) MelList.Add(Content.Load<Texture2D>("mel" + a)); mouse = Content.Load<Texture2D>("mouse"); song = Content.Load<Song>("Leaves_in_the_Wind"); font = Content.Load<SpriteFont>("SpriteFont1");
      
      







ところで、コンテンツは次のようにロードされます。Content.Load<>( "asset")を呼び出します。

コンテンツプロセッサは三角形の括弧で示されています。この場合、Texture2D、Song、SpriteFontです。 プロセッサを使用できます。これについては後で説明します。



コンテンツが読み込まれ、Game1()コンストラクターに移動して、以下を記述します。

 graphics = new GraphicsDeviceManager(this); graphics.PreferredBackBufferWidth = 800; //   graphics.PreferredBackBufferHeight = 600; //   graphics.IsFullScreen = false; //    graphics.ApplyChanges(); //   Content.RootDirectory = "Content";
      
      







アプリケーションが初期化されます。



「ゲームロジック」を書く




次に、パーティクルシステムのコントローラーとパーティクル自体(注)を作成する必要があります。これらをマウスで見事にキャッチします。

Catcher(パーティクル自体)とCatcherHolder(パーティクルシステム)の2つのクラスを作成します。



コメント付きのキャッチャーリスト:

 using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; namespace MusicCatch { public class Catcher { public Texture2D Texture { get; set; } //   public Vector2 Position { get; set; } //   public Vector2 Velocity { get; set; } //   public float Angle { get; set; } //    public float AngularVelocity { get; set; } //    public Color Color { get; set; } //   public float Size { get; set; } //   public int TTL { get; set; } //    private float RComponent; //   RGB private float GComponent; //   RGB private float BComponent; //   RGB public int type; //   private Random random; //    public Catcher(Texture2D texture, Vector2 position, Vector2 velocity, float angle, float angularVelocity, int type, float size, int ttl) { //     Texture = texture; Position = position; Velocity = velocity; Angle = angle; AngularVelocity = angularVelocity; this.type = type; Size = size; TTL = ttl; SetType(type); //      } public void ApplyImpulse(Vector2 vector) //   ( ) { Velocity += vector; } public void Update() //    { TTL--; Position += Velocity; Angle += AngularVelocity; if (type != -1) { Velocity = new Vector2(Velocity.X, Velocity.Y - .1f); Size = (10 + Velocity.Y) / 20; if(Size > 0.8f) Size = 0.8f; } if (type == 0) { GComponent -= 0.005f; BComponent += 0.005f; Color = new Color(RComponent, GComponent, BComponent); } else if (type == 4) { Color = new Color((float)(1f * random.NextDouble()), (float)(1f * random.NextDouble()), (float)(1f * random.NextDouble())); } } public void Draw(SpriteBatch spriteBatch) //   { Rectangle sourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height); Vector2 origin = new Vector2(Texture.Width / 2, Texture.Height / 2); spriteBatch.Draw(Texture, Position, sourceRectangle, Color, Angle, origin, Size, SpriteEffects.None, 0f); } public void SetType(int type) //    { this.type = type; Color StartColor = new Color(1f, 1f, 1f); switch (type) { case 0: StartColor = new Color(0f, 1f, 0f); break; //  case 1: StartColor = new Color(1f, 0f, 0f); break; //  case 2: StartColor = new Color(1f, 0f, 1f); break; //  case 3: StartColor = new Color(1f, 1f, 0f); break; //  case 4: random = new Random(); break; //  } RComponent = ((int)StartColor.R) / 255f; GComponent = ((int)StartColor.G) / 255f; BComponent = ((int)StartColor.B) / 255f; Color = new Color(RComponent, GComponent, BComponent); if (type == -1) { Color = new Color(1f, 1f, 1f, 0.1f); } } } }
      
      







コメント付きのCatcherHolderリスト:

 using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; namespace MusicCatch { class CatcherHolder { private Random random; //    public List<Catcher> particles; //   (Catcher) private List<Texture2D> textures; //   public List<float> accomulator { get; set; } //  float-,      accomulator —   . public CatcherHolder(List<Texture2D> textures) { this.textures = textures; this.particles = new List<Catcher>(); random = new Random(); accomulator = new List<float>(); //       128  — 1.0f for (int a = 0; a < 128; a++) { accomulator.Add(1.0f); } } //    // Wave - ,   0f   . private Catcher GenerateNewParticle(float Wave) { Texture2D texture = textures[random.Next(textures.Count)]; //      Vector2 position = new Vector2(Wave, 0); //   Vector2 velocity = new Vector2((float)(random.NextDouble() - 0.5), (float)(random.NextDouble() * 10)); //  , 0.5f  X  10f  Y float angle = 0; //   = 0 float angularVelocity = 0.05f * (float)(random.NextDouble()*2 - 1 ); //    Color color = new Color(0f, 1f, 0f); //   (     Catcher) float size = (float)random.NextDouble()*.8f + .2f; //   int ttl = 400; //    400 (400    , .. 400 / 60 — 6   . int type = 0; —   0 //   if (random.Next(10000) > 9900) //  type = 1; else if (random.Next(10000) > 9950) //  type = 3; else if (random.Next(10000) > 9997) //  type = 2; else if (random.Next(10000) > 9998) //  type = 4; return new Catcher(texture, position, velocity, angle, angularVelocity, type, size, ttl); //      } //         public void GenerateYellowExplossion(int x, int y, int radius) { Texture2D texture = textures[random.Next(textures.Count)]; Vector2 direction = Vector2.Zero; float angle = (float)Math.PI * 2.0f * (float)random.NextDouble(); float length = radius * 4f; direction.X = (float)Math.Cos(angle); direction.Y = -(float)Math.Sin(angle); Vector2 position = new Vector2(x, y) + direction * length; Vector2 velocity = direction * 4f; float angularVelocity = 0.05f * (float)(random.NextDouble() * 2 - 1); float size = (float)random.NextDouble() * .8f + .2f; int ttl = 400; int type = 3; particles.Add(new Catcher(texture, position, velocity, 0, angularVelocity, type, size, ttl)); } // "" ,   public void Beat(float Wave) { particles.Add(GenerateNewParticle(Wave)); } public void Update() //    { for (int particle = 0; particle < particles.Count; particle++) { particles[particle].Update(); if (particles[particle].Size <= 0 || particles[particle].TTL <= 0) { //        ,   particles.RemoveAt(particle); particle--; } } //  ,     1f,   ,     Constants — ACCUMULATE_SPEED,  Constanst - . for (int a = 0; a < 128; a++) if (accomulator[a] < 1.0f) accomulator[a] += Constanst.ACCUMULATE_SPEED; } public void Draw(SpriteBatch spriteBatch) { //   ,   BlendState.Additive,     "". spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive); for (int index = 0; index < particles.Count; index++) { particles[index].Draw(spriteBatch); } spriteBatch.End(); } } }
      
      







Constant.csのリスト:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MusicCatch { public class Constanst { public const float BEAT_COST = .4f; // "" ,        public const float ACCUMULATE_SPEED = .01f; //   public const float BEAT_REACTION = .5f; //    ""   public const float ACCOMULATOR_REACTION = .5f; //      ,      } }
      
      







神秘的なバッテリーとは何か、なぜ必要なのかを説明します。 「音楽」のスペクトルについて話しましょう。



音楽信号は、オーディオシステムにとって重要な要素です。 より正確に-そうではありません。 スピーカーは音楽を聴かないで、脳によって復元され、多くの周波数成分を含む複雑な信号を受け取ります。

ダック、アイデアは、各更新プログラムの「頻度」を聞いて、たとえばVisualizationDataなどに書き込むことです。 単純に、0fから1fの範囲の128要素の配列に入れます。



これはどのように使用できますか?

各更新:配列の値は音楽に従って変化します。要素の値が0.6fを超える場合、128個の要素すべてをチェックし、Beat関数を呼び出してWave(イベントが発生した配列要素のインデックス)を渡す必要があります。 すべてが良好です。Beatでメモを作成できます。 しかし、同じインデックスで値が0.6fを超える3つの更新が連続しており、その結果、1秒あたり100500個のパーティクルがあると想像してください。 このようなことが起こらないようにするには、バッテリーを使用します。 その意味は単純です:ビート中、定数BEAT_COSTは対応するWaveインデックスのバッテリーアレイのセルから削除されます。 各アップデートは、すべてのバッテリーセルにACCUMULATE_SPEEDを追加します。 Beatを呼び出す前に、条件が満たされているかどうかが確認されます-バッテリー値> ACCOMULATOR_REACTION、もしそうなら、Beatを呼び出します。 これにより問題が解決します。



ところで、BEAT_REACTIONは値です。その後、Beatを呼び出す価値があるかどうかを確認する必要があります。



以下は、GameLogic(Game1)の完全なリストです。 たくさんのコードがありますが、コメントに書いてみます。

 using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; namespace MusicCatch { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; private List<Texture2D> MelList; private Texture2D mouse; private CatcherHolder m_cHolder; MediaLibrary mediaLibrary; //   "" Song song; //   VisualizationData visualizationData; SpriteFont font; private int scores = 0; //  private float self_size = 1f; //  "" private int xsize = 1; //   private float power = 0f; //     private float activity = 0f; //     public Game1() { graphics = new GraphicsDeviceManager(this); graphics.PreferredBackBufferWidth = 800; graphics.PreferredBackBufferHeight = 600; graphics.IsFullScreen = false; graphics.ApplyChanges(); Content.RootDirectory = "Content"; //   mediaLibrary = new MediaLibrary(); visualizationData = new VisualizationData(); scores = 0; } protected override void Initialize() { m_cHolder = new CatcherHolder(MelList); MediaPlayer.Play(song); //    MediaPlayer.IsVisualizationEnabled = true; //   base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); MelList = new List<Texture2D>(); for(int a = 1; a <= 5; a++) MelList.Add(Content.Load<Texture2D>("mel" + a)); mouse = Content.Load<Texture2D>("mouse"); song = Content.Load<Song>("Leaves_in_the_Wind"); font = Content.Load<SpriteFont>("SpriteFont1"); } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { m_cHolder.Update(); MediaPlayer.GetVisualizationData(visualizationData); //   // ""   ,   for (int a = 0; a < 128; a++) { if (visualizationData.Frequencies[a] > Constanst.BEAT_REACTION && m_cHolder.accomulator[a] > Constanst.ACCOMULATOR_REACTION) { m_cHolder.Beat(a * 3.125f * 2); //  "",   . m_cHolder.accomulator[a] -= Constanst.BEAT_COST; //   } } // ,   ,       if (power > 0f) { for (int particle = 0; particle < m_cHolder.particles.Count; particle++) { if (m_cHolder.particles[particle].type != 1) //   ,   { float body1X = m_cHolder.particles[particle].Position.X; float body1Y = m_cHolder.particles[particle].Position.Y; float body2X = (float)Mouse.GetState().X; float body2Y = (float)Mouse.GetState().Y; float Angle = (float)Math.Atan2(body2X - body1X, body2Y - body1Y) - ((float)Math.PI / 2.0f); //     float Lenght = (float)(5000f * power) / (float)Math.Pow((float)Distance(body1X, body1Y, body2X, body2Y), 2.0f); //   m_cHolder.particles[particle].ApplyImpulse(AngleToV2(Angle, Lenght)); //    } } power -= 0.001f; //   } activity -= 0.001f; //    if (activity < 0.0f) activity = 0.0f; else if (activity > 0.5f) activity = 0.5f; //     0f  .5f //    :    for (int particle = 0; particle < m_cHolder.particles.Count; particle++) { int x = (int)m_cHolder.particles[particle].Position.X; int y = (int)m_cHolder.particles[particle].Position.Y; int radius = (int)(16f * m_cHolder.particles[particle].Size); if (circlesColliding(Mouse.GetState().X, Mouse.GetState().Y, (int)(16f * self_size), x, y, radius)) { scores += (int)(10f * m_cHolder.particles[particle].Size * xsize); //  ,        activity += 0.005f; //   int type = m_cHolder.particles[particle].type; //   ,     switch (type) { case 3: //  self_size += 0.1f; xsize += 1; //      if (self_size > 4.0f) self_size = 4.0f; break; case 2: //  power = 1f; //   ,       break; case 4: //  for (int b = 0; b < m_cHolder.particles.Count; b++) m_cHolder.particles[b].SetType(3); //     —  break; case 1: //  () for(int a = 1; a < xsize; a++) m_cHolder.GenerateYellowExplossion(Mouse.GetState().X, Mouse.GetState().Y, (int)(16f * self_size)); xsize = 1; self_size = 1f; scores -= (int)(scores / 4); break; } //   m_cHolder.particles[particle].TTL = 0; m_cHolder.particles.RemoveAt(particle); particle--; } } base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); m_cHolder.Draw(spriteBatch); //  CatcherHolder spriteBatch.Begin(); Rectangle sourceRectangle = new Rectangle(0, 0, mouse.Width, mouse.Height); //   Vector2 origin = new Vector2(mouse.Width / 2, mouse.Height / 2); // offset  Vector2 mouse_vector = new Vector2(Mouse.GetState().X, Mouse.GetState().Y); // ()  string xtext = "x" + xsize.ToString(); //  Vector2 text_vector = font.MeasureString(xtext) / 2.0f; //  offset'a  spriteBatch.Draw(mouse, mouse_vector, sourceRectangle, new Color(0.5f - power/2.0f + activity, 0.5f, 0.5f - power/2.0f), 0.0f, origin, self_size, SpriteEffects.None, 0f); //   spriteBatch.DrawString(font, xtext, mouse_vector - text_vector, Color.White); //   spriteBatch.DrawString(font, "Score: " + scores.ToString(), new Vector2(5, graphics.PreferredBackBufferHeight - 34), Color.White); //   spriteBatch.End(); base.Draw(gameTime); } // ,       bool circlesColliding(int x1, int y1, int radius1, int x2, int y2, int radius2) { int dx = x2 - x1; int dy = y2 - y1; int radii = radius1 + radius2; if ((dx * dx) + (dy * dy) < radii * radii) { return true; } else { return false; } } //      public Vector2 AngleToV2(float angle, float length) { Vector2 direction = Vector2.Zero; direction.X = (float)Math.Cos(angle) * length; direction.Y = -(float)Math.Sin(angle) * length; return direction; } //  public float Distance(float x1, float y1, float x2, float y2) { return (float)Math.Sqrt((float)Math.Pow(x2 - x1, 2) + (float)Math.Pow(y2 - y1, 2)); } } }
      
      







これはとても簡単なおもちゃです。 XNA 4.0および.NETは、ユーザーのエンドマシンにインストールする必要があります



リンク: ゲーム自体直接 )| ソースコード直接 )| XNA Frameworkエンドユーザー



スクリーンショット:





PSアイデアは私のものではなく、そのようなゲームはすでにフラッシュの下でリリースされています。 このゲームは記事専用に作成されたため、これ以上の開発は行われません。

PSSまた、私はXNA /レッスンを理解するのに役立ちます。これは、個人のhabrまたはプロファイル内の連絡先に書いてください。



All Articles