How to move static element?

Mar 21, 2010 at 11:59 PM
Hello, I tried to add "Mouse Drag Element Behavior" into "Is static" element, but "physics" does not move with the object. Can you help me how to do that? Thanks
Coordinator
Mar 22, 2010 at 12:29 PM

You want to make sure you change the position of the physics object itself, not the visual UI element. So in your mouse move event you would be setting something like -

_physicsController.PhysicsObjects[

"ball"].BodyObject.Position = new Vector2(x, y);

Mar 22, 2010 at 11:19 PM
Edited Mar 22, 2010 at 11:19 PM
Thank you Andy, it works, but it is not ideal for me. I want to create Air Hockey Game with one puck and two player circle handlers. Handlers should be static elements, but should be moveable by mouse. What is your suggestion how to do that?
Coordinator
Mar 23, 2010 at 3:09 PM

This might be hard to do with Farseer, because the mouse movement can be very jerky (what if the user slides the mouse rapidly from side to side?), causing tunneling problems in the simulation (objects pass thru each other).

You might want to try a pick spring that is attached to the paddles, but I'm not sure that would even feel natural.

You could also post this question on the Farseer forum at http://farseerphysics.codeplex.com

 

Mar 23, 2010 at 3:15 PM

Thank you for your opinion Andy.

Apr 27, 2010 at 2:13 AM
Edited Apr 27, 2010 at 2:43 AM

The advice on the Farseer forum was to "apply impulse in the direction of the mouse." Sounds easy enough. The devil was SO in the details. Hopefully this is useful to someone. Thanks to Andy for writing this library and helping me get the hang of triggers and behaviors.

[Edit: I realize this won't move static objects. You will have to add code to "unstatic" them during move.]

/* PhysicsMouseDrag.cs
 * 
 * By Dustin Andrews
 * 
 * A behavior for Expressions Blend and Physics Helper for Silverlight, WPF, Blend, and Farseer by Andy Beaulieu.
 * 
 * This behavior allows users to click object with the mouse and have them follow the pointer as long as the left
 * mouse button is down.
 * 
 */

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using FarseerGames.FarseerPhysics.Mathematics;
using Spritehand.FarseerHelper;

namespace Spritehand.PhysicsBehaviors
{
    [Category("Physics")]
    [Description("Allows physics objects to be moved with the mouse by applying force to the object in the direction of the mouse.")]
    public class PhysicsMouseDragBehavior : Behavior<FrameworkElement>
    {
        #region private members
        //Internal state indicators
        private bool isDragActive = false;
        private bool isControllerInitialized = false;
        private bool isMouseOver = false;

        //private member objects
        private Point mouseLocation;
        private Canvas canvas;
        private PhysicsControllerMain physicsControllerMain = null; 
        #endregion

        #region parameters
        private double velocityLimit = 500.0d;
        [Category("Physics")]
        [Description("The amount of speed to apply when dragging the with the mouse.")]                
        public double VelocityLimit 
        {
            get { return velocityLimit; }
            set {velocityLimit = value;} 
        }

        #endregion

        #region Overrides
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
            this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
            this.AssociatedObject.MouseLeftButtonUp -= Canvas_MouseLeftButtonUp;
            physicsControllerMain.TimerLoop -= controller_TimerLoop;
            this.AssociatedObject.MouseEnter -= AssociatedObject_MouseEnter;
            this.AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
        }

        #endregion

        #region Event handlers
        /// <summary>
        /// Signs up for important events when the object is loaded.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
        {
            PhysicsControllerMain controller = PhysicsControllerMain.FindController(this.AssociatedObject);
            if (controller == null)
            {
                throw new Exception("You must add a PhysicsController Behavior to the Canvas representing the main Container.");
            }            

            controller.Initialized += new PhysicsControllerMain.InitializedHandler(controller_Initialized);
            this.physicsControllerMain = controller;

            //Sign up for canvas events.
            canvas = this.AssociatedObject.Parent as Canvas;
            canvas.MouseLeftButtonUp += new MouseButtonEventHandler(Canvas_MouseLeftButtonUp);
            canvas.MouseMove += new MouseEventHandler(canvas_MouseMove);            

            //Sign up for this objects events.
            this.AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
            this.AssociatedObject.MouseEnter += new MouseEventHandler(AssociatedObject_MouseEnter);
            this.AssociatedObject.MouseLeave += new MouseEventHandler(AssociatedObject_MouseLeave);
            this.AssociatedObject.MouseLeftButtonUp += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);

        }

        private void controller_Initialized(object source)
        {
            isControllerInitialized = true;
        }

        void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragActive)
            {
                mouseLocation = e.GetPosition(null);
            }
        }

        void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isDragActive = false;
        }

        void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isDragActive = false;
        }

        void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
        {
            isMouseOver = false;
        }

        void AssociatedObject_MouseEnter(object sender, MouseEventArgs e)
        {
            isMouseOver = true;
        }


        void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            mouseLocation = e.GetPosition(null);
            if (isMouseOver && isDragActive == false)
            {
                isDragActive = true;
                //Normally onMouseMove would work, but we can move that fast with physics active.
                //Sign up for the timerloop (until the user releases the left mouse button.)
                physicsControllerMain.TimerLoop += new PhysicsControllerMain.TimerLoopHandler(controller_TimerLoop);
            }
        }

        void controller_TimerLoop(object source)
        {
            if (isDragActive)
            {
                mouseDrag();
            }
            else
            {
                stopObject();                
                physicsControllerMain.TimerLoop -= controller_TimerLoop;
            }
        }     
        #endregion

        #region Public Static Methods
        /// <summary>
        /// Returns the amount of force to apply to stop an objects current motion.
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="mass"></param>
        /// <returns></returns>
        public static Vector2 invertVector(Vector2 vector, float mass)
        {
            Vector2 returnVector = new Vector2();
            //factor in mass to prevent rubber band type effects.
            returnVector.X = (vector.X * mass) * -1;
            returnVector.Y = (vector.Y * mass) * -1;
            return returnVector;
        } 
        #endregion

        #region Private methods
        /// <summary>
        /// Converts relative force into an absolute force.
        /// </summary>
        /// <param name="force"></param>
        /// <returns></returns>
        private float applySpeedLimit(float force, float mass)
        {
            //light objects will oscilate unless the force is dialed down and heavy ones will not move fast enough.
            force = force * mass;

            if (Math.Abs(force) > velocityLimit)
            {
                if (force > 0)
                {
                    return (float)velocityLimit;
                }
                else
                {
                    return (float)velocityLimit * -1;
                }
            }
            return force;
        }


        private void mouseDrag()
        {
            #region code checks
            if (!isControllerInitialized || mouseLocation == null)
            {
                return;
            }


            string body = this.AssociatedObject.Name;
            PhysicsSprite sprite;
            if (!physicsControllerMain.PhysicsObjects.TryGetValue(body, out sprite))
                return;

            //if (isMouseOver) { return; }
            #endregion

            #region MoveObjectTowardsMouse

            float mass = physicsControllerMain.PhysicsObjects[this.AssociatedObject.Name].BodyObject.Mass;
            physicsControllerMain.PhysicsObjects[body].BodyObject.ClearTorque();

            Point thisControl = new Point();
            thisControl.X = physicsControllerMain.PhysicsObjects[body].BodyObject.Position.X;
            thisControl.Y = physicsControllerMain.PhysicsObjects[body].BodyObject.Position.Y;

            Point delta = new Point();

            delta.X = this.mouseLocation.X - thisControl.X;
            delta.Y = this.mouseLocation.Y - thisControl.Y;

            Vector2 lv = physicsControllerMain.PhysicsObjects[body].BodyObject.LinearVelocity;           

            if (velocityLimit == 0) { velocityLimit = 500; }

            float forceX = (float)delta.X;
            float forceY = (float)delta.Y;

            forceX = applySpeedLimit(forceX, mass);
            forceY = applySpeedLimit(forceY, mass);


            Vector2 desiredForce = new Vector2((float)forceX, (float)forceY);



            Vector2 calculatedForce = new Vector2();
            calculatedForce.X = desiredForce.X - lv.X;
            calculatedForce.Y = desiredForce.Y - lv.Y;

            //ApplyForce() won't work correctly for objects that have "applyForceEqualToRotation" set.

            physicsControllerMain.PhysicsObjects[body].BodyObject.ApplyImpulse(calculatedForce);
#if debug
            System.Diagnostics.Debug.WriteLine("lvx: " + lv.X + " lvy: " + lv.Y);
#endif

            #endregion
        }




        private void stopObject()
        {
            PhysicsSprite sprite;
            if (!physicsControllerMain.PhysicsObjects.TryGetValue(this.AssociatedObject.Name, out sprite))
                return;
            Vector2 lv = physicsControllerMain.PhysicsObjects[this.AssociatedObject.Name].BodyObject.LinearVelocity;
            float mass = physicsControllerMain.PhysicsObjects[this.AssociatedObject.Name].BodyObject.Mass;
            physicsControllerMain.PhysicsObjects[this.AssociatedObject.Name].BodyObject.ApplyImpulse(invertVector(lv, mass));
        } 
        #endregion
    }


}
Jan 31, 2011 at 6:12 PM

Hi, 

Sorry for reopening this thread after such a long time since last reply.

I am interested in the code that Andy wrote as I need to use something similar to this but basically I don't want the user to click on a object, what I want them to do is that the object follows the mouse without having to click. This is because I will then combine the code to React Division so when they are shown, the object will move along. 

In the code I am not sure where you get 

using FarseerGames.FarseerPhysics.Mathematics;

using Spritehand.FarseerHelper;

I tried searching online but I seem to fail to find the .dll for them or unless they are somewhere in your code or a separate application? 

 

If any of you know by any chance how I could implement the mouse-to-body in a easier way please let me know as it would help a lot when it comes to translating it to RD

 

Thanks

Coordinator
Jan 31, 2011 at 6:19 PM

Note that the newer version (4.x) of the Physics Helper uses a different version of the Farseer Physics Engine (3.x). This has some significant changes.

One of the changes you might want to look at is the Mouse Pick Behavior. If you set MousePickEnabled to True on the PhysicsController, the effect of moving items around is much less "springy" by default, and may be what you need.

If you haven't updated to 4.x yet, I would take a look at the video on the "Home" page of this Codeplex site for the Helper. It goes through install and basics.