/// <summary> /// This works the same as retrieve(Tangible) but retrieves all the actors in /// neighbour nodes as well. /// </summary> /// <param name="actor"></param> /// <returns></returns> public List <Tangible> retrieveNeighbors(Tangible actor) { /** RightTop = 0, LeftTop = 1, LeftBottom = 2, RightBottom = 3 */ int index = getIndex(actor); possibleCollisions.Clear(); if (index != -1 && nodes[0] != null) { int neigbour1 = index - 1; int neigbour2 = index + 1; if (neigbour1 < 0) { neigbour1 = 3; } if (neigbour2 > 3) { neigbour2 = 0; } possibleCollisions.AddRange(nodes[neigbour1].retrieve(actor)); possibleCollisions.AddRange(nodes[index].retrieve(actor)); possibleCollisions.AddRange(nodes[neigbour2].retrieve(actor)); } possibleCollisions.AddRange(tangibles); return(possibleCollisions); }
public virtual void update(GameTime gameTime, QuadTree quadTree) { /* Debugging */ currentVelocity = playerShip.velocity; accel = (currentVelocity - lastVelocity) / (float)gameTime.ElapsedGameTime.TotalSeconds; totalTime += gameTime.ElapsedGameTime.TotalSeconds; if (playerShip.collideTarget != null) { target = playerShip.collideTarget; } /* Debugging */ }
/// <summary> /// Since collisions are added as the original updater being tangible1, any overlaps are likely /// to have tangible2 being the key, therefore tangible2 is checked first in each && statement /// </summary> /// <param name="tangible1"></param> /// <param name="tangible2"></param> /// <returns>True if the collision already exists, false otherwise</returns> private bool collisionExists(Tangible tangible1, Tangible tangible2) { // If this collision already exists, dictionary has to contain one or the other as a key //if (!collisions.ContainsKey(tangible2) && !collisions.ContainsKey(tangible1)) { // return false; //} // The collision may exist as key-value pair of <tangible1, tangible2> or <tangible2, tangible1> if (!collisions.ContainsValue(tangible2, tangible1) && !collisions.ContainsValue(tangible1, tangible2)) { return(false); } return(true); }
/// <summary> /// Add a collision to be handled. /// </summary> /// <param name="tangible1"></param> /// <param name="tangible2"></param> public void addCollision(Tangible tangible1, Tangible tangible2, GameTime gameTime) { // check to see if collision already is in dictionary to prevent a double calculation of collision if (!collisionExists(tangible1, tangible2)) { collisions.Add(tangible1, tangible2); System.Console.WriteLine("new collision " + tangible1.GetType().Name + " with " + tangible2.GetType().Name + " at " + gameTime.TotalGameTime.TotalMilliseconds); } else { System.Console.WriteLine("Collision already exists at " + gameTime.TotalGameTime.TotalMilliseconds); } }
/// <summary> /// Return all objects close enough that could potentially collide with this actor. /// </summary> /// <param name="actor"></param> /// <returns></returns> public List <Tangible> retrieve(Tangible actor) { int index = getIndex(actor); possibleCollisions.Clear(); if (index != -1 && nodes[0] != null) { possibleCollisions.AddRange(nodes[index].retrieve(actor)); } possibleCollisions.AddRange(tangibles); return(possibleCollisions); }
/// <summary> /// "Bounce" an object off of another. Not great.... :/ /// @Written by Tristan with help fromXNA 4.0 Game Development by Example -Jaegers Packt(2010) /// </summary> public void reflect(Tangible tangible1, Tangible tangible2) { Vector2 combinedMassVel = // if both masses stick together (inelastic collision) than the resulting velocity is combinedMassVel (tangible1.velocity + tangible2.velocity) / 2; Vector2 normal1 = tangible2.position - tangible1.position; normal1.Normalize(); Vector2 normal2 = tangible1.position - tangible2.position; normal2.Normalize(); tangible1.velocity -= combinedMassVel; tangible1.velocity = Vector2.Reflect(tangible1.velocity, normal1); tangible1.velocity += combinedMassVel; tangible2.velocity -= combinedMassVel; tangible2.velocity = Vector2.Reflect(tangible2.velocity, normal1); tangible2.velocity += combinedMassVel; }
/// <summary> /// Determine which node the object belongs to. -1 means /// object cannot completely fit within a child node and is part /// of the parent node. /// </summary> /// <param name="pRect"></param> /// <returns></returns> private int getIndex(Tangible tangible) { /** RightTop = 0, LeftTop = 1, LeftBottom = 2, RightBottom = 3 */ int index = -1; double verticalMidpoint = bounds.X + bounds.Width / 2; double horizontalMidpoint = bounds.Y + bounds.Height / 2; // Object can completely fit within the top quadrants bool topQuadrant = (tangible.position.X < horizontalMidpoint && tangible.position.Y + tangible.height < horizontalMidpoint); // Object can completely fit within the bottom quadrants bool bottomQuadrant = (tangible.position.Y > horizontalMidpoint); // Object can completely fit within the left quadrants if (tangible.position.X < verticalMidpoint && tangible.position.X + tangible.width < verticalMidpoint) { if (topQuadrant) { index = 1; } else if (bottomQuadrant) { index = 2; } } // Object can completely fit within the right quadrants else if (tangible.position.X > verticalMidpoint) { if (topQuadrant) { index = 0; } else if (bottomQuadrant) { index = 3; } } return(index); }
/// <summary> /// An elastic collision that takes angle of contact into consideration but not mass. /// @Written by Tristan /// </summary> /// <param name="tangible1"></param> /// <param name="tangible2"></param> public void elasticCollision(Tangible tangible1, Tangible tangible2) { Vector2 v2 = tangible2.velocity; // Change frame of reference to tangible2 Vector2 u1 = tangible1.velocity - v2; Vector2 u2 = Vector2.Zero; // Find vector between center of masses Vector2 c = new Vector2(tangible2.position.X - tangible1.position.X, tangible2.position.Y - tangible1.position.Y); c.Normalize(); // Find rotation for u1 to be aligned with y-axis and rotate c by that angle float rotation = (float)Math.Acos(u1.Y / u1.Length()); Vector2 cR = Vector2.Transform(c, Matrix.CreateRotationZ(rotation)); // Find angle of deflection float deflection = (float)Math.Acos(cR.Y / cR.Length()); // Solve for u1prime and u2prime scalar values Vector2 u1prime = Vector2.Zero, u2prime = Vector2.Zero; float u1primeLength = u1.Length() * (float)Math.Sin(deflection); float u2primeLength = u1.Length() * (float)Math.Cos(deflection); // find c's parallel Vector2 p = new Vector2(-c.Y, c.X); // find u1prime & u2prime u1prime.X = u1primeLength * p.X; u1prime.Y = u1primeLength * p.Y; u2prime.X = u2primeLength * c.X; u2prime.Y = u2primeLength * c.Y; // Change back to lab frame and calculate final velocities tangible1.velocity = u1prime + v2; tangible2.velocity = u2prime + v2; }
/// <summary> /// A pixel-by-pixel collision detector. Useing texture.GetData(rawDataA) might be a little bit sketchy... /// Code from http://gamedev.stackexchange.com/questions/15191/is-there-a-good-way-to-get-pixel-perfect-collision-detection-in-xna /// Implemented by Tristan /// </summary> /// <param name="target"></param> /// <returns></returns> private bool fineCheck(Tangible target) { Color[] rawDataA = new Color[this.width * this.height]; this.texture.GetData(rawDataA); Color[] rawDataB = new Color[target.width * target.height]; target.texture.GetData(rawDataB); Rectangle thisRect = this.getHitBox().getArray(); Rectangle targetRect = target.getHitBox().getArray(); // Find the bounds of the rectangle intersection int top = Math.Max(thisRect.Top, targetRect.Top); int bottom = Math.Min(thisRect.Bottom, targetRect.Bottom); int left = Math.Max(thisRect.Left, targetRect.Left); int right = Math.Min(thisRect.Right, targetRect.Right); // for each pixel in the intersecting rectangle for (int y = top; y < bottom; ++y) { for (int x = left; x < right; ++x) { Color colorA = rawDataA[(x - thisRect.Left) + (y - thisRect.Top) * thisRect.Width]; Color colorB = rawDataB[(x - targetRect.Left) + (y - targetRect.Top) * targetRect.Width]; // if both colors are not transparent if (colorA.A != 0 && colorB.A != 0) { //GUI.hitDetected = true; return(true); } } } //GUI.hitDetected = false; return(false); }
/// <summary> /// Inserts object into quadtree. If the node exceeds capacity it will /// split and add all objects to their corresponging nodes /// </summary> /// <param name="pRect"></param> public void insert(Tangible tangible) { if (nodes[0] != null) // if child nodes exist { int index = getIndex(tangible); // into which node should this go? if (index != -1) { nodes[index].insert(tangible); return; } } tangibles.Add(tangible); if (tangibles.Count > MAX_OBJECTS && level < MAX_LEVELS) // if this node is fulll { if (nodes[0] == null) { split(); // create new child nodes if not done already } //cycle through all objects in this node for (int i = tangibles.Count - 1; i >= 0; --i) { Tangible tang = tangibles[i]; int index = getIndex(tang); if (index != -1) // into which node should this go? { nodes[index].insert(tang); // place object in proper child node tangibles.Remove(tang); // and remove from this one } } } }
public virtual void update(GameTime gameTime, MouseState mouseState, Vector2 mousePos, QuadTree quadTree) { /* Debugging */ mouseScreen = mouseState; mouseWorld = mousePos; if (playerShip.position.X <= 7999) { currentVelocity = playerShip.velocity; accel = (currentVelocity - lastVelocity) / (float)gameTime.ElapsedGameTime.TotalSeconds; totalTime += gameTime.ElapsedGameTime.TotalSeconds; } nearBy = quadTree.retrieve(playerShip); if (playerShip.collideTarget != null) { target = playerShip.collideTarget; } /* Debugging */ }
/// <summary> /// The actions to take when a collision occurs. /// @Written by Tristan /// </summary> /// <param name="target"></param> /// <param name="gameTime"></param> public abstract void collide(Tangible target, GameTime gameTime);
private void setTarget(float dist, ref float shortestDist, Tangible target) { shortestDist = dist; willCollide = true; collideTarget = target; }
/// <summary> /// An attempt at collision prediction using an array of rays /// </summary> protected void checkForCollision2(QuadTree quadTree, GameTime gameTime) { willCollide = false; collideTarget = null; if (moveThisUpdate.Length() == 0) { return; } Vector2 move = new Vector2(); Vector2.Normalize(ref moveThisUpdate, out move); List <Tangible> possibleCollisions = quadTree.retrieve(this); Ray2 ray0 = new Ray2(hitBox.topLeft, move); Ray2 ray1 = new Ray2(hitBox.bottomLeft, move); Ray2 ray2 = new Ray2(hitBox.bottomRight, move); Ray2 ray3 = new Ray2(hitBox.topRight, move); Ray2 ray4 = new Ray2(hitBox.position, move); float dist0, dist1, dist2, dist3, dist4; float velLength = move.Length(); float shortestDist = velLength; foreach (Tangible target in possibleCollisions) { if (target == this) { continue; } bool r0, r1, r2, r3, r4; //note: it is necessary that each of these functions run r0 = ray0.intersectsToRange(target.getHitBox()); r1 = ray1.intersectsToRange(target.getHitBox()); r2 = ray2.intersectsToRange(target.getHitBox()); r3 = ray3.intersectsToRange(target.getHitBox()); r4 = ray4.intersectsToRange(target.getHitBox()); if (!r0 && !r1 && !r2 && !r3 && !r4) // If no rays intersect { continue; } dist0 = ray0.getDistance(); dist1 = ray1.getDistance(); dist2 = ray2.getDistance(); dist3 = ray3.getDistance(); dist4 = ray4.getDistance(); //possible.Clear(); if (0 <= dist0 && dist0 <= shortestDist) { setTarget(dist0, ref shortestDist, target); } //possible.Add(dist0); if (0 <= dist1 && dist1 <= shortestDist) { setTarget(dist1, ref shortestDist, target); } //possible.Add(dist1); if (0 <= dist2 && dist2 <= shortestDist) { setTarget(dist2, ref shortestDist, target); } //possible.Add(dist2); if (0 <= dist3 && dist3 <= shortestDist) { setTarget(dist3, ref shortestDist, target); } //possible.Add(dist3); if (0 <= dist4 && dist4 <= shortestDist) { setTarget(dist4, ref shortestDist, target); } //possible.Add(dist4); //float temp = possible.Min(); //if (temp < shortestDist) { // shortestDist = temp; // willCollide = true; // collideTarget = target; //} } if (willCollide) { Vector2.Normalize(ref velocity, out moveThisUpdate); moveThisUpdate = moveThisUpdate * shortestDist; } }