Files
2024-12-11 13:26:13 +02:00

96 lines
3.6 KiB
C#

/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using FarseerPhysics.Collision;
using FarseerPhysics.Dynamics;
namespace FarseerPhysics.Common.PhysicsLogic
{
/// <summary>
/// Creates a simple explosion that ignores other bodies hiding behind static bodies.
/// </summary>
public sealed class SimpleExplosion : PhysicsLogic
{
public SimpleExplosion(World world): base(world)
{
Power = 1; //linear
}
/// <summary>
/// This is the power used in the power function. A value of 1 means the force
/// applied to bodies in the explosion is linear. A value of 2 means it is exponential.
/// </summary>
public float Power { get; set; }
/// <summary>
/// Activate the explosion at the specified position.
/// </summary>
/// <param name="pos">The position (center) of the explosion.</param>
/// <param name="radius">The radius of the explosion.</param>
/// <param name="force">The force applied</param>
/// <param name="maxForce">A maximum amount of force. When force gets over this value, it will be equal to maxForce</param>
/// <returns>A list of bodies and the amount of force that was applied to them.</returns>
public Dictionary<Body, Vector2> Activate(Vector2 pos, float radius, float force, float maxForce = float.MaxValue)
{
HashSet<Body> affectedBodies = new HashSet<Body>();
AABB aabb;
aabb.LowerBound = pos - new Vector2(radius);
aabb.UpperBound = pos + new Vector2(radius);
// Query the world for bodies within the radius.
World.QueryAABB(fixture =>
{
if (Vector2.Distance(fixture.Body.Position, pos) <= radius)
{
if (!affectedBodies.Contains(fixture.Body))
affectedBodies.Add(fixture.Body);
}
return true;
}, ref aabb);
return ApplyImpulse(pos, radius, force, maxForce, affectedBodies);
}
private Dictionary<Body, Vector2> ApplyImpulse(Vector2 pos, float radius, float force, float maxForce, HashSet<Body> overlappingBodies)
{
Dictionary<Body, Vector2> forces = new Dictionary<Body, Vector2>(overlappingBodies.Count);
foreach (Body overlappingBody in overlappingBodies)
{
if (IsActiveOn(overlappingBody))
{
float distance = Vector2.Distance(pos, overlappingBody.Position);
float forcePercent = GetPercent(distance, radius);
Vector2 forceVector = pos - overlappingBody.Position;
forceVector *= 1f / MathF.Sqrt(forceVector.X * forceVector.X + forceVector.Y * forceVector.Y);
forceVector *= MathHelper.Min(force * forcePercent, maxForce);
forceVector *= -1;
overlappingBody.ApplyLinearImpulse(forceVector);
forces.Add(overlappingBody, forceVector);
}
}
return forces;
}
private float GetPercent(float distance, float radius)
{
//(1-(distance/radius))^power-1
float percent = MathF.Pow(1 - ((distance - radius) / radius), Power) - 1;
if (float.IsNaN(percent))
return 0f;
return MathHelper.Clamp(percent, 0f, 1f);
}
}
}