Monogame Animation: Position of the animation goes out of bounds when I hold down the input key

Hi i am trying to make a simple character where different animations play depending on if the character is going left, right or idle. I have an animation class with a bunch of fields, an animation manager that makes use of said fields for the animation logic and draws it with spritebatch.Draw() and with the players position and I also have a player class.

The following is the Animation.cs file:
public class Animation
{

    public float speed;
    public Rectangle[] sourceRectangles;
    public Texture2D texture;
    public int currentframe;
    public int frameCount;
    int frameWidth;
    int frameHeight;

    public Animation(Texture2D spriteSheet, int frameCount)
    {
        texture = spriteSheet;
        this.frameCount = frameCount;
        
        speed = 0.1f;
        frameWidth = texture.Width;
        frameHeight = texture.Height / frameCount;      //divide by framecount to get the height of each individual frame

        sourceRectangles = new Rectangle[frameCount];   //intialize a new array of rectangles
        for(int i = 0; i < frameCount;i++)              //fill up the array with
        {
            sourceRectangles[i] = new Rectangle(0, (i) * frameHeight, frameWidth, frameHeight);
        }

        currentframe = 0;       //start at the first frame of the animation

    }
}

This is the AnimationManager.cs

public class AnimationManager
{
float timer;

    Animation animation;

    public AnimationManager(Animation Animation)    //we send an animation to be played from outside the class
    {
        animation = Animation;
        timer = 0;
        
    }
    
    public void Play(Animation currentAnimation)    //the parameter is the animation that we want to play
    {
        if (animation == currentAnimation)
            return;
        animation = currentAnimation;       
        animation.currentframe = 0;
        timer = 0f;
    }
    public void Stop()
    {
        timer = 0f;
        animation.currentframe = 0;
    }

    public void Update(GameTime gameTime)   //animation logic
    {
        timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
        if (timer > animation.speed)
        {
            if (animation.currentframe >= 4)
            {
                animation.currentframe = 0;
            }

            animation.currentframe++;
            timer = 0f;
        }
    }

    public void Draw(SpriteBatch spriteBatch, Vector2 position) //Vector2 position will be the players position 
    {
        spriteBatch.Draw(animation.texture, position, animation.sourceRectangles[animation.currentframe], Color.White);
    }
}

And here is the Player.cs file:

internal class Player1
{
public Vector2 position; //player position
Vector2 velocity; //players speed
bool hasJumped;

    AnimationManager animationManager;

    Dictionary<string, Animation> animations;   //we use a dictionary to store the animations
   
  

    public Player1(Dictionary<string, Animation> Animations)    //we get the dictionary, which was created and initialized in game1.LoadContent
    {
        //startposition for player
        this.position.X = 50;
        this.position.Y = 50;

        hasJumped = true; //a bool to check if we are mid jumpp or not

        animations = Animations;
        animationManager = new AnimationManager(animations.First().Value);  //play the first animation, i.e Idle, by default

    }


    public void Update(GameTime gameTime, ContentManager content)
    {
       
        KeyboardState keyboardState = Keyboard.GetState();
        animationManager.Update(gameTime);
        position += velocity; //the players position is always dependent on the velocity which changes depending on input from the keyboard

        if (keyboardState.IsKeyDown(Keys.Right))    //the horizontal speed changes when you press right or left keys
        {
            velocity.X = 5f;
            animationManager.Play(animations["RunRight"]);
        }            
        else if (keyboardState.IsKeyDown(Keys.Left))
        {
            velocity.X = -5f;
            animationManager.Play(animations["RunLeft"]);
        }
        else
        {
            velocity.X = 0f;
            animationManager.Play(animations["Idle"]);
        }
            

        Jump(keyboardState);

        

    }

    private void Jump(KeyboardState keyboardState)
    {
        if (keyboardState.IsKeyDown(Keys.Space) && hasJumped == false)   //if you have pressed space and are not in the air the vertical speed and hasjumped change and the player is shot upwards
        {
            position.Y -= 15f;
            velocity.Y = -20f;
            hasJumped = true;
        }
        if (hasJumped == true)   //this is the gravity code
        {
            velocity.Y += 2f;   //since the speed was negativ when the player was shot up, it goes slowly back to a positiv value everytime player.Update() runs, , 
                                //and since the position is dependent on the velocity, the player is forced downward naturally
        }                     
        if (position.Y + 200 >= 450) //when you reach groundlevel, hasjumped becomes false and the gravity code above stops
            hasJumped = false;
        if (hasJumped == false)     //but since we change the value of velocity.Y with gravity, the player will keep dropping down even when hasJumped is false
            velocity.Y = 0f;       //therefore we change velocity.Y back to 0 at groundlevel, so the player doesnt fall through the ground

    }




    public void Draw(SpriteBatch spriteBatch)
    {
        animationManager.Draw(spriteBatch, position);   //we send the players position so that the animation follows the player
    }
}

The problem I am having is that when I run the program and I hold down the left or the right key for a while, it crashes and i get an IndexOutOfRangeException in the AnimationManager.Draw() method, i think the position is out of bounds but I don’t understand why that is. Can anyone help? I apologize in advance for the sloppy code or unclear explanations, please say if something is unclear.

Could you show the code where you are initializing the RunLeft, RunRight, and Idle instances of the Animation class, as well as the sourceRectangle instances for each?

An “IndexOutOfRangeException” is thrown when you index into an array (or other collection) outside of it’s bounds. So, a -1 or a number larger than the arrays length.

You’re only indexing into one array in the Animation.Draw() method, here: animation.sourceRectangles[animation.currentframe].

Logically speaking, then, animation.currentframe must be either less than 0 or greater than frameCount. (When an exception is thrown and the debugger pauses on a line, you can hover over variables to get their value, so you can work out which it is)

Since I don’t see any evidence that animation.currentframe can go lower than 0 (no subtractions), my assumption is that framecount is being initialised to something smaller than 5 (the magic number in AnimationManager.Update plus 1).

(Edit) After update is called, animation.currentframe will range from 1 to 4. Arrays in C# are zero indexed, so you’ll never see the first animation frame with this code.

yes, i initialized them in the game1.LoadContent():

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

        var animations = new Dictionary<string, Animation>()
        {
            {"Idle", new Animation(Content.Load<Texture2D>("images/player/idlebig"), 5) },
            {"RunRight", new Animation(Content.Load<Texture2D>("images/player/runrightbig"), 4) },
            {"RunLeft", new Animation(Content.Load<Texture2D>("images/player/runleftbig"), 4) },
        };
        player = new Player1(animations);
        
        
    }

Im, not sure what you mean by the sourceRectangle instances, but i make all the rectangles in a for loop in the Animation class’s constructor:

sourceRectangles = new Rectangle[frameCount]; //intialize a new array of rectangles
for(int i = 0; i < frameCount;i++) //fill up the array with
{
sourceRectangles[i] = new Rectangle(0, (i) * frameHeight, frameWidth, frameHeight);
}

Totally unrelated but

public AnimationManager(Animation Animation)    //we send an animation to be played from outside the 
class
{
    animation = Animation;
    timer = 0;          }

You can use the same name in your method as your class, but use this.animation to refer to your class level property

public AnimationManager(Animation animation)    //we send an animation to be played from outside the 
class
{
    this.animation = animation;
    timer = 0;
    
}

I see, after reading you comment I found the problem. It was in the AnimationManager.Update(). In the animation logic, I had previously been testing the code out with only the “idle” animation which has 5 frames and so i had written:

if (animation.currentframe>= 4)
{
animation.currentframe = 0;
}

But of course the other animations have less then 5 frames, they’re framecount was correctly initialized in the game1.LoadContent() section but the animation logic was tailored only to the “idle” animation and so the animation.currenframe ended up being bigger than the sourceRectangle array. I changed it so now it looks like this and everything is working fine:

if (animation.currentframe>= (animation.frameCount - 1))
{
animation.currentframe = 0;
}

thank you soooo much for taking the time to comment, this really helps me out a lot as I can finally get going with the rest of game!