Had to optimize the physics so that the client runs on my laptop.

This commit is contained in:
Acruid
2020-07-29 00:47:47 -07:00
parent 3648f43f5c
commit 876d528578
6 changed files with 99 additions and 113 deletions

View File

@@ -36,9 +36,9 @@ namespace Robust.Client.Physics
SimulateWorld((float) diff.TotalSeconds, ActuallyRelevant());
}
private List<IPhysicsComponent> ActuallyRelevant()
private List<ICollidableComponent> ActuallyRelevant()
{
var relevant = _componentManager.GetAllComponents<IPhysicsComponent>().Where(p => p.Predict)
var relevant = _componentManager.GetAllComponents<ICollidableComponent>().Where(p => p.Predict)
.ToList();
return relevant;
}

View File

@@ -17,7 +17,7 @@ namespace Robust.Server.GameObjects.EntitySystems
{
SimulateWorld(frameTime,
RelevantEntities.Where(e => !e.Deleted && !_pauseManager.IsEntityPaused(e))
.Select(p => p.GetComponent<IPhysicsComponent>()).ToList());
.Select(p => p.GetComponent<ICollidableComponent>()).ToList());
}
}
}

View File

@@ -161,6 +161,12 @@ namespace Robust.Shared.GameObjects.Components
/// </summary>
/// <returns>True if all of the controllers were reset, false otherwise.</returns>
bool Stop();
/// <summary>
/// Can this body be moved?
/// </summary>
/// <returns></returns>
bool CanMove();
}
partial class CollidableComponent : ICollidableComponent
@@ -418,5 +424,11 @@ namespace Robust.Shared.GameObjects.Components
return successful;
}
/// <inheritdoc />
public bool CanMove()
{
return !Anchored && !Deleted;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects.Components;
@@ -35,10 +36,13 @@ namespace Robust.Shared.GameObjects.Systems
[MethodImpl(MethodImplOptions.NoInlining)]
protected void SimulateWorld(float frameTime, List<IPhysicsComponent> physicsComponents)
protected void SimulateWorld(float frameTime, List<ICollidableComponent> physicsComponents)
{
foreach (var physics in physicsComponents)
{
if(!physics.CanMove())
continue;
var linearVelocity = Vector2.Zero;
foreach (var controller in physics.Controllers.Values)
@@ -51,7 +55,7 @@ namespace Robust.Shared.GameObjects.Systems
}
// Calculate collisions and store them in the cache
ProcessCollisions();
ProcessCollisions(physicsComponents);
// Remove all entities that were deleted during collision handling
physicsComponents.RemoveAll(p => p.Deleted);
@@ -89,7 +93,8 @@ namespace Robust.Shared.GameObjects.Systems
{
foreach (var physics in physicsComponents)
{
UpdatePosition(physics, frameTime / divisions);
if(physics.CanMove())
UpdatePosition(physics, frameTime / divisions);
}
for (var j = 0; j < divisions; ++j)
@@ -103,11 +108,11 @@ namespace Robust.Shared.GameObjects.Systems
}
// Runs collision behavior and updates cache
private void ProcessCollisions()
private void ProcessCollisions(IEnumerable<ICollidableComponent> bodies)
{
var collisionsWith = new Dictionary<ICollideBehavior, int>();
FindCollisions();
FindCollisions(bodies);
var counter = 0;
@@ -115,14 +120,14 @@ namespace Robust.Shared.GameObjects.Systems
{
counter++;
var impulse = _physicsManager.SolveCollisionImpulse(collision);
if (collision.APhysics != null)
if (collision.A.CanMove())
{
collision.APhysics.Momentum -= impulse;
collision.A.Momentum -= impulse;
}
if (collision.BPhysics != null)
if (collision.B.CanMove())
{
collision.BPhysics.Momentum += impulse;
collision.B.Momentum += impulse;
}
}
@@ -167,34 +172,28 @@ namespace Robust.Shared.GameObjects.Systems
}
}
private void FindCollisions()
private void FindCollisions(IEnumerable<ICollidableComponent> bodies)
{
_collisionCache.Clear();
var combinations = new HashSet<(EntityUid, EntityUid)>();
foreach (var physics in _componentManager.GetAllComponents<IPhysicsComponent>())
foreach (var aCollidable in bodies)
{
if (!physics.Owner.TryGetComponent<ICollidableComponent>(out var aCollidable))
{
continue;
}
aCollidable.SleepAccumulator++;
if (physics.LinearVelocity == Vector2.Zero)
if (aCollidable.LinearVelocity == Vector2.Zero)
{
continue;
}
FindCollisionsFor(aCollidable, physics, combinations);
FindCollisionsFor(aCollidable, combinations);
}
}
private void FindCollisionsFor(ICollidableComponent a, IPhysicsComponent aPhysics,
HashSet<(EntityUid, EntityUid)> combinations)
private void FindCollisionsFor(ICollidableComponent a, HashSet<(EntityUid, EntityUid)> combinations)
{
foreach (var b in a.GetCollidingEntities(Vector2.Zero))
foreach (var b in _physicsManager.GetCollidingEntities(a, Vector2.Zero))
{
var aUid = ((IPhysBody)a).Entity.Uid;
var aUid = a.Entity.Uid;
var bUid = b.Uid;
if (bUid.CompareTo(aUid) > 0)
@@ -210,14 +209,7 @@ namespace Robust.Shared.GameObjects.Systems
}
var bCollidable = b.GetComponent<ICollidableComponent>();
if (b.TryGetComponent<IPhysicsComponent>(out var bPhysics))
{
_collisionCache.Add(new Manifold(a, bCollidable, aPhysics, bPhysics, a.Hard && bCollidable.Hard, _physicsManager));
}
else
{
_collisionCache.Add(new Manifold(a, bCollidable, aPhysics, null, a.Hard && bCollidable.Hard, _physicsManager));
}
_collisionCache.Add(new Manifold(a, bCollidable, a.Hard && bCollidable.Hard));
}
}
@@ -248,38 +240,38 @@ namespace Robust.Shared.GameObjects.Systems
return false;
}
private void ProcessFriction(IPhysicsComponent physics)
private void ProcessFriction(ICollidableComponent body)
{
if (physics.LinearVelocity == Vector2.Zero) return;
if (body.LinearVelocity == Vector2.Zero) return;
// Calculate frictional force
var friction = GetFriction(physics);
var friction = GetFriction(body);
// Clamp friction because friction can't make you accelerate backwards
friction = Math.Min(friction, physics.LinearVelocity.Length);
friction = Math.Min(friction, body.LinearVelocity.Length);
// No multiplication/division by mass here since that would be redundant.
var frictionVelocityChange = physics.LinearVelocity.Normalized * -friction;
var frictionVelocityChange = body.LinearVelocity.Normalized * -friction;
physics.LinearVelocity += frictionVelocityChange;
body.LinearVelocity += frictionVelocityChange;
}
private void UpdatePosition(IPhysicsComponent physics, float frameTime)
private static void UpdatePosition(ICollidableComponent body, float frameTime)
{
var ent = physics.Owner;
physics.LinearVelocity = new Vector2(Math.Abs(physics.LinearVelocity.X) < Epsilon ? 0.0f : physics.LinearVelocity.X, Math.Abs(physics.LinearVelocity.Y) < Epsilon ? 0.0f : physics.LinearVelocity.Y);
if (physics.Anchored ||
physics.LinearVelocity == Vector2.Zero && Math.Abs(physics.AngularVelocity) < Epsilon) return;
var ent = body.Owner;
body.LinearVelocity = new Vector2(Math.Abs(body.LinearVelocity.X) < Epsilon ? 0.0f : body.LinearVelocity.X, Math.Abs(body.LinearVelocity.Y) < Epsilon ? 0.0f : body.LinearVelocity.Y);
if (body.Anchored ||
body.LinearVelocity == Vector2.Zero && Math.Abs(body.AngularVelocity) < Epsilon) return;
if (ContainerHelpers.IsInContainer(ent) && physics.LinearVelocity != Vector2.Zero)
if (ContainerHelpers.IsInContainer(ent) && body.LinearVelocity != Vector2.Zero)
{
ent.Transform.Parent!.Owner.SendMessage(ent.Transform, new RelayMovementEntityMessage(ent));
// This prevents redundant messages from being sent if solveIterations > 1 and also simulates the entity "colliding" against the locker door when it opens.
physics.LinearVelocity = Vector2.Zero;
body.LinearVelocity = Vector2.Zero;
}
physics.Owner.Transform.WorldRotation += physics.AngularVelocity * frameTime;
physics.Owner.Transform.WorldPosition += physics.LinearVelocity * frameTime;
body.Owner.Transform.WorldRotation += body.AngularVelocity * frameTime;
body.Owner.Transform.WorldPosition += body.LinearVelocity * frameTime;
}
// Based off of Randy Gaul's ImpulseEngine code
@@ -296,32 +288,31 @@ namespace Robust.Shared.GameObjects.Systems
}
var penetration = _physicsManager.CalculatePenetration(collision.A, collision.B);
if (penetration > allowance)
{
done = false;
var correction = collision.Normal * Math.Abs(penetration) * percent;
if (collision.APhysics != null && !collision.APhysics.Anchored && !collision.APhysics.Deleted)
collision.APhysics.Owner.Transform.WorldPosition -= correction;
if (collision.BPhysics != null && !collision.BPhysics.Anchored && !collision.BPhysics.Deleted)
collision.BPhysics.Owner.Transform.WorldPosition += correction;
}
if (penetration <= allowance)
continue;
done = false;
var correction = collision.Normal * Math.Abs(penetration) * percent;
if (collision.A.CanMove())
collision.A.Owner.Transform.WorldPosition -= correction;
if (collision.B.CanMove())
collision.B.Owner.Transform.WorldPosition += correction;
}
return done;
}
private float GetFriction(IPhysicsComponent physics)
private float GetFriction(ICollidableComponent body)
{
var ent = physics.Owner;
if (ent.HasComponent<ICollidableComponent>() && physics.OnGround)
{
var location = ent.Transform;
var grid = _mapManager.GetGrid(location.GridPosition.GridID);
var tile = grid.GetTileRef(location.GridPosition);
var tileDef = _tileDefinitionManager[tile.Tile.TypeId];
return tileDef.Friction;
}
return 0.0f;
if (!body.OnGround)
return 0.0f;
var location = body.Owner.Transform;
var grid = _mapManager.GetGrid(location.GridPosition.GridID);
var tile = grid.GetTileRef(location.GridPosition);
var tileDef = _tileDefinitionManager[tile.Tile.TypeId];
return tileDef.Friction;
}
}
}

View File

@@ -71,14 +71,6 @@ namespace Robust.Shared.Interfaces.Physics
/// <returns>The distance the ray traveled while colliding with entities</returns>
public float IntersectRayPenetration(MapId mapId, CollisionRay ray, float maxLength, IEntity? ignoredEnt = null);
/// <summary>
/// Calculates the normal vector for two colliding bodies
/// </summary>
/// <param name="target"></param>
/// <param name="source"></param>
/// <returns></returns>
Vector2 CalculateNormal(IPhysBody target, IPhysBody source);
/// <summary>
/// Calculates the penetration depth of the axis-of-least-penetration for a
/// </summary>
@@ -127,54 +119,38 @@ namespace Robust.Shared.Interfaces.Physics
public float MaxLength { get; }
}
public struct Manifold
public readonly struct Manifold
{
public readonly ICollidableComponent A;
public readonly ICollidableComponent B;
public readonly Vector2 Normal;
public readonly bool Hard;
public Vector2 RelativeVelocity
{
get
{
if (APhysics != null)
if (A != null)
{
if (BPhysics != null)
{
return BPhysics.LinearVelocity - APhysics.LinearVelocity;
}
else
{
return -APhysics.LinearVelocity;
}
if (B != null)
return B.LinearVelocity - A.LinearVelocity;
return -A.LinearVelocity;
}
if (BPhysics != null)
{
return BPhysics.LinearVelocity;
}
else
{
return Vector2.Zero;
}
if (B != null)
return B.LinearVelocity;
return Vector2.Zero;
}
}
public readonly Vector2 Normal;
public readonly IPhysBody A;
public readonly IPhysBody B;
public IPhysicsComponent? APhysics;
public IPhysicsComponent? BPhysics;
public readonly bool Hard;
public float InvAMass => 1 / APhysics?.Mass ?? 0.0f;
public float InvBMass => 1 / BPhysics?.Mass ?? 0.0f;
public bool Unresolved => Vector2.Dot(RelativeVelocity, Normal) < 0 && Hard;
public Manifold(IPhysBody A, IPhysBody B, IPhysicsComponent? aPhysics, IPhysicsComponent? bPhysics, bool hard,
IPhysicsManager physicsManager)
public Manifold(ICollidableComponent a, ICollidableComponent b, bool hard)
{
this.A = A;
this.B = B;
Normal = physicsManager.CalculateNormal(A, B);
APhysics = aPhysics;
BPhysics = bPhysics;
A = a;
B = b;
Normal = PhysicsManager.CalculateNormal(a, b);
Hard = hard;
}
}

View File

@@ -50,7 +50,14 @@ namespace Robust.Shared.Physics
return !_mapManager.GetGrid(gridPosition.GridID).HasGravity || tile.IsEmpty;
}
public Vector2 CalculateNormal(IPhysBody target, IPhysBody source)
/// <summary>
/// Calculates the normal vector for two colliding bodies
/// </summary>
/// <param name="target"></param>
/// <param name="source"></param>
/// <returns></returns>
public static Vector2 CalculateNormal(IPhysBody target, IPhysBody source)
{
var manifold = target.WorldAABB.Intersect(source.WorldAABB);
if (manifold.IsEmpty()) return Vector2.Zero;
@@ -80,8 +87,8 @@ namespace Robust.Shared.Physics
// Impulse resolution algorithm based on Box2D's approach in combination with Randy Gaul's Impulse Engine resolution algorithm.
public Vector2 SolveCollisionImpulse(Manifold manifold)
{
var aP = manifold.APhysics;
var bP = manifold.BPhysics;
var aP = manifold.A;
var bP = manifold.B;
if (aP == null && bP == null) return Vector2.Zero;
var restitution = 0.01f;
var normal = CalculateNormal(manifold.A, manifold.B);