public virtual void Collided(PhysicsObject collider, Edge collidingEdge, Vector2 projectionVector, float deltaTime) { }
private void CheckCollisions(PhysicsObject physicsObject, float deltaTime) { // Check collision with all objects within our cells Bag <PhysicsObject> collidedObjects = new Bag <PhysicsObject>(); physicsObject.ContainingCells.ForEach((cell) => { Bag <PhysicsObject> possibleColliders = _grid[cell.Column, cell.Row]; possibleColliders.ForEachWith((collider) => { /// IMPLEMENT BETTER MOUDLAR VERSION TO FIGURE OUT IF OBJECTS SHOULD COLLIDE. MAYBE SOMETHING LIKE BITWISE COLLISION GROUPS ETC... if (physicsObject is CharacterObject && collider is CharacterObject) { return; } #region Depricated collision filters /// IMPLEMENT A BETTER MODULAR VERSION TO FIGURE OUT COLLISIONS. MAYBE USE BITWISE COLLISION GROUPS ETC... /*if (physicsObject.Owner != null && physicsObject.Owner.Type == Actor.ActorType.Spirit) * { * // Spirits don't collide with objects * if (curCollider.Owner != null && curCollider.Owner.Type == Actor.ActorType.Object) * continue; * } * if (curCollider.Owner != null && curCollider.Owner.Type == Actor.ActorType.Spirit) * { * // Spirits don't collide with objects * if (physicsObject.Owner != null && physicsObject.Owner.Type == Actor.ActorType.Object) * continue; * }*/ #endregion // Don't collide with ourselfs. if (collider == physicsObject) { return; } // Has this object already been checked if (collider.IsCollisionChecked) { return; } #region Collide with AABB shape // Coarse collision. AABB A = physicsObject.BoundingBox; AABB B = collider.BoundingBox; float xDist = Math.Abs(A.Position.X - B.Position.X); float yDist = Math.Abs(A.Position.Y - B.Position.Y); // Project half widths. float xProj = A.XHalfWidth.X + B.XHalfWidth.X; float yProj = A.YHalfWidth.Y + B.YHalfWidth.Y; // Early exit. if (xProj < xDist) { return; } if (yProj < yDist) { return; } #endregion // If we should test AABB only we are done now. if (physicsObject.TestAABBOnly && collider.TestAABBOnly) { #region Test AABB only // Calculate projection vector. Vector2 projectionVector; Vector2 diff = B.Position - A.Position; float xDiff = ((xProj - xDist) * 1.0f) + ConvertUnits.ToSimUnits(1); float yDiff = ((yProj - yDist) * 1.0f) + ConvertUnits.ToSimUnits(1); if (xDiff < yDiff) { projectionVector = new Vector2(xDiff * Math.Sign(diff.X), 0); } else { projectionVector = new Vector2(0, yDiff * Math.Sign(diff.Y)); } // Are we dealing with sensors if (!physicsObject.IsSensor && !collider.IsSensor) { // Get colliding edge (O(4)) Edge collidingEdge = null; if (collider.CollisionShape.Type == Shape.ShapeType.SH_POLYGON) { var poly = collider.CollisionShape as Polygon; var projDir = Vector2.Normalize(projectionVector); for (int i = 0; i < poly.Edges.Length; i++) { var edge = poly.Edges[i]; if (Vector2.Dot(projDir, edge.Normal) > 0.9f) { collidingEdge = edge; } } } physicsObject.Collided(collider, collidingEdge, projectionVector, deltaTime); } if (physicsObject.OnCollision != null) { physicsObject.OnCollision(physicsObject, collider, projectionVector); } if (collider.OnCollision != null) { collider.OnCollision(collider, physicsObject, Vector2.Zero); } collider.IsCollisionChecked = true; collidedObjects.Add(collider); #endregion } else { #region Test Polygon if (physicsObject.CollisionShape.Type == Shape.ShapeType.SH_POLYGON && collider.CollisionShape.Type == Shape.ShapeType.SH_POLYGON) { if (CollidePolyPoly(physicsObject.CollisionShape as Polygon, collider.CollisionShape as Polygon, deltaTime)) { collidedObjects.Add(collider); // Flag object as checked and add to collided list. } } #endregion } }, (collider) => { return(collider.IsActive); }); }); // Uncheck collision flags for next object collidedObjects.ForEach((collider) => { collider.IsCollisionChecked = false; }); }
public Shape(PhysicsObject owner) { Owner = owner; }
private bool CheckLineCollisions(int col, int row, Line line, RayCastCallback callback, bool isFirst, bool isLast, bool ignoreSensor, bool ignoreProjectile) { Bag <PhysicsObject> possibleColliders = _grid[col, row]; Vector2 collisionPoint = line.P2; Vector2 collisionNormal = Vector2.Zero; PhysicsObject collidingObject = null; float distance = float.PositiveInfinity; if (possibleColliders == null) { return(false); } /// I actually don't now why I put this in - it may causes bugs in future but for the moment it seems to work when commented. /*if (possibleColliders.Count > 0) * { * // Cut out segment within the current cell. * // * * // Create a sensor within the cell to collide with * float xCell = (col + 0.5f) * _cellSize + Origin.X; * float yCell = (row + 0.5f) * _cellSize + Origin.Y; * * var cellCollider = new Sensor(this, new Vector2(xCell, yCell), new Vector2(_cellSize, _cellSize)); * * var newStart = Vector2.Zero; * var newEnd = Vector2.Zero; * var newNorm = Vector2.Zero; * var mirrorLine = new Line(line.P2, line.P1); * * if (!CollideLinePoly(line, (Polygon)cellCollider.CollisionShape, out newStart, out newNorm)) * { * newStart = line.P1; * } * if (!CollideLinePoly(mirrorLine, (Polygon)cellCollider.CollisionShape, out newEnd, out newNorm)) * { * newEnd = line.P2; * } * * line = new Line(newStart, newEnd); * * cellCollider.Dispense(); // Remove from world * }*/ // Test each object to collide with line. possibleColliders.ForEachWith((collider) => { if (ignoreSensor) { if (collider.IsSensor) { return; } } if (ignoreProjectile) { if (collider.IsProjectile) { return; } } if (collider.CollisionShape.Type == Shape.ShapeType.SH_POLYGON) { var poly = collider.CollisionShape as Polygon; if (isFirst) { if (CollidePointPoly(line.P1, poly)) { return; } } Vector2 curCollisionPoint, curCollisionNormal; if (CollideLinePoly(line, poly, out curCollisionPoint, out curCollisionNormal)) { // Check for closest point float curDistance = (curCollisionPoint - line.P1).Length(); if (curDistance < distance) { distance = curDistance; collidingObject = collider; collisionPoint = curCollisionPoint; collisionNormal = curCollisionNormal; } } } }, (collider) => { return(collider.IsActive); }); if (collidingObject != null) { callback(collidingObject, collisionPoint, collisionNormal); _drawPoints.Add(collisionPoint); return(true); } return(false); }
public override void Collided(PhysicsObject collider, Edge collidingEdge, Vector2 projectionVector, float deltaTime) { if (!IsActive) { return; } if (IsEthereal || collider.IsEthereal) { return; } _projectionVector = projectionVector; // After collision project out of colliding surface. Position -= projectionVector; if (projectionVector.Length() == 0.0f) // Return when no projection. { return; } // Split reflection velocity into friction and bounce vector. var surfaceNorm = Vector2.Normalize(projectionVector); var surfaceDir = new Vector2(surfaceNorm.Y, -surfaceNorm.X); _surfaceNormal = surfaceNorm; // Get collision point // // Distance to line Vector2 p1 = collidingEdge.P1; Vector2 p2 = collidingEdge.P2; float normalLength = (float)Math.Sqrt((p2.X - p1.X) * (p2.X - p1.X) + (p2.Y - p1.Y) * (p2.Y - p1.Y)); float dist = Math.Abs((Position.X - p1.X) * (p2.Y - p1.Y) - (Position.Y - p1.Y) * (p2.X - p1.X)) / normalLength; Vector2 collisionPoint = Position + surfaceNorm * dist; if ((p1 - collisionPoint).Length() < 0.3f || (p2 - collisionPoint).Length() < 0.3f) { //Position += surfaceDir * 0.09f * Math.Sign(Velocity.X); } Vector2 bounce = surfaceNorm * Vector2.Dot(Velocity, surfaceNorm) * Restitution * -1 * 0; Vector2 friction = surfaceDir * Vector2.Dot(Velocity, surfaceDir) * (1 - Friction); if (Vector2.Dot(surfaceNorm, Vector2.UnitY) > _maxSlope) { MotionState = MotionStates.MS_LANDED; // Enable character to walk up and down a specific slope at a certain speed Vector2 velDirection = surfaceDir; if (Vector2.Dot(velDirection, Velocity) < 0) { velDirection = -velDirection; } Velocity = velDirection * Velocity.Length() * Friction; // Move with dynamic object. if (collider.IsStatic == false) { var dynamicObject = collider as DynamicObject; Position += dynamicObject.Velocity * deltaTime; } } else { _bounceVector = bounce; _frictionVector = friction; // Add up to reflection vector. _reflectionVector = bounce + friction; Velocity = _reflectionVector; } // Check if we push colliding object. float xProj = Math.Abs(Vector2.Dot(surfaceNorm, Vector2.UnitX)); if (xProj > 0.9f && xProj < 1.1f) { CharacterState = CharacterPhysicsState.CPS_PUSHING; } }