[SOLVED] Strange ghosting on moving sprites

Hello.

(be warned: the fomatting is bad in this post)
I’m having a graphics problem with my game. Whenever I move my player sprite, it leaves a black trail behind it. and the sprite also stutters a lot. here’s a video:

Another note: When I shake the window, the black trails also appear on my player, but not on the box (Which also inherits from object). Here’s all of the code that seems to be relevant:

Player.cs:
public class Player : Object
{
public Texture2D Texture;
float speed = 500;

    public Player()
    {
        Collider = new Rectangle(0, 0, 100, 100);
    }

    public void PlayerUpdate(GameTime gameTime)
    {

        MoveColliderStart();

        if (Input.state.IsKeyDown(Keys.Left))
        {
            Collider = Move(new Rectangle(Collider.X - (int)(speed * gameTime.ElapsedGameTime.TotalSeconds), Collider.Y, Collider.Width, Collider.Height));
        }
        if (Input.state.IsKeyDown(Keys.Right))
        {
            Collider = Move(new Rectangle(Collider.X + (int)(speed * gameTime.ElapsedGameTime.TotalSeconds), Collider.Y, Collider.Width, Collider.Height));
        }
        if (Input.state.IsKeyDown(Keys.Up))
        {
            Collider = Move(new Rectangle(Collider.X, Collider.Y - (int)(speed * gameTime.ElapsedGameTime.TotalSeconds), Collider.Width, Collider.Height));
        }
        if (Input.state.IsKeyDown(Keys.Down))
        {
            Collider = Move(new Rectangle(Collider.X, Collider.Y + (int)(speed * gameTime.ElapsedGameTime.TotalSeconds), Collider.Width, Collider.Height));
        }

        MoveColliderEnd();
    }
}

}`

Object.cs

namespace City.Objects
{
public class Object
{
/*Adds a Collider, which holds the position and size of the
* object (An object can have only one collider)
*/
public Rectangle Collider;

    /*Creates a list of all the colliders in the state. See 
     * the ChangeState method in state to see how it clears.
     */
    public static List<Rectangle> PhysicsColliders = new List<Rectangle>();

    public Object()
    {
        /*If you are creating a moveable object,
         * don't add it to PhysicsColliders in the 
         * constructor. Only do that with Stationary
         * objects.
         */
    }

    /*Move creates a rectangle that takes a position and checks if it 
     * collides with anything. To use it, assuming you are using the 
     * collider as the position argument in the draw method of the 
     * state, you can set the collider position to the move method.
     */
    public Rectangle Move(Rectangle position)
    {
        //Creates a rectangle to hold the return value. Sets it to the current posiiton.
        Rectangle newPos = Collider;

        bool CanChangeX = true;
        bool CanChangeY = true;

        //Checks all of the colliders in the PhysicsColliders list.
        for (int i = 0; i < PhysicsColliders.Count; i++)
        {
            /*Checks if the requested position is beyond the edge of another object. If 
             * It is, it checks to see if the collider intersects with the object. If either
             * of these are false, the requested position becomes the new position.
             */
            if (CanChangeX)
            {
                if (!((position.X + position.Width > PhysicsColliders[i].X || position.X < PhysicsColliders[i].X)
                        && position.Intersects(PhysicsColliders[i])))
                {
                    CanChangeX = true;
                }
                else
                {
                    CanChangeX = false;
                }
            }

            if (CanChangeY)
            {
                if (!((position.Y + position.Height > PhysicsColliders[i].Y || position.Y < PhysicsColliders[i].Y)
                    && position.Intersects(PhysicsColliders[i])))
                {
                    CanChangeY = true;
                }
                else
                {
                    CanChangeY = false;
                }
            }
        }

        if (CanChangeX)
        {
            newPos.X = position.X;
        }

        if (CanChangeY)
        {
            newPos.Y = position.Y;
        }
        
        //Returns the filtered position
        return newPos;
    }

    //These methods need to be specified before and after moving an object
    public void MoveColliderStart()
    {
        PhysicsColliders.Remove(PhysicsColliders.Find(x => x == Collider));
    }

    public void MoveColliderEnd()
    {
        PhysicsColliders.Add(Collider);
    }
}

}`

TestState.cs
`using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using City.Objects;

namespace City.States
{
public class TestState : State
{
Player player = new Player();
PhysicsBox box = new PhysicsBox();
Camera camera;

    FrameCounter frameCounter = new FrameCounter();

    SpriteFont font;

    Tiles.Tilemap map = new Tiles.Tilemap();

    public override void Init()
    {
        player.Texture = content.Load<Texture2D>("WhiteSquare");
        box.Texture = player.Texture;
        font = content.Load<SpriteFont>("File");

        camera = new Camera(device.Viewport);

        map.Map.Add(new string[] { "xxxx", "xxxx"});

        map.CreateMap();

        base.Init();
    }

    public override void Update(GameTime gameTime)
    {
        camera.Update(gameTime);
        player.PlayerUpdate(gameTime);
    }

    public override void Draw(GameTime gameTime)
    {
        spriteBatch.Begin(SpriteSortMode.FrontToBack, null, null, null, null, null, camera.transform);

        spriteBatch.Draw(player.Texture, player.Collider, Color.Red);
        spriteBatch.Draw(box.Texture, box.Collider, Color.White);

        var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

        frameCounter.Update(deltaTime);

        var fps = string.Format("FPS: {0}", frameCounter.AverageFramesPerSecond);

        spriteBatch.DrawString(font, fps, new Vector2(1, 1), Color.Black);

        spriteBatch.End();
    }
}

}`

Game1.cs
`using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using City.States;
using System.Diagnostics;

namespace City
{
///


/// This is the main type for your game.
///

public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;

    public static ContentManager _content;
    public static SpriteBatch _spriteBatch;

    public static List<State> _states = new List<State>();
    public static List<Texture2D> LoadedTextures = new List<Texture2D>();

    //Make States
    public static State testState;

    public static State currentState = null;

    public static bool CanCheck;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this)
        {
            SynchronizeWithVerticalRetrace = false,
        };
        graphics.ApplyChanges();
        Content.RootDirectory = "Content";

        _content = Content;
        
    }

    /// <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()
    {
        //Create States
        testState = new TestState();

        testState.isCurrentState = true;
        CanCheck = true;

        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);
        State.spriteBatch = spriteBatch;

        State.device = this.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
    /// game-specific 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)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        Input.GetKeyboardInput();

        //makes sure the code to set the current state doesn't run unnessesarily
        if (CanCheck)
        {
            //Checks which is the current state
            foreach (State i in _states)
            {
                if (i.isCurrentState)
                {
                    //stores the current state object in a variable
                    currentState = i;
                }
            }

            CanCheck = false;
        }

        //runs the init if it hasn't already
        if (currentState != null)
        {
            if (!currentState.isFinishedInit)
                currentState.Init();
            currentState.Update(gameTime);
        }

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

        // TODO: Add your drawing code here

        if(currentState != null)
        {
            currentState.Draw(gameTime);
        }

        base.Draw(gameTime);
    }
}

}`

If you need any more information, I’d be happy to give it to you.

Hi @Diagonal_Dice!

Nice to meet you.
I’ll give it a try.
The first thing that comes to mind is that you never seem to set the IsFinishedInit in the TestState. So you seem to do that every update-cycle. Of course I may be wrong since the relevant code is missing there.

Most of the time such stutters come from the doing update-stuff in the draw-method (don’t do that) since update and draw methods are done a different amount of times… But I don’t see that happening here.

And another thing is: Red-green contrast is the worst :slight_smile: Seems stupid, but does it happen with different colors as well?

That’s all from me. I’m sorry, but apart from the stuff above I don’t see a problem.
Maybe someone smarter has to stop by :smile:
Don’t worry; plenty of people here trying to help.
cu

1 Like

I do not see any black trail in the video. Are you sure it is not a problem with your monitor?

1 Like

If you run the video at a slower speed, you will not see it but you will find at 0.25x that your code is not smooth…

EDIT

In other words, what’s your draw speed?

1 Like

Yes, I have seen it with other colors. It’s just much clearer with red and green. Also, though you can’t see it, when I run base.Init isFinishedInit is switched to true. However, getting rid of that if statement in the draw command got rid of the stuttering, so I thank you greatly for that. As bjornenalfa pointed out, the black trails don’t seem to appear on my phone screen, but if it were a monitor problem I would have probably noticed it in other things by now. Thanks for all the support!

I had an if statement in the draw command. It got rid of the stuttering, but the ghosting remains.

I see… Its like that on my phone screen as well.

https://www.computerhope.com/jargon/g/ghosting.htm , definition #2

Go to your youtube video and move the frames step by step. (you can do it by pausing the video, then using dot and comma to step 1 frame forward / step 1 frame backward).

Do you see the ghosting there, with still frames?

If you see it, it’s a bug. If you don’t see it, it’s just the way your LCD works.

It is a problem with my monitor. Whenever I moved the window in my game, the ghosting would appear. So, I tried doing that with my chrome window and i got the same effect.

Thanks to everyone for helping me with this. At the very least I got the stuttering to stop, and someone having the same problem might see this and stop them from thinking its a bug like me.

Again, KakCAT, MrValentine, bjornenalfa and throbax, thank you all so much.

4 Likes