Search code examples
c#gridxnamonogame

Getting location of mouse on a grid


So I'm stuck on how to be able to convert my mouse position in monogame/XNA to a position of my grid. What I am trying to achieve is basically to change the color of the rectangle on that specific cell in the grid to a different color when I click on them.

This is my first time working on a grid system and I don't really have a clue on how to do this.

Here is how I built my grid. What basically happens here is I first populate every cell in the grid with 0s. And depending on what value I assign in the update method in the Game1 class, the color of the rectangle will change.

Thank you!

 public int[,] gridCell;
    Texture2D texture;
    public Rectangle rect;
    int col;
    int row;
    const int gridSize = 32;



    //Initializes the constructor
    public Grid(int sizeCol, int sizeRow)
    {
        //texture = sprite;
        col = sizeCol;
        row = sizeRow;
        gridCell = new int[col, row];

        for(int i = 0; i<col;i++)
        {
           for(int j = 0; j<row;j++)
            {
                gridCell[i, j] = 0;
            }
        }
    }


    public void Draw(SpriteBatch spritebatch)
    {
        for (int i = 0; i <= col-1; i++)
        {
            for (int j = 0; j <= row-1; j++)
            {
                if (gridCell[i, j] == 0)
                {
                    spritebatch.FillRectangle(i * 32, j * 32, 31, 31, Color.CornflowerBlue, 0f);
                    spritebatch.DrawRectangle(new Vector2(i * 32, j * 32), new Vector2(32, 32), Color.Black, 1f);
                }

                else if( gridCell[i,j] == 1)
                {
                    spritebatch.FillRectangle(i * 32, j * 32, 31, 31, Color.Yellow, 0f);
                    spritebatch.DrawRectangle(new Vector2(i * 32, j * 32), new Vector2(32, 32), Color.Black, 1f);
                }
                else if (gridCell[i, j] == 2)
                {
                    spritebatch.FillRectangle(i * 32, j * 32, 31, 31, Color.Red, 0f);
                    spritebatch.DrawRectangle(new Vector2(i * 32, j * 32), new Vector2(32, 32), Color.Black, 1f);
                }
                else if (gridCell[i, j] == 3)
                {
                    spritebatch.FillRectangle(i * 32, j * 32, 31, 31, Color.Purple, 0f);
                    spritebatch.DrawRectangle(new Vector2(i * 32, j * 32), new Vector2(32, 32), Color.Black, 1f);
                }
                else if (gridCell[i, j] == 4)
                {
                    spritebatch.FillRectangle(i * 32, j * 32, 31, 31, Color.Blue, 0f);
                    spritebatch.DrawRectangle(new Vector2(i * 32, j * 32), new Vector2(32, 32), Color.Black, 1f);
                }
                else if (gridCell[i, j] == 5)
                {
                    spritebatch.FillRectangle(i * 32, j * 32, 31, 31, Color.Black, 0f);
                    spritebatch.DrawRectangle(new Vector2(i * 32, j * 32), new Vector2(32, 32), Color.Black, 1f);
                }
            }
        }
    }

Solution

  • Ok, here is how it goes: lets say your grid's one square is 8x8. Position of your mouse can be between 8 and 0, and you need it to be only 0/8/16/24/... When you divide mouse position by 8 (lets say Mposition is 4x20), you'll get 0.5x2.5, and if you multiply that with 8, you'll get 4x20 again, BUT: if you truncate division result, youll get 0x2, and that multiplied by 8 is 0x16, so that (0x16) is the real position of the square that mouse is inside of, and 0x2 is the matrix position of your mouse. Here is a simple function that will get you what you need:

    public Vector2 PositionByGrid (int gridSize)
        {
            Vector2 result = new Vector2(MouseState.GetState().X, MouseState.GetState().Y);
            result.X = (int)(result.X / gridSize) * gridSize;
            result.Y = (int)(result.Y / gridSize) * gridSize;
            return result;
        }
    

    This function will return position of the square. If you want the position in matrix, just put result.X = (int)(result.X / gridSize); instead of result.X = (int)(result.X / gridSize) * gridSize;

    EDIT:
    As @Slubberdegullion suggested, here is the function if your grid is not in squares, but in rectangles (same principle for the matrix applies):

    public Vector2 PositionByGrid (int gridWidth, int gridHeight)
        {
            Vector2 result = new Vector2(MouseState.GetState().X, MouseState.GetState().Y);
            result.X = (int)(result.X / gridWidth) * gridWidth;
            result.Y = (int)(result.Y / gridHeight) * gridHeight;
            return result;
        }
    



    EDIT 2: This is a suggestion
    Also, you could shorten the Draw function, to look like this (explanation in Suggestion No2):

    public void Draw(SpriteBatch spritebatch)
    {
        for (int i = 0; i <= col-1; i++)
        {
            for (int j = 0; j <= row-1; j++)
            {
                Color newColor;
                switch(gridCell[i, j])
                {
                    case 0: newColor = Color.CornflowerBlue; break;
                    case 1: newColor = Color.Yellow; break;
                    case 2: newColor = Color.Red; break;
                    case 3: newColor = Color.Purple; break;
                    case 4: newColor = Color.Blue; break;
                    case 5: newColor = Color.Black; break;
                    default: newColor = Color.White; break;
                }
                spritebatch.FillRectangle(i * 32, j * 32, 31, 31, newColor, 0f);
                spritebatch.DrawRectangle(new Vector2(i * 32, j * 32), new Vector2(32, 32), Color.Black, 1f);
    
            }
        }
    }
    

    It is true that this will use a bit more RAM (equal to adding 4 new integer variables to the game because color is a Vector4 basicaly but with int instead of float) and CPU time, but that cost is very low for the cost of your games integrity and readability.
    Suggestion No2: From your code I beleave I see that you are a beginner, and with that said, I feel my obligation is to tell you not to repeat the same (group of) lines of code, in code. Instead, create a function / do what I did with your Draw function / etc. Your way, if you get to the point where you have to change 1 thing, you'll have to change it in X places. And what if then you have to change it again, or a couple more times? By keeping repetative code in one function, when need arises, you'll have to change that 1 thing only in that function.