/// <summary> /// Decides on the collision detection method for this and the given object /// </summary>) /// <param name="obj">object we are testing</param> public bool HandleCollisions(GameObject obj) { if (!obj.IsSquare ^ !mIsSquare) return HandleCollideCircleAndBox(obj) == 1; else if (obj.IsSquare & mIsSquare) return HandleCollideBoxAndBox(obj) == 1; else return HandleCollideCircleAndCircle(obj) == 1; }
/// <summary> /// Returns true if the physics objects are colliding with each other /// (only good for 2 boxes) /// </summary> /// <param name="otherObject">The other object to test against</param> /// <returns>True if they are colliding with each other; False otherwise</returns> public virtual bool IsCollidingBoxAndBox(GameObject otherObject) { // if player has not collided with a hazard deeper than HAZARDFORGIVENESS pixels, do not handle the collision if (((this is Player) && (otherObject.CollisionType == XmlKeys.HAZARDOUS)) || ((this.CollisionType == XmlKeys.HAZARDOUS) && (otherObject is Player))) { # region Find depth //Find Center float halfHeight1 = this.BoundingBox.Height / 2; float halfWidth1 = this.BoundingBox.Width / 2; //Calculate Center Position Vector2 center1 = new Vector2(this.BoundingBox.Left + halfWidth1, this.BoundingBox.Top + halfHeight1); //Find Center of otherObject float halfHeight2 = otherObject.BoundingBox.Height / 2; float halfWidth2 = otherObject.BoundingBox.Width / 2; //Calculate Center Position Vector2 center2 = new Vector2(otherObject.BoundingBox.Left + halfWidth2, otherObject.BoundingBox.Top + halfHeight2); //Center distances between both objects float distX = center1.X - center2.X; float distY = center1.Y - center2.Y; //Minimum distance float minDistX = halfWidth1 + halfWidth2; float minDistY = halfHeight1 + halfHeight2; float depthX, depthY; if (distX > 0) { depthX = minDistX - distX; } else { depthX = -minDistX - distX; } if (distY > 0) { depthY = minDistY - distY; } else { depthY = -minDistY - distY; } depthX = Math.Abs(depthX); depthY = Math.Abs(depthY); #endregion float shallow = Math.Min(depthX, depthY); if (shallow < HAZARDFORGIVENESS) return false; }
/// <summary> /// Handles collision for circle and Box (circle =this) /// </summary> /// <param name="otherObject">object to do collision on(box)</param> /// <returns>1 if collided; 0 if no collision</returns> public virtual int HandleCollideCircleAndBox(GameObject otherObject) { if (!IsCollidingBoxAndBox(otherObject)) { return 0;// no collision } //Player collided with collectable if (otherObject.CollisionType == XmlKeys.COLLECTABLE || this.CollisionType == XmlKeys.COLLECTABLE && !(otherObject is StaticObject)) return 1; // get points of square Point[] p = new Point[4]; // top left p[0] = new Point(otherObject.mBoundingBox.X,otherObject.mBoundingBox.Y); // top right p[1] = new Point(otherObject.mBoundingBox.X + otherObject.mBoundingBox.Width, p[0].Y); // bottom right p[2] = new Point(p[1].X, otherObject.mBoundingBox.Y + otherObject.mBoundingBox.Height); // bottom left p[3] = new Point(p[0].X, p[2].Y); Point center = this.BoundingBox.Center; // if not going to collide with a corner if (((center.X >= p[0].X) && (center.X <= p[1].X)) // top/bottom side || ((center.Y >= p[1].Y) && (center.Y <= p[2].Y)))// right/left side { // then treat like a square /square return HandleCollideBoxAndBox(otherObject); } else // going to hit a corner { // treat like circle/point collision Point centerA = this.mBoundingBox.Center; Point centerB = new Point(); if ((center.X < p[0].X) && (center.Y < p[0].Y))// top left corner { centerB = p[0]; } else if ((center.X > p[1].X) && (center.Y < p[1].Y))// top right corner { centerB = p[1]; } else if ((center.X > p[2].X) && (center.Y > p[2].Y))// bottom right corner { centerB = p[2]; } else if ((center.X < p[3].X) && (center.Y > p[3].Y))// bottom left corner { centerB = p[3]; } Vector2 centerDiff = new Vector2((float)(centerA.X - centerB.X), (float)(centerA.Y - centerB.Y)); float radiusA = this.mBoundingBox.Width / 2; float delta = (radiusA) - centerDiff.Length(); centerDiff.Normalize(); Vector2 add = Vector2.Multiply(centerDiff, delta); //Do not change velocity because we want an inelastic collision on corners. #region Corner magic // the best I could do for the constraints i am given. // Screw you people. // Love, // Curtis Taylor if ((Environment.GravityDirection == GravityDirections.Down) || (Environment.GravityDirection == GravityDirections.Up)) { mVelocity.X = 0f; mVelocity.Y *= .95f; } else { mVelocity.X *= .95f; mVelocity.Y = 0f; } #endregion // place the Y pos just so it is not colliding. if (!mIsRail) mPosition += add; else if (mOriginalInfo.mProperties[XmlKeys.RAIL] == XmlKeys.RAIL_X) mPosition.X += add.X; else mPosition.Y += add.Y; UpdateBoundingBoxes(); if (add.Length() > 0.5f) { return 1; // changed enough to call a collision } } return 0; // did not collide }
/// <summary> /// Handles collision for circle and circle /// </summary> /// <param name="otherObject">object to do collision on(circle)</param> /// <returns>1 if collided; 0 if no collision</returns> public virtual int HandleCollideCircleAndCircle(GameObject otherObject) { if (!IsCollidingCircleandCircle(otherObject)) { return 0; } //Player collided with collectable if (otherObject.CollisionType == XmlKeys.COLLECTABLE || this.CollisionType == XmlKeys.COLLECTABLE && !(otherObject is StaticObject)) return 1; Point centerA = this.mBoundingBox.Center; Point centerB = otherObject.BoundingBox.Center; Vector2 colDepth = GetCollitionDepth(otherObject); Vector2 centerDiff = new Vector2((float)(centerA.X - centerB.X), (float)(centerA.Y - centerB.Y)); float radiusA = this.mBoundingBox.Width / 2; float radiusB = otherObject.mBoundingBox.Width / 2; float delta = (radiusA + radiusB) - centerDiff.Length(); centerDiff.Normalize(); Vector2 add = Vector2.Multiply(centerDiff, delta); HandleVelocitiesAfterCollision(otherObject, centerDiff); // place it just so it is not colliding. if (otherObject is PhysicsObject) { if (!mIsRail) mPosition += add / 2; else if (mOriginalInfo.mProperties[XmlKeys.RAIL] == XmlKeys.RAIL_X) mPosition.X += add.X / 2; else mPosition.Y += add.Y / 2; if(!((PhysicsObject)otherObject).IsRail) ((PhysicsObject)otherObject).mPosition -= add / 2; else if (otherObject.OriginalInfo.mProperties[XmlKeys.RAIL] == XmlKeys.RAIL_X) ((PhysicsObject)otherObject).mPosition.X -= add.X / 2; else ((PhysicsObject)otherObject).mPosition.Y -= add.Y / 2; } else { // do not move a static object if(!mIsRail) mPosition += add; else if (mOriginalInfo.mProperties[XmlKeys.RAIL] == XmlKeys.RAIL_X) mPosition.X += add.X; else mPosition.Y += add.Y; } UpdateBoundingBoxes(); if (add.Length() > 1.0f) { return 1; // changed enough to call a collision } return 0; }
/// <summary> /// finds how deep they are intersecting (That is what she said!) /// </summary> /// <returns>vector decribing depth</returns> public Vector2 GetCollitionDepth(GameObject otherObject) { //Find Center float halfHeight1 = this.BoundingBox.Height / 2; float halfWidth1 = this.BoundingBox.Width / 2; //Calculate Center Position Vector2 center1 = new Vector2(this.BoundingBox.Left + halfWidth1, this.BoundingBox.Top + halfHeight1); //Find Center of otherObject float halfHeight2 = otherObject.BoundingBox.Height / 2; float halfWidth2 = otherObject.BoundingBox.Width / 2; //Calculate Center Position Vector2 center2 = new Vector2(otherObject.BoundingBox.Left + halfWidth2, otherObject.BoundingBox.Top + halfHeight2); //Center distances between both objects float distX = center1.X - center2.X; float distY = center1.Y - center2.Y; //Minimum distance float minDistX = halfWidth1 + halfWidth2; float minDistY = halfHeight1 + halfHeight2; if (!IsCollidingBoxAndBox(otherObject)) { return Vector2.Zero; } float depthX, depthY; if (distX > 0) { depthX = minDistX - distX; } else { depthX = -minDistX - distX; } if (distY > 0) { depthY = minDistY - distY; } else { depthY = -minDistY - distY; } return new Vector2(depthX, depthY); }
/// <summary> /// Handles collision for two boxes (this, and other) /// </summary> /// <param name="otherObject">object to do collision on(box)</param> /// <returns>1 if collided; 0 if no collision</returns> public virtual int HandleCollideBoxAndBox(GameObject otherObject) { if (!IsCollidingBoxAndBox(otherObject)) { return 0; } //Player collided with collectable if (otherObject.CollisionType == XmlKeys.COLLECTABLE || this.CollisionType == XmlKeys.COLLECTABLE && !(otherObject is StaticObject)) return 1; Vector2 colDepth = GetCollitionDepth(otherObject); // handle the shallowest collision if (Math.Abs(colDepth.X) > Math.Abs(colDepth.Y))// colliding top or bottom { //Reset Y Velocity to 0 mVelocity.Y = 0; if (!mIsRail || (mIsRail && !(mOriginalInfo.mProperties[XmlKeys.RAIL] == XmlKeys.RAIL_Y))) // reduce x velocity for friction mVelocity.X *= otherObject.mFriction; // place the Y pos just so it is not colliding. mPosition.Y += colDepth.Y; } else// colliding left or right { //Reset X Velocity to 0 mVelocity.X = 0; if (!mIsRail || (mIsRail && !(mOriginalInfo.mProperties[XmlKeys.RAIL] == XmlKeys.RAIL_X))) // reduce Y velocity for friction mVelocity.Y *= otherObject.mFriction; // place the X pos just so it is not colliding. mPosition.X += colDepth.X; } UpdateBoundingBoxes(); return 1;// handled collision }
/// <summary> /// Checks to see if given object is colliding with any other object and handles the collision /// </summary> /// <param name="physObj">object to see if anything is colliding with it</param> private void HandleCollisions(PhysicsObject physObj, ref GameStates gameState) { // keep track of all object colliding with physObj List<GameObject> collidingList = new List<GameObject>(); Vector2 gridPos = GridSpace.GetGridCoord(physObj.mPosition); //Goes through the 9 possible positions for collision to see if this physics object is colliding with anything for (int i = -1; i < 2; i++) { if (gridPos.Y + i < 0 || gridPos.Y + i >= mCollisionMatrix.Length) continue;//Bounds check for (int j = -1; j < 2; j++) { if (gridPos.X + j < 0 || gridPos.X + j >= mCollisionMatrix[(int)gridPos.Y + i].Length) continue;//Bounds check foreach (GameObject obj in mCollisionMatrix[(int)gridPos.Y+i][(int)gridPos.X+j]) { bool collided = false; if (physObj.IsSquare && obj.IsSquare)// both squares { collided = physObj.IsCollidingBoxAndBox(obj); } else if (!physObj.IsSquare && obj.IsSquare) // phys obj is circle { collided = physObj.IsCollidingCircleAndBox(obj); } else if (physObj.IsSquare && !obj.IsSquare) //obj is circle { collided = physObj.IsCollidingBoxAndCircle(obj); } else // both circles { collided = physObj.IsCollidingCircleandCircle(obj); } if (obj.Equals(physObj) || obj is PlayerEnd && !(physObj is Player)) continue; if (collided && !(obj is PlayerEnd)) { collidingList.Add(obj); } //If player reaches the end, set the timer to 0 if (collided && obj is PlayerEnd && physObj is Player) { mPlayer.mCurrentTexture = PlayerFaces.FromString("Laugh"); mPlayerEnd.mCurrentTexture = PlayerFaces.FromString("GirlLaugh3"); GameSound.StopOthersAndPlay(GameSound.level_stageVictory); mPhysicsEnvironment.GravityDirection = GravityDirections.Down; gameState = GameStates.Unlock; } //If player collided with a collectable object if (collided && ((physObj is Player) && obj.CollisionType == XmlKeys.COLLECTABLE || (obj is Player) && physObj.CollisionType == XmlKeys.COLLECTABLE)) { if (physObj.CollisionType == XmlKeys.COLLECTABLE) { mCollected.Add(physObj); mRemoveCollected.Add(physObj); mCollectableLocations.Remove(physObj.mPosition); } else if (obj.CollisionType == XmlKeys.COLLECTABLE) { mCollected.Add(obj); mRemoveCollected.Add(obj); mCollectableLocations.Remove(obj.mPosition); } GameSound.playerCol_collectable.Play(GameSound.volume * 0.8f, 0f, 0f); collectibleEngine.EmitterLocation = new Vector2(obj.mPosition.X + 32, obj.mPosition.Y + 32); collectibleEngine.Update(10); } //If player hits a hazard else if (collided && ((physObj is Player) && obj.CollisionType == XmlKeys.HAZARDOUS || (obj is Player) && physObj.CollisionType == XmlKeys.HAZARDOUS)) { // Particle Effects (don't work). //Vector2 one = new Vector2(obj.mPosition.X + 32, obj.mPosition.Y + 32); //Vector2 two = new Vector2(physObj.mPosition.X + 32, physObj.mPosition.Y + 32); //Vector2 midpoint = new Vector2((one.X + two.X) / 2, (one.Y + two.Y) / 2); //wallEngine.EmitterLocation = midpoint; //wallEngine.Update(10); GameSound.playerSound_death.Play(GameSound.volume * 0.8f, 0.0f, 0.0f); if (physObj is Player) { physObj.Kill(); mPlayerEnd.mCurrentTexture = PlayerFaces.FromString("GirlSad"); } else { ((Player)obj).Kill(); mPlayerEnd.mCurrentTexture = PlayerFaces.FromString("GirlSad"); } //Get difference of two positions mDeathPanLength = Vector3.Subtract(new Vector3(mPlayer.SpawnPoint.X - 275, mPlayer.SpawnPoint.Y - 100, 0), mCam.Position); //Divide by scaling factor to get camera pan at each update. mDeathPanLength = Vector3.Divide(mDeathPanLength, SCALING_FACTOR); //Set the update counter to zero mDeathPanUpdates = 0; gameState = GameStates.Death; mDeathState = DeathStates.Respawning; mHasRespawned = false; return; } } //Start any animations on walls we are touching if (physObj is Player) foreach (GameObject cObject in collidingList) { if (cObject is Wall) { KeyValuePair<Vector2, string> animation = ((Wall)cObject).NearestWallPosition(physObj.mPosition); if (!mActiveAnimations.ContainsKey(animation.Key)) mActiveAnimations.Add(animation.Key, GetAnimation(animation.Value)); // Particle Effects. //if (cObject != lastCollided[0] && cObject != lastCollided[1]) if (cObject != lastCollided) { Vector2 one = new Vector2(mPlayer.Position.X + 32, mPlayer.Position.Y + 32); Vector2 two = new Vector2(animation.Key.X + 32, animation.Key.Y + 32); Vector2 midpoint = new Vector2((one.X + two.X) / 2, (one.Y + two.Y) / 2); wallEngine.EmitterLocation = midpoint; wallEngine.Update(10); // play wall collision sound GameSound.playerCol_wall.Play(GameSound.volume * 0.8f, 0f, 0f); //lastCollided[1] = lastCollided[0]; //lastCollided[0] = cObject; lastCollided = cObject; } } else if (cObject is MovingTile && !((MovingTile)cObject).BeingAnimated && cObject.CollisionType != XmlKeys.HAZARDOUS) ((MovingTile)cObject).StartAnimation(GetAnimation(cObject.mName)); else if (cObject is ReverseTile && !((ReverseTile)cObject).BeingAnimated && cObject.CollisionType != XmlKeys.HAZARDOUS) ((ReverseTile)cObject).StartAnimation(GetAnimation(cObject.mName)); else if (cObject is StaticObject && cObject.CollisionType != XmlKeys.COLLECTABLE) { if (!mActiveAnimations.ContainsKey(cObject.mPosition)) mActiveAnimations.Add(cObject.mPosition, GetAnimation(cObject.mName)); // Particle Effects. //if (cObject != lastCollided[0] && cObject != lastCollided[1]) if (cObject != lastCollided) { Vector2 one = new Vector2(mPlayer.Position.X + 32, mPlayer.Position.Y + 32); Vector2 two = new Vector2(cObject.mPosition.X + 32, cObject.mPosition.Y + 32); Vector2 midpoint = new Vector2((one.X + two.X) / 2, (one.Y + two.Y) / 2); wallEngine.EmitterLocation = midpoint; wallEngine.Update(10); // play wall collision sound GameSound.playerCol_wall.Play(GameSound.volume * 0.8f, 0f, 0f); //lastCollided[1] = lastCollided[0]; //lastCollided[0] = cObject; lastCollided = cObject; } } } physObj.HandleCollisionList(collidingList); } } }
/// <summary> /// Loads the level from the content manager /// </summary> /// <param name="content">Content Manager to load from</param> public void Load(ContentManager content, string assetName) { mKootenay = content.Load<SpriteFont>("fonts/Kootenay"); mQuartz = content.Load<SpriteFont>("fonts/QuartzLarge"); // mDirections = new Texture2D[4]; // mDirections[3] = content.Load<Texture2D>("HUD/arrow_left"); // mDirections[2] = content.Load<Texture2D>("HUD/arrow_down"); // mDirections[1] = content.Load<Texture2D>("HUD/arrow_right"); // mDirections[0] = content.Load<Texture2D>("HUD/arrow_up"); mRailLeft = content.Load<Texture2D>("Images/NonHazards/Rails/RailLeft"); mRailHor = content.Load<Texture2D>("Images/NonHazards/Rails/RailHorizontal"); mRailRight = content.Load<Texture2D>("Images/NonHazards/Rails/RailRight"); mRailTop = content.Load<Texture2D>("Images/NonHazards/Rails/RailTop"); mRailBottom = content.Load<Texture2D>("Images/NonHazards/Rails/RailBottom"); mRailVert = content.Load<Texture2D>("Images/NonHazards/Rails/RailVertical"); mContent = content; mNumCollected = 0; mNumCollectable = 0; // Particle Engine List<Texture2D> textures = new List<Texture2D>(); textures.Add(content.Load<Texture2D>("Images/Particles/diamond")); textures.Add(content.Load<Texture2D>("Images/Particles/star")); collectibleEngine = new ParticleEngine(textures, new Vector2(400, 240), 20); collectibleEngine.colorScheme = "Yellow"; textures = new List<Texture2D>(); textures.Add(content.Load<Texture2D>("Images/Particles/line")); textures.Add(content.Load<Texture2D>("Images/Particles/square")); wallEngine = new ParticleEngine(textures, new Vector2(400, 240), 20); wallEngine.colorScheme = "Blue"; backGroundParticleCount = 500; backgroundParticles = new Particle[backGroundParticleCount]; Random random = new Random(); for (int i = 0; i < backGroundParticleCount; i++) { Vector2 pos = new Vector2(random.Next(-mScreenRect.Width / 2,3 * mScreenRect.Width / 2), random.Next(-mScreenRect.Height / 2,3* mScreenRect.Height / 2)); backgroundParticles[i] = new Particle(content.Load<Texture2D>("Images/Particles/diamond"), pos, random); } //lastCollided = new GameObject[2]; //lastCollided[0] = lastCollided[1] = null; lastCollided = null; mCollectableLocations = new List<Vector2>(); }
/// <summary> /// Updates the collision matrix with the new position /// </summary> /// <param name="obj">Object we want to update</param> /// <param name="oldPosition">Position where object was before</param> private void UpdateCollisionMatrix(GameObject obj, Vector2 oldPosition) { Vector2 newPosition = GridSpace.GetGridCoord(obj.mPosition); if (oldPosition.Equals(newPosition)) return; mCollisionMatrix[(int)oldPosition.Y][(int)oldPosition.X].Remove(obj); if(!mCollisionMatrix[(int)newPosition.Y][(int)newPosition.X].Contains(obj)) mCollisionMatrix[(int)newPosition.Y][(int)newPosition.X].Add(obj); }
/// <summary> /// Removes the object from the matrix(for collectables /// </summary> /// <param name="obj">Object we want to remove</param> private void RemoveFromMatrix(GameObject obj) { Vector2 position = GridSpace.GetGridCoord(obj.mPosition); mCollisionMatrix[(int)position.Y][(int)position.X].Remove(obj); }
/// <summary> /// Loads the level from the content manager /// </summary> /// <param name="content">Content Manager to load from</param> public void Load(ContentManager content, string assetName) { mKootenay = content.Load<SpriteFont>("fonts/Kootenay"); mQuartz = content.Load<SpriteFont>("fonts/QuartzLarge"); // mDirections = new Texture2D[4]; // mDirections[3] = content.Load<Texture2D>("HUD/arrow_left"); // mDirections[2] = content.Load<Texture2D>("HUD/arrow_down"); // mDirections[1] = content.Load<Texture2D>("HUD/arrow_right"); // mDirections[0] = content.Load<Texture2D>("HUD/arrow_up"); mRailLeft = content.Load<Texture2D>("Images/NonHazards/Rails/RailLeft"); mRailHor = content.Load<Texture2D>("Images/NonHazards/Rails/RailHorizontal"); mRailRight = content.Load<Texture2D>("Images/NonHazards/Rails/RailRight"); mRailTop = content.Load<Texture2D>("Images/NonHazards/Rails/RailTop"); mRailBottom = content.Load<Texture2D>("Images/NonHazards/Rails/RailBottom"); mRailVert = content.Load<Texture2D>("Images/NonHazards/Rails/RailVertical"); mContent = content; mNumCollected = 0; mNumCollectable = 0; // Particle Engine List<Texture2D> textures = new List<Texture2D>(); textures.Add(content.Load<Texture2D>("Images/Particles/diamond")); textures.Add(content.Load<Texture2D>("Images/Particles/star")); collectibleEngine = new ParticleEngine(textures, new Vector2(400, 240), 20); collectibleEngine.colorScheme = "Yellow"; textures = new List<Texture2D>(); textures.Add(content.Load<Texture2D>("Images/Particles/line")); textures.Add(content.Load<Texture2D>("Images/Particles/square")); wallEngine = new ParticleEngine(textures, new Vector2(400, 240), 20); wallEngine.colorScheme = "Blue"; //lastCollided = new GameObject[2]; //lastCollided[0] = lastCollided[1] = null; lastCollided = null; mCollectableLocations = new List<Vector2>(); }