A game framework written with osu! in mind.
at master 133 lines 5.1 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using osuTK; 6using osu.Framework.Graphics; 7 8namespace osu.Framework.Physics 9{ 10 /// <summary> 11 /// Contains physical state and methods necessary for rigid body simulation. 12 /// </summary> 13 public interface IRigidBody : IDrawable 14 { 15 /// <summary> 16 /// The <see cref="Drawable"/> which is currently performing a simulation on this <see cref="IRigidBody"/>. 17 /// </summary> 18 Drawable Simulation { get; set; } 19 20 /// <summary> 21 /// Controls how elastic the material is. A value of 1 means perfect elasticity 22 /// (kinetic energy is fully preserved). A value of 0 means all energy is absorbed 23 /// on collision, i.e. no rebound occurs at all. 24 /// </summary> 25 float Restitution { get; set; } 26 27 /// <summary> 28 /// How much friction happens between objects. 29 /// </summary> 30 float FrictionCoefficient { get; set; } 31 32 Vector2 Centre { get; set; } 33 34 float RotationRadians { get; set; } 35 36 float Mass { get; set; } 37 38 Vector2 Velocity { get; } 39 40 Vector2 Momentum { get; set; } 41 42 float AngularVelocity { get; } 43 44 float AngularMomentum { get; set; } 45 46 float MomentOfInertia { get; } 47 48 /// <summary> 49 /// Total velocity at a given location. Includes angular velocity. 50 /// </summary> 51 Vector2 VelocityAt(Vector2 pos); 52 53 /// <summary> 54 /// Applies a given impulse attacking at a given position. 55 /// </summary> 56 void ApplyImpulse(Vector2 impulse, Vector2 pos); 57 58 /// <summary> 59 /// Checks for and records all collisions with another body. If collisions were found, 60 /// their aggregate is handled. 61 /// </summary> 62 bool CheckAndHandleCollisionWith(IRigidBody other); 63 64 /// <summary> 65 /// Performs an integration step over time. More precisely, updates the 66 /// physical state as dependent on time according to the forces and torques 67 /// acting on this body. 68 /// </summary> 69 void Integrate(Vector2 force, float torque, float dt); 70 71 /// <summary> 72 /// Reads the positional and rotational state of this rigid body from its source. 73 /// </summary> 74 void ReadState(); 75 76 /// <summary> 77 /// Applies the positional and rotational state of this rigid body to its source. 78 /// </summary> 79 void ApplyState(); 80 81 /// <summary> 82 /// Whether the given screen-space position is contained within the rigid body. 83 /// </summary> 84 bool BodyContains(Vector2 screenSpacePos); 85 } 86 87 /// <summary> 88 /// Helper extension methods operating on <see cref="IRigidBody"/>. 89 /// </summary> 90 public static class RigidBodyExtensions 91 { 92 /// <summary> 93 /// Helper function for code brevity in <see cref="ComputeImpulse(IRigidBody, IRigidBody, Vector2, Vector2)"/>. 94 /// Can be moved into the function as a nested method once C# 7 is out. 95 /// </summary> 96 public static float ImpulseDenominator(this IRigidBody body, Vector2 pos, Vector2 normal) 97 { 98 Vector2 diff = pos - body.Centre; 99 float perpDot = Vector2.Dot(normal, diff.PerpendicularRight); 100 return 1.0f / body.Mass + perpDot * perpDot / body.MomentOfInertia; 101 } 102 103 /// <summary> 104 /// Computes the impulse of a collision of 2 rigid bodies, given the other body, the impact position, 105 /// and the surface normal of this body at the impact position. 106 /// </summary> 107 public static Vector2 ComputeImpulse(this IRigidBody body, IRigidBody other, Vector2 pos, Vector2 normal) 108 { 109 Vector2 vrel = body.VelocityAt(pos) - other.VelocityAt(pos); 110 float vrelOrtho = -Vector2.Dot(vrel, normal); 111 112 // We don't want to consider collisions where objects move away from each other. 113 // (Or with negligible velocity. Let repulsive forces handle these.) 114 if (vrelOrtho > -0.001f) 115 return Vector2.Zero; 116 117 float impulseMagnitude = -(1.0f + body.Restitution) * vrelOrtho; 118 impulseMagnitude /= body.ImpulseDenominator(pos, normal) + other.ImpulseDenominator(pos, normal); 119 120 //impulseMagnitude = Math.Max(impulseMagnitude - 0.01f, 0.0f); 121 122 Vector2 impulse = -normal * impulseMagnitude; 123 124 // Add "friction" to the impulse. We arbitrarily reduce the planar velocity relative to the impulse magnitude. 125 Vector2 vrelPlanar = vrel + vrelOrtho * normal; 126 float vrelPlanarLength = vrelPlanar.Length; 127 if (vrelPlanarLength > 0) 128 impulse -= vrelPlanar * Math.Min(impulseMagnitude * 0.05f * body.FrictionCoefficient * other.FrictionCoefficient / vrelPlanarLength, body.Mass); 129 130 return impulse; 131 } 132 } 133}