Search code examples
c#xnamonogame

Getting mouse position when scrolling right or left (Monogame), and choosing a unit from grid


I am creating a level editor for my game using Monogame. At the level editor , the grid is printed on the screen . The grid corresponds to a 2d array which represents the game map. When selecting a square at the grid , it changes the border's color according to the item selected at the menu.

For example, if i picked the second item from the left at the top of the menu, it will color the border of a selected square to green.

My problem is , when i scroll to the right and select a square , i struggle to make a correct detection of where i clicked.

See an example of the problem here: https://i.sstatic.net/CPi7r.jpg // After scrolling right , i struggle to get the correct position

Any idea?

Here is my code, using an offset for scrolling , and the background itself is 64 pixel per unit. I have tried using camera from monogame.extended to solve the problem, but couldnt find a way to help it. Thanks alot!

            Point offset = new Point();
            Texture2D onePixelTex;
            int scrollSpeed = 7;
            int[,] map;
            int selectedTileStatus = 0;
            Color[] tileStatusColor = new Color[] { Color.Red, 
            Color.Green, Color.Blue };

              if (mouseState.LeftButton == ButtonState.Pressed)
        {
          //  var _worldPosition = _camera.ScreenToWorld(new Vector2(mouseState.X, mouseState.Y));
        //    Vector2 worldPosition = Vector2.Transform(new Vector2(ms.X,ms.Y), Matrix.Invert(_camera.GetViewMatrix()));

            var xIndex = ms.X / 64 ;
            var yIndex = ms.Y/ 64;
            Console.WriteLine(xIndex + " " + yIndex);
            bool handled = false;
            //for the menu to be selected
            for (int i = 0; i < tileRects.Length; i++)
            {
                if (tileRects[i].Contains(ms.X, ms.Y))
                {
                    selectedTileStatus = i;// choose 0 1 2 at menu. then use value to set at the matrix.
                    handled = true;
                }
            }

            if (!handled && xIndex >= 0 && xIndex < map.GetLength(0) && yIndex >= 0 && yIndex < map.GetLength(1))
            {
                map[xIndex, yIndex] = selectedTileStatus;
            }

        }
         public void DrawGrid(int[,] gameMap, SpriteBatch spriteBatch, SpriteFont f)
    {
        for (int x = 0; x < gameMap.GetLength(0); x++)
        {
            for (int y = 0; y < gameMap.GetLength(1); y++)
            {

                Color color = tileStatusColor[map[x, y]];

                if (isGridActive)
                {
                    DrawBorder(new Rectangle(x * 64 + offset.X, y * 64, 64, 64), 2, color); // can draw any rectangle here 
                }
            }

        }

    }

       private void DrawBorder(Rectangle rect, int thicknessOfBorder, Color borderColor)
    {
        var pixel = onePixelTex;

        // Draw top line
        spriteBatch.Draw(pixel, new Rectangle(rect.X, rect.Y, rect.Width, thicknessOfBorder), borderColor);

        // Draw left line
        spriteBatch.Draw(pixel, new Rectangle(rect.X, rect.Y, thicknessOfBorder, rect.Height), borderColor);

        // Draw right line
        spriteBatch.Draw(pixel,
            new Rectangle(
                (rect.X + rect.Width - thicknessOfBorder),
                rect.Y,
                thicknessOfBorder,
                rect.Height),
            borderColor);
        // Draw bottom line
        spriteBatch.Draw(pixel,
            new Rectangle(
                rect.X,
                rect.Y + rect.Height - thicknessOfBorder,
                rect.Width,
                thicknessOfBorder),
            borderColor);
    }
    protected override void LoadContent()
    {
        this.IsMouseVisible = true;
        backGround1 = Content.Load<Texture2D>("layer/level_01_A");
        backGround2 = Content.Load<Texture2D>("layer/level_01_B");
        backGround3 = Content.Load<Texture2D>("layer/level_01_C");
        backGround4 = Content.Load<Texture2D>("layer/level_01_D");
        backGround5 = Content.Load<Texture2D>("layer/level_01_E");
        int totalWidth = backGround1.Width + backGround2.Width + backGround3.Width + backGround4.Width + backGround5.Width;
        map = new int[totalWidth / 64 , backGround1.Height / 64];
        font = Content.Load<SpriteFont>("Fonts/Font");
        spriteBatch = new SpriteBatch(GraphicsDevice);
        onePixelTex = new Texture2D(GraphicsDevice, 1, 1);
        onePixelTex.SetData(new Color[] { Color.White });
        // TODO: use this.Content to load your game content here
    }
      protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        //offset is needed for scrolling
        spriteBatch.Draw(backGround1, new Rectangle(0 + offset.X, -700, 3840, 1984), Color.White);
        spriteBatch.Draw(backGround2, new Rectangle(3840 + offset.X, -700, 3840, 1984), Color.White);
        spriteBatch.Draw(backGround3, new Rectangle((3840 * 2 )+ offset.X, -700, 3840, 1984), Color.White);
        spriteBatch.Draw(backGround4, new Rectangle(3840*3 + offset.X, -700, 3840, 1984), Color.White);
        spriteBatch.Draw(backGround5, new Rectangle(3840*4 + offset.X, -700, 3840, 1984), Color.White);
        DrawGrid(map, spriteBatch, font);
        spriteBatch.End();
        base.Draw(gameTime);
    }

Solution

  • If the variable named ms is your mouse position, then you will need to translate this against your camera offset.

            var xIndex = (ms.X - offset.X) / 64;
            var yIndex = (ms.Y - offset.Y) / 64;
    

    If you don't need the functionality of a full camera class with rotation and scaling, this should work but I would recommend changing some lines in your draw function. You can still use a matrix with your spritebatch which should help with keeping everything working and allow you to write less code when adding things that are going to move with the camera.

    Matrix transformMatrix = Matrix.CreateTranslation(new Vector3(offset.X, offset.Y, 0.0f));
    spritebatch.Begin(sortMode, blendState, samplerState, depthStencilState,
                      rasterizerState, effect, transformMatrix);
    

    I haven't tested this yet but this should work and allow for you to remove the "+ offset.X" in your draw calls. I'm pretty sure that translation is correct but I'll double check it once I get home and update this if it's not.