Walker Effekt

Bakgrund

Denna artikel har inspirerats av programspråket Processing. Processing är ett ”enkelt-att-komma-igång-med” programspråk som fokuserar på visuella/grafiska element. Att snabbt och enkelt kunna rita ut linje, cirklar och enklare former är grundläggande.

Eftersom vi tidigare på csharpskolan kikat lite på hur man just kan rita linjer, se artikeln Cirklar och Linjer, så passar vi nu på att presentera en så kallad ”Random Walker” effekt. Videon ger en snabb genomgång av programmet. Sist i artikeln finns ett nedladdningsbart projekt.

Walker

Vad är en ”Walker” då? Enkelt uttryckt så är det en linje som ritas i slumpvis riktning. En linje dras från punkt A till en slumpvis punkt B inom en viss räckvidd. Därefter slumpas en längd och en riktning från punkt B så att linjen ritas till en ny punkt, C. Detta upprepas och får då effekten av att se ut som om linjen ”går” slumpvis och irrar omkring i ett slumpartat mönster.

En ”walker” skulle vi kunna beskriva men en klass. Vi behöver några egenskaper; tidigare position, nuvarande position, max längd och färg på uppritningen. Detta representerar vi med klassen Walker.cs nedan.

Walker.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Walking
{
    public class Walker
    {
        public Vector2 Position { get; set; }
        public Vector2 PreviousPosition { get; set; }
        public Color Color { get; set; }
        public int StepSize { get; set; }

        public Walker()
        {
            Color = Color.White;
        }
    }
}

Låter vi sedan 300 små ”walkers” med olika färger få jobba så får vi en fin effekt. Till detta så har vi ändrat på metoden DrawLine lite så att den alltid ritar med en faktor * 0,1 på färgerna. Detta ger en transparent effekt och låter bilden växa fram sakta då uppritningen hela tiden sker ovanpå tidigare uppritning.

Det kompletta programmet listas nedan.

Game1.cs
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Walking
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Random rnd = new Random();
        List<Walker> _walkers = new List<Walker>();
        private Texture2D pixelTexture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferHeight = 720;
            graphics.PreferredBackBufferWidth = 1280;
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

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

            int w = GraphicsDevice.Viewport.Width;
            int h = GraphicsDevice.Viewport.Height;
            Vector2 center = new Vector2(w / 2, h / 2);

            pixelTexture = new Texture2D(GraphicsDevice, 1, 1);
            Color[] data = {Color.White};
            pixelTexture.SetData(data);

            for (int i = 0; i < 300; i++)
            {
                var color = new Color(
                    rnd.Next(100, 256),
                    rnd.Next(100, 256),
                    rnd.Next(100, 256));

                var walker = new Walker
                {
                    PreviousPosition = center,
                    Position = center,
                    Color = color,
                    StepSize = 6
                };
                _walkers.Add(walker);
            }
        }

        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();

            foreach (var walker in _walkers)
            {
                walker.PreviousPosition = walker.Position;
                walker.Position = walker.Position +
                                   new Vector2((float) rnd.NextDouble() - 0.5f, 
                                       (float) rnd.NextDouble() - 0.5f) * 2 *
                                   walker.StepSize;

                int w = GraphicsDevice.Viewport.Width;
                int h = GraphicsDevice.Viewport.Height;

                if (walker.Position.X < 0 || walker.Position.X > w || walker.Position.Y < 0 ||
                    walker.Position.Y > h)
                {
                    walker.Position = new Vector2(w / 2, h / 2);
                    walker.PreviousPosition = walker.Position;
                }
            }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            spriteBatch.Begin();

            foreach (var walker in _walkers)
            {
                DrawLine(walker.PreviousPosition, walker.Position, walker.Color);
            }

            spriteBatch.End();

            base.Draw(gameTime);
        }

        public void DrawLine(Vector2 pos1, Vector2 pos2, Color color, int width = 1)
        {
            Vector2 diff = (pos1 - pos2);
            float length = diff.Length();
            float angle = (float)Math.Atan2(diff.Y, diff.X) + MathHelper.Pi;

            spriteBatch.Draw(pixelTexture, 
                new Rectangle((int)(pos1.X), (int)(pos1.Y), (int)length, width),
                null, color * 0.1f, angle, new Vector2(0, width / 2), 
                SpriteEffects.None, 0f);
        }
    }
}
Scroll to top