Spritebatch-Zeichnungssprite mit gezackten Rändern


7

Okay, ich habe gerade eine Sprite-Klasse und einen Sprite-Sheet-Manager erstellt, bin aber auf dieses Problem gestoßen. Das Projekt verhält sich so ziemlich so; zum Beispiel:

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

Nehmen wir dieses PNG-Bild mit transparentem Hintergrund. Beachten Sie, wie es in der Linear Alpha-transparente Pixel umgibt.

Geben Sie hier die Bildbeschreibung ein

Im Bild des letzteren Links wird links (mit CornflowerBlue-Hintergrund) das Bild angezeigt, das in einem anderen Projekt (nennen wir es "Projekt1") mit einer einfacheren Sprite-Klasse gezeichnet wurde - dort funktioniert es. Das rechte (mit lila Hintergrund zur Unterscheidung) zeigt es mit einer anderen Klasse in "Project2" gezeichnet - wo sich das Problem manifestiert.

Dies ist die Sprite-Klasse von 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
            );
        }
    }
}

Und dies ist der Code in Project2, einer neu geschriebenen Version der vorherigen Klasse. In diesem Fall habe ich die Verwaltung von Sprite-Blättern hinzugefügt und insbesondere Load and Become entfernt, damit statische Ressourcen und nur tatsächliche Sprites instanziiert werden können.

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

In jedem Fall ist dies die SheetManager-Klasse, die im vorherigen Code enthalten ist:

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)
        {   }
    }
}

Um bei Bedarf noch besser zu verstehen, sieht der Hauptcode bei Bedarf so aus (er beeinträchtigt nur die Kapazitäten der Klasse, eigentlich nichts; das Ergebnis ist eine körperlose Fuß-an-Ort-Animation oben links auf dem Bildschirm und eine statische Aufladung Axt in der Nähe):

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

Bitte helfen Sie mir herauszufinden, was ich übersehen habe!


Eine Sache, die mir aufgefallen ist und helfen könnte, ist die, wenn das Äquivalent dieses Codes eingefügt wird

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

In Project2s Draw (GameTime) der Hauptschleife funktioniert es.


BEARBEITEN

Ok, auch wenn die Angelegenheit ungelöst bleibt, habe ich weitere Fortschritte gemacht!

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen, habe ich es geschafft, die beiden Arten des Renderns im selben Projekt zu erhalten (das oben erwähnte Projekt2 mit der komplexeren Sprite-Klasse).

Dies wurde erreicht, indem Draw (GameTime) der folgende Code hinzugefügt wurde:

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

(Angenommen, Axe ist der SheetManager, der die Textur enthält. Tut mir leid, wenn die "Jargons" meines Codes Sie verwirren: s)

Daher habe ich festgestellt, dass das Problem in der Sprite-Klasse liegt. Aber ich werde nur ahnungsloser, weil selbst nach dem Ändern der Draw-Funktion dies:

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

Um es so einfach wie den Code-Patch zu machen, der funktioniert, zeichnet es das Sprite immer noch gezackt!

Antworten:


1

Wie dumm von mir!

Dies geschah nur, weil bei 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);
}

Ich habe vergessen, die Sprite-Liste zu löschen, nachdem alles gezeichnet wurde!

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, diese kleinen Dinge, die beim Programmieren passieren.


1
Was genau ist ToDraw?
Asche999

Es ist eine Liste von Sprites, die gezeichnet werden müssen. Sie fügen sich in den Update-Teil (GameTime) des Codes ein.
Mutoh

2
Das scheint nicht die Lösung für das von Ihnen beschriebene Problem zu sein. Ihre gezackten Kanten schienen eine von denen zu sein, die mit vormultipliziertem Alpha gelöst werden konnten, aber ich sehe nicht, wie das mit dem Löschen einer Liste zusammenhängt.
Seth Battin

@SethBattin Im vorletzten Code-Auszug in meiner Frage können Sie in der dort implementierten Update-Methode sehen, dass ToDraw die zeichnbaren Variablen in jeder Schleife hinzufügt. Das Problem ist, dass bei jeder Schleife, die kommt, wenn ich ToDraw nicht lösche, die Variable immer wieder hinzugefügt wird. Nach einer Weile wird es immer wieder selbst gezeichnet, was die Bildqualität dämpft. Das war alles nur, dass ich mich in meine eigene Logik verwickelte und die Werkzeuge beschuldigte.
Mutoh
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.