Moving or Dragging Objects Using Box2D and XNA for Windows Phone 7

So for this, all it shows is how to move or drag any objects you want using the Mouse and getting realistic physics simulation with Box2D.XNA. I will update the example soon when I have a Touch monitor or get a phone to use the TouchPanel class.Obviously with the touch you can move more than one box at a time if you have two or more fingers on it, so stay tuned but this is a good example to get you started. Download the Asset here

Here is the Video


One important thing to take note of,

Notice how the joint needs to be nulled. If you do not you will get an Assertion Failed on/around line 344.

Debug.Assert(_jointCount > 0);

So what exactly is happening. Take a look at this code that checks whether there has been a button release

if(state.LeftButton==ButtonState.Released && _prevState.LeftButton==ButtonState.Released)
            {
                DestroyJoints();
            }


The first time this happens, The DestroyJoints function will be called and the world will destroy the joint. However the next update, the ButtonState will still be released (unless your really really fast) and the DestroyJoints function will be called. The joint is still not null so the _physicsWorld (Box2D) will attempt to destroy the same joint that it had already destroyed from the previous update but that joint is long gone so you get the Assertion Failed. This is kind of hard to convey so if you want to grasp the idea just comment out the _joint==null line and run the application. Select a box move it around then let go

private void DestroyJoints()
    {
        if (_joint == null) return;
        _physicsWorld.DestroyJoint(_joint);
        _joint = null; 
        _choosenBoxBody = null;
    }

Here is the Code

 
public class Game1 : Microsoft.Xna.Framework.Game
    {
        readonly GraphicsDeviceManager graphics;
        SpriteBatch _spriteBatch;
 
        private const float scale = .01f;
 
        private Texture2D _boxTexture;
 
        private World _physicsWorld;
        /**
         * Keeps track of the positions of all our boxes
         */
        private List<Body> _boxBodies;
 
        private Body _groundBody;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
 
            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);
 
            // Pre-autoscale settings.
            graphics.PreferredBackBufferWidth = 480;
            graphics.PreferredBackBufferHeight = 800;
            
            graphics.IsFullScreen = true;
        }
 
       
        protected override void Initialize()
        {
            
            _physicsWorld = new World(new Vector2(0, 10), true);
            _boxBodies = new List<Body>();
            base.Initialize();
        }
      
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            _spriteBatch = new SpriteBatch(GraphicsDevice);
 
            _boxTexture = Content.Load<Texture2D>("box64x64");
            CreateGroundAndWalls();
            CreateBoxStacks();
        }
 
        private static readonly Random Random = new Random();
        /**
         * Create the Boxes, Random Location
         */
        private void CreateBoxStacks()
        {
            var bodyDef = new BodyDef {type = BodyType.Dynamic};
 
 
            var bodyShape = new PolygonShape();
            bodyShape.SetAsBox(32*scale, 32*scale);<h4></h4>//box is 64,64. Half is 32,32 and scale it to box2d
 
            var bodyFixture = new FixtureDef {shape = bodyShape, restitution = 0.4f, density = 1.5f};
            for(var i=0;i<6;i++)
            {
                var body = _physicsWorld.CreateBody(bodyDef);
                body.CreateFixture(bodyFixture);
                body.Position = new Vector2((float)Random.NextDouble()*4.0f + .32f, (float)Random.NextDouble()+.20f);
                _boxBodies.Add(body);
            }
        }
        /*
         * Creates the boundries for us notice the scale used is still 1/100
         */
        private void CreateGroundAndWalls()
        {
            var grounDef = new BodyDef { type = BodyType.Static };
 
            var groundFix = new FixtureDef { restitution = 0.0f, density = 0.0f };
 
 
            var groundShape = new PolygonShape();
 
            groundShape.SetAsEdge(new Vector2(0, 8f), new Vector2(4.8f, 8f));
            _groundBody = _physicsWorld.CreateBody(grounDef);
            groundFix.shape = groundShape;
            _groundBody.CreateFixture(groundFix);
 
 
 
 
            groundShape.SetAsEdge(new Vector2(0, 0), new Vector2(0f, 8.0f));
            var leftBody = _physicsWorld.CreateBody(grounDef);
            groundFix.shape = groundShape;
            leftBody.CreateFixture(groundFix);
 
 
 
 
            groundShape.SetAsEdge(new Vector2(4.8f, 0), new Vector2(4.8f, 8.0f));
            var rightBody = _physicsWorld.CreateBody(grounDef);
            groundFix.shape = groundShape;
            rightBody.CreateFixture(groundFix);
 
 
            groundShape.SetAsEdge(new Vector2(0, 0), new Vector2(4.8f, 0));
            var topBody = _physicsWorld.CreateBody(grounDef);
            groundFix.shape = groundShape;
            topBody.CreateFixture(groundFix);
 
 
        }
        
      
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
 
           
            _physicsWorld.Step(1/60f, 10, 10);
            HandleMouseClicks();
          
            base.Update(gameTime);
        }
 
        private MouseState _prevState;
        private MouseJoint _joint;
        Body _choosenBoxBody = null;
        private void HandleMouseClicks()
        {
            var state = Mouse.GetState();
            //this makes sure that the user hasn't let go of the mouse and is still holding it down
            if(state.LeftButton==ButtonState.Pressed && _prevState.LeftButton==ButtonState.Pressed)
            {
                var mousePosition = new Vector2(state.X*scale, state.Y*scale);
                FindAndMoveBoxes(mousePosition);
                
            }
           //when we know the mouse button has been released, we need to null and destroy the joints
            if(state.LeftButton==ButtonState.Released && _prevState.LeftButton==ButtonState.Released)
            {
                DestroyJoints();
            }
            _prevState = state;
 
        }
 
 
        private void DestroyJoints()
        {
            if (_joint == null) return;
            _physicsWorld.DestroyJoint(_joint);
            _joint = null; //very important to null it, other wise if a user clicks on empty space, then lets go. The joint wont be null,
                           //then when box2d trys to destroy the same joint over again it will throw an exception since the joint had been destroyed previously
            _choosenBoxBody = null;
        }
        private void FindAndMoveBoxes(Vector2 position)
        {
            if (_choosenBoxBody == null)
            {
                //create a little box
                var aabb = new AABB
                               {
                                   upperBound = new Vector2(position.X + .001f, position.Y + .001f),
                                   lowerBound = new Vector2(position.X - .001f, position.Y - .001f)
                               };
 
                _physicsWorld.QueryAABB(found =>
                {
                    if (found.GetBody().GetType() == BodyType.Dynamic)
                    {
                        if (found.TestPoint(position))
                        {
                            _choosenBoxBody = found.GetBody();
                            return true;
                        }
                    }
                    return false;
                }
                                       , ref aabb);
            }
           
            if (_choosenBoxBody == null) return;
            if (_joint == null)
            {
                var mouseJointDef = new MouseJointDef
                                        {
                                            collideConnected = true,
                                            bodyA = _groundBody,
                                            bodyB = _choosenBoxBody,
                                            target = position,
                                            maxForce = 10000*_choosenBoxBody.GetMass()
                                        };
                _joint = (MouseJoint)_physicsWorld.CreateJoint(mouseJointDef);
            }
            else
            {
                _joint.SetTarget(position);
            }
        }
        
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            _spriteBatch.Begin();
            foreach (var boxBody in _boxBodies)
            {
                _spriteBatch.Draw(_boxTexture, boxBody.Position/scale, null, Color.White, boxBody.Rotation,
                                 new Vector2(32, 32), 1, SpriteEffects.None, 0);
            }
            _spriteBatch.End();
            base.Draw(gameTime);
        }
    }
Tags: , ,
Categories: Windows Phone 7 | WP7 | XNA

Permalink E-mail | Kick it! | DZone it! | del.icio.us Comments (0) Post RSSRSS comment feed

Comments

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading