private void CheckForCollisions() { contactX = true; contactYbottom = true; contactYtop = true; contactLeft = true; contactRight = true; contactFoliage = false; isInClimbingArea = false; insideClimbingArea = null; IsInAir = true; bool ray1Intersect = false, ray2Intersect = false; bool rayLeftIntersect = false, rayRightIntersect = false; TrapPlacementPossible = false; //Multiple iterations are used for collision detection, since one correction could cause another collision Collidable trapPlacementRay = new Ray(new Vector2(TrapPlacementPos.X, TrapPlacementPos.Y - 50), new Vector2(TrapPlacementPos.X, TrapPlacementPos.Y + 50)); for (var iteration = 0; iteration < collisionTestIterations && (contactX || contactYbottom || contactYtop); iteration++) { // Calculate the amount of X and Y movement expected by the player this frame var nextMove = speed * delta; // Store the original final expected movement of the player so we can // see if it has been modified due to a collision or potential collision later var originalMove = nextMove; // No collisions found yet contactX = contactYbottom = contactYtop = contactLeft = contactRight = false; insideClimbingArea = new CollidableResults() { IntersectLeft = false, IntersectRight = false, IntersectUp = false, IntersectDown = false, }; rayCastResult = new CollidableResults() { Intersect = false }; trapPlacementCastResult = new CollidableResults() { Intersect = false }; //check for potential collisions with polygons float tempX, tempY, tempWidth, tempHeight; tempX = (float)Math.Round(position.X + hitboxOffset.X - 1, MidpointRounding.AwayFromZero); tempY = (float)Math.Round(position.Y + hitboxOffset.Y - 1, MidpointRounding.AwayFromZero); tempWidth = (float)Math.Round(tempX + BoundingBox.Width + 1, MidpointRounding.AwayFromZero); tempHeight = (float)Math.Round(tempY + BoundingBox.Height + 1, MidpointRounding.AwayFromZero); //Calculate the bounding polygon of the player at the current position var playerNextBounds = new Polygon(new List <Vector2> { new Vector2(tempX, tempY), new Vector2(tempWidth, tempY), new Vector2(tempWidth, tempHeight), new Vector2(tempX, tempHeight) }, false, false); var rayHeight = (tempHeight + tempY) / 2f; Collidable ray1 = new Ray(new Vector2(tempX, tempHeight - 10), new Vector2(tempX, tempHeight + 10)); Collidable ray2 = new Ray(new Vector2(tempWidth, tempHeight - 10), new Vector2(tempWidth, tempHeight + 10)); Collidable rayLeft = new Ray(new Vector2(tempX + 10, rayHeight), new Vector2(tempX - 10, rayHeight)); Collidable rayRight = new Ray(new Vector2(tempWidth - 10, rayHeight), new Vector2(tempWidth + 10, rayHeight)); climableYCenter = -1f; for (var i = 0; i < scene.GetCollidables().Count; i++) { var currentCollidable = scene.GetCollidables()[i]; if (!currentCollidable.IsClimbable) { //----------------- Ray - Slope detection ---------------------------// //The following code section shoots rays from the bottom of the player's bounding box to the ground. //The results of the rays are used to check, whether the player is standing on a slope. If yes, the .. //.. movement of the players is adjusted according to the steepness of the slope. //!contactX && !contactYbottom && !contactYtop if (!rayCastResult.Intersect) { rayCastResult = RayCast.DetectSlope(ray1, ray2, currentCollidable); } if (!ray1Intersect) { ray1Intersect = RayCast.CheckIfInsideClimbingArea(ray1, currentCollidable); } if (!ray2Intersect) { ray2Intersect = RayCast.CheckIfInsideClimbingArea(ray2, currentCollidable); } if (ray1Intersect || ray2Intersect) { IsInAir = false; } //Check if player collides left or right using rays if (!rayLeftIntersect) { rayLeftIntersect = RayCast.CheckIfInsideClimbingArea(rayLeft, currentCollidable); } if (!rayRightIntersect) { rayRightIntersect = RayCast.CheckIfInsideClimbingArea(rayRight, currentCollidable); } //----------------- Ray - Slope detection -END---------------------------// } //------------------- Trap Placement Check -------------------------------// if (!trapPlacementCastResult.Intersect) { trapPlacementCastResult.Intersect = RayCast.CheckIfInsideClimbingArea(trapPlacementRay, currentCollidable); } //------------------- Trap Placement Check - END -------------------------------// var polygonCollisionResult = currentCollidable.CheckForCollision(playerNextBounds, speed * delta); //Only adjust speed of the player if the polygon can't be climbed up or down( => IsClimbable) if (polygonCollisionResult.WillIntersect && !currentCollidable.IsClimbable) { nextMove = polygonCollisionResult.MinimumTranslationVector + speed * delta; } else if ((polygonCollisionResult.WillIntersect || polygonCollisionResult.Intersect) && currentCollidable.IsClimbable) { isInClimbingArea = true; bool atTop = false; //--------- Ray- Climbing area detection ----------- // //The following code section shoots ray left and right out of the players bounding box, in order //to verify, whether the player is still inside the bounds of the climbing area and limit his movement otherwise if (IsClimbing) { //check if player is still inside the climbable area rayHeight = (tempHeight + tempY) / 2f; var rayWidth = (tempWidth + tempX) / 2f; var deltaHeight = (tempHeight - tempY) / 2f; float collidableXMin = currentCollidable.MinWidth; float collidableXMax = currentCollidable.MaxWidth; float collidableYMax = currentCollidable.MaxHeight; float collidableYMin = currentCollidable.ClimbGroupMinY; if (rayHeight - currentCollidable.CenterYClimbGroup < 0) { atTop = true; } if (tempX >= collidableXMin) { Collidable leftRay = new Ray(new Vector2(collidableXMin - 10, rayHeight), new Vector2(tempX + 10, rayHeight)); if (RayCast.CheckIfInsideClimbingArea(leftRay, currentCollidable)) { insideClimbingArea.IntersectLeft = true; } } if (tempWidth <= collidableXMax) { Collidable rightRay = new Ray(new Vector2(tempWidth - 10, rayHeight), new Vector2(collidableXMax + 10, rayHeight)); if (RayCast.CheckIfInsideClimbingArea(rightRay, currentCollidable)) { insideClimbingArea.IntersectRight = true; } } if (tempHeight <= collidableYMax) { Collidable downRayLeft = new Ray(new Vector2(tempX, tempHeight + 20), new Vector2(tempX, collidableYMax + 30)); Collidable downRayRight = new Ray(new Vector2(tempWidth, tempHeight + 20), new Vector2(tempWidth, collidableYMax + 30)); Collidable downRayMiddle = new Ray(new Vector2(rayWidth, tempHeight - 2), new Vector2(rayWidth, collidableYMax + 10)); if (atTop) { if (RayCast.CheckIfInsideClimbingArea(downRayMiddle, currentCollidable)) { insideClimbingArea.IntersectDown = true; } } else { if (RayCast.CheckIfInsideClimbingArea(downRayLeft, currentCollidable) && RayCast.CheckIfInsideClimbingArea(downRayRight, currentCollidable)) { insideClimbingArea.IntersectDown = true; } } } if (atTop) { insideClimbingArea.IntersectUp = !(tempY + deltaHeight < collidableYMin); } else { insideClimbingArea.IntersectUp = true; } climbableYMax = currentCollidable.ClimbGroupMaxY; climableYCenter = currentCollidable.CenterYClimbGroup; } //--------- Ray- Climbing area detection -END----------- // } // Detect what type of contact has occurred based on a comparison of // the original expected movement vector and the new one if (nextMove.Y > originalMove.Y) { contactYtop = true; } if (nextMove.Y < originalMove.Y) { contactYbottom = true; } if (nextMove.X < originalMove.X) { contactRight = true; } if (nextMove.X > originalMove.X) { contactLeft = true; } contactX = contactLeft || contactRight; if (contactX && contactYtop && speed.Y < 0) { speed.Y = nextMove.Y = 0; } if (contactX && contactYbottom && speed.Y > 0) { speed.Y = nextMove.Y = 0; } } if (!isInClimbingArea) { IsClimbing = false; } if (IsClimbing) { nextMove = originalMove; var anyContact = contactYbottom || contactYtop || contactX; if (anyContact && Math.Abs(originalMove.X) > 0.001) { if (!insideClimbingArea.IntersectLeft && originalMove.X < 0 && contactLeft) { nextMove.X = 0; speed.X = 0; } if (!insideClimbingArea.IntersectRight && originalMove.X > 0 && contactRight) { nextMove.X = 0; speed.X = 0; } } if (anyContact && originalMove.Y > 0.001) { if ((insideClimbingArea.IntersectLeft || insideClimbingArea.IntersectRight) && !insideClimbingArea.IntersectDown) { nextMove.Y = 0; speed.Y = 0; IsClimbing = false; } } if (originalMove.Y < 0f && !insideClimbingArea.IntersectUp) { nextMove.Y -= CharacterConfig.JUMP_START_SPEED_Y / 10f; IsClimbing = false; } } // If a contact has been detected, apply the re-calculated movement vector // and disable any further movement this frame (in either X or Y as appropriate) if (contactYtop || contactYbottom) { position.Y += nextMove.Y; speed.Y = 0; if (contactYbottom) { jumping = false; IsInAir = false; } } if (contactX) { if (rayCastResult.Intersect && !contactYtop && !(rayLeftIntersect && originalMove.X < 0) && !(rayRightIntersect && originalMove.X > 0)) { position.X += nextMove.X * 0.5f * Math.Abs(rayCastResult.CollisionNormal.X); if (Math.Abs(nextMove.X) > 0.0001f) { float newSpeed = speed.X * (1 - 0.3f * Math.Abs(rayCastResult.CollisionNormal.X)); speed.X = 0.6f * speed.X + 0.4f * newSpeed; } else { speed.X = 0; } } else { if (!(rayLeftIntersect && nextMove.X < 0) && !(rayRightIntersect && nextMove.X > 0)) { position.X += nextMove.X; } speed.X = 0; } } else if (insideClimbingArea != null) { position.X += nextMove.X; } if (jumping && IsClimbing) { jumping = false; speed.X = 0; } if (((contactX || contactYtop) && (IsInAir) && !IsClimbing)) { speed.X = nextMove.X = 0; speed.Y += 3 * accY * delta; nextMove.Y = speed.Y * delta; } if (IsInAir && !jumping) { speed.X *= 0.9f; } if (trapPlacementCastResult.Intersect && !(rayLeftIntersect && !IsFacingRight()) && !(rayRightIntersect && IsFacingRight())) { TrapPlacementPossible = true; } } //Check if the player intersects any of the other actors (like undergrowth, treasure, base etc.) IsTouchingShopkeeper = false; contactFoliage = false; touchedActors.Clear(); foreach (var actor in scene.GetActors()) { Rectangle boundingBoxActor; if (actor.GetType() == typeof(ShopKeeper)) { ShopKeeper shopKeeper = actor as ShopKeeper; boundingBoxActor = shopKeeper.BoundingBox; if (!BoundingBox.Intersects(boundingBoxActor)) { continue; } IsTouchingShopkeeper = true; if (character.InvTreasure != null) { shopKeeper.ContactPlayer = true; } } else { boundingBoxActor = new Rectangle((int)actor.GetPosition().X, (int)(actor.GetPosition().Y - actor.GetSize().Y), (int)actor.GetSize().X, (int)actor.GetSize().Y); if (!BoundingBox.Intersects(boundingBoxActor)) { continue; } if (actor.GetType() == typeof(Projectile) && character.GetPosession().GetType() != typeof(AiIndigenController)) { scene.MatchSoundManager.PlaySoundEffect(SoundEffectEnumeration.Hit); ((Projectile)actor).DestroyProjectile(); IsStunned = true; CurrentStunTime = ProjectileConfig.BULLET_STUN_TIME; } } touchedActors.Add(actor); } foreach (Foliage foliage in scene.GetFoliage()) { Rectangle boundingBoxActor = new Rectangle((int)foliage.GetPosition().X, (int)(foliage.GetPosition().Y - foliage.GetSize().Y), (int)foliage.GetSize().X, (int)foliage.GetSize().Y); if (!BoundingBox.Intersects(boundingBoxActor)) { continue; } if (foliage.Active) { contactFoliage = true; } touchedActors.Add(foliage); } }