Changing Geom friction has no effect

Topics: User Forum
Jan 5, 2010 at 5:11 AM

I'm using farseer in a platformer. I'm using a high friction to stop the player sliding around when he's standing on shallow slopes. When in the air, however, the player should have no friction. I'm successfully detecting when the player leaves the ground, and changing the friction coefficient on the associated Geom. But the results are very confusing. It seems to always take the smallest value assigned to it. If I set a high value at first, then a small one, the small one sticks. If I set a small one then a large one, the large one is ignored. I've looked inside the library and it doesn't seem to be caching anything.

 

An unrelated question specifically for Genbox; How is farseer related to box2d, if at all, and what's different between it and box2d.xna?

Coordinator
Jan 5, 2010 at 11:33 AM

The friction coefficient can only be used when 2 values (2 materials) are combined. There are no single way to combine the coefficients, so we have 2 different methods of combining the friction.

One is to take the average of the coefficients and apply that, another is to take the minimum value and apply that. The default method is to take the average. You also have linear drag that is applied to the body, remember to take that into account.

As for the relation to Box2D, Farseer Physics Engine 2.x is built on Box2D Lite and is very simple by nature. We have extended it using many different broadphases and we even have 2 different narrow phases. Distance Grid (Signed distance field) is the default narrow phase collider because it supports concave polygons. Farseer Physics Engine 3.0 will be based on Box2D, we use Box2D.XNA as the C# port. The Box2DX version which I myself and several others ported is 99% identical to Box2D.XNA. The choice of Box2D.XNA over Box2DX was simply made to ease the development of Farseer Physics Engine 3.0.

Jan 5, 2010 at 8:58 PM

I understand how the friction works. Let me clarify the problem I'm having.

I'm detecting when the player is touching the ground, and when he is not. This works great, and it knows the difference between floors and walls. I've confirmed that everything is being called properly in the debugger. I mentioned friction because the LinearDrag (Which also changes - the player has different maximum speeds on the ground vs when airborne) functions properly. However, the friction always seems to have the airborne friction value - no matter that I set it different when I detect a Landing event. (I have an idea of what could be going on, I'll have to dig into the library a bit first, but I'll get to that in a bit.)

Some examples of behavior. When the player is standing on sloped ground with friction, they stand still. Without friction, the player slides downward. Now consider a player that jumps up and into a wall at a steep angle - they still have mostly upward velocity. If there is no friction, the player will slide up the wall to the height of the jump, and be able to slip onto the platform at the top.  With friction, running into the wall slows the player down. The player does not slide up the wall at all. That's why I am trying to change the friction when the player is airborne.

Here is what I think might be going on. When I detect the landing event, a collision has already occurred. Since I detect the landing event by looking at contacts, it's obvious that the contacts have already been created (Code in a sec.) So two questions : Are contacts cached; do they stick around for the duration of the contact, or are they re-created every single update? And, is the friction at any given contact point only calculated once, and stored in the contact? If both of these are true, it would explain the behavior I'm getting.

Here is the relevant code. Note that the Geoms making up the world all have a FrictionCoefficient of 0, and I am using the Average FrictionType so that I can control friction entirely from the players Geom's settings.

 

class PhysicsComponent : EntityComponent
{
	Body PhysicsBody;
	Geom PhysicsGeom;
	Joint FixedAngleJoint;

	bool IsOnGround { get; set; }

	public override void Initialize()
	{
		IsOnGround = true;
		
		PhysicsBody = BodyFactory.Instance.CreateRectangleBody(Me.Dimensions.X, Me.Dimensions.Y, PlayerConstants.PlayerMass);
		PhysicsGeom = GeomFactory.Instance.CreateRectangleGeom(PhysicsBody, Me.Dimensions.X, Me.Dimensions.Y);
		FixedAngleJoint = new FixedAngleJoint(PhysicsBody, 0);

		PhysicsBody.MinimumVelocity = PlayerConstants.MinimumVelocity;
		PhysicsBody.IsAutoIdle = true;
		PhysicsBody.LinearDragCoefficient = PlayerConstants.GroundLinearDrag;
		PhysicsGeom.FrictionCoefficient = PlayerConstants.GroundFriction;

		Me.World.PhysicsSimulator.Add(PhysicsBody);
		Me.World.PhysicsSimulator.Add(PhysicsGeom);
		Me.World.PhysicsSimulator.Add(FixedAngleJoint);

		PhysicsGeom.OnCollision += OnCollision;

		base.Initialize();
	}

	bool OnCollision(Geom geom1, Geom geom2, ContactList contacts) 
	{
		foreach (Contact contact in contacts)
			if (contact.Normal.Y > System.Math.Abs(contact.Normal.X)) IsOnGround = true;
		return true;
	}

	public void EnterAirState()
	{
		PhysicsBody.LinearDragCoefficient = PlayerConstants.AirLinearDrag;
		PhysicsGeom.FrictionCoefficient = PlayerConstants.AirFriction;
	}

	public void EnterLandState()
	{
		PhysicsBody.LinearDragCoefficient = PlayerConstants.GroundLinearDrag;
		PhysicsGeom.FrictionCoefficient = PlayerConstants.GroundFriction;
	}

	public override void Update(float ElapsedSeconds)
	{
		Me.Position = PhysicsBody.Position;

		if (IsOnGround)
		{
			EnterLandState();
		}
		else
		{
			EnterAirState();
		}

		IsOnGround = false;
	}
}

 

Jan 5, 2010 at 9:13 PM
Edited Jan 5, 2010 at 9:15 PM

How about that. The combined friction coefficient was cached in the Arbiter.

I changed it as such

 

		private float _frictionCoefficientCombined
		{
			get
			{
				return (GeometryA.FrictionCoefficient + GeometryB.FrictionCoefficient) / 2f;
			}
		}
		
        //private float _frictionCoefficientCombined;

(And removed the part that sets it in ConstructArbiter) and it works how I originally expected.

I'm using 2.1.3.

Feb 25, 2010 at 10:38 PM

Thanks! Had exactly the same problem :)