Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Alright, I've been on the making of a sprite class and a sprite sheet manager, but have come across this problem. Pretty much, the project is acting like so; for example:

http://i.stack.imgur.com/kg4rK.png

Let's take this .png image, with a transparent background. Note how it has alpha-transparent pixels around it in the lineart.

enter image description here

Now, in the latter link's image, in the left (with CornflowerBlue background) it is shown the image drawn in another project (let's call it "Project1") with a simpler sprite class - there, it works. The right (with Purple background for differentiating) shows it drawn with a different class in "Project2" - where the problem manifests itself.

This is the Sprite class of Project1:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace WindowsGame2
{
    class Sprite
    {
        Vector2 pos = new Vector2(0, 0);
        Texture2D image;

        Rectangle size;
        float scale = 1.0f;

        // ---

        public float X
        {
            get
            {
                return pos.X;
            }
            set
            {
                pos.X = value;
            }
        }

        public float Y
        {
            get
            {
                return pos.Y;
            }
            set
            {
                pos.Y = value;
            }
        }

        public float Width
        {
            get
            {
                return size.Width;
            }
        }

        public float Height
        {
            get
            {
                return size.Height;
            }
        }

        public float Scale
        {
            get
            {
                return scale;
            }
            set
            {
                if (value < 0) value = 0;
                scale = value;

                if (image != null)
                {
                    size.Width = (int)(image.Width * scale);
                    size.Height = (int)(image.Height * scale);
                }
            }
        }

        // ---

        public void Load(ContentManager Man, string filename)
        {
            image = Man.Load<Texture2D>(filename);

            size = new Rectangle(
                0, 0,
                (int)(image.Width * scale), (int)(image.Height * scale)
            );
        }

        public void Become(Texture2D frame)
        {
            image = frame;

            size = new Rectangle(
                0, 0,
                (int)(image.Width * scale), (int)(image.Height * scale)
            );
        }

        public void Draw(SpriteBatch Desenhista)
        {
            // Desenhista.Draw(image, pos, Color.White);
            Desenhista.Draw(
                image, pos,
                new Rectangle(
                    0, 0,
                    image.Width, image.Height
                ),
                Color.White,
                0.0f,
                Vector2.Zero,
                scale,
                SpriteEffects.None, 0
            );
        }
    }
}

And this is the code in Project2, a rewritten, pretty much, version of the previous class. In this one I added sprite sheet managing and, in particular, removed Load and Become, to allow for static resources and only actual Sprites to be instantiated.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Mobby_s_Adventure
{
    // Actually, I might desconsider this, and instead use static AnimationLocation[] and instanciated ID and Frame;

    // For determining the starting frame of an animation in a sheet and being able to iterate through
    // the Rectangles vector of the Sheet;
    class AnimationLocation
    {
        public int Location;
        public int FrameCount;

        // ---

        public AnimationLocation(int StartingRow, int StartingColumn, int SheetWidth, int NumberOfFrames)
        {
            Location = (StartingRow * SheetWidth) + StartingColumn;
            FrameCount = NumberOfFrames;
        }

        public AnimationLocation(int PositionInSheet, int NumberOfFrames)
        {
            Location = PositionInSheet;
            FrameCount = NumberOfFrames;
        }

        public static int CalculatePosition(int StartingRow, int StartingColumn, SheetManager Sheet)
        {
            return ((StartingRow * Sheet.Width) + StartingColumn);
        }
    }

    class Sprite
    {
        // The general stuff;
        protected SheetManager Sheet;

        protected Vector2 Position;
        public Vector2 Axis;

        protected Color _Tint;

        public float Angle;
        public float Scale;

        protected SpriteEffects _Effect;

        // ---

        // protected AnimationManager Animation;

        // For managing the animations;
        protected AnimationLocation[] Animation;

        public int AnimationID;
        protected int Frame;

        // ---

        // Properties for easy accessing of the position of the sprite;
        public float X
        {
            get { return Position.X; }
            set { Position.X = Axis.X + value; }
        }

        public float Y
        {
            get { return Position.Y; }
            set { Position.Y = Axis.Y + value; }
        }

        // ---

        // Properties for knowing the size of the sprite's frames
        public float Width
        {
            get { return Sheet.FrameWidth * Scale; }
        }

        public float Height
        {
            get { return Sheet.FrameHeight * Scale; }
        }

        // ---

        // Properties for more stuff;
        public Color Tint
        {
            set { _Tint = value; }
        }

        public SpriteEffects Effect
        {
            set { _Effect = value; }
        }

        public int FrameID
        {
            get { return Frame; }
            set
            {
                if (value >= (Animation[AnimationID].FrameCount)) value = 0;
                Frame = value;
            }
        }

        // ---

        // The only things that will be constantly modified will be AnimationID and FrameID, anything else only
        // occasionally;

        public Sprite(SheetManager SpriteSheet, AnimationLocation[] Animations, Vector2 Location, Nullable<Vector2> Origin = null)
        {
            // Assign the sprite's sprite sheet;
            // (Passed by reference! To allow STATIC sheets!)
            Sheet = SpriteSheet;

            // Define the animations that the sprite has available;
            // (Passed by reference! To allow STATIC animation boundaries!)
            Animation = Animations;

            // Defaulting some numerical values;
            Angle = 0.0f;
            Scale = 1.0f;

            _Tint = Color.White;
            _Effect = SpriteEffects.None;

            // If the user wants a default Axis, it is set in the middle of the frame;
            if (Origin != null) Axis = Origin.Value;
            else Axis = new Vector2(
                Sheet.FrameWidth / 2,
                Sheet.FrameHeight / 2
            );

            // Now that we have the axis, we can set the position with no worries;
            X = Location.X;
            Y = Location.Y;
        }

        // Simply put, draw the sprite with all its characteristics;
        public void Draw(SpriteBatch Drafter)
        {
            Drafter.Draw(
                 Sheet.Texture,
                 Position,
                 Sheet.Rectangles[Animation[AnimationID].Location + FrameID], // Find the rectangle which frames the wanted image;
                 _Tint,
                 Angle,
                 Axis,
                 Scale,
                 _Effect,
                 0.0f
             );
        }
    }
}

And, in any case, this is the SheetManager class found in the previous code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Mobby_s_Adventure
{
    class SheetManager {
        protected Texture2D SpriteSheet; // For storing the sprite sheet;

        // Number of rows and frames in each row in the SpriteSheet;
        protected int NumberOfRows;
        protected int NumberOfColumns;

        // Size of a single frame;
        protected int _FrameWidth;
        protected int _FrameHeight;

        public Rectangle[] Rectangles; // For storing each frame;

        // ---

        public int Width {
            get { return NumberOfColumns; }
        }

        public int Height {
            get { return NumberOfRows; }
        }

        // ---

        public int FrameWidth {
            get { return _FrameWidth; }
        }

        public int FrameHeight {
            get { return _FrameHeight; }
        }

        // ---

        public Texture2D Texture {
            get { return SpriteSheet; }
        }

        // ---

        public SheetManager (Texture2D Texture, int Rows, int FramesInEachRow) {
            // Normal assigning
            SpriteSheet = Texture;

            NumberOfRows = Rows;
            NumberOfColumns = FramesInEachRow;

            _FrameHeight = Texture.Height / NumberOfRows;
            _FrameWidth = Texture.Width / NumberOfColumns;

            // Framing everything
            Rectangles = new Rectangle[NumberOfRows * NumberOfColumns]; 

            int ID = 0;
            for (int i = 0; i < NumberOfRows; i++) {
                for (int j = 0; j < NumberOfColumns; j++) {

                    Rectangles[ID] = new Rectangle (
                        _FrameWidth * j, _FrameHeight * i,
                        _FrameWidth, _FrameHeight
                    );

                    ID++;
                }
            }
        }

        public SheetManager (Texture2D Texture, int NumberOfFrames): this(Texture, 1, NumberOfFrames)
        {   }
    }
}

For even more comprehending, if needed, here is how the main code looks like (it's just messing with the class' capacities, nothing actually; the result is a disembodied feet walking in place animation on the top-left of the screen and a static axe nearby):

using System;
using System.Collections.Generic;
using System.Linq;
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.Media;

using System.Threading;

namespace Mobby_s_Adventure
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        static List<Sprite> ToDraw;

        static Texture2D AxeSheet;
        static Texture2D FeetSheet;

        static SheetManager Axe;
        static Sprite Jojora;
        static AnimationLocation[] Hack = new AnimationLocation[1];

        static SheetManager Feet;
        static Sprite Mutoh;
        static AnimationLocation[] FeetAnimations = new AnimationLocation[2];

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

            this.TargetElapsedTime = TimeSpan.FromMilliseconds(100);
            this.IsFixedTimeStep = true;
        }

        /// <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

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

            // Loading logic
            ToDraw = new List<Sprite>();

            AxeSheet = Content.Load<Texture2D>("Sheet");
            FeetSheet = Content.Load<Texture2D>("Feet Sheet");

            Axe = new SheetManager(AxeSheet, 1);
            Hack[0] = new AnimationLocation(0, 1);
            Jojora = new Sprite(Axe, Hack, new Vector2(100, 100), new Vector2(5, 55));

            Jojora.AnimationID = 0;
            Jojora.FrameID = 0;

            Feet = new SheetManager(FeetSheet, 8);
            FeetAnimations[0] = new AnimationLocation(1, 7);
            FeetAnimations[1] = new AnimationLocation(0, 1);
            Mutoh = new Sprite(Feet, FeetAnimations, new Vector2(0, 0));

            Mutoh.AnimationID = 0;
            Mutoh.FrameID = 0;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // 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();

            // Update logic
            Mutoh.FrameID++;
            ToDraw.Add(Mutoh);

            ToDraw.Add(Jojora);

            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)
        {
            GraphicsDevice.Clear(Color.Purple);

            // Drawing logic
            spriteBatch.Begin();
            foreach (Sprite Element in ToDraw)
            {
                Element.Draw(spriteBatch);
            }
            spriteBatch.Draw(Content.Load<Texture2D>("Sheet"), new Rectangle(50, 50, 55, 60), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Please help me find out what I'm overlooking!


One thing that I have noticed and could aid is that, if inserted the equivalent of this code

spriteBatch.Draw(
    Content.Load<Texture2D>("Image Location"),
    new Rectangle(X, Y, images width, height),
    Color.White
);

in Project2's Draw(GameTime) of the main loop, it works.


EDIT

Ok, even if the matter remains unsolved, I have made some more progress!

enter image description here

As you see, I managed to get the two kinds of rendering in the same project (the aforementioned Project2, with the more complex Sprite class).

This was achieved by adding the following code to Draw(GameTime):

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

        // Drawing logic
        spriteBatch.Begin();
        foreach (Sprite Element in ToDraw)
        {
            Element.Draw(spriteBatch);
        }

        // Starting here
        spriteBatch.Draw(
            Axe.Texture,
            new Vector2(65, 100),
            new Rectangle (
                0, 0,
                Axe.FrameWidth, Axe.FrameHeight
            ),
            Color.White,
            0.0f,
            new Vector2(0, 0),
            1.0f,
            SpriteEffects.None,
            0.0f
        );
        // Ending here

        spriteBatch.End();

        base.Draw(gameTime);
    }

(Supposing that Axe is the SheetManager containing the texture, sorry if the "jargons" of my code confuse you :s)

Thus, I have noticed that the problem is within the Sprite class. But I only get more clueless, because even after modifying its Draw function to this:

    public void Draw(SpriteBatch Drafter)
    {
        /*Drafter.Draw(
             Sheet.Texture,
             Position,
             Sheet.Rectangles[Animation[AnimationID].Location + FrameID], // Find the rectangle which frames the wanted image;
             _Tint,
             Angle,
             Axis,
             Scale,
             _Effect,
             0.0f
         );*/

        Drafter.Draw(
            Sheet.Texture, Position,
            new Rectangle(
                0, 0,
                Sheet.FrameWidth, Sheet.FrameHeight
            ),
            Color.White,
            0.0f,
            Vector2.Zero,
            Scale,
            SpriteEffects.None, 0
        );
    }

to make it as simple as the patch of code that works, it still draws the sprite jaggedly!

share|improve this question
add comment

1 Answer 1

up vote 1 down vote accepted

How silly of me!

This was only happening because at Draw(GameTime)

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

    // Drawing logic
    spriteBatch.Begin();

    foreach (Sprite Element in ToDraw)
    {
        Element.Draw(spriteBatch);
    }

    spriteBatch.End();

    base.Draw(gameTime);
}

I forgot to the clear the sprite list after everything had been drawn!

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

    // Drawing logic
    spriteBatch.Begin();

    foreach (Sprite Element in ToDraw)
        {
        Element.Draw(spriteBatch);
    }

    // Alright!
    ToDraw.Clear();

    spriteBatch.End();

    base.Draw(gameTime);
}

Oh, these small things that happen while programming.

share|improve this answer
1  
What exactly is ToDraw? –  ashes999 Nov 29 '12 at 21:51
    
It's a list of Sprites to draw; They insert themselves in it in the Update(GameTime) part of the code. –  Mutoh Nov 30 '12 at 14:22
2  
That does not seem like the solution to the problem you described. Your jagged edges appeared to be one of those that could be solved with pre-multiplied alpha, but I don't see how that's related to clearing a list. –  Seth Battin May 17 '13 at 15:34
    
@SethBattin In the third to last excerpt of code in my question, you can see in the Update method implemented there that ToDraw adds the drawable variables to it in every loop. The problem is that, with every loop that comes, if I don't clear ToDraw the variable will keep being added over and over. After a little while, it is being drawn over and over itself, which dampens the quality of the image. This was all just me being tangled in my own logic, and blaming the tools. –  Mutoh May 17 '13 at 21:17
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.