Fun Infused Games  |   Smooth Operator RSS 

  Home   |    Archive   |    Subscribe   |    Search   |    About
A Better Three-Level Parallax Star Field using XNA
Date 4/6/2011    Tags XNA    (1)

A while back I posted the article A Simple Three-Level Parallax Star Field using XNA on my blog. This has been a seemingly popular post but hackers last summer largely destroyed it. Because I have gotten several requests to fix the article, I'm reposting with a newer version.

Below is a Star class and a Stars class (unlike the original version of this post that was just a Star class). This makes it much easier to implement. Just create one Stars object and call the Update() and Draw() methods. Additionally this class will make the stars twinkle.

The below code is hacked out of what I used in my game Hypership Out of Control. I believe it should work (or be close to working) but because I hacked it out of existing code, I wouldn't rule out a mistake or two. If you do find an issue, please let me know in the comments so I can fix it for others.



public enum ColorOverlayType
{
    Red,
    Blue,
    Green
}

/// <summary>
/// A single star.
/// </summary>
public class Star
{
    private Vector2 direction;
    private Color drawColor;
    private StarType currentStarType;

    public enum StarLayer
    {
        Top,
        Middle,
        Bottom
    }

    public enum StarType
    {
        Star1 = 1, // Dark
        Star2 = 2, // Dark
        Star3 = 3, // Medium
        Star4 = 4, // Medium
        Star5 = 5, // Light
        Star6 = 6, // Light
        Star7 = 7  // Light
    }

    private Texture2D texture;

    public Vector2 Position
    {
        get;
        set;
    }

    public StarLayer CurrentStarLayer
    {
        get;
        set;
    }

    public Vector2 Speed
    {
        get;
        set;
    }

    public int Width
    {
        get;
        set;
    }

    public int Height
    {
        get;
        set;
    }

    public Rectangle DrawRectangle(bool maxSpeed)
    {
        return new Rectangle(Convert.ToInt32(this.Position.X), Convert.ToInt32(this.Position.Y), this.Width, this.Height);
    }

    public Star(ContentManager content, StarType initialStarType, StarLayer initialStarLayer)
    {
        this.direction = new Vector2(0, 1);
        this.Speed = new Vector2(0, 50);
        this.currentStarType = initialStarType;
        this.drawColor = Color.White;
        this.CurrentStarLayer = initialStarLayer;

    // You must create textures named Star1 to Star7 in the Background folder of your
    // content project. This gives the stars a more varried look.
        switch (initialStarType)
        {
            case StarType.Star1:
                {
                    this.texture = content.Load<Texture2D>("Background\\Star1");
                    break;
                }
            case StarType.Star2:
                {
                    this.texture = content.Load<Texture2D>("Background\\Star2");
                    break;
                }
            case StarType.Star3:
                {
                    this.texture = content.Load<Texture2D>("Background\\Star3");
                    break;
                }
            case StarType.Star4:
                {
                    this.texture = content.Load<Texture2D>("Background\\Star4");
                    break;
                }
            case StarType.Star5:
                {
                    this.texture = content.Load<Texture2D>("Background\\Star5");
                    break;
                }
            case StarType.Star6:
                {
                    this.texture = content.Load<Texture2D>("Background\\Star6");
                    break;
                }
            case StarType.Star7:
                {
                    this.texture = content.Load<Texture2D>("Background\\Star7");
                    break;
                }
        }

        this.Width = this.texture.Width;
        this.Height = this.texture.Height;
    }

    public void Draw(SpriteBatch spriteBatch, bool MaxSpeed)
    {
        // This should really be all on one line but I needed to make it two to fit nicely on this page.
        spriteBatch.Draw(this.texture, new Rectangle(Convert.ToInt32(this.Position.X), Convert.ToInt32(this.Position.Y), 
            this.Width, this.Height), this.drawColor);
    }
}


/// <summary>
/// All your stars.
/// </summary>
public class Stars
{
    private float MAX_POSITION = 800f;  // How far to travel before looping to the top
    private float MIN_POSITION = -200f; 
    private int TOP_LEVEL_STARS = 50;    // How many top level start to make.
    private int MIDDLE_LEVEL_STARS = 25; // How many middle level stars to make.
    private int BOTTOM_LEVEL_STARS = 25; // How many bottom level stars to make.
    private int LEFT_BORDER = 0;     // Furthest left area on the screen.
    private int RIGHT_BORDER = 1280; // Furthest right area on the screen. 
    private float TWINKLE_TIME = .05f;
    private float FADE_SPEED = .15f;
    private float FADE_TRANSPARENCY = .05f;
    private float FADE_TRANSPARENCY_FUN_MODE = .5f;

    private Random random;
    private Vector2 topSpeed;
    private Vector2 middleSpeed;
    private Vector2 bottomSpeed;
    private Texture2D starCover; // 1x1 black pixel
    private int twinkleStar1;
    private int twinkleStar2;
    private int twinkleStar3;
    private int twinkleStar4;
    private int twinkleStar5;
    private float twinkleTimer;
    private Color twinkleColor;
    private Color overlayColor;
    private ColorOverlayType colorOverlayType;
    private float red;
    private float blue;
    private float green;

    public List<Star> ListOfStars
    {
        get;
        set;
    }

    /// <summary>
    /// Create our paralax stars.
    /// </summary>
    public Stars(ContentManager content)
    {
        this.ListOfStars = new List<Star>();
        random = new Random();
        this.random = new Random();
        this.starCover = content.Load<Texture2D>("Background\\StarCover");

        // Start game as  0, 1, 0
        this.overlayColor = new Color(0, 1f, 0) * FADE_TRANSPARENCY;

        this.twinkleTimer = TWINKLE_TIME;
        this.twinkleColor = new Color(1f, 1f, 1f) * .5f;
        this.colorOverlayType = ColorOverlayType.Red;

        // Add different layers of stars. Add in reverse order so bottom stars are drawn first.
        for (int i = 0; i < BOTTOM_LEVEL_STARS; i++)
        {
            int newStarType = random.Next(1, 2);
            Star newStar = new Star(content, (Star.StarType)newStarType, Star.StarLayer.Bottom);
            newStar.Position = new Vector2(random.Next(LEFT_BORDER, RIGHT_BORDER), random.Next(-200, 850));
            this.ListOfStars.Add(newStar);
        }

        for (int i = 0; i < MIDDLE_LEVEL_STARS; i++)
        {
            int newStarType = random.Next(3, 4);
            Star newStar = new Star(content, (Star.StarType)newStarType, Star.StarLayer.Middle);
            newStar.Position = new Vector2(random.Next(LEFT_BORDER, RIGHT_BORDER), random.Next(-200, 850));
            this.ListOfStars.Add(newStar);
        }

        for (int i = 0; i < TOP_LEVEL_STARS; i++)
        {
            int newStarType = random.Next(5, 7);
            Star newStar = new Star(content, (Star.StarType)newStarType, Star.StarLayer.Top);
            newStar.Position = new Vector2(random.Next(LEFT_BORDER, RIGHT_BORDER), random.Next(-200, 850));
            this.ListOfStars.Add(newStar);
        }
    }

    public void Update()
    {
        this.topSpeed = new Vector2(0, (Program.LevelSpeed / 2) * Program.ElapsedTime);
        this.middleSpeed = new Vector2(0, (Program.LevelSpeed / 4) * Program.ElapsedTime);
        this.bottomSpeed = new Vector2(0, (Program.LevelSpeed / 8) * Program.ElapsedTime);
        this.twinkleTimer -= Program.ElapsedTime;

    this.UpdateStarFading(); 

        for (int i = 0; i < this.ListOfStars.Count(); i++)
        {
            switch (this.ListOfStars[i].CurrentStarLayer)
            {
                case Star.StarLayer.Top:
                    {
                        this.ListOfStars[i].Position += this.topSpeed;
                        break;
                    }
                case Star.StarLayer.Middle:
                    {
                        this.ListOfStars[i].Position += this.middleSpeed;
                        break;
                    }
                case Star.StarLayer.Bottom:
                    {
                        this.ListOfStars[i].Position += this.bottomSpeed;
                        break;
                    }
            }

            // Move star back to the top of the screen.
            if (this.ListOfStars[i].Position.Y > MAX_POSITION)
            {
                this.ListOfStars[i].Position = new Vector2(this.ListOfStars[i].Position.X, MIN_POSITION);
            }
        }
    }


    private void UpdateStarFading()
    {
        // Red to blue to green.
        switch (colorOverlayType)
        {
            case ColorOverlayType.Red:
                // 1, 0, 0
                this.red -= Program.ElapsedTime * FADE_SPEED;
                this.blue += Program.ElapsedTime * FADE_SPEED;
                
                if (this.blue > 1f)
                {
                    this.red = 0f;
                    this.blue = 1f;
                    this.colorOverlayType = ColorOverlayType.Blue;
                }
                break;
            case ColorOverlayType.Blue:
                // 0, 0, 1
                this.blue -= Program.ElapsedTime * FADE_SPEED;
                this.green += Program.ElapsedTime * FADE_SPEED;

                if (this.green > 1f)
                {
                    this.green = 1f;
                    this.blue = 0f;
                    this.colorOverlayType = ColorOverlayType.Green;
                }
                break;
            case ColorOverlayType.Green:
                // 0, 1, 0
                this.green -= Program.ElapsedTime * FADE_SPEED;
                this.red += Program.ElapsedTime * FADE_SPEED;

                if (this.red > 1f)
                {
                    this.green = 0f;
                    this.red = 1f;
                    this.colorOverlayType = ColorOverlayType.Red;
                }
                break;
        }

        this.overlayColor = new Color(this.red, this.green, this.blue) * FADE_TRANSPARENCY;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        if (this.twinkleTimer < 0)
        {
            this.twinkleTimer = TWINKLE_TIME;
            this.twinkleStar1 = this.random.Next(1, 99);
            this.twinkleStar2 = this.random.Next(1, 99);
            this.twinkleStar3 = this.random.Next(1, 99);
            this.twinkleStar4 = this.random.Next(1, 99);
            this.twinkleStar5 = this.random.Next(1, 99);
        }

        for (int i = 0; i < this.ListOfStars.Count(); i++)
        {
            this.ListOfStars[i].Draw(spriteBatch);
        }

        // Draw black over stars to make them twinkle.
        spriteBatch.Draw(this.starCover, this.ListOfStars[this.twinkleStar1].DrawRectangle(), this.twinkleColor);
        spriteBatch.Draw(this.starCover, this.ListOfStars[this.twinkleStar2].DrawRectangle(), this.twinkleColor);
        spriteBatch.Draw(this.starCover, this.ListOfStars[this.twinkleStar3].DrawRectangle(), this.twinkleColor);
        spriteBatch.Draw(this.starCover, this.ListOfStars[this.twinkleStar4].DrawRectangle(), this.twinkleColor);
        spriteBatch.Draw(this.starCover, this.ListOfStars[this.twinkleStar5].DrawRectangle(), this.twinkleColor);
    }
}
Do this right and you'll have a starfield that looks just like it does in the video below.





This article has been view 3310 times.


Comments

Tony

Avatar

N/A
3/11/2015 4:04:57 AM

Hi, I'm Tony (tomymail@gmx.com) I don't suppose you could help me convert your lovely starfield for my game could you? I have it about 90% done and I'm a noob to Unity (3 months) so I'm getting things under my belt, now if you check the link below you'll see my days progress so far in converting your script to a working state, link here: http://forum.unity3d.com/threads/help-me-convert-this-starfield-script-please.309279/

But I suppose I just need to know what your spritebatch.draw does and how I should show my stars, anyways hope you can help, I've been reading your blog and I know your very busy with development with your PS4 side of things etc, but If you could help it would be a boon to me, cheers


Add Comments

Name *
Website
  Name the animal in the picture below:

*  
Comment *
Insert Cancel


Tags
ASP.net (18)  Fin (1)  Video Games (7)  Game Dev (11)  Abduction Action (1)  WP7 (8)  Visual Studio (1)  Hypership (28)  Advise (14)  C# (14)  FIN (20)  World of Chalk (2)  Absurd (2)  Abduction Action! (27)  Nasty (34)  PC (1)  Cool (2)  Sports (11)  Rant (50)  VolChaos (1)  Development (13)  Design (2)  Volchaos (11)  XNA (40)  Nastier (4)  Xbox (1)  iOS (3)  SQL (1)  XBLIG (32)  Trivia or Die (3)  Web (19)  Trivia Or Die (1)  Abdction Action! (1)