Launcher2D is developed with XNA Framework 4.0. It is a simple 2D game which uses simple parabolic motion, simple 2D collision detection and particle system. The following will explain how the game is developed.
Since the game is to shoot missiles to the planes which are flying in the sky. We will first design and code the Missile object. In fact, Missile object is the most important object in the game.
Before, we actually write the code. Let us give a thought about what should include in the Missile Object. Graphically, the missile will be composed of 2 objects: the rocket and the smoke tail. Therefore, before doing anything else, we need to define 2 Texture2D Rocket and Smoke. Texture2D objects are nothing but image objects. After adding 2 Graphical Objects for display, we need to define how the missile will actually behave.
Here, we have to think about it in 2 different inter-related perspective: object states and laws of physics. Object States will define the life cycle of the object. In our case, it is all about when we will create the Missile, when we will destroy the Missile, etc. Well, obviously, there are at least 4 different states for a missile: Shooting, Flying, Hitting, Deleting. When user press a key or click a mouse, the missile object will be created and it will be in Shooting State. Of course, the missile has to move, so as soon as it is shot it will be in flying state. When the missile hit the target, it will be in hitting state. Also, the missile cannot exist forever, either if it hits the target or not, so after some time, the missile has to be deleted from the memory.
Also, the Missile has to follow the laws of physics so that it will look realistic when flying. So, we need to define the position of missile, its direction, its velocity etc.
Missile Class:
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 Launcher2D { class Missile { public Texture2D Rocket; public Texture2D Smoke; public Vector2 Position; public float Angle; public Vector2 Direction; public Boolean IsFlying; public Boolean IsClearing; public Boolean IsHit; public Boolean IsAvailable { get { return !(IsFlying || IsClearing); } } public Rectangle MyBound { get { Rectangle rec = new Rectangle((int)Position.X, (int)Position.Y, Rocket.Width, Rocket.Height); return rec; } } public float Power; public float Scale; private float scalefactor; Vector2[] smokeList; int smokeindex; Random randomizer = new Random(); public Missile(Texture2D rocket, Texture2D smoke) { Rocket = rocket; Smoke = smoke; IsClearing = false; smokeList = new Vector2[300]; smokeindex = 0; scalefactor = Scale; } public void DrawRocket(SpriteBatch spriteBatch, GameTime gameTime) { if (IsFlying && !IsHit) { spriteBatch.Draw(Rocket, Position, null, Color.White, Angle, Vector2.Zero, Scale, SpriteEffects.None, 1); for (int i = smokeList.Count()-1; i >=0; i--) { spriteBatch.Draw(Smoke, smokeList[i], null, Color.White, Angle, Vector2.Zero, Scale, SpriteEffects.None, 1); } scalefactor = Scale; } if (IsClearing) { if (scalefactor <= 0) { IsClearing = false; IsHit = false; for (int i = smokeList.Count() - 1; i >= 0; i--) { smokeList[i] = new Vector2(-100,-100); } } else { scalefactor -= 0.001f; for (int i = smokeList.Count() - 1; i >= 0; i--) { spriteBatch.Draw(Smoke, smokeList[i], null, Color.White, Angle, Vector2.Zero, scalefactor, SpriteEffects.None, 1); } } } } public void Update() { if (IsFlying && !IsHit) { Matrix rotMatrix = Matrix.CreateRotationZ(Angle); Direction = Vector2.Transform(new Vector2(0,-1), rotMatrix); Direction *= Power/10; Vector2 smokePos = Position; smokePos.X += randomizer.Next(10) - 5; smokePos.Y += randomizer.Next(10) - 5; smokeList[smokeindex] = smokePos; smokeindex = (smokeindex + 1); if (smokeindex == smokeList.Count() - 1) { IsFlying = false; smokeindex = 0; IsClearing = true; } Vector2 gravity = new Vector2(0, 1); Direction += gravity / 10; Position += Direction; Angle = (float)Math.Atan2(Direction.X, -Direction.Y); float x = (Rocket.Width * Scale) / 2; float y = (Rocket.Height * Scale) / 100f; float x1 = (Smoke.Width * Scale) / 2; float y1 = (Smoke.Height * Scale) / 2; } } } } |
Plane Class:
class Plane { public Texture2D Airplane; public Vector2 Position; public float Angle; public Boolean IsAlive; public float Scale; public Rectangle MyBound { get { Rectangle rec = new Rectangle((int)Position.X, (int)Position.Y, Airplane.Width, Airplane.Height); return rec; } } public Plane(Texture2D plane) { Airplane = plane; Position.X = 200; Position.Y = 100; IsAlive = true; } public void Draw(SpriteBatch spriteBatch, GameTime gameTime) { if (IsAlive) { spriteBatch.Draw(Airplane, Position + new Vector2(20, 10), null, Color.White, Angle, Vector2.Zero, Scale, SpriteEffects.None, 0); } } public void Update(float Left, float Right, float Speed, Boolean IsAngle) { if (IsAlive) { if (Position.X < Left) { Position.X = Right; } Position.X -= Speed; Position.Y = 50 * (float)Math.Sin(0.008 * (double)Position.X) + 100; if (IsAngle) Angle = 0.9f * (float)Math.Cos(0.008 * (double)Position.X); else Angle = 0; } else { Position.X = Right; Position.Y = 100; IsAlive = true; } } } |
Launcher Class:
class Launcher { public Texture2D Missile; public Texture2D Career; public Vector2 Position; public float Angle; public float Power; public float Scale; public Launcher(Texture2D missile) { Missile = missile; } public void Draw(SpriteBatch spriteBatch, GameTime gameTime) { spriteBatch.Draw(Missile, Position, null, Color.White, Angle, Vector2.Zero, Scale, SpriteEffects.None, 0); } } } |
Game Class, which includes particles, collision detection, and game loop.
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 Launcher2D { public struct ParticleData { public float BirthTime; public float MaxAge; public Vector2 OrginalPosition; public Vector2 Accelaration; public Vector2 Velocity; public Vector2 Position; public float Scaling; public Color ModColor; } /// /// This is the main type for your game /// public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D Background; Texture2D Mountain; Texture2D Explosion; Texture2D Smoke; SpriteFont MyFont; Launcher L1; Plane P1, P2, P3; Missile R1, R2,R3,R4, R5; int MissileCount = 5; String Text; Random Randomizer; List KeyboardState OldState; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } /// /// Allows the game to perform any initialization it needs to before starting to run. /// This is where it can query for any required services and load any non-graphic /// related content. Calling base.Initialize will enumerate through any components /// and initialize them as well. /// protected override void Initialize() { // TODO: Add your initialization logic here OldState = Keyboard.GetState(); base.Initialize(); } /// /// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); Background = Content.Load Mountain = Content.Load Explosion = Content.Load Smoke = Content.Load ParticleList = new List Randomizer = new Random(); L1 = new Launcher(Content.Load L1.Position = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height-50); L1.Power = 20; L1.Scale = 0.2f; MyFont = Content.Load P1 = new Plane(Content.Load P1.Position = new Vector2(100, 40); P1.Scale = 0.8f; P2 = new Plane(Content.Load P2.Position = new Vector2(200, 60); P2.Scale = 0.7f; P3 = new Plane(Content.Load P3.Position = new Vector2(300, 80); P3.Scale = 0.9f; R1 = new Missile(Content.Load Content.Load R1.Scale = 0.2f; R2 = new Missile(Content.Load Content.Load R2.Scale = 0.2f; R3 = new Missile(Content.Load Content.Load R3.Scale = 0.2f; R4 = new Missile(Content.Load Content.Load R4.Scale = 0.2f; R5 = new Missile(Content.Load Content.Load R5.Scale = 0.2f; // TODO: use this.Content to load your game content here } private void AddExplosion(Vector2 explosionPos, int numberOfParticles, float size, float maxAge, GameTime gameTime) { ParticleList.Clear(); for (int i = 0; i < numberOfParticles; i++) AddExplosionParticle(explosionPos, size, maxAge, gameTime); } private void AddExplosionParticle(Vector2 explosionPos, float explosionSize, float maxAge, GameTime gameTime) { ParticleData particle = new ParticleData(); particle.OrginalPosition = explosionPos; particle.Position = particle.OrginalPosition; particle.BirthTime = (float)gameTime.TotalGameTime.TotalMilliseconds; particle.MaxAge = maxAge; particle.Scaling = 1f; particle.ModColor = Color.White; float particleDistance = (float) Randomizer.NextDouble() * explosionSize; Vector2 displacement = new Vector2(particleDistance, 0); float angle = MathHelper.ToRadians(Randomizer.Next(360)); displacement = Vector2.Transform(displacement, Matrix.CreateRotationZ(angle)); particle.Velocity = displacement; particle.Accelaration = 2.0f * particle.Velocity; ParticleList.Add(particle); } private void DrawExplosion() { for (int i = 0; i < ParticleList.Count(); i++) { ParticleData particle = ParticleList[i]; spriteBatch.Draw(Explosion, particle.Position, null, particle.ModColor, i, new Vector2(256, 256), particle.Scaling, SpriteEffects.None, 1); spriteBatch.Draw(Smoke, particle.Position, null, particle.ModColor, i, new Vector2(256, 256), 3, SpriteEffects.None, 1); } } private bool IntersectPixels(Missile M1, Plane P1, out Vector2 ExplosionPoint) { if (M1.MyBound.Intersects(P1.MyBound)) { ExplosionPoint = M1.Position; P1.IsAlive = false; M1.IsHit = true; M1.Position.X = 0; M1.Position.Y = 800; M1.IsClearing = true; M1.IsFlying = false; return true; } ExplosionPoint = new Vector2(-100,-100); return false; } private void DrawFont() { spriteBatch.DrawString(MyFont, Text + " Power : " + L1.Power.ToString(), Vector2.Zero, Color.White); } private void ProcessKeyboard() { KeyboardState keybState = Keyboard.GetState(); if (keybState.IsKeyDown(Keys.Left)) L1.Angle += 0.01f; if (keybState.IsKeyDown(Keys.Right)) L1.Angle -= 0.01f; if (keybState.IsKeyDown(Keys.Up) && L1.Power <= 100) L1.Power += 5f; if (keybState.IsKeyDown(Keys.Down) && L1.Power>= 30) L1.Power -= 5f; if (keybState.IsKeyDown(Keys.Space)) { if (!OldState.IsKeyDown(Keys.Space)) { MissileCount = Math.Abs((MissileCount - 1) % 5); Text = "Missiles : " + MissileCount.ToString(); if (R1.IsAvailable) { R1.IsFlying = true; R1.Position = L1.Position; R1.Angle = L1.Angle; R1.Power = L1.Power; } else if (R2.IsAvailable) { R2.IsFlying = true; R2.Position = L1.Position; R2.Angle = L1.Angle; R2.Power = L1.Power; } else if (R3.IsAvailable) { R3.IsFlying = true; R3.Position = L1.Position; R3.Angle = L1.Angle; R3.Power = L1.Power; } else if (R4.IsAvailable) { R4.IsFlying = true; R4.Position = L1.Position; R4.Angle = L1.Angle; R4.Power = L1.Power; } else if (R5.IsAvailable) { R5.IsFlying = true; R5.Position = L1.Position; R5.Angle = L1.Angle; R5.Power = L1.Power; } else { Text = "Reloading..."; MissileCount = 5; } } } OldState = keybState; } private void UpdateParticles(GameTime gameTime) { float now = (float)gameTime.TotalGameTime.TotalMilliseconds; for (int i = ParticleList.Count - 1; i >= 0; i--) { ParticleData particle = ParticleList[i]; float timeAlive = now - particle.BirthTime; if (timeAlive > particle.MaxAge) { ParticleList.RemoveAt(i); } else { float relAge = timeAlive / particle.MaxAge; particle.Position = 0.5f * particle.Accelaration * relAge * relAge + particle.Velocity * relAge + particle.OrginalPosition; float invAge = 1.0f - relAge; particle.ModColor = new Color(new Vector4(invAge, invAge, invAge, invAge)); Vector2 positionFromCenter = particle.Position - particle.OrginalPosition; float distance = positionFromCenter.Length(); particle.Scaling = (50.0f + distance) / 200.0f; ParticleList[i] = particle; } } } /// /// UnloadContent will be called once per game and is the place to unload /// all content. /// protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here ProcessKeyboard(); R1.Update(); R2.Update(); R3.Update(); R4.Update(); R5.Update(); P1.Update(0,GraphicsDevice.Viewport.Width,1.5f,false); P2.Update(0, GraphicsDevice.Viewport.Width, 1f, false); P3.Update(0, GraphicsDevice.Viewport.Width, 2f, false); if (R1.IsFlying) { Vector2 Pos; if (IntersectPixels(R1, P1, out Pos) || IntersectPixels(R1, P2, out Pos) || IntersectPixels(R1, P3, out Pos)) { AddExplosion(Pos, 10,70 , 1000, gameTime); } } if (R2.IsFlying) { Vector2 Pos; if (IntersectPixels(R2, P1, out Pos) || IntersectPixels(R2, P2, out Pos) || IntersectPixels(R2, P3, out Pos)) { AddExplosion(Pos, 10, 70, 1000, gameTime); } } if (R3.IsFlying) { Vector2 Pos; if (IntersectPixels(R3, P1, out Pos) || IntersectPixels(R3, P2, out Pos) || IntersectPixels(R3, P3, out Pos)) { AddExplosion(Pos, 10, 70, 1000, gameTime); } } if (R4.IsFlying) { Vector2 Pos; if (IntersectPixels(R4, P1, out Pos) || IntersectPixels(R4, P2, out Pos) || IntersectPixels(R4, P3, out Pos)) { AddExplosion(Pos, 10, 70, 1000, gameTime); } } if (R5.IsFlying) { Vector2 Pos; if (IntersectPixels(R5, P1, out Pos) || IntersectPixels(R5, P2, out Pos) || IntersectPixels(R5, P3, out Pos)) { AddExplosion(Pos, 10, 70, 1000, gameTime); } } UpdateParticles(gameTime); base.Update(gameTime); } /// /// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here Rectangle rect1 = new Rectangle(0,0,GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height); Rectangle rect = new Rectangle(0, 140, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height); spriteBatch.Begin(); spriteBatch.Draw(Background, rect1, Color.White); spriteBatch.Draw(Mountain, rect, Color.Gray); L1.Draw(spriteBatch, gameTime); P1.Draw(spriteBatch, gameTime); P2.Draw(spriteBatch, gameTime); P3.Draw(spriteBatch, gameTime); DrawFont(); R1.DrawRocket(spriteBatch, gameTime); R2.DrawRocket(spriteBatch, gameTime); R3.DrawRocket(spriteBatch, gameTime); R4.DrawRocket(spriteBatch, gameTime); R5.DrawRocket(spriteBatch, gameTime); spriteBatch.End(); spriteBatch.Begin(SpriteSortMode.Deferred,BlendState.Additive); DrawExplosion(); spriteBatch.End(); base.Draw(gameTime); } } } |
0 comments:
Post a Comment