
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
このレッスンで分解して実行するものは何ですか?
- XNA Frameworkアセンブリを接続する
- 空白の背景ペイントアプリケーションを作成する
- コンテンツのアップロードを学ぶ
- サウンドの操作方法を学びます。
- グラフィックスの使用方法を学ぶ
どのゲームを実装しますか?
ゲームの仕組みは狂気に単純です。 このゲームの場合、意味は音楽に基づいて構築され、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またはプロファイル内の連絡先に書いてください。