Bodies appear to be damped when bouncing off rotating object

Jul 15, 2009 at 11:00 PM

Hey guys I started using your physics engine this evening. I'm amazed by how easy it is to use.

I followed the tutorial at http://www.techiscool.com/wingwarp/silverstripe/physics-Lesson/, and it worked pretty well.

I chose to modify it so the platform rotates when I press the W and S keys. It rotates pretty fast (4 degrees per frame, at 60fps) and I would expect the ball(s) to go flying off it when they stop being in contact with the platform. However on leaving contact they slow down dramatically, as if they are experiencing massive damping. I tried altering the linear drag coefficient (down to 0) and that didn't solve anything. I also tried altering the mass and the gravity, but it doesn't do anything unfortunately. Finally I tried changing the bounce value for the geometry of the ball, and once again it didnt work.

Any ideas? I guess I would like it to react as if you had hit a ball with a baseball bat, the ball would just go flying away.

I hope you don't mind me posting my code!!!

 

#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
// Farseer Physics
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Controllers;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Mathematics;
#endregion
namespace _2d_Phy_Lesson1
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Class Level Variables
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        PhysicsSimulator phySim;
        Body platformOneBody;
        Geom platformOneGeo;
        
        List<Body> ballBodyList = new List<Body>();
        List<Geom> ballGeomList = new List<Geom>();
        Texture2D ballTex;
        Texture2D platformTex;
        bool mouseReleased = true;
        #endregion
        #region Constructor
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
        #endregion
        #region Initialize
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
        }
        #endregion
        #region LoadContent
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            // Load Textures
            ballTex = Content.Load<Texture2D>(@"Images\circle32,32");
            platformTex = Content.Load<Texture2D>(@"Images\rectangle200,75");
            // Create the Physics Simulator
            phySim = new PhysicsSimulator(new Vector2(0, 1000));
            
            
            platformOneBody = BodyFactory.Instance.CreateRectangleBody(
                platformTex.Width, 
                platformTex.Height, 
                1);
            platformOneBody.IsStatic = true;
            platformOneBody.Position = new Vector2(250, 300);
            platformOneBody.Rotation = MathHelper.ToRadians(0f);
            platformOneGeo = GeomFactory.Instance.CreateRectangleGeom(
                phySim,
                platformOneBody, 
                platformTex.Width, 
                platformTex.Height);
        }
        #endregion
        #region Unload
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        #endregion
        #region Update
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            KeyboardState keyboardState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();
            if ((mouseState.LeftButton == ButtonState.Pressed) && mouseReleased)
            {
                AddBall(new Vector2(mouseState.X, mouseState.Y));
                mouseReleased = false;
            }
            else mouseReleased = true;
            if (keyboardState.IsKeyDown(Keys.W)) 
                platformOneBody.Rotation += MathHelper.ToRadians(4f);
            if (keyboardState.IsKeyDown(Keys.S))
                platformOneBody.Rotation -= MathHelper.ToRadians(4f);
            phySim.Update(gameTime.ElapsedGameTime.Milliseconds * .001f);
            base.Update(gameTime);
        }
        #endregion
        #region Draw
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            MouseState mouseState = Mouse.GetState();
            spriteBatch.Begin(
                SpriteBlendMode.AlphaBlend,
                SpriteSortMode.FrontToBack, 
                SaveStateMode.None);
            spriteBatch.Draw(
                ballTex,
                new Vector2(mouseState.X, mouseState.Y),
                null,
                Color.Red,
                0f,
                new Vector2(ballTex.Width/2,ballTex.Height/2),
                1f,
                SpriteEffects.None,
                0f);
            foreach (Body b in ballBodyList)
            {
                spriteBatch.Draw(
                    ballTex,
                    b.Position,
                    null,
                    Color.White,
                    b.Rotation,
                    new Vector2(ballTex.Width / 2, ballTex.Height / 2),
                    1f,
                    SpriteEffects.None,
                    1f);
            }
            spriteBatch.Draw(
                platformTex, 
                platformOneBody.Position, 
                null, 
                Color.White, 
                platformOneBody.Rotation,
                new Vector2(platformTex.Width / 2, platformTex.Height / 2), 
                1f, 
                SpriteEffects.None, 
                1f);
            spriteBatch.End();
            
            base.Draw(gameTime);
        }
        #endregion
        public void AddBall(Vector2 position)
        {
            ballBodyList.Add(BodyFactory.Instance.CreateCircleBody(ballTex.Width / 2, 10f));
            Body b = ballBodyList[ballBodyList.Count - 1];
            b.Position = position;
            b.LinearDragCoefficient = 0f;
            ballGeomList.Add(GeomFactory.Instance.CreateCircleGeom(
                phySim,
                b,
                ballTex.Width / 2,
                32));
            Geom g = ballGeomList[ballGeomList.Count - 1];
            g.RestitutionCoefficient = 0.9f;
            
            phySim.Add(b);
            phySim.Add(g);
        }
    }
}

 

 

#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
// Farseer Physics
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Controllers;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Mathematics;
#endregion

namespace _2d_Phy_Lesson1
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Class Level Variables
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        PhysicsSimulator phySim;

        Body platformOneBody;
        Geom platformOneGeo;
        
        List<Body> ballBodyList = new List<Body>();
        List<Geom> ballGeomList = new List<Geom>();

        Texture2D ballTex;
        Texture2D platformTex;

        bool mouseReleased = true;

        #endregion

        #region Constructor
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
        #endregion

        #region Initialize
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }
        #endregion

        #region LoadContent
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load Textures
            ballTex = Content.Load<Texture2D>(@"Images\circle32,32");
            platformTex = Content.Load<Texture2D>(@"Images\rectangle200,75");

            // Create the Physics Simulator
            phySim = new PhysicsSimulator(new Vector2(0, 1000));
            
            

            platformOneBody = BodyFactory.Instance.CreateRectangleBody(
                platformTex.Width, 
                platformTex.Height, 
                1);
            platformOneBody.IsStatic = true;
            platformOneBody.Position = new Vector2(250, 300);
            platformOneBody.Rotation = MathHelper.ToRadians(0f);
            platformOneGeo = GeomFactory.Instance.CreateRectangleGeom(
                phySim,
                platformOneBody, 
                platformTex.Width, 
                platformTex.Height);




        }
        #endregion

        #region Unload
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        #endregion

        #region Update
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            KeyboardState keyboardState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();


            if ((mouseState.LeftButton == ButtonState.Pressed) && mouseReleased)
            {
                AddBall(new Vector2(mouseState.X, mouseState.Y));
                mouseReleased = false;
            }
            else mouseReleased = true;

            if (keyboardState.IsKeyDown(Keys.W)) 
                platformOneBody.Rotation += MathHelper.ToRadians(4f);
            if (keyboardState.IsKeyDown(Keys.S))
                platformOneBody.Rotation -= MathHelper.ToRadians(4f);

            phySim.Update(gameTime.ElapsedGameTime.Milliseconds * .001f);

            base.Update(gameTime);
        }
        #endregion

        #region Draw
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            MouseState mouseState = Mouse.GetState();
            spriteBatch.Begin(
                SpriteBlendMode.AlphaBlend,
                SpriteSortMode.FrontToBack, 
                SaveStateMode.None);


            spriteBatch.Draw(
                ballTex,
                new Vector2(mouseState.X, mouseState.Y),
                null,
                Color.Red,
                0f,
                new Vector2(ballTex.Width/2,ballTex.Height/2),
                1f,
                SpriteEffects.None,
                0f);

            foreach (Body b in ballBodyList)
            {
                spriteBatch.Draw(
                    ballTex,
                    b.Position,
                    null,
                    Color.White,
                    b.Rotation,
                    new Vector2(ballTex.Width / 2, ballTex.Height / 2),
                    1f,
                    SpriteEffects.None,
                    1f);
            }
            spriteBatch.Draw(
                platformTex, 
                platformOneBody.Position, 
                null, 
                Color.White, 
                platformOneBody.Rotation,
                new Vector2(platformTex.Width / 2, platformTex.Height / 2), 
                1f, 
                SpriteEffects.None, 
                1f);

            spriteBatch.End();
            
            base.Draw(gameTime);
        }
        #endregion

        public void AddBall(Vector2 position)
        {
            ballBodyList.Add(BodyFactory.Instance.CreateCircleBody(ballTex.Width / 2, 10f));
            Body b = ballBodyList[ballBodyList.Count - 1];
            b.Position = position;
            b.LinearDragCoefficient = 0f;
            ballGeomList.Add(GeomFactory.Instance.CreateCircleGeom(
                phySim,
                b,
                ballTex.Width / 2,
                32));

            Geom g = ballGeomList[ballGeomList.Count - 1];
            g.RestitutionCoefficient = 0.9f;
            
            phySim.Add(b);
            phySim.Add(g);
        }
    }
}

 

 

 

 

Jul 16, 2009 at 7:59 AM

You need to be changing AngularVelocity or adding torque to the ball to see some inertia. Just change Rotation to AngularVelocity and you will see effect you want.

Jul 16, 2009 at 4:24 PM

Thank you Dusho!!

With a bit of experimentation, I'm definitely getting somewhere. I find that setting the Angular Velocity roughly twice that of your rotation step gives more realistic results.

 

	if (keyboardState.IsKeyDown(Keys.W))
            {
                platformOneBody.AngularVelocity = MathHelper.ToRadians(4f);
                platformOneBody.Rotation += MathHelper.ToRadians(2f);
            }
            if (keyboardState.IsKeyDown(Keys.S))
            {
                platformOneBody.AngularVelocity = MathHelper.ToRadians(-4f);
                platformOneBody.Rotation -= MathHelper.ToRadians(2f);
            }
            if (!(keyboardState.IsKeyDown(Keys.S) || keyboardState.IsKeyDown(Keys.A)))
            {
                platformOneBody.AngularVelocity = 0;
            }
Also I have found that putting restitution coefficients at 0.8f on both bodies results in more realism.
Thanks for your help.

 

 

Jul 16, 2009 at 9:21 PM

And if you will setup friction coeficients for geometries (say something over 0.1-0.2), you can drop the rotation update. Friction between geometries will cause natural rotation.