MonoGame Worms – Del 5

Denna serie är gjorde med MonoGame 3.8.1. Du hittar källkod och övrigt material länkat.

MapGenerator.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;

namespace WormsGame
{
    internal class MapGenerator
    {
        public int Width { get; set; }
        public int Height { get; set; }
        public Texture2D LevelTexture { get; set; }

        private Random rnd = new Random();
        private Color[] colorData;

        public MapGenerator(int width, int height)
        {
            Width = width;
            Height = height;
        }

        public void Generate(GraphicsDevice device)
        {
            colorData = new Color[Width * Height];
            List<double> HeightMap = new List<double> { 0, 0 };
            LevelTexture = new Texture2D(device, Width, Height);

            double Max = 1.0;
            double Roughness = ((double)100 / 100.0);

            for (int j = 0; j < 10; j++)
            {
                int count = HeightMap.Count;
                for (int i = 0; i < count - 1; i++)
                {
                    double tmp = (HeightMap[i * 2] + HeightMap[i * 2 + 1]) / 2.0;
                    double offset = (rnd.NextDouble() * 2.0 - 1.0) * Max;
                    HeightMap.Insert(i * 2 + 1, tmp + offset);
                }
                Max = Max * Math.Pow(2, -Roughness);
            }

            for(int x = 0; x < Width; x++)
            {
                double index = (x / (double)Width) * (HeightMap.Count - 1);
                int start = (int)Math.Floor(index);

                double r2 = index - Math.Floor(index);
                double r1 = 1.0 - r2;
                double height = HeightMap[start] * r1 + HeightMap[start + 1] * r2;

                var yStart = (int)(300 + 250.0 * height);
                for (int y = yStart; y < Height; y++)
                    colorData[y * Width + x] = Color.Chocolate;
            }

            LevelTexture.SetData(colorData);
        }

        public bool IsLand(Vector2 pos)
        {
            int x = (int)pos.X;
            int y = (int)pos.Y;

            if (x < 0 || x >= Width || y >= Height)
                return true;
            if (y < 0)
                return false;

            return (colorData[x + y * Width] != Color.Transparent);
        }

        public static Rectangle Intersection(Rectangle r1, Rectangle r2)
        {
            int x1 = Math.Max(r1.Left, r2.Left);
            int y1 = Math.Max(r1.Top, r2.Top);
            int x2 = Math.Min(r1.Right, r2.Right);
            int y2 = Math.Min(r1.Bottom, r2.Bottom);

            if ((x2 >= x1) && (y2 >= y1))
            {
                return new Rectangle(x1, y1, x2 - x1, y2 - y1);
            }
            return Rectangle.Empty;
        }

        public void Explode(Vector2 pos, Texture2D circle)
        {
            var area =
                    Intersection(
                        new Rectangle((int)pos.X - circle.Width / 2, (int)pos.Y - circle.Height / 2, circle.Width, circle.Height),
                        new Rectangle(0, 0, Width, Height));
            Color[] explosion = new Color[circle.Width * circle.Height];
            circle.GetData(explosion);

            int offset = (area.X == 0 && area.Width < circle.Width) ? circle.Width - area.Width : 0;
            for (int y = 0; y < area.Height; y++)
            {
                for (int x = 0; x < area.Width; x++)
                {
                    if (explosion[x + offset + y * circle.Width] != Color.Transparent)
                    {
                        colorData[area.X + x + (area.Y + y) * Width] = Color.Transparent;
                    }
                }
            }

            LevelTexture.SetData(colorData);
        }

        public int FindLand(Vector2 pos)
        {
            int x = (int)MathHelper.Clamp(pos.X, 0, Width - 1);
            int y = (int)MathHelper.Clamp(pos.Y, 0, Height - 1);

            if (colorData[x + y * Width] == Color.Transparent)
            {
                for (int i = y; i < Height; i++)
                    if (colorData[x + i * Width] != Color.Transparent)
                        return i;
                return Height;
            }
            else
            {
                for (int i = y; i >= 0; i--)
                    if (colorData[x + i * Width] == Color.Transparent)
                        return i;
                return 0;
            }
        }

    }
}
TankManager.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WormsGame
{
    internal class TankManager
    {
        private Texture2D tankTexture2D;
        private Texture2D tankCannonTexture2D;
        private Texture2D explosionTexture2D;
        private Texture2D shotTexture2D;
        private List<Tank> tanks = new List<Tank>();
        private List<GameObj> shots = new List<GameObj>();
        private KeyboardState prevState;

        private MapGenerator mapGenerator;
        public TankManager(MapGenerator mapGenerator)
        {
            this.mapGenerator = mapGenerator;
        }

        public  void LoadContent(ContentManager content)
        {
            tankTexture2D = content.Load<Texture2D>("Tank");
            tankCannonTexture2D = content.Load<Texture2D>("Tank_Cannon");
            shotTexture2D = content.Load<Texture2D>("Projectile");
            explosionTexture2D = content.Load<Texture2D>("Circle");

            tanks.Add(new Tank()
            {
                Position = new Vector2(300, 0),
                Width = tankTexture2D.Width,
                Height = tankTexture2D.Height
            });
        }

        public void Update()
        {
            var kbState = Keyboard.GetState();
            var player1 = tanks.First();

            if (kbState.IsKeyDown(Keys.Left))
                player1.Speed = new Vector2(-1, player1.Speed.Y);
            if (kbState.IsKeyDown(Keys.Right))
                player1.Speed = new Vector2(1, player1.Speed.Y);
            if (kbState.IsKeyDown(Keys.Q))
                player1.CanonAngle -= 0.07f;
            if (kbState.IsKeyDown(Keys.E))
                player1.CanonAngle += 0.07f;

            if (kbState.IsKeyDown(Keys.Space) && !prevState.IsKeyDown(Keys.Space))
            {
                shots.Add(new GameObj()
                {
                    Position = player1.CannonPosition,
                    Speed = new Vector2((float)Math.Cos(player1.ShotAngle), (float)Math.Sin(player1.ShotAngle)) * 8
                });
            }

            foreach (var tank in tanks)
            {
                var oldPos = player1.Position;
                var oldSpeed = player1.Speed;
                var oldAngle = player1.Angle;

                tank.Speed += new Vector2(0, 0.1f);
                tank.Position += tank.Speed;

                // kollision med kartan?
                var landPosition = FindLandPosition(tank.Position);
                if(landPosition.Item1.Y <= tank.Position.Y)
                {
                    tank.Speed = Vector2.Zero;
                    tank.Angle = landPosition.Item2;
                    tank.Position = landPosition.Item1;
                }

                if ((tank.Angle > 1.0f && oldSpeed.X < 0) || (tank.Angle < -1.0f && oldSpeed.X > 0))
                {
                    tank.Position = oldPos;
                    tank.Angle = oldAngle;
                }
            }

            foreach(var shot in shots)
            {
                shot.Speed += new Vector2(0, 0.1f);
                var oldPos = shot.Position;
                var normSpeed = shot.Speed;
                normSpeed.Normalize();
                shot.Position += shot.Speed;

                for (int i = 0; i < (int)shot.Speed.Length(); i++)
                {
                    oldPos += normSpeed;
                    if (mapGenerator.IsLand(oldPos))
                    {
                        shot.FlaggedForRemoval= true;
                        mapGenerator.Explode(oldPos, explosionTexture2D);
                    }
                }
            }

            shots.RemoveAll(s => s.FlaggedForRemoval);
            prevState = kbState;
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            foreach(var tank in tanks)
            {
                spriteBatch.Draw(tankCannonTexture2D, tank.CannonPosition, null, Color.White, tank.CannonTransformedAngle,
                    new Vector2(tankCannonTexture2D.Width / 2, tankCannonTexture2D.Height), 1.0f, SpriteEffects.None, 0);
                spriteBatch.Draw(tankTexture2D, tank.Position, null, Color.White, tank.Angle,
                        new Vector2(tankTexture2D.Width / 2, tankTexture2D.Height), 1.0f, SpriteEffects.None, 0);
            }

            foreach(var shot in shots)
            {
                spriteBatch.Draw(shotTexture2D, shot.Position, null, Color.White, 0,
                    new Vector2(shotTexture2D.Width / 2, shotTexture2D.Height / 2), 1.0f, SpriteEffects.None, 0);
            }
        }

        private Tuple<Vector2, float> FindLandPosition(Vector2 position)
        {
            int x1 = (int)position.X - tankTexture2D.Width / 2 + 4;
            int x2 = (int)position.X + tankTexture2D.Width / 2 - 4;
            int y1 = mapGenerator.FindLand(new Vector2(x1, position.Y));
            int y2 = mapGenerator.FindLand(new Vector2(x2, position.Y));

            var angle = (float)Math.Atan2(y2 - y1, x2 - x1);
            var pos = new Vector2(position.X, (y1 + y2) / 2);
            return new Tuple<Vector2, float>(pos, angle);
        }
    }
}
Scroll to top