private void InitClimbReachableNodes() { foreach (ClimbGroup climbGroup in CurrClimbGroup) { List <Collidable> climbCollidables = climbGroup.ClimbCollidables; Collidable upperClimbCollidable = climbCollidables[0]; foreach (var navGraphNode1 in NavGraphNodes) { CollidableResults result1 = upperClimbCollidable.CheckForCollision(navGraphNode1.Platform, Vector3.Zero); //check which platform intersects the upper part of the climbable if (result1.Intersect && upperClimbCollidable.MaxHeight >= navGraphNode1.Platform.MaxHeight - 10 && navGraphNode1.Platform.MinWidth <= upperClimbCollidable.MinWidth + 20 && navGraphNode1.Platform.MaxWidth >= upperClimbCollidable.MaxWidth - 20) { Vector2 startPos = new Vector2(upperClimbCollidable.Center.X, upperClimbCollidable.MinHeight); Collidable lowerClimbCollidable = climbCollidables[climbCollidables.Count - 1]; foreach (var navGraphNode2 in NavGraphNodes) { if (navGraphNode1 == navGraphNode2) { continue; } CollidableResults result2 = lowerClimbCollidable.CheckForCollision(navGraphNode2.Platform, Vector3.Zero); //check which platform intersects the lower part of the climbable if (result2.Intersect && lowerClimbCollidable.MaxHeight < navGraphNode2.Platform.MaxHeight) { Vector2 endPos = new Vector2(lowerClimbCollidable.Center.X, navGraphNode2.Platform.MinHeight); if (startPos.Y < endPos.Y) { navGraphNode1.ReachableNodes.Add( new Tuple <NavGraphNode, Vector2, Vector2>(navGraphNode2, startPos - new Vector2(0, 20), endPos - new Vector2(0, 20))); navGraphNode2.ReachableNodes.Add( new Tuple <NavGraphNode, Vector2, Vector2>(navGraphNode1, endPos - new Vector2(0, 120), startPos - new Vector2(0, 20))); } else { navGraphNode1.ReachableNodes.Add( new Tuple <NavGraphNode, Vector2, Vector2>(navGraphNode2, startPos - new Vector2(0, 120), endPos - new Vector2(0, 20))); navGraphNode2.ReachableNodes.Add( new Tuple <NavGraphNode, Vector2, Vector2>(navGraphNode1, endPos - new Vector2(0, 20), startPos - new Vector2(0, 20))); } } } } } } }
public CharacterPhysics(Character character, Vector3 characterPosition, Vector3 characterSize, Rectangle hitboxOffset, Scene scene) { this.character = (DefaultCharacter)character; initPosition = characterPosition; position = initPosition; this.hitboxOffset = hitboxOffset; BoundingBox = new Rectangle((int)(characterPosition.X + hitboxOffset.X), (int)(characterPosition.Y + hitboxOffset.Y), hitboxOffset.Width + 5, hitboxOffset.Height); this.scene = scene; touchedActors = new List <Actor>(); Projectiles = new List <Projectile>(); InitMovementFields(); rayCastResult = new CollidableResults(); insideClimbingArea = new CollidableResults(); //Init Particle effects for footsteps footstepEffectFactory = new FootstepParticleEffectFactory(new Vector2(BoundingBox.Left, BoundingBox.Bottom), 20, 250, Vector2.Zero, Vector2.Zero, 1f, 0.7f, Color.White, Color.White, 250); FootstepEffectManager = new ParticleEffectManager(ParticleConfig.FOOTSTEP_TEXTURE, footstepEffectFactory, 1, 128, true, BlendState.NonPremultiplied) { Paused = true }; scene.ParticleEffects.Add(FootstepEffectManager); Player currentPlayer = character.GetPosession() as Player; if (currentPlayer != null) { playerIndex = (int)currentPlayer.playerIndex; } }
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); } }
public static CollidableResults DetectSlope(Collidable ray1, Collidable ray2, Collidable collidable) { CollidableResults rayCastCollisionResult = new CollidableResults() { Intersect = false, HitPoint = Vector3.Zero, CollisionNormal = Vector3.Zero }; if (ray1.Points.Count > 2 || ray2.Points.Count > 2) { return(null); } var rayCastResult1 = ray1.CheckForCollision(collidable, Vector3.Zero); var rayCastResult2 = ray2.CheckForCollision(collidable, Vector3.Zero); bool slopeOnHitPoint1 = false; bool slopeOnHitPoint2 = false; if (rayCastResult1.Intersect) { if (Math.Abs(Vector3.Dot(rayCastResult1.CollisionNormal, Vector3.UnitX)) > 0.000001) { slopeOnHitPoint1 = true; } } if (rayCastResult2.Intersect) { if (Math.Abs(Vector3.Dot(rayCastResult2.CollisionNormal, Vector3.UnitX)) > 0.000001) { slopeOnHitPoint2 = true; } } //Conditons for slope collision: //1: Both rays hit on a slope //2: One ray hits a slope and the hit point where the slope is detected is higher than the other hitpoint bool condition1 = slopeOnHitPoint1 && slopeOnHitPoint2; bool condition2 = slopeOnHitPoint1 && rayCastResult1.HitPoint.Y > rayCastResult2.HitPoint.Y; bool condition3 = slopeOnHitPoint2 && rayCastResult2.HitPoint.Y > rayCastResult1.HitPoint.Y; if (condition1) { //Ray centerRay = new Ray(new Vector2((ray1.Points[0].X + ray2.Points[0].X)*0.5f,ray1.Points[0].Y), new Vector2((ray1.Points[1].X + ray2.Points[1].X) * 0.5f, ray1.Points[1].Y)); if (Math.Abs(rayCastResult1.CollisionNormal.X) < Math.Abs(rayCastResult2.CollisionNormal.X)) { rayCastCollisionResult = rayCastResult1; } else { rayCastCollisionResult = rayCastResult2; } //rayCastCollisionResult = rayCastResult1; } else if (condition2) { rayCastCollisionResult = rayCastResult1; } else if (condition3) { rayCastCollisionResult = rayCastResult2; } return(rayCastCollisionResult); }