Sprites Pause Before Moving Again

In my game, I have aliens that move from the bottom of the screen to the top. Along the way, they select a point to head to, and once they get there, they set a new target. However, once they get to a point, they pause, and then keep going. The amount of time they pause for is inconsistent as well (sometimes they pause for >1 second, sometimes there’s barely any pause). If I’m clicked out of the game, they jitter in place. I think it’s worth nothing that I’m not using a fixed timestep. Here is a video of what I’m talking about.
I’m calculating the movement of the aliens with the following bit of code:
_position += velocity * (float)gameTime.ElapsedGameTime.TotalMilliseconds * _speed;

Since I’ve used the above line before with no problem (without a fixed timestep as well), the problem might have something to do with the way I’m handling the entities.

I don’t know if this affects anything or not, but I’m using MonoGame.Extended’s Screen system. I’m also using my own entity system (there’s an IEntity interface with an Update and Draw method, and an EntityList class that has an IEntity list, where drawing/updating is done. Each scene has its own EntityList object called “Entities.” It’s very basic, but I don’t need anything more complicated right now.)

The game is on GitHub if you want to look at more of the code. Here are the related classes:
Alien
GameScene
IEntity
EntityList

Any help would be greatly appreciated!

Could the problem may be with the way the alien reaches its target? When they reach the point, does the game correctly know that it happened? Or is it jittering back and forth to actually hit the trigger to set a new point to start moving to?

Thanks for the help! The problem seems to be with setting a new target. I’m drawing a rectangle at the location of the target, and once they reach the target, it takes a second for the target to change. I’m still not sure about the jittering, though. The problem might be with the SetTarget() method, and how I’m checking to see if they’ve reached the target, as the jittering only starts when they reach their target.

If the problem is the jitter in the video, this is something I’ve seen before.

When you move towards a target in this way, you’re adding a discrete amount of distance towards that point every frame. This means that you can often overshoot and so, on the next frame, you move back towards the point. However, you will then overshoot again and this is what results in you jittering back and forth.

You can use a dot product to detect the scenario when you’ve overshot. I just happen to have some example code kicking around, I’ll dump it raw for you here. You can bring this into a new project, but you’ll either need to add a sprite font named DebugFont to your project, or remove those lines from the code.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Diagnostics;

namespace TargetTest
{
    /// <summary>
    /// This is the main type for your game.
    /// </summary>
    public class Game1 : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private SpriteFont _debugFont;

        private Texture2D _pixel;

        private Vector2 _entityPos = Vector2.Zero;
        private Vector2 _entitySize = new Vector2(50f);
        private float _entitySpeed = 100f;

        private Vector2? _target = null;
        private Vector2 _targetSize = new Vector2(25f);

        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            this.IsMouseVisible = true;
        }

        protected override void Initialize()
        {
            _graphics.PreferredBackBufferWidth = 1280;
            _graphics.PreferredBackBufferHeight = 720;
            _graphics.ApplyChanges();

            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            _debugFont = Content.Load<SpriteFont>("DebugFont");

            _pixel = new Texture2D(_graphics.GraphicsDevice, 1, 1);
            _pixel.SetData<Color>(new Color[] { Color.White });

            _entityPos = 0.5f * new Vector2(_graphics.PreferredBackBufferWidth, _graphics.PreferredBackBufferHeight);
        }

        private void HandleInput()
        {
            MouseState mouseState = Mouse.GetState();

            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                // Update target
                _target = new Vector2(mouseState.X, mouseState.Y);
            }
        }

        protected override void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            HandleInput();

            if (_target != null)
            {
                if (_target.Value != _entityPos)
                {
                    // Move to target
                    Vector2 dir = _target.Value - _entityPos;
                    dir.Normalize();

                    //if (float.IsNaN(dir.X) || float.IsNaN(dir.Y))
                    //    Debug.WriteLine("I'm broken!");

                    _entityPos += (float)gameTime.ElapsedGameTime.TotalSeconds * _entitySpeed * dir;

                    // Check if we overshot
                    Vector2 newDir = _target.Value - _entityPos;
                    newDir.Normalize();

                    float r = Vector2.Dot(dir, newDir);
                    if (r < 0)
                    {
                        _entityPos = _target.Value;
                    }
                }
            }

            base.Update(gameTime);
        }

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

            _spriteBatch.Begin();
            {
                // Draw Entity
                _spriteBatch.Draw(
                    _pixel,
                    new Rectangle(
                        (int)(_entityPos.X - _entitySize.X / 2),
                        (int)(_entityPos.Y - _entitySize.Y / 2),
                        (int)_entitySize.X,
                        (int)_entitySize.Y
                    ),
                    Color.Red
                );

                if (_target != null)
                {
                    // Draw Target
                    _spriteBatch.Draw(
                        _pixel,
                        new Rectangle(
                            (int)(_target.Value.X - _targetSize.X / 2),
                            (int)(_target.Value.Y - _targetSize.Y / 2),
                            (int)_targetSize.X,
                            (int)_targetSize.Y
                        ),
                        Color.LightBlue
                    );
                }

                // Draw positions
                _spriteBatch.DrawString(_debugFont, $"Entity Position: {_entityPos}", new Vector2(0, 0 * _debugFont.LineSpacing), Color.White);
                _spriteBatch.DrawString(_debugFont, $"Target Position: {((_target == null) ? "<null>" : _target.Value.ToString())}", new Vector2(0, 1 * _debugFont.LineSpacing), Color.White);
            }
            _spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Thank you, it works quite well!

1 Like