axiosengine/axios/Dynamics/BreakableBody.cs
2012-03-19 18:57:59 -05:00

142 lines
4.4 KiB
C#

using System;
using System.Collections.Generic;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Dynamics
{
/// <summary>
/// A type of body that supports multiple fixtures that can break apart.
/// </summary>
public class BreakableBody
{
public bool Broken;
public Body MainBody;
public List<Fixture> Parts = new List<Fixture>(8);
/// <summary>
/// The force needed to break the body apart.
/// Default: 500
/// </summary>
public float Strength = 500.0f;
private float[] _angularVelocitiesCache = new float[8];
private bool _break;
private Vector2[] _velocitiesCache = new Vector2[8];
private World _world;
public BreakableBody(IEnumerable<Vertices> vertices, World world, float density)
: this(vertices, world, density, null)
{
}
public BreakableBody()
{
}
public BreakableBody(IEnumerable<Vertices> vertices, World world, float density, object userData)
{
_world = world;
_world.ContactManager.PostSolve += PostSolve;
MainBody = new Body(_world);
MainBody.BodyType = BodyType.Dynamic;
foreach (Vertices part in vertices)
{
PolygonShape polygonShape = new PolygonShape(part, density);
Fixture fixture = MainBody.CreateFixture(polygonShape, userData);
Parts.Add(fixture);
}
}
private void PostSolve(Contact contact, ContactConstraint impulse)
{
if (!Broken)
{
if (Parts.Contains(contact.FixtureA) || Parts.Contains(contact.FixtureB))
{
float maxImpulse = 0.0f;
int count = contact.Manifold.PointCount;
for (int i = 0; i < count; ++i)
{
maxImpulse = Math.Max(maxImpulse, impulse.Points[i].NormalImpulse);
}
if (maxImpulse > Strength)
{
// Flag the body for breaking.
_break = true;
}
}
}
}
public void Update()
{
if (_break)
{
Decompose();
Broken = true;
_break = false;
}
// Cache velocities to improve movement on breakage.
if (Broken == false)
{
//Enlarge the cache if needed
if (Parts.Count > _angularVelocitiesCache.Length)
{
_velocitiesCache = new Vector2[Parts.Count];
_angularVelocitiesCache = new float[Parts.Count];
}
//Cache the linear and angular velocities.
for (int i = 0; i < Parts.Count; i++)
{
_velocitiesCache[i] = Parts[i].Body.LinearVelocity;
_angularVelocitiesCache[i] = Parts[i].Body.AngularVelocity;
}
}
}
private void Decompose()
{
//Unsubsribe from the PostSolve delegate
_world.ContactManager.PostSolve -= PostSolve;
for (int i = 0; i < Parts.Count; i++)
{
Fixture fixture = Parts[i];
Shape shape = fixture.Shape.Clone();
object userdata = fixture.UserData;
MainBody.DestroyFixture(fixture);
Body body = BodyFactory.CreateBody(_world);
body.BodyType = BodyType.Dynamic;
body.Position = MainBody.Position;
body.Rotation = MainBody.Rotation;
body.UserData = MainBody.UserData;
body.CreateFixture(shape, userdata);
body.AngularVelocity = _angularVelocitiesCache[i];
body.LinearVelocity = _velocitiesCache[i];
}
_world.RemoveBody(MainBody);
_world.RemoveBreakableBody(this);
}
public void Break()
{
_break = true;
}
}
}