MonoAsteroids – Del 2

Inledning

Denna artikel är en direkt fortsättning på artikeln MonoAsteroids - Del 1 . Vi ska nu lägga till både meteoriter och skott i spelet. Vi avslutar sedan med att fixa en enkel kollisionshantering så att det faktisk går att skjuta ned meteoriterna.

Artikeln är mer ett stöd till den videogenomgång som mer steg för steg går igenom alla moment. Videogenomgången hittar du i slutet av artikeln.

Två nya klasser och en basklass

De två nya klasserna vi skapar döper vi till Shot.cs och Meteor.cs. Eftersom dessa är IGameObject så passar vi även på att föra in en riktig basklass som definierar upp egenskapena en gång för alla så att vi slipper dubbel kod.

Basklassen döper vi till Gameobject.cs.

GameObject.cs

using Microsoft.Xna.Framework;

namespace MonoAsteroids
{
    abstract class GameObject : IGameObject
    {
        public bool IsDead { get; set; }
        public Vector2 Position { get; set; }
        public float Radius { get; set; }
        public Vector2 Speed { get; set; }
        public float Rotation { get; set; }

        public bool CollidesWith(IGameObject other)
        {
            return (this.Position - other.Position).LengthSquared() < 
				(Radius + other.Radius) * (Radius + other.Radius);
        }
    }
}

Skotten är relativt enkla objekt. De ska flytta och snurra på sig. Det svåra är att placera ut dem på rätt ställe och ge dem rätt hastighet och riktning.

Shot.cs

using Microsoft.Xna.Framework;

namespace MonoAsteroids
{
    class Shot : GameObject
    {
        public Shot()
        {
            Radius = 16;
        }

        public void Update(GameTime gameTime)
        {
            Position += Speed;
            Rotation += 0.08f;

            if (Rotation > MathHelper.TwoPi)
                Rotation = 0;
        }
    }
}

Även meteorit-klassen ärver från basklassen GameObject. Meteoriterna kommer att finnas i tre varianter: stora, mellan och små. Därför för vi redan nu in en enum för att beskriva typen på meteoriterna.

Meteor.cs

using Microsoft.Xna.Framework;

namespace MonoAsteroids
{
    enum MeteorType
    {
        Big,
        Medium,
        Small
    }

    class Meteor : GameObject
    {
        public MeteorType Type { get; private set; }

        public Meteor(MeteorType type)
        {
            Type = type;

            switch(Type)
            {
                case MeteorType.Big:
                    Radius = 42; //48
                    break;
                case MeteorType.Medium:
                    Radius = 24; // 21
                    break;
                case MeteorType.Small:
                    Radius = 16; // 8
                    break;
            }
        }

        public void Update(GameTime gameTime)
        {
            Position += Speed;

            if (Position.X < Globals.GameArea.Left)
                Position = new Vector2(Globals.GameArea.Right, Position.Y);
            if (Position.X > Globals.GameArea.Right)
                Position = new Vector2(Globals.GameArea.Left, Position.Y);
            if (Position.Y < Globals.GameArea.Top)
                Position = new Vector2(Position.X, Globals.GameArea.Bottom);
            if (Position.Y > Globals.GameArea.Bottom)
                Position = new Vector2(Position.X, Globals.GameArea.Top);

            Rotation += 0.04f;
            if (Rotation > MathHelper.TwoPi)
                Rotation = 0;
        }
    }
}

Videogenomgång

Ytterligare ändringar i kod

Som tidigare nämnts så är texten i artikeln här mest ett stöd för videogenomgången. Följ först videogenomgången sedan kan du jämföra din kod med den färdiga som listas under detta avsnitt.

Undvik klipp och klistra! Du lär dig mer att skriva koden steg för steg när du följer videogenomgången.

Globals.cs

using Microsoft.Xna.Framework;

namespace MonoAsteroids
{
    class Globals
    {
        public static int ScreenWidth = 1280;
        public static int ScreenHeight = 720;

        public static Rectangle GameArea
        {
            get
            {
                return new Rectangle(-80, -80, ScreenWidth + 160, 
					ScreenHeight + 160);
            }
        }

        public static Rectangle RespawnArea
        {
            get
            {
                return new Rectangle((int)CenterScreen.X - 200, 
					(int)CenterScreen.Y - 200, 400, 400);
            }
        }

        public static Vector2 CenterScreen
        {
            get { return new Vector2(ScreenHeight / 2, 
				ScreenHeight / 2); }
        }
    }
}
Player.cs

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;

namespace MonoAsteroids
{
    class Player : DrawableGameComponent, IGameObject
    {
        public bool IsDead { get; set; }
        public Vector2 Position { get; set; }
        public float Radius { get; set; }
        public Vector2 Speed { get; set; }
        public float Rotation { get; set; }

        public bool CanShoot { get { return reloadTimer == 0; } }

        private Texture2D playerTexture;
        private int reloadTimer = 0;
        private Random rnd = new Random();

        public Player(Game game) : base(game)
        {
            Position = new Vector2(Globals.ScreenWidth / 2, Globals.ScreenHeight / 2);
        }

        protected override void LoadContent()
        {
            playerTexture = Game.Content.Load<Texture2D>("player");

            base.LoadContent();
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(playerTexture, Position, null, Color.White, Rotation + MathHelper.PiOver2,
                new Vector2(playerTexture.Width / 2, playerTexture.Height / 2), 1.0f, SpriteEffects.None, 0f);
        }

        public override void Update(GameTime gameTime)
        {
            Position += Speed;

            if (reloadTimer > 0)
                reloadTimer--;

            if (Position.X < Globals.GameArea.Left)
                Position = new Vector2(Globals.GameArea.Right, Position.Y);
            if(Position.X > Globals.GameArea.Right)
                Position = new Vector2(Globals.GameArea.Left, Position.Y);
            if (Position.Y < Globals.GameArea.Top)
                Position = new Vector2(Position.X, Globals.GameArea.Bottom );
            if (Position.Y > Globals.GameArea.Bottom)
                Position = new Vector2(Position.X, Globals.GameArea.Top);

            base.Update(gameTime);
        }

        public void Accelerate()
        {
            Speed += new Vector2((float)Math.Cos(Rotation), 
                (float)Math.Sin(Rotation))* 0.15f;

            if(Speed.LengthSquared() > 25)
                Speed = Vector2.Normalize(Speed) * 5;
        }

        public Shot Shoot()
        {
            if (!CanShoot)
                return null;

            reloadTimer = 20;

            return new Shot()
            {
                Position = Position,
                Speed = Speed + 10f * new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation)),
                Rotation = rnd.Next() * MathHelper.TwoPi
            };
        }
    }
}
MonoAsteroids.cs

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MonoAsteroids
{
    public class MonoAsteroids : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D backgroundTexture;
        Player player;
        KeyboardState previousKbState;

        List<Shot> shots = new List<Shot>();
        Texture2D laserTexture;

        List<Meteor> meteors = new List<Meteor>();
        Texture2D meteorBigTexture;
        Random rnd = new Random();

        public MonoAsteroids()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferHeight = Globals.ScreenHeight;
            graphics.PreferredBackBufferWidth = Globals.ScreenWidth;

            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            player = new Player(this);
            Components.Add(player);
            ResetMeteors();

            base.Initialize();
        }

        public void ResetMeteors()
        {
            while (meteors.Count < 10)
            {
                var angle = rnd.Next() * MathHelper.TwoPi;
                var m = new Meteor(MeteorType.Big)
                {
                    Position = new Vector2(Globals.GameArea.Left + (float)rnd.NextDouble() * Globals.GameArea.Width,
                        Globals.GameArea.Top + (float)rnd.NextDouble() * Globals.GameArea.Height),
                    Rotation = angle,
                    Speed = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * rnd.Next(20, 60) / 30.0f
                };

                if (!Globals.RespawnArea.Contains(m.Position))
                    meteors.Add(m);
            }
        }


        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            backgroundTexture = Content.Load<Texture2D>("background");
            laserTexture = Content.Load<Texture2D>("laser");
            meteorBigTexture = Content.Load<Texture2D>("meteorBrown_big4");
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            KeyboardState state = Keyboard.GetState();

            if (state.IsKeyDown(Keys.Up))
                player.Accelerate();
            if (state.IsKeyDown(Keys.Left))
                player.Rotation -= 0.05f;
            else if (state.IsKeyDown(Keys.Right))
                player.Rotation += 0.05f;

            if(state.IsKeyDown(Keys.Space))
            {
                Shot s = player.Shoot();
                if(s != null)
                    shots.Add(s);
            }

            foreach (Shot shot in shots)
            {
                shot.Update(gameTime);
                Meteor meteor = meteors.FirstOrDefault(m => m.CollidesWith(shot));

                if(meteor != null)
                {
                    meteors.Remove(meteor);
                    shot.IsDead = true;
                }
            }

            foreach (Meteor metor in meteors)
                metor.Update(gameTime);

            shots.RemoveAll(s => s.IsDead || !Globals.GameArea.Contains(s.Position));

            player.Update(gameTime);
            previousKbState = state;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();

            for (int y = 0; y < Globals.ScreenHeight; y += backgroundTexture.Width)
            {
                for (int x = 0; x < Globals.ScreenWidth; x += backgroundTexture.Width)
                {
                    spriteBatch.Draw(backgroundTexture, new Vector2(x, y), Color.White);
                }
            }

            foreach (Shot s in shots)
            {
                spriteBatch.Draw(laserTexture, s.Position, null, Color.White, s.Rotation,
                new Vector2(laserTexture.Width / 2, laserTexture.Height / 2), 1.0f, SpriteEffects.None, 0f);
            }

            foreach(Meteor meteor in meteors)
            {
                spriteBatch.Draw(meteorBigTexture, meteor.Position, null, Color.White, meteor.Rotation,
                new Vector2(meteorBigTexture.Width / 2, meteorBigTexture.Height / 2), 1.0f, SpriteEffects.None, 0f);
            }

            player.Draw(spriteBatch);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *

Scroll to top