BUG: Index was outside the bounds of the array.

Hi, I've been trying to code a video game called Galaga using C# and the Raylib_cs library, but I feel frustrated right now because I got a System.IndexOutOfRangeException bug that says "Index was outside the bounds of the array". Also, I noticed that this error occurs everytime 1, 2 or all enemies try to shoot and destroy the player because they aren't destroying it since the bullet is going up instead of downwards it and there might be something wrong with the collisions because the enemies' bullet is destroying everything that collides with it, including these enemies (aliens) themselves, and it is not supposed to happen and the idea is that every once in a while, an enemy has to shoot a few bullets randomly to only destroy the player. and once it gets destroyed, it explodes and you lose the game. Can you help me to solve this problem, please? Also, here's the code, so you can help me to find a solution, and the zipped .rar file of the game that contains everything including the code below

GalagaSantiagoRestrepo.rar

.

using System; using System.Numerics; using Raylib_cs;

class Enemy { public Vector2 Position; public bool IsActive; public Texture2D Texture;

public Enemy(float x, float y, Texture2D texture)
{
    Position = new Vector2(x, y);
    IsActive = true;
    Texture = texture;
}

}

class Galaga { static Vector2 playerPosition = new Vector2(200, 600); static float playerSpeed = 400; static Enemy[] enemyGroup;

static float enemySpeed = 160;
static int direction = 1;
static bool bulletActive = false;
static float bulletSpeed = 750;
static Vector2 bulletPosition = new Vector2(-100, -100);
static double totalTime;

private static Enemy[] InitializeEnemyGroup(Texture2D alien1Texture, Texture2D alien2Texture, Texture2D alien3Texture)
{
    Enemy[] enemies = new Enemy[15];

    for (int i = 0; i < enemies.Length; i++)
    {
        float x = 100 + (i % 5) * 120;
        float y = 100 + (i / 5) * 120;

        if (i % 3 == 0)
        {
            enemies[i] = new Enemy(x, y, alien1Texture);
        }
        else if (i % 3 == 1)
        {
            enemies[i] = new Enemy(x, y, alien2Texture);
        }
        else
        {
            enemies[i] = new Enemy(x, y, alien3Texture);
        }
    }

    return enemies;
}

static int playerLives = 3;
static bool playerExploded = false;

public static void Main()
{
    const int screenWidth = 1280;
    const int screenHeight = 720;

    Raylib.InitWindow(screenWidth, screenHeight, "Galaga");

    // Load enemy textures
    Texture2D playerTexture = LoadTexture("player.png", 2);
    Texture2D alien1Texture = LoadTexture("alien1.png", 2);
    Texture2D alien2Texture = LoadTexture("alien2.png", 2);
    Texture2D alien3Texture = LoadTexture("alien3.png", 2);

    // Initialize enemy group with different enemy types
    enemyGroup = InitializeEnemyGroup(alien1Texture, alien2Texture, alien3Texture);

    Texture2D explosionTexture = LoadTexture("explosion.png", 2);

    while (!Raylib.WindowShouldClose())
    {
        float delta = Raylib.GetFrameTime();

        // Update player position
        if (!playerExploded)
        {
            if (Raylib.IsKeyDown(KeyboardKey.KEY_LEFT) && playerPosition.X > 0)
            {
                playerPosition.X -= playerSpeed * delta;
            }

            if (Raylib.IsKeyDown(KeyboardKey.KEY_RIGHT) && playerPosition.X < (screenWidth - 96)) // Assuming the player texture width is 96
            {
                playerPosition.X += playerSpeed * delta;
            }

            // Update bullet position
            UpdateBullet();

            // Update enemy group
            UpdateEnemyGroup(delta, screenWidth);

            // Update player explosion state
            if (playerExploded && Raylib.GetTime() > totalTime + 2.0) // 2 seconds for explosion animation
            {
                playerExploded = false;
            }

            // Check for collisions
            CheckCollisions(screenWidth);

            // Check for enemy shooting
            if (Raylib.GetRandomValue(0, 100) < 1) // Adjust the probability as needed
            {
                // Find an active enemy to shoot
                int activeEnemyCount = enemyGroup.Count(enemy => enemy.IsActive);

                if (activeEnemyCount > 0)
                {
                    int randomEnemyIndex;
                    do
                    {
                        randomEnemyIndex = Raylib.GetRandomValue(0, enemyGroup.Length);
                    } while (!enemyGroup[randomEnemyIndex].IsActive);  // This is were the bug /System.IndexOutOfRangeException occurs

                    ShootEnemy(enemyGroup[randomEnemyIndex]);
                }
            }
        }

        // Draw everything
        Raylib.BeginDrawing();
        Raylib.ClearBackground(Color.DARKBLUE);

        Raylib.DrawText("Press ESC to exit", 10, 10, 20, Color.WHITE);

        // Draw player or explosion
        if (!playerExploded)
        {
            Raylib.DrawTexture(playerTexture, (int)playerPosition.X, (int)playerPosition.Y, Color.WHITE);
        }
        else
        {
            Raylib.DrawTexture(explosionTexture, (int)playerPosition.X, (int)playerPosition.Y, Color.WHITE);
        }

        // Draw enemies
        DrawEnemies(enemyGroup);

        // Draw bullet
        if (bulletActive)
        {
            Raylib.DrawTexture(LoadTexture("bullet.png", 2), (int)bulletPosition.X, (int)bulletPosition.Y, Color.WHITE);
        }

        Raylib.EndDrawing();
    }
}

private static void UpdateBullet()
{
    if (!bulletActive && Raylib.IsKeyDown(KeyboardKey.KEY_SPACE))
    {
        bulletActive = true;
        bulletPosition = new Vector2(playerPosition.X + 48, playerPosition.Y); // Assuming the player texture width is 96
    }

    if (bulletActive)
    {
        bulletPosition.Y -= bulletSpeed * Raylib.GetFrameTime();

        if (bulletPosition.Y < 0)
        {
            bulletActive = false;
        }
    }
}
private static void ShootEnemy(Enemy enemy)
{
    bulletActive = true;
    bulletPosition = new Vector2(enemy.Position.X + enemy.Texture.Width / 2, enemy.Position.Y + enemy.Texture.Height);
}

private static void UpdateEnemyGroup(float delta, int screenWidth)
{
    for (int i = 0; i < enemyGroup.Length; i++)
    {
        if (enemyGroup[i].IsActive)
        {
            enemyGroup[i].Position.X += enemySpeed * direction * delta;

            // Check if an enemy has hit the screen borders
            if (enemyGroup[i].Position.X < 0 || enemyGroup[i].Position.X > (screenWidth - 96)) // Assuming the alien texture width is 96
            {
                // If any enemy hits the screen border, change direction for all enemies
                direction *= -1;
            }
        }
    }
}
private static void CheckCollisions(int screenWidth)
{
    if (bulletActive)
    {
        Rectangle bulletBounds = new Rectangle(bulletPosition.X, bulletPosition.Y, LoadTexture("bullet.png", 2).Width, LoadTexture("bullet.png", 2).Height);

        for (int i = 0; i < enemyGroup.Length; i++)
        {
            if (enemyGroup[i].IsActive)
            {
                Rectangle enemyBounds = new Rectangle(enemyGroup[i].Position.X, enemyGroup[i].Position.Y, enemyGroup[i].Texture.Width, enemyGroup[i].Texture.Height);

                if (Raylib.CheckCollisionRecs(bulletBounds, enemyBounds))
                {
                    // Collision detected, handle it as needed
                    bulletActive = false;
                    enemyGroup[i].IsActive = false; // Disable the enemy
                    break; // Exit the loop once a collision is detected
                }
            }
        }
    }
}

private static void DrawEnemies(Enemy[] enemies)
{
    for (int i = 0; i < enemies.Length; i++)
    {
        if (enemies[i].IsActive)
        {
            Raylib.DrawTexture(enemies[i].Texture, (int)enemies[i].Position.X, (int)enemies[i].Position.Y, Color.WHITE);
        }
    }
}

private static Texture2D LoadTexture(string path, int resize = 1)
{
    Image image = Raylib.LoadImage(path);
    Texture2D texture = Raylib.LoadTextureFromImage(image);
    Raylib.UnloadImage(image);
    texture.Width = texture.Width / resize;
    texture.Height = texture.Height / resize;

    return texture;
}

}

Please put ``` (three backticks) on separate line before and after code fragment you put in the post.

Where exactly exception is thrown? Which line? What is on the callstack? What are the values of variables (the array and the index)?

When you do

randomEnemyIndex = Raylib.GetRandomValue(0, enemyGroup.Length);

You need to use enemyGroup.Length - 1.

From the raylib cheatsheet:

int GetRandomValue(int min, int max); // Get a random value between min and max (both included)