Turn based course, out of Boundary error when you try to shoot

Hello Every one.
I’ve made some test in the scene and when I move one of my units far away, push in the end turn button and try to shooot, I have a error message of type:
IndexOutOfRangeException:

I’m on the tile 0.10, so over the max shootRange of 7.
I understand the way to check the distance by adding the X and Z absolute value but In this case we have a problem no?
If our unit is on a tile where the add is over 7.
Thanks.
François

I’m not sure what you did. Judging by the error message it looks like you skipped some code.

The error comes from ShootAction.GetValidActionGridPositionList(). This function only checks grid positions within the range. However, your error comes when you call LevelGrid.HasAnyUnitOnGridPosition, which should only be called if the grid position being checked is valid. In your case it is not valid. So, it looks like you missed a few checks.

if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition))
{
    continue;
}

This is what prevents us from trying to check grid positions that are not on the grid. Only if this is valid will we proceed to the next checks

Next, we check the distance (once we know the grid position is on the grid)

int testDistance = Mathf.Abs(x) + Mathf.Abs(z);
if (testDistance > maxShootDistance)
{
    continue;
}

This will further prevent us from trying to check grid positions that are out of range. If the distance is greater than the max shoot distance, we will not proceed further

In your case, it does proceed further, so there is something wrong in your checks.

If you could share your ShootAction.GetValidActionGridPositionList() method, we may be able to determine what the problem is

1 Like

Hello Bixarrio.
I’ve checked my method, nothing seems different for me from the native code …

    public override List<GridPosition> GetValidActionGridPositionList()//Création de la liste des cases valides pour le déplacement
    {
        List<GridPosition> validGridPositionList = new List<GridPosition>();//Création de la liste nommée validGridActionPosition

        GridPosition unitGridPosition = unit.GetGridPosition();//On récupère l'information de la position de l'unité en allant la chercher dans le script Unit.
        
        for (int x = -maxShootDistance; x <= maxShootDistance; x++)
        {
            for (int z = -maxShootDistance; z <= maxShootDistance; z++)
            {
                GridPosition offsetGridPosition = new GridPosition(x, z);
                GridPosition testGridPosition = unitGridPosition + offsetGridPosition;//La variable TestPosition est le vecteur 2D X,Z qui correspond à la position actuelle de l'unité + la valeur de l'offset choisie.

                if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition))//N'affiche que les positions atteignables mais aussi la position d'origine. ! logique inverse
                {
                    //Si la case vérifiée n'est pas dans celles possible, logique inverse, alors on continue de chercher les possibles.
                    continue;
                }

                //Création de la variable de test de distance de tir
                //On additionne la valeur absolue en x et en z de la valeur des cases (leurs coordonnées x et z)
                int TestShootDistance = Math.Abs(x) + Math.Abs(z);
                //Si cette valeur est supèrieure à la valeur de tir alors on ne fait rien et on continue le script.
                if (TestShootDistance > maxShootDistance)
                {
                    continue;
                }

                if (!LevelGrid.Instance.HasAnyUnitOnGridPosition(testGridPosition))
                {
                    //Si la case n'est occupée par aucune unité, alors on continue la recherche des possibles.
                    continue;
                }


                //Définition de l'unité ciblée.
                Unit targetUnit = LevelGrid.Instance.GetUnitAtGridPosition(testGridPosition);

                //Test pour vérifier que l'unité ne cible pas une unité du même camp:
                if (targetUnit.IsEnemy() == unit.IsEnemy())
                {
                    continue;
                }

                validGridPositionList.Add(testGridPosition);
                //Debug.Log(testGridPosition);
            }
        }

        return validGridPositionList;//On renvoie la liste des positions d'arrivée valide
    }

The thing I don’t understand is that my first unit at the beginning is already out of range when I choose Shoot and I haven’t error message:

It’s when I want to go away with the second unit and ending the turn that the problem appears…

At the beginning of the third round, by default I’m in the moving action so I haven’t yet the error.
If I select the other unit nearest us and select shoot action, it’s out of range so the square ender the enemy doesn’t appear, no error message:

but if I select the most far unit, the one I’ve moved, it’s out of shoot range, like the other but I have the message error:

The error messages for Unity seem to come from the GetGridObject method in the GridSystem script:

    public GridObject GetGridObject(GridPosition gridPosition)
    {
        return gridObjectArray[gridPosition.x, gridPosition.z];
    }

From the level grid, the line where we create the gridObject variable (the first line)

    public bool HasAnyUnitOnGridPosition (GridPosition gridPosition)
    {
        GridObject gridObject = gridSystem.GetGridObject(gridPosition);
        return gridObject.HasAnyUnit();//Récupère la valeur de la booléenne du script gridObject qui vérifie si une unité est déjà sur la case.
    }

From the line in the shootAction Script in the GetValidGridPositionList method:

                if (!LevelGrid.Instance.HasAnyUnitOnGridPosition(testGridPosition))
                {
                    //Si la case n'est occupée par aucune unité, alors on continue la recherche des possibles.
                    continue;
                }

In the GridSystemVidual, in the update method, the last line ( ShowGridPositionList(
selectedAction.GetValidActionGridPositionList()):

    public void UpdateGridVisual()
    {
        HideAllGridPosition();

        BaseAction selectedAction = UnitActionSystem.Instance.GetSelectedAction(); // Ancienne ligne avec problème 2022-10-15

        ShowGridPositionList(
            selectedAction.GetValidActionGridPositionList()); //Ancienne ligne avec problème 2022-10-15
    }

And the last in the GridSystemVisual scripot, in the Update method:

    public void Update()
    {
        UpdateGridVisual();
    }

If it can help :slight_smile:
Thanks, take care.
François

Can you share your GridPosition.cs code?

Hello Bixarrio, thanks for your patience you and Brian :wink:
Here is my GridPosition Code:

using System;

public struct GridPosition : IEquatable<GridPosition>
{
    public int x;
    public int z;

    public GridPosition(int x, int z)
    {
        this.x = x;
        this.z = z;
    }

    public override bool Equals(object obj)
    {
        return obj is GridPosition position &&
               x == position.x &&
               z == position.z;
    }

    public bool Equals(GridPosition other)
    {
        return this == other;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(x, z);
    }

    public override string ToString()
    {
        return "x: " + x + " z: " + z;
    }

    public static bool operator ==(GridPosition a, GridPosition b)
    {
        return a.x == b.x && a.z == b.z;
    }

    public static bool operator !=(GridPosition a, GridPosition b)
    {
        return !(a ==b);
    }

    public static GridPosition operator +(GridPosition a, GridPosition b)//Création de la méthode pour autoriser les additions de grid position dans le script MoveAction ligne 65.
    {
        return new GridPosition(a.x + b.x, a.z + b.z);
    }

    public static GridPosition operator -(GridPosition a, GridPosition b)//Création de la méthode pour autoriser les soustarctions de grid position dans le script MoveAction ligne 65.
    {
        return new GridPosition(a.x - b.x, a.z - b.z);
    }

}

OK, that looks fine. I thought that we are perhaps calculating an invalid position (in hindsight, that would still not have passed the validation)

The error happens when we are trying to get the object out of the array. This suggests that - even though it passed validation - the position is not valid in terms of the array. Can you share the code in GridSystem.cs where you create the array. This should be the constructor. Also, what is the dimensions of your grid? Looks like 15x15?

1 Like

:slight_smile:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GridSystem // on ne rend pas ce script monobehavior pour pouvoir se servir du constructor.
{
    private int width;// variable pour la largeur de la grille
    private int height;// variable pour la longueur de la grille
    private float cellSize;// variable pour la taille des cellules
    private GridObject[,] gridObjectArray;//liste de 2 valeurs seulement, 2D.

    public GridSystem(int width, int height, float cellSize)
    {
        this.width = width;
        this.height = height;
        this.cellSize = cellSize;

        gridObjectArray = new GridObject[width, height];

        for (int x = 0; x < width; x++)//Pour x compris entre 0 et la valeur de largeur
        {
            for (int z = 0; z < height; z++)//Pour z comris entre 0 et la valeur de hauteur
            {
                GridPosition gridPosition = new GridPosition(x, z);// On créé une nouvelle position de grille de valeur x et z (depuis le script GridPosition)
                gridObjectArray[x,z]= new GridObject(this, gridPosition);//Le nouveau GridObject est lui-même affecté des valeurs de la ligne sup.
                //Debug.DrawLine(GetWorldPosition(x, z), GetWorldPosition(x,z) + Vector3.right *0.2f, Color.yellow, 1000f);//dessine une ligne position de départ  à position d'arrivée où l'arrivée est la position de départ + 0.2 fois la position de départ de couleur rouge durant 1000 frames.
            }
        }

    }
    
    public Vector3 GetWorldPosition(GridPosition gridPosition)
    {
        return new Vector3(gridPosition.x, 0, gridPosition.z) * cellSize;
    }

    public GridPosition GetGridPosition(Vector3 worldPosition)
    {
        return new GridPosition(
            Mathf.RoundToInt(worldPosition.x / cellSize),
            Mathf.RoundToInt(worldPosition.z / cellSize)
            );
    }

    public void CreateDebugObjects(Transform debugPrefab)
    {
        for (int x = 0; x < width; x++)//Pour x compris entre 0 et la valeur de largeur
        {
            for (int z = 0; z < height; z++)//Pour z comris entre 0 et la valeur de hauteur
            {
                GridPosition gridPosition = new GridPosition (x,z);

                Transform debugTransform = GameObject.Instantiate(debugPrefab, GetWorldPosition(gridPosition), Quaternion.identity);
                GridDebugObject gridDebugObject = debugTransform.GetComponent<GridDebugObject>();
                gridDebugObject.SetGridObject(GetGridObject(gridPosition));
            }

        }

    }

    //Gréation de la méthodeGetGridObject qui récupèrera les informations de localisation de chaque case.
    //Elle retournera les valeurs x et z de chacune des cases pour afficher leur visuel.
    public GridObject GetGridObject(GridPosition gridPosition)
    {
        return gridObjectArray[gridPosition.x, gridPosition.z];
    }

    //Création de la booléenne qui vérifie que la destination est valide, qu'elle se trouve bien à l'intèrieur des dimensions max.
    public bool IsValidGridPosition (GridPosition gridPosition)
    {
        return 
            gridPosition.x >= 0 && 
            gridPosition.z >= 0 && 
            gridPosition.x <= width && 
            gridPosition.z <= height;
    }

    //Methode pour rendre public et accessible d'un autre script la longueur totale
    public int GetHeight()
    {
        return height;
    }

    //Methode pour rendre public et accessible d'un autre script la longueur totale
    public int GetWidht()
    {
        return width;
    }

}

Thank you Bixarrio.
And Yes its an 15x15x2 grid.
I enjoy go out of the border when I learn to Code.
Since the 80th and my Amstrad 6128 and without internet to help you :grimacing:

There’s a problem here. A valid grid position is less than width and height

    public bool IsValidGridPosition (GridPosition gridPosition)
    {
        return 
            gridPosition.x >= 0 && 
            gridPosition.z >= 0 && 
            gridPosition.x < width && 
            gridPosition.z < height;
    }

Your character is standing at (9,1) - in the screenshot - so the range it will check will go beyond the grid boundary, meaning this will say it’s valid when it is not. That is, it will try to validate (15,0), for instance, and this will say it’s valid, but there is no element at gridObjectArray[15,0]

3 Likes

Hello Bixarrio.
It was that, it works perfectly if I put Strictly minor than.
Thanks a lot :slight_smile:
François

Great. It’s my pleasure.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms