Create Path from Mouse [Solved]

Topics: Developer Forum, User Forum
Nov 8, 2010 at 11:22 AM

Hi, i have been trying to create a path from a line drawn by the user on the screen, but the positions of the bodies created by the PathManager are always incorrect. I have two vectors, one for the initial position of the mouse and other for the final. I'm using LineBrush and the line is drawn correctly on screen.

So, each time the mouse is clicked i clear the previous path, and add the initial and the final positions. But the bodies appear always at the same position, only with minor differences. Below is the function i use to create the path.

Thanks in advance!

 private void HandleMouse()
        {
            //the left button has been pressed.
            if (inputState.LastMouseState.LeftButton == ButtonState.Released && inputState.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                start = new Vector2(inputState.CurrentMouseState.X, inputState.CurrentMouseState.Y);
                end = start;
                path.ControlPoints.Clear();
                hasCreatedPath = false;
                path.Add(ConvertUnits.ToSimUnits(start));
            }

            //the left button is on hold.
            if (inputState.LastMouseState.LeftButton == ButtonState.Pressed && inputState.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                end.X = inputState.CurrentMouseState.X;
                end.Y = inputState.CurrentMouseState.Y;
                //path.Add(ConvertUnits.ToSimUnits(end));
            }

            if (inputState.LastMouseState.LeftButton == ButtonState.Released && inputState.CurrentMouseState.LeftButton == ButtonState.Released)
            {                               
                if (start != end && !hasCreatedPath) {
                    hasCreatedPath = true;
                    path.Add(ConvertUnits.ToSimUnits(end));
                    List<Shape> shapes = new List<Shape>(1);
                    shapes.Add(new CircleShape(0.5f));                   
                    bodies = PathManager.EvenlyDistibuteShapesAlongPath(_world, path, shapes, BodyType.Dynamic, 20, 1);
                    //PathManager.AttachBodiesWithRevoluteJoint(_world, bodies, new Vector2(0, 0.5f), new Vector2(0, -0.5f), true, true);
                }               
            }
        }

Developer
Nov 8, 2010 at 1:30 PM

Can you write a quick complete demo of your problem? I can't see anything wrong with your code, I would have to step through the code and see what's up.

Nov 8, 2010 at 2:26 PM

Here is the file that i've been working. It's really simple for the moment. I wanted for the path to be created in the same position as the line drawn by the user.

Thanks for your help!

using FarseerPhysics.DebugViewXNA;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using FarseerPhysics.DemoBaseXNA.Components;
using FarseerPhysics.DemoBaseXNA.ScreenSystem;
using FarseerPhysics.DemoBaseXNA.DrawingSystem;
using FarseerPhysics.Common;
using System.Collections.Generic;
using FarseerPhysics.Collision.Shapes;
using System;
using FarseerPhysics.DemoBaseXNA;

namespace HelloWorld
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Game
    {
        // Usage:
        //
        // Press A or D to rotate the ball

        private GraphicsDeviceManager _graphics;

        //The most important object of them all; the world.
        private World _world = new World(new Vector2(0, -20));

        private Fixture _rectangleFixture;
        private Fixture _circleFixture;

        private DebugViewXNA _debugView;
        private LineBrush brush;
        private SpriteBatch spriteBatch;
        private InputState inputState;
        private Vector2 start, end;
        private bool hasCreatedPath;
        private Path path;

        private List<Body> bodies;


        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            _debugView = new DebugViewXNA(_world);
            //_debugView.Flags = FarseerPhysics.DebugViewFlags.DebugPanel;
            brush = new LineBrush(2, Color.Black);
            inputState = new InputState();
            path = new Path();
            bodies = new List<Body>();
            IsMouseVisible = true;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            DebugViewXNA.LoadContent(_graphics.GraphicsDevice, Content);

            brush.Load(_graphics.GraphicsDevice);
            spriteBatch = new SpriteBatch(_graphics.GraphicsDevice);

            _rectangleFixture = FixtureFactory.CreateRectangle(_world, 50, 5, 1, new Vector2(0, 0));
            _rectangleFixture.Body.IsStatic = true;



            //Give it some bounce and friction
            _rectangleFixture.Restitution = 0.3f;
            _rectangleFixture.Friction = 0.5f;

            //Create the circle fixture
            _circleFixture = FixtureFactory.CreateCircle(_world, 2, 1, new Vector2(10, 10));
            _circleFixture.Body.BodyType = BodyType.Dynamic;

            _circleFixture.Restitution = 0.3f;
            _circleFixture.Friction = 0.5f;
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState s = Keyboard.GetState();

            inputState.Update();


            HandleMouse();

            //We make it possible to rotate the circle body
            if (s.IsKeyDown(Keys.A))
                _circleFixture.Body.ApplyTorque(100);

            if (s.IsKeyDown(Keys.D))
                _circleFixture.Body.ApplyTorque(-100);

            //You can rotate the circle using the triggers on the Xbox360 controller.
            GamePadState gamepad = GamePad.GetState(PlayerIndex.One);

            if (gamepad.IsConnected)
            {
                float rotation = 40 * gamepad.Triggers.Left;
                _circleFixture.Body.ApplyTorque(rotation);

                rotation = -40 * gamepad.Triggers.Right;
                _circleFixture.Body.ApplyTorque(rotation);
            }

            //We update the world
            _world.Step((float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.001f);

            base.Update(gameTime);
        }
        //=======================================================================================================
        private void HandleMouse()
        {
            //the left button has been pressed.
            if (inputState.LastMouseState.LeftButton == ButtonState.Released && inputState.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                start = new Vector2(inputState.CurrentMouseState.X, inputState.CurrentMouseState.Y);
                end = start;
                path.ControlPoints.Clear();
                hasCreatedPath = false;
                path.Add(ConvertUnits.ToSimUnits(start));
            }

            //the left button is on hold.
            if (inputState.LastMouseState.LeftButton == ButtonState.Pressed && inputState.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                end.X = inputState.CurrentMouseState.X;
                end.Y = inputState.CurrentMouseState.Y;
                //path.Add(ConvertUnits.ToSimUnits(end));
            }

            if (inputState.LastMouseState.LeftButton == ButtonState.Released && inputState.CurrentMouseState.LeftButton == ButtonState.Released)
            {                               
                if (start != end && !hasCreatedPath) {
                    hasCreatedPath = true;
                    path.Add(ConvertUnits.ToSimUnits(end));
                    List<Shape> shapes = new List<Shape>(1);
                    shapes.Add(new CircleShape(0.5f));                   
                    bodies = PathManager.EvenlyDistibuteShapesAlongPath(_world, path, shapes, BodyType.Dynamic, 20, 1);
                    //PathManager.AttachBodiesWithRevoluteJoint(_world, bodies, new Vector2(0, 0.5f), new Vector2(0, -0.5f), true, true);
                }               
            }
        }
        //=======================================================================================================
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            Matrix proj = Matrix.CreateOrthographic(50 * _graphics.GraphicsDevice.Viewport.AspectRatio, 50, 0, 1);
            Matrix view = Matrix.Identity;

            spriteBatch.Begin();
            brush.Draw(spriteBatch, start, end);


            //foreach (Body item in bodies)
            //{
            //    item.Active = true;
            //}


            spriteBatch.End();

            //this.debugView.RenderDebugValue(Camera.ProjectionMatrix, Camera.ViewMatrix * Matrix.CreateRotationX(MathHelper.Pi) * Matrix.CreateTranslation(new Vector3(-Engine.GraphicsDevice.Viewport.Width / 2, Engine.GraphicsDevice.ViewPort.Height / 2, 0)) * Camera.TransformMatrix(scrollRateX, -scrollRateY));  

            _debugView.RenderDebugData(ref proj, ref view);

            base.Draw(gameTime);
        }
    }
}

Developer
Nov 8, 2010 at 2:55 PM

Ok here it is. Problem was you were trying to convert screen coordinates to world coordinates just using ConvertUnits. All that does is scale the vector. You need to use Viewport.Unproject() as shown below.

I also had to move your projection and view matrices up to your Game class.

I hope this helps!

using FarseerPhysics.DebugViewXNA;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using FarseerPhysics.DemoBaseXNA.Components;
using FarseerPhysics.DemoBaseXNA.ScreenSystem;
using FarseerPhysics.DemoBaseXNA.DrawingSystem;
using FarseerPhysics.Common;
using System.Collections.Generic;
using FarseerPhysics.Collision.Shapes;
using System;
using FarseerPhysics.DemoBaseXNA;

namespace HelloWorld
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Game
    {
        // Usage:
        //
        // Press A or D to rotate the ball

        private GraphicsDeviceManager _graphics;

        //The most important object of them all; the world.
        private World _world = new World(new Vector2(0, -20));

        private Fixture _rectangleFixture;
        private Fixture _circleFixture;

        private DebugViewXNA _debugView;
        private LineBrush brush;
        private SpriteBatch spriteBatch;
        private InputState inputState;
        private Vector2 start, end;
        private bool hasCreatedPath;
        private Path path;

        private List<Body> bodies;

        Matrix proj;
        Matrix view = Matrix.Identity;


        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            _debugView = new DebugViewXNA(_world);
            //_debugView.Flags = FarseerPhysics.DebugViewFlags.DebugPanel;
            brush = new LineBrush(2, Color.Black);
            inputState = new InputState();
            path = new Path();
            bodies = new List<Body>();
            IsMouseVisible = true;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            DebugViewXNA.LoadContent(_graphics.GraphicsDevice, Content);

            brush.Load(_graphics.GraphicsDevice);
            spriteBatch = new SpriteBatch(_graphics.GraphicsDevice);

            _rectangleFixture = FixtureFactory.CreateRectangle(_world, 50, 5, 1, new Vector2(0, 0));
            _rectangleFixture.Body.IsStatic = true;



            //Give it some bounce and friction
            _rectangleFixture.Restitution = 0.3f;
            _rectangleFixture.Friction = 0.5f;

            //Create the circle fixture
            _circleFixture = FixtureFactory.CreateCircle(_world, 2, 1, new Vector2(10, 10));
            _circleFixture.Body.BodyType = BodyType.Dynamic;

            _circleFixture.Restitution = 0.3f;
            _circleFixture.Friction = 0.5f;

            proj = Matrix.CreateOrthographic(50 * _graphics.GraphicsDevice.Viewport.AspectRatio, 50, 0, 1);
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState s = Keyboard.GetState();

            inputState.Update();


            HandleMouse();

            //We make it possible to rotate the circle body
            if (s.IsKeyDown(Keys.A))
                _circleFixture.Body.ApplyTorque(100);

            if (s.IsKeyDown(Keys.D))
                _circleFixture.Body.ApplyTorque(-100);

            //You can rotate the circle using the triggers on the Xbox360 controller.
            GamePadState gamepad = GamePad.GetState(PlayerIndex.One);

            if (gamepad.IsConnected)
            {
                float rotation = 40 * gamepad.Triggers.Left;
                _circleFixture.Body.ApplyTorque(rotation);

                rotation = -40 * gamepad.Triggers.Right;
                _circleFixture.Body.ApplyTorque(rotation);
            }

            //We update the world
            _world.Step((float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.001f);

            base.Update(gameTime);
        }
        //=======================================================================================================
        private void HandleMouse()
        {
            //the left button has been pressed.
            if (inputState.LastMouseState.LeftButton == ButtonState.Released && inputState.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                start = new Vector2(inputState.CurrentMouseState.X, inputState.CurrentMouseState.Y);
                end = start;
                path.ControlPoints.Clear();
                hasCreatedPath = false;

                Vector3 point = GraphicsDevice.Viewport.Unproject(new Vector3(start, 0), proj, view, Matrix.Identity);
                path.Add(new Vector2(point.X, point.Y));
            }

            //the left button is on hold.
            if (inputState.LastMouseState.LeftButton == ButtonState.Pressed && inputState.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                end.X = inputState.CurrentMouseState.X;
                end.Y = inputState.CurrentMouseState.Y;
                //path.Add(ConvertUnits.ToSimUnits(end));
            }

            if (inputState.LastMouseState.LeftButton == ButtonState.Released && inputState.CurrentMouseState.LeftButton == ButtonState.Released)
            {
                if (start != end && !hasCreatedPath)
                {
                    hasCreatedPath = true;
                    Vector3 point = GraphicsDevice.Viewport.Unproject(new Vector3(end, 0), proj, view, Matrix.Identity);
                    path.Add(new Vector2(point.X, point.Y));
                    List<Shape> shapes = new List<Shape>(1);
                    shapes.Add(new CircleShape(0.5f));
                    bodies = PathManager.EvenlyDistributeShapesAlongPath(_world, path, shapes, BodyType.Static, 20, 1);
                    //PathManager.AttachBodiesWithRevoluteJoint(_world, bodies, new Vector2(0, 0.5f), new Vector2(0, -0.5f), true, true);
                }
            }
        }
        //=======================================================================================================
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

           

            spriteBatch.Begin();
            brush.Draw(spriteBatch, start, end);


            //foreach (Body item in bodies)
            //{
            //    item.Active = true;
            //}


            spriteBatch.End();

            //this.debugView.RenderDebugValue(Camera.ProjectionMatrix, Camera.ViewMatrix * Matrix.CreateRotationX(MathHelper.Pi) * Matrix.CreateTranslation(new Vector3(-Engine.GraphicsDevice.Viewport.Width / 2, Engine.GraphicsDevice.ViewPort.Height / 2, 0)) * Camera.TransformMatrix(scrollRateX, -scrollRateY)); 

            _debugView.RenderDebugData(ref proj, ref view);

            base.Draw(gameTime);
        }
    }
}

 

Nov 8, 2010 at 3:34 PM

Awesome!!!! It works!!!

Thanks a lot for your time :)