/// <summary> /// Applies collision detection and response between an object and the tiles of the tilemap. /// Uses an object's position to efficiently find neighboring tiles to check for collision in the tiles two-dimensional array. /// </summary> /// <param name="gameObject">The object to check for collisions.</param> /// <param name="callback">The delegate method that will be invoked if a collision occurs.</param> /// <returns>True is a collision occurs, false if not.</returns> public bool CollideObject(GenObject gameObject, CollideEvent callback) { GetTileBounds( gameObject.MoveBounds.Left, gameObject.MoveBounds.Right, gameObject.MoveBounds.Top, gameObject.MoveBounds.Bottom); bool collided = false; for (int y = _tileBounds[2]; y <= _tileBounds[3]; y++) { for (int x = _tileBounds[0]; x <= _tileBounds[1]; x++) { // If the tile is empty, do not check for a collision. if (Tiles[y][x] != null) { if (GenCollide.Collide(gameObject, Tiles[y][x], callback, Tiles[y][x].OpenEdges)) collided = true; } } } return collided; }
public override void Update() { base.Update(); // Do collision checking first. //GenG.Collide(Player, Text); // Cache the HitCave delegate to avoid creating garbage for each collision check. if (HitCaveCache == null) { HitCaveCache = HitCave; } GenG.Collide(Cave, Player, HitCaveCache); GenG.Collide(Cave, Boxes); //GenG.Collide(Chain, Player); //GenG.Collide(Warthog4, Warthog5); //GenG.Collide(Cave, Chain); //GenG.Collide(Cave, Warthog3); //GenG.Collide(Player, Warthog3); //GenG.Collide(Player, Warthog4); GenG.Collide(Cave, Emitter); //GenG.Collide(Emitter, Emitter); GenG.Collide(Player, Boxes); GenG.Collide(Boxes, Boxes); //GenG.Collide(Boxes, Emitter); //Text.Y = GenU.SineWave(200, 2, 10); //Text.Rotation = GenU.SineWave(0, 3, 10); if (GenG.GamePads[(int)PlayerIndex.One].JustPressed(Buttons.X)) GenG.IsDebug = !GenG.IsDebug; #if WINDOWS if (GenG.Keyboards[(int)PlayerIndex.One].JustPressed(Keys.R)) GenG.ResetState(new LoadingState()); if (GenG.Keyboards[(int)PlayerIndex.One].IsPressed(Keys.Z)) GenG.TimeScale = 0.2f; else if (GenG.Keyboards[(int)PlayerIndex.One].IsPressed(Keys.X)) GenG.TimeScale = 2f; else GenG.TimeScale = 1f; if (GenG.Keyboards[(int)PlayerIndex.One].JustPressed(Keys.A)) GenG.Paused = !GenG.Paused; if (GenG.Keyboards[(int)PlayerIndex.One].JustPressed(Keys.Escape)) GenG.Game.Exit(); #endif if (GenG.GamePads[(int)PlayerIndex.One].JustPressed(Buttons.Y)) GenG.ResetState(new LoadingState()); /*if (GenG.GamePads[(int)PlayerIndex.One].IsPressed(Buttons.LeftTrigger)) GenG.TimeScale = 0.2f; else if (GenG.GamePads[(int)PlayerIndex.One].IsPressed(Buttons.RightTrigger)) GenG.TimeScale = 2f; else GenG.TimeScale = 1f;*/ if (GenG.GamePads[(int)PlayerIndex.One].JustPressed(Buttons.LeftTrigger)) Camera.Flash(0.5f, 0.1f, Color.CornflowerBlue); if (GenG.GamePads[(int)PlayerIndex.One].JustPressed(Buttons.Start)) GenG.Paused = !GenG.Paused; if (GenG.GamePads[(int)PlayerIndex.One].JustPressed(Buttons.Back)) GenG.Game.Exit(); //((GenObject)Chain.Members[0]).X = Player.X; //((GenObject)Chain.Members[0]).Y = Player.Y; if (!GenG.Paused) { //GenMove.AccelerateToPoint(Boxes, Player.CenterPosition, 200); GenMove.AccelerateToPoint(Emitter, Player.CenterPosition, 500, 100); //GenMove.AccelerateToPoint(Cloth, Player.CenterPosition, Player.Velocity.Length() * 3, 70); } Warthog4.Velocity.X = GenU.SineWave(0, 2, 200); //Warthog4.Color = new Color(1, GenU.SineWave(0.5f, 10, 0.5f), GenU.CosineWave(0.5f, 10, 0.5f)); //Warthog3.Rotation = GenMove.VectortoAngle(Warthog3.CenterPosition, Player.CenterPosition); //GenMove.AccelerateAtAngle(Warthog3, Warthog3.Rotation, 500); if (Warthog5.Parent != null) Warthog5.Rotation = Warthog5.Parent.Rotation; //Chain.LineColor = GenU.RandomColor(); //Player.Rotation++; if (Player.Velocity.Y < 0) { Player.Scale.X = MathHelper.Clamp(Math.Abs((Player.Velocity.Y) / Player.MaxVelocity.Y) + 1, 1f, 1.2f); Player.Scale.Y = 1 - (Player.Scale.X - 1); } else if (Player.Velocity.Y > 0) { Player.Scale.X = MathHelper.Clamp(1 - Math.Abs((Player.Velocity.Y) / Player.MaxVelocity.Y), 0.8f, 1f); Player.Scale.Y = 1 + (1 - Player.Scale.X); } else { Player.Scale.X = GenU.CosineWave(1, 8, 0.1f); Player.Scale.Y = GenU.SineWave(1, 8, 0.1f); } ProgressBar.Scale.Y = GenU.SineWave(1, 8, 0.2f); ProgressBar.Value = GenU.SineWave(50, 2, 51); //Cloth.LineColor = new Color(0.5f, (float)MathHelper.Lerp(0, 1, GenU.SineWave(0.5f, 10f, 0.5f)), 0.5f); //Player.Alpha = GenU.SineWave(0.5f, 2f, 0.5f); /*if (Player.IsTouching(GenObject.Direction.Down)) Player.Rotation = 0; else if (Player.IsTouching(GenObject.Direction.Right)) Player.Rotation = 270; else if (Player.IsTouching(GenObject.Direction.Left)) Player.Rotation = 90; else if (Player.IsTouching(GenObject.Direction.Up)) Player.Rotation = 180; else if (!Player.IsTouching(GenObject.Direction.Any)) Player.RotationSpeed = Player.Velocity.X * 4;*/ //if (GenG.Keyboards[(int)PlayerIndex.One].JustPressed(Keys.Space)) // Cave.AddTile((int)(Player.OriginPosition.X / Cave.TileWidth), (int)(Player.OriginPosition.Y / Cave.TileHeight), false); if (Warthog5.X < -2000) { Warthog5.X = 5000; } }
/// <summary> /// Applies collision detection and response between an object or group of objects and the tiles of the tilemap. /// Uses an object's position to efficiently find neighboring tiles to check for collision in the tiles two-dimensional array. /// </summary> /// <param name="objectOrGroup">The object or group to check for collisions.</param> /// <param name="callback">The delegate method that will be invoked if a collision occurs.</param> /// <returns>True is a collision occurs, false if not.</returns> public bool Collide(GenBasic objectOrGroup, CollideEvent callback) { if (objectOrGroup is GenObject) return CollideObject(objectOrGroup as GenObject, callback); if (objectOrGroup is GenGroup) { bool collided = false; foreach (GenBasic basic in (objectOrGroup as GenGroup).Members) { if (CollideObject(basic as GenObject, callback)) collided = true; } return collided; } return false; }
/// <summary> /// Checks for overlaps or collisions against each object within this node. /// </summary> /// <param name="gameObject">The game object to check for overlaps or collisions.</param> /// <param name="callback">The delegate method that will be invoked if an overlap occurs.</param> /// <param name="separate">A flag used to determine if objects should collide with each other.</param> /// <param name="collidableEdges">A bit field of flags determining which edges of the quadtree objects are collidable.</param> /// <returns>True if an overlap occurs, false if not.</returns> public bool OverlapObjects(GenObject gameObject, CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { bool overlap = false; foreach (GenObject nodeObject in _objects) { if (separate) { if (GenCollide.Collide(gameObject, nodeObject, callback, collidableEdges)) overlap = true; } else { if (GenCollide.Overlap(gameObject, nodeObject, callback)) overlap = true; } } return overlap; }
/// <summary> /// Checks for overlaps or collisions between the given game object and objects within the node and its leaf nodes. /// A broad phase check is done by only checking for overlaps or collisions against objects within the leaf nodes that intersect with the given game object. /// </summary> /// <param name="gameObject">The game object to check for overlaps or collisions.</param> /// <param name="callback">The delegate method that will be invoked if an overlap occurs.</param> /// <param name="separate">A flag used to determine if objects should collide with each other.</param> /// <param name="collidableEdges">A bit field of flags determining which edges of the quadtree objects are collidable.</param> /// <returns>True if an overlap occurs, false if not.</returns> internal bool Overlap(GenObject gameObject, CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { bool overlap = false; if (_nodes[0] != null) { // If the game object's movement bounds intersects with any leaf node, do overlap and collision checks against any objects in that node. if (gameObject.MoveBounds.Intersects(this)) { if (gameObject.MoveBounds.Top <= _midpointY) { if (gameObject.MoveBounds.Left <= _midpointX) // Top-left leaf node. { if (_nodes[0].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } if (gameObject.MoveBounds.Right >= _midpointX) // Top-right leaf node. { if (_nodes[1].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } } if (gameObject.MoveBounds.Bottom >= _midpointY) { if (gameObject.MoveBounds.Left <= _midpointX) // Bottom-left leaf node. { if (_nodes[2].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } if (gameObject.MoveBounds.Right >= _midpointX) // Bottom-right leaf node. { if (_nodes[3].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } } } } // Check for overlaps and collisions against this node's objects. if (OverlapObjects(gameObject, callback, separate, collidableEdges)) overlap = true; return overlap; }
public bool OverlapSelf(CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { bool overlap = false; foreach (GenObject gameObject in _activeObjects) { GenQuadtreeNode parentNode = _objectLocations[gameObject]._parentNode; // Iterate up the parent nodes, and do overlap and collision checks against their objects. while (parentNode != null) { if (parentNode.OverlapObjects(gameObject, callback, separate, collidableEdges)) overlap = true; parentNode = parentNode._parentNode; } // Do overlap and collision checks against objects within its containing node and any remaining leaf nodes. if (_objectLocations[gameObject].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } return overlap; }
/// <summary> /// Checks for overlaps or collisions between the given game object and objects within the quadtree. /// A broad phase check is done by only checking for overlaps or collisions against objects within the nodes that intersect with the given game object. /// </summary> /// <param name="gameObject">The game object to check for overlaps or collisions.</param> /// <param name="callback">The delegate method that will be invoked if an overlap occurs.</param> /// <param name="separate">A flag used to determine if objects should collide with each other.</param> /// <param name="collidableEdges">A bit field of flags determining which edges of the quadtree objects are collidable.</param> /// <returns>True if an overlap occurs, false if not.</returns> public bool Overlap(GenObject gameObject, CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { return _rootNode.Overlap(gameObject, callback, separate, collidableEdges); }
public virtual void OnCollide(T col) { CollideEvent?.Invoke(col); }
/// <summary> /// Applies collision detection and response against two objects that may overlap. /// </summary> /// <param name="objectA">The first object to check for a collision.</param> /// <param name="objectB">The second object to check for a collision.</param> /// <param name="callback">The delegate method that will be invoked if a collision occurs.</param> /// <param name="collidableEdges">A bit field of flags determining which edges of the second object are collidable.</param> /// <returns>True if a collision occurs, false if not.</returns> public static bool Collide(GenObject objectA, GenObject objectB, CollideEvent callback, GenObject.Direction collidableEdges = GenObject.Direction.Any) { // Do not check for collisions if the objects are the same. if (objectA == objectB) return false; // Do not check for collisions if either object is not solid, or if both objects are immovable. if ((!objectA.Solid || !objectB.Solid) || (objectA.Immovable && objectB.Immovable)) return false; // If the movement bounding boxes of each object are overlapping, check for a collision. if (Overlap(objectA, objectB, callback)) { // Get the normal direction of the collision from bounding box A to B along the least penetrating axis. // Return no collision if the surface normal is projected towards a non-collidable edge of bounding box B. _distance = objectA.Bounds.GetDistanceAABB(objectB.Bounds); // The greatest absolute distance value between the two bounding boxes is along the axis of least penetration. if (_distance.X > _distance.Y) { if (objectA.Bounds.MidpointX > objectB.Bounds.MidpointX) { // The collision normal is pointing from the left edge of bounding box A. _collisionNormal.X = -1f; _collisionNormal.Y = 0f; if ((collidableEdges & GenObject.Direction.Right) == 0) return false; } else { // The collision normal is pointing from the right edge of bounding box A. _collisionNormal.X = 1f; _collisionNormal.Y = 0f; if ((collidableEdges & GenObject.Direction.Left) == 0) return false; } } else { if (objectA.Bounds.MidpointY > objectB.Bounds.MidpointY) { // The collision normal is pointing from the top edge of bounding box A. _collisionNormal.X = 0f; _collisionNormal.Y = -1f; if ((collidableEdges & GenObject.Direction.Down) == 0) return false; } else { // The collision normal is pointing from the bottom edge of bounding box A. _collisionNormal.X = 0f; _collisionNormal.Y = 1f; if ((collidableEdges & GenObject.Direction.Up) == 0) return false; } } float distance = Math.Max(_distance.X, _distance.Y); float remove; // Apply an alternative collision response against tiles for pixel-perfect accuracy. if (objectB is GenTile) { // Get the amount of normal velocity to remove from the object so that it just touches along the collision surface of the tile. remove = Vector2.Dot(-objectA.Velocity, _collisionNormal) + Math.Max(distance, 0f) * GenG.InverseTimeStep; // If the amount of velocity to remove is positive, the object will separate itself from the tile. if (remove >= 0) return false; if (_collisionNormal.X != 0) { objectA.X = (_collisionNormal.X == 1) ? objectB.X - objectA.Bounds.Width : objectB.Bounds.Right; objectA.Velocity.X = 0f; } else { objectA.Y = (_collisionNormal.Y == 1) ? objectB.Y - objectA.Bounds.Height : objectB.Bounds.Bottom; objectA.Velocity.Y = 0f; } } else { // Get the relative velocity along the collision normal. float relativeNormalVelocity = Vector2.Dot(objectB.Velocity - objectA.Velocity, _collisionNormal); // Get the amount of relative normal velocity to remove so that the bounding boxes are just touching along the collision surface. remove = relativeNormalVelocity + distance * GenG.InverseTimeStep; // If the amount of velocity to remove is positive, the objects will separate themselves. if (remove >= 0) return false; float impulse = remove / (objectA.InverseMass + objectB.InverseMass); if (!objectA.Immovable) objectA.Velocity += impulse * _collisionNormal * objectA.InverseMass; if (!objectB.Immovable) objectB.Velocity -= impulse * _collisionNormal * objectB.InverseMass; } // Use a bit field of flags to provide the direction that each object is colliding in during the current collision. GenObject.Direction touchingA = GenObject.Direction.None; GenObject.Direction touchingB = GenObject.Direction.None; if (_collisionNormal.X != 0) { if (_collisionNormal.X == 1) { objectA.Touching |= GenObject.Direction.Right; objectB.Touching |= GenObject.Direction.Left; touchingA |= GenObject.Direction.Right; touchingB |= GenObject.Direction.Left; } else { objectA.Touching |= GenObject.Direction.Left; objectB.Touching |= GenObject.Direction.Right; touchingA |= GenObject.Direction.Left; touchingB |= GenObject.Direction.Right; } } else { if (_collisionNormal.Y == 1) { objectA.Touching |= GenObject.Direction.Down; objectB.Touching |= GenObject.Direction.Up; touchingA |= GenObject.Direction.Down; touchingB |= GenObject.Direction.Up; if (objectB.IsPlatform) { if (objectA.Acceleration.X == 0) objectA.Platform = objectB; } } else { objectA.Touching |= GenObject.Direction.Up; objectB.Touching |= GenObject.Direction.Down; touchingA |= GenObject.Direction.Up; touchingB |= GenObject.Direction.Down; if (objectA.IsPlatform) { if (objectB.Acceleration.X == 0) objectB.Platform = objectA; } } } if (callback != null) { _collideEventArgs.ObjectA = objectA; _collideEventArgs.ObjectB = objectB; _collideEventArgs.TouchingA = touchingA; _collideEventArgs.TouchingB = touchingB; callback(_collideEventArgs); } return true; } return false; }
/// <summary> /// Checks for overlap between the movements bounds of two objects. /// </summary> /// <param name="objectA">The first object to check for an overlap.</param> /// <param name="objectB">The second object to check for an overlap.</param> /// <param name="callback">The delegate method that will be invoked if an overlap occurs.</param> /// <returns>True if an overlap occurs, false if not.</returns> public static bool Overlap(GenObject objectA, GenObject objectB, CollideEvent callback) { // Check if both objects are alive to avoid unwanted overlap checks that may be called by a quadtree. if ((objectA.Exists && objectA.Active) && (objectB.Exists && objectB.Active)) { if (objectA.MoveBounds.Intersects(objectB.MoveBounds)) { if (callback != null) { _collideEventArgs.ObjectA = objectA; _collideEventArgs.ObjectB = objectB; _collideEventArgs.TouchingA = GenObject.Direction.None; _collideEventArgs.TouchingB = GenObject.Direction.None; callback(_collideEventArgs); } return true; } } return false; }