Simulate temporal changes

Topics: Developer Forum, User Forum
Sep 17, 2012 at 2:42 PM
Edited Sep 17, 2012 at 2:44 PM

Hi,

First of all, good job for your physics engine. It's amazing :)

I tried to simulate a temporal vortex that slows objects that go through it.

More the object is closed from the center of the vortex, more the time of the object is slow.

I want to all of objects retreive their linearVelocity according to their initial value. Like they don't go through the vortex.

I found this post : http://farseerphysics.codeplex.com/discussions/240883

  • But using drag coefficient reduce the forces over the time.
  • Use several instance of physics engine for each timeFactor is too heavy
  • The velocity limitter doesn't work but still don't get why.
  • And i found my own way to simulate the behaviour that i want but it's not enougth accurate (2 same objects launch at the same speed don't follow the same way after go through the vortex or not)

Here my code for the last two solution.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using FarseerPhysics.Factories;
using FarseerPhysics.Dynamics;
using SharedContent;
using FarseerPhysics.Controllers;

namespace GravitProto
{
    public class TemporalVortex : PhysicObject
    {
        protected List<Fixture> m_FixtureInCollision = new List<Fixture>();
        private const float k_Radius = 2;
        private const float k_TimeFactorCenter = 0.125f;

        List<TemporalData> m_TemporalDatas = new List<TemporalData>();
        
        private class TemporalData
        {
            public Body body;
            public Vector2 LastLinearVelocity;
            public float LastAngularVelocity;
            public float LastTimeFactor;
            public float Mass;
            public VelocityLimitController m_VelLimCtrl = null;
        }

        public TemporalVortex(Game game)
            : base(game)
        {
            DrawCollider = true;
        }

        public override Saves.BaseObjectGameData GameData
        {
            get
            {
                return null;
            }
        }

        public override void Initialize()
        {
            m_body = BodyFactory.CreateCircle(Game.World, (float)k_Radius, 1);

            base.Initialize();

            m_body.IsSensor = true;
            m_body.OnCollision += new OnCollisionEventHandler(m_body_OnCollision);
            m_body.OnSeparation += new OnSeparationEventHandler(m_body_OnSeparation);
            m_body.BodyType = BodyType.Static;
        }

        protected virtual void m_body_OnSeparation(Fixture fixtureA, Fixture fixtureB)
        {
            m_FixtureInCollision.Remove(fixtureB);
        }

        protected virtual bool m_body_OnCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact)
        {
            if (!m_FixtureInCollision.Contains(fixtureB))
                m_FixtureInCollision.Add(fixtureB);

            return true;
        }


        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            List<Body> bodies = new List<Body>();
            foreach (var item in m_FixtureInCollision)
            {
                bool isExist = m_TemporalDatas.Exists((var) => { return var.body == item.Body; });

                if (isExist) continue;

                m_TemporalDatas.Add(new TemporalData()
                {
                    body = item.Body,
                    LastTimeFactor = 1,
                    LastAngularVelocity = item.Body.AngularVelocity,
                    LastLinearVelocity = item.Body.LinearVelocity,
                    Mass = item.Body.Mass,
                });
            }

            LimitControllerMethod();
            //ScalingVelocityMethod();

        }

       

        private void LimitControllerMethod()
        {
            foreach (var data in m_TemporalDatas.ToArray())
            {
                Body body = data.body;
                float dist = (body.Position - Body.Position).LengthSquared();

                float t = dist / (k_Radius * k_Radius);//MathHelper.Clamp(dist / (k_Radius * k_Radius), 0, 1);

                if (t >= 1)
                {
                    continue;
                }

                float timeFactor = MathHelper.Lerp(k_TimeFactorCenter, 1, t);

                if (data.m_VelLimCtrl == null)
                {
                    data.m_VelLimCtrl = new VelocityLimitController();
                    data.m_VelLimCtrl.AddBody(body);
                    data.m_VelLimCtrl.LimitAngularVelocity = true;
                    data.m_VelLimCtrl.LimitLinearVelocity = true;
                    Game.World.AddController(data.m_VelLimCtrl);
                }

                data.m_VelLimCtrl.MaxLinearVelocity = body.LinearVelocity.Length() * timeFactor;
                data.m_VelLimCtrl.MaxAngularVelocity = body.AngularVelocity * timeFactor;

                if (timeFactor >= 1)
                {
                    Game.World.RemoveController(data.m_VelLimCtrl);
                    m_TemporalDatas.Remove(data);
                }
            }
        }

        private void ScalingVelocityMethod()
        {
            foreach (var data in m_TemporalDatas.ToArray())
            {
                Body body = data.body;
                float dist = (body.Position - Body.Position).LengthSquared();

                float t = dist / (k_Radius * k_Radius);//MathHelper.Clamp(dist / (k_Radius * k_Radius), 0, 1);

                if (t >= 1)
                {
                    data.LastLinearVelocity = body.LinearVelocity;
                    data.LastAngularVelocity = body.AngularVelocity;
                    continue;
                }

                float timeFactor = MathHelper.Lerp(k_TimeFactorCenter, 1, t);

                Vector2 diffLinearVelocity = body.LinearVelocity - data.LastLinearVelocity;
                float diffAngularVelocity = body.AngularVelocity - data.LastAngularVelocity;
                float diffTimeFactor = timeFactor / data.LastTimeFactor;

                diffLinearVelocity *= data.LastTimeFactor;
                diffAngularVelocity *= data.LastTimeFactor;

                body.LinearVelocity = (data.LastLinearVelocity + diffLinearVelocity) * diffTimeFactor;
                body.AngularVelocity = (data.LastAngularVelocity + diffAngularVelocity) * diffTimeFactor;

                data.LastTimeFactor = timeFactor;
                data.LastLinearVelocity = body.LinearVelocity;
                data.LastAngularVelocity = body.AngularVelocity;

                if (timeFactor >= 1)
                {
                    m_TemporalDatas.Remove(data);
                }
            }
        }
    }
}

If you have other solution or a way to trick mines, i'm listen to you.

Thanks for your time.

Regards

Racines

Sep 24, 2012 at 2:22 PM

UP

Sep 26, 2012 at 1:25 AM

You'll probably want to restore the original velocities when the object separates from your sensor, but maybe I'm misunderstanding what you're trying to accomplish. You might also run into rounding/damping issues - you're moving two objects at a fraction of their original velocity. Say you have a cue ball traveling through your zone and it hits another ball with equal mass - you're now moving slower, which might get cut off if it's below a certain threshold. You might also need to scale any damping or friction on these components for a similar reason.

I'm relatively new to Farseer so the above advice is just guesses.