Cellular Automaton in XNA - Need way to create changing textures

Started by
2 comments, last by bmanruler 16 years, 2 months ago
This is my first actual project using C# and XNA. I would really appreciate it if people could point out any flaws/hack code so I can learn. It is a simple cellular automaton that is initially randomly seeded, then updated every frame. The part I feel is most hack-ish is where I recreate the texture each frame. It would be great to know a better way. If something isn't clear from the code ask away. *edit* I guess what I really would like to know is, is there a bettere way to draw individual pixels to screen then creating a texture, destroying it and creating a new one. I suppose you could multiple textures and create a buffering system but that still would leave you with remaking alot of textures. Also if anyone knows a performance analyzer for C# that would help also, didn't find anything that looked good when I looked.

#region Using Statements
using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace CellAuto
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        int screenWidth = 800;
        int screenHeight = 600;
        int screenSize = 800 * 600;

        Texture2D CA;
        Color[] caTexture;
        Color[] caBuffer;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            caTexture = new Color[screenSize];
            caBuffer = new Color[screenSize];
        }

        public void InitializeCA()
        {
            Random random = new Random();
            Color black = Color.Black;
            Color white = Color.White;

            // Seed the CA with random values
            for (int i = 0; i < screenSize; i++)
            {
                int RandomBinary = random.Next(2); // generate a random number between 0 and 1
                if( 0 == RandomBinary ) // if 0, then add black
                {
                    caTexture = black;
                }
                else // if 1, then add white
                {
                    caTexture = white;
                }
            }
            // Create the texture
            CA = new Texture2D(graphics.GraphicsDevice, screenWidth, screenHeight, 1, TextureUsage.None, SurfaceFormat.Color);
            // Set the new data to the texture
            CA.SetData<Color>(caTexture);
        }

        public void GameOfLife()
        {
            // Update buffer using ruleset
            for (int y = 0; y < screenHeight; y++)
            {
                for (int x = 0; x < screenWidth; x++)
                {
                    caBuffer[y * screenWidth + x] = GameOfLifeRules(x, y);
                }
            }
            // replace original with buffer
            for (int i = 0; i < screenSize; i++)
            {
                caTexture = caBuffer;
            }
        }

        private Color GameOfLifeRules(int X, int Y)
        {
            // Original game of life rules made image converge too quickly, so this is used.
            int temp = LiveNeighborCount(X, Y);
            if (0 <= temp && temp <= 2)
            {
                return Color.Black;
            }
            if (3 == temp)
            {
                return Color.White;
            }
            if (5 == temp)
            {
                return Color.Black;
            }
            if (6 <= temp)
            {
                return Color.White;
            }
            return caTexture[Y*screenWidth+X];
        }

        private int LiveNeighborCount(int X, int Y)
        {
            int retVal = 0;
            // Test for bounds
            int left = 0,
                leftAbove = 0,
                above = 0,
                rightAbove = 0,
                right = 0,
                rightBelow = 0,
                below = 0,
                leftBelow = 0;

            // Left
            if (X > 0)
            {
                if (Color.White == caTexture[Y * screenWidth + X - 1])
                {
                    left = 1;
                }
            }
            // Left Above
            if (X > 0 && Y > 0)
            {
                if (Color.White == caTexture[(Y - 1) * screenWidth + X - 1])
                {
                    leftAbove = 1;
                }
            }
            // Above
            if (Y > 0)
            {
                if (Color.White == caTexture[(Y - 1) * screenWidth + X])
                {
                    above = 1;
                }
            }
            // Right Above
            if (X+1 < screenWidth && Y > 0)
            {
                if (Color.White == caTexture[(Y - 1) * screenWidth + X + 1])
                {
                    rightAbove = 1;
                }
            }
            // Right
            if (X+1 < screenWidth)
            {
                if (Color.White == caTexture[Y * screenWidth + X + 1])
                {
                    right = 1;
                }
            }
            // Right Below
            if (X+1 < screenWidth && Y+1 < screenHeight)
            {
                if (Color.White == caTexture[(Y + 1) * screenWidth + X + 1])
                {
                    rightBelow = 1;
                }
            }
            // Below
            if (Y + 1 < screenHeight)
            {
                if (Color.White == caTexture[(Y + 1) * screenWidth + X])
                {
                    below = 1;
                }
            }
            // Left Below
            if (X > 0 && Y + 1 < screenHeight)
            {
                if (Color.White == caTexture[(Y + 1) * screenWidth + X - 1])
                {
                    leftBelow = 1;
                }
            }

            retVal = left + leftAbove + above + rightAbove + right + rightBelow + below + leftBelow;
            return retVal;
        }

        /// <summary>
        /// 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.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            InitializeCA();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            CA.Dispose();
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            // This updates the data in the CA so it actually evolves
            // Uses the game of life rule
            GameOfLife();

            // Hack to refresh the texture, discard the previous one and create a new texture with the
            //  updated buffer
            CA.Dispose();
            CA = new Texture2D(graphics.GraphicsDevice, screenWidth, screenHeight, 1, TextureUsage.None, SurfaceFormat.Color);
            CA.SetData<Color>(caTexture);

            // Draw the sprites to screen
            spriteBatch.Begin();
            spriteBatch.Draw(CA, Vector2.Zero, Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}


[Edited by - bmanruler on January 28, 2008 12:03:11 PM]
Advertisement
">Here is a video of it in action.
Just change the data in the texture instead of creating and destroying it every frame. You can also just swap references to the buffers instead of copying every single pixel (in the CPU part of things).
I tried modifying the data without recreating the texture and I got the exception "The operation was aborted. You may not modify a resource that has been set on a device, or after it has been used within a tiling bracket."

It does swap the buffer's by reference now though.

This topic is closed to new replies.

Advertisement