/// <summary> /// Decide if a collision can occur between this character and another object. /// </summary> /// <param name="otherObject">The object that this character collided with.</param> /// <returns>Whether or not a collision should occur.</returns> public override bool canCollideWith(GameObject otherObject) { if ((otherObject is Objects.Bullet) && !(((Bullet)otherObject).isPCBullet)) { return false; } else if (otherObject is Character && !(otherObject is Helix || this is Helix)) { return false; } return base.canCollideWith(otherObject); }
/// <summary> /// Determines whether another GameObject will collide with this GameObject if it follows a specified movement vector. /// </summary> /// <param name="movementVector">The movement vector of the other GameObject.</param> /// <param name="otherObject">The other GameObject.</param> /// <returns>True if these GameObjects will intersect.</returns> public bool willIntersect(Vector2 movementVector, GameObject otherObject) { // Check for obvious intersections in each direction. float left = position.X - size.X / 2; left = Math.Min(left, left + movementVector.X); if (left > otherObject.bounds.Right) { return false; } float right = position.X + size.X / 2; right = Math.Max(right, right + movementVector.X); if (otherObject.bounds.Left > right) { return false; } float bottom = position.Y - size.Y / 2; bottom = Math.Min(bottom, bottom + movementVector.Y); if (bottom > otherObject.bounds.Top) { return false; } float top = position.Y + size.Y / 2; top = Math.Max(top, top + movementVector.Y); if (otherObject.bounds.Bottom > top) { return false; } // If no obvious intersections were found, check point by point using the GameObjectBounds. return bounds.WillIntersect(otherObject.bounds, movementVector, this is Bullet); }
/// <summary> /// Function called when this GameObject collides with another GameObject. /// </summary> /// <param name="otherObject">The other GameObject.</param> public virtual void collidedWith(GameObject otherObject) { // Do nothing? }
/// <summary> /// Determine if this GameObject can collide with another GameObject. /// </summary> /// <param name="otherObject">The other GameObject.</param> /// <returns>If these objects can collide with each other.</returns> public virtual bool canCollideWith(GameObject otherObject) { return true; }
/// <summary> /// Move objects, as long as they don't collide with anything /// </summary> /// <param name="movingObject">The object that is moving</param> /// <param name="collidableObjects">Objects that might collide with the moving object</param> /// <param name="elapsedTime">The time (in seconds) since the last update</param> /// <param name="boundsSize">The size of the region that collision detection can reliably be performed</param> /// <param name="boundsCenter">The center of the region where collision detection can reliably be performed</param> private void MoveOrCollide(GameObject movingObject, List<GameObject> collidableObjects, float elapsedTime, Vector2 boundsSize, Vector2 boundsCenter) { // Make sure the object will actually be moving if it doesn't collide if ((gravityEnabled && movingObject.affectedByGravity) || movingObject.velocity.Length() > 0 || movingObject.direction.Length() > 0) { Vector2 objectVelocity = GetObjectVelocity(movingObject, elapsedTime); Vector2 objectMovement = Vector2.Multiply(objectVelocity, elapsedTime); #region Make sure the object isn't moving outside the bounds if (!IsMovingWithinBounds(movingObject, objectMovement, boundsSize, boundsCenter)) { if (movingObject is Bullet) { bullets.Remove((Bullet)movingObject); } return; } #endregion Vector2 resultingMovement = Vector2.Zero; bool upwardCreep = false; // Check to see if the object can move all the way without colliding GameObject collidedObject = CheckForCollision(movingObject, collidableObjects, objectMovement); if (collidedObject == null) { // The object didn't collide, move all the way resultingMovement = objectMovement; } else if (movingObject is Bullet) { // If the object collides with a wall and is bounceable, then bounce! if (!(collidedObject is Character) && movingObject.bounceable) { collisionLine.Normalize(); collisionLine.X = Math.Abs(collisionLine.X); collisionLine.Y = Math.Abs(collisionLine.Y); float mod = 0.8f, xmod = 0f, ymod = 0f; float diff = Math.Abs(collisionLine.X - collisionLine.Y); if (diff < 0.25) { xmod = -mod; ymod = -mod; } else if (collisionLine.X > collisionLine.Y) { xmod = collisionLine.X * mod; ymod = (1 - collisionLine.Y) * -mod; } else { xmod = (1 - collisionLine.X) * -mod; ymod = collisionLine.Y * mod; } resultingMovement = new Vector2(objectMovement.X * xmod, objectMovement.Y * ymod); objectVelocity = new Vector2(objectVelocity.X * xmod, objectVelocity.Y * ymod); // Bounce sound! if (movingObject.lastbounce > 500) { Engine.sound.play("bounce"); movingObject.lastbounce = 0; } } // The object collided, and was a bullet (damage taken care of below) // If it hit a non-character, or if the bullet is not supposed to move through things, explode else if (!(collidedObject is Character) || ((Bullet)movingObject).destroy) { #region Make the bullet explode! Bullet b = (Bullet)movingObject; bullets.Remove(b); Explosion explosion = b.explosion; explosion.position = b.position; explosions.Add(explosion); if (explosion.cue != "") { Engine.sound.play(explosion.cue); } #endregion } } else { #region Try to move the object a little closer to whatever it collided with bool noSecondXCollision = true; bool noThirdXCollision = true; bool noSecondYCollision = true; float xTick = objectMovement.X * 0.25f; float yTick = objectMovement.Y * 0.25f; // Until we can't get any closer... while ((noThirdXCollision && (Math.Abs(resultingMovement.X) < Math.Abs(objectMovement.X))) || (noSecondYCollision && (Math.Abs(resultingMovement.Y) < Math.Abs(objectMovement.Y)))) { #region If we can move closer vertically, move a little closer if (noSecondYCollision && yTick != 0) { resultingMovement.Y += yTick; collidedObject = CheckForCollision(movingObject, collidableObjects, resultingMovement); if (collidedObject != null) { noSecondYCollision = false; resultingMovement.Y -= yTick; } } #endregion #region If we can move closer horizontally, move a little closer if (noSecondXCollision && xTick != 0) { resultingMovement.X += xTick; collidedObject = CheckForCollision(movingObject, collidableObjects, resultingMovement); if (collidedObject != null) { noSecondXCollision = false; resultingMovement.X -= xTick; } } #endregion #region If we failed to move closer horizontally before, move up a little while moving horizontally (walk up gentle slopes) else if (!noSecondXCollision) { resultingMovement.X += xTick; resultingMovement.Y += elapsedTime * 32; collidedObject = CheckForCollision(movingObject, collidableObjects, resultingMovement); if (collidedObject == null) { // We could move! Continue creeping closer to our target noSecondXCollision = true; upwardCreep = true; } else { // We couldn't move, stop trying to move horizontally resultingMovement.X -= xTick; resultingMovement.Y -= elapsedTime * 32; noThirdXCollision = false; } } #endregion } #endregion } #region If it was Helix that moved, check to see if his movement caused him to start or stop flying if (movingObject is Helix) { if (resultingMovement.Y == 0) { if (objectVelocity.Y < 0) { Player.helix.flying = false; } } else { if (!upwardCreep) { Player.helix.flying = true; } } } #endregion #region If the object didn't move, collide with whatever got in the way if (resultingMovement.Length() == 0) { movingObject.collidedWith(collidedObject); if (movingObject is Bullet) { // Keep moving at full speed resultingMovement = objectMovement; } else { // Reset velocity and gravitational velocity movingObject.velocity = Vector2.Zero; movingObject.velocityFromGravity = Vector2.Zero; } } #endregion #region Update the object's velocity, position, and velocity from gravity appropriately movingObject.position += resultingMovement; movingObject.velocity = objectVelocity; if (resultingMovement.Y >= 0) { movingObject.velocityFromGravity = Vector2.Zero; } else { if (movingObject.velocity.Y > movingObject.velocityFromGravity.Y) { movingObject.velocityFromGravity.Y = movingObject.velocity.Y; } } if (resultingMovement.Y == 0) { movingObject.velocity.Y = 0; } if (resultingMovement.X == 0) { movingObject.velocity.X = 0; } if (movingObject is Bullet) { if (movingObject.bounceable) { movingObject.bounceTime -= gameTime.ElapsedGameTime.TotalMilliseconds; movingObject.lastbounce += gameTime.ElapsedGameTime.TotalMilliseconds; if (movingObject.bounceTime < 0) { #region Make the bullet explode! Bullet b = (Bullet)movingObject; bullets.Remove(b); Explosion explosion = b.explosion; explosion.position = b.position; explosions.Add(explosion); if (explosion.cue != "") { Engine.sound.play(explosion.cue); } #endregion } } if (movingObject.affectedByGravity) { Vector2 normalized = new Vector2(movingObject.velocity.X, movingObject.velocity.Y); normalized.Normalize(); float mod = normalized.X >= 0 ? -1 : 1; float tvel = movingObject.maxVelocity; float vel = movingObject.velocity.Length(); float value = Math.Min(tvel / vel, 1.0f); movingObject.rotation += mod * value * (float)gameTime.ElapsedGameTime.TotalSeconds; /// TODO: Make it so we don't need to recreate GOB's movingObject.bounds = new GameObjectBounds(movingObject.size, movingObject.position, movingObject.rotation); } } #endregion } }
/// <summary> /// Check for collisions between objects. /// </summary> /// <param name="movingObject">An object in motion.</param> /// <param name="collidableObjects">All objects that could be collided with.</param> /// <param name="motionVector">The vector of motion.</param> /// <returns>The GameObject that has been collided with.</returns> private GameObject CheckForCollision(GameObject movingObject, List<GameObject> collidableObjects, Vector2 motionVector ) { if (movingObject.collidable && collisionDetectionOn) { List<GameObject>.Enumerator collideableObjEnumerator = collidableObjects.GetEnumerator(); while (collideableObjEnumerator.MoveNext()) { if (collideableObjEnumerator.Current.collidable && collideableObjEnumerator.Current != movingObject) { if (movingObject.willIntersect(motionVector, collideableObjEnumerator.Current)) { if (movingObject.canCollideWith(collideableObjEnumerator.Current)) { #if DEBUG if (SnailsPace.debugCollisions) { SnailsPace.debug("Collision: " + movingObject.position); } #endif return collideableObjEnumerator.Current; } } } } } return null; }
/// <summary> /// Get the velocity of a moving object. /// </summary> /// <param name="movingObject">The moving GameObject.</param> /// <param name="elapsedTime">The amount of time elapsed.</param> /// <returns>The object's velocity as a vector.</returns> private Vector2 GetObjectVelocity(GameObject movingObject, float elapsedTime) { Vector2 objectVelocity = Vector2.Zero; Vector2 objectAccel = Vector2.Zero; // Calculate their velocity after acceleration if (movingObject.direction.Length() > 0) { movingObject.direction.Normalize(); if (movingObject.desiredMaxVelocity <= 0 || movingObject.desiredMaxVelocity > movingObject.maxVelocity) { movingObject.desiredMaxVelocity = movingObject.maxVelocity; } objectAccel = movingObject.direction * movingObject.acceleration * elapsedTime; objectVelocity = objectAccel + movingObject.velocity; Vector2 objectVelocityLessGravity = objectVelocity - movingObject.velocityFromGravity; if (objectVelocityLessGravity.Y > movingObject.maxVelocity - movingObject.terminalVelocity) { if (objectVelocity.Length() > movingObject.desiredMaxVelocity) { objectVelocity.Normalize(); objectVelocity = objectVelocity * movingObject.desiredMaxVelocity; } } else { if (objectVelocityLessGravity.Length() > movingObject.desiredMaxVelocity) { objectVelocityLessGravity.Normalize(); objectVelocityLessGravity = objectVelocityLessGravity * movingObject.desiredMaxVelocity; objectVelocity = objectVelocityLessGravity + movingObject.velocityFromGravity; } } } else { objectVelocity = movingObject.velocity; if (objectVelocity.X == 0) { // stay at 0 } else if (objectVelocity.X > 0) { objectVelocity.X -= movingObject.horizontalFriction * elapsedTime; if (objectVelocity.X < 0) { objectVelocity.X = 0; } } else { objectVelocity.X += movingObject.horizontalFriction * elapsedTime; if (objectVelocity.X > 0) { objectVelocity.X = 0; } } } // Calculate their velocity after gravity; if (gravityEnabled && movingObject.affectedByGravity) { if (-objectVelocity.Y < movingObject.terminalVelocity) { Vector2 velocityFromGravity = gravity * elapsedTime; objectVelocity += velocityFromGravity; movingObject.velocityFromGravity += velocityFromGravity; if (-objectVelocity.Y > movingObject.terminalVelocity) { objectVelocity.Y = -movingObject.terminalVelocity; movingObject.velocityFromGravity = new Vector2(0, -movingObject.terminalVelocity); } } } return objectVelocity; }
/// <summary> /// Determine if an object is moving within a specified bounds. /// </summary> /// <param name="movingObject">The GameObject to check.</param> /// <param name="objectMovement">The movement vector of the specified bounds.</param> /// <param name="boundsSize">The size of the specified bounds.</param> /// <param name="boundsCenter">The center of the specified bounds.</param> /// <returns>Whether or not an object is moving within a specified bounds.</returns> private bool IsMovingWithinBounds(GameObject movingObject, Vector2 objectMovement, Vector2 boundsSize, Vector2 boundsCenter) { if (movingObject.position.X < boundsCenter.X) { if (objectMovement.X < 0) { float left = movingObject.position.X - movingObject.size.X / 2; float leftDiff = left - boundsCenter.X; float leftDiffAbs = Math.Abs(leftDiff); if (leftDiffAbs > boundsSize.X / 2) { return false; } } } else { if (objectMovement.X > 0) { float right = movingObject.position.X + movingObject.size.X / 2; float rightDiff = right - boundsCenter.X; float rightDiffAbs = Math.Abs(rightDiff); if (rightDiffAbs > boundsSize.X / 2) { return false; } } } if (movingObject.position.Y < boundsCenter.Y) { if (objectMovement.Y < 0) { float bottom = movingObject.position.Y - movingObject.size.Y / 2; float bottomDiff = bottom - boundsCenter.Y; float bottomDiffAbs = Math.Abs(bottomDiff); if (bottomDiffAbs > boundsSize.Y / 2) { return false; } } } else { if (objectMovement.Y > 0) { float top = movingObject.position.Y + movingObject.size.Y / 2; float topDiff = top - boundsCenter.Y; float topDiffAbs = Math.Abs(topDiff); if (topDiffAbs > boundsSize.Y / 2) { return false; } } } return true; }
/// <summary> /// Determine if an object is within specified bounds. /// </summary> /// <param name="objectToCheck">The GameObject to check.</param> /// <param name="boundsCenter">The center of the specified bounds.</param> /// <param name="boundsSize">The size of the specified bounds.</param> /// <returns>Whether or not an object is within specified bounds.</returns> public bool IsWithinBounds(GameObject objectToCheck, Vector2 boundsCenter, Vector2 boundsSize) { float left = objectToCheck.position.X - objectToCheck.size.X / 2; float right = objectToCheck.position.X + objectToCheck.size.X / 2; float top = objectToCheck.position.Y + objectToCheck.size.Y / 2; float bottom = objectToCheck.position.Y - objectToCheck.size.Y / 2; float leftDiff = left - boundsCenter.X; float leftDiffAbs = Math.Abs(leftDiff); float rightDiff = right - boundsCenter.X; float rightDiffAbs = Math.Abs(rightDiff); float topDiff = top - boundsCenter.Y; float topDiffAbs = Math.Abs(topDiff); float bottomDiff = bottom - boundsCenter.Y; float bottomDiffAbs = Math.Abs(bottomDiff); if (((leftDiffAbs < boundsSize.X / 2) || (rightDiffAbs < boundsSize.X / 2) || (rightDiff < 0 && leftDiff > 0 || rightDiff > 0 && leftDiff < 0)) && ((topDiffAbs < boundsSize.Y / 2) || (bottomDiffAbs < boundsSize.Y / 2) || (topDiff < 0 && bottomDiff > 0 || topDiff > 0 && bottomDiff < 0))) { return true; } return false; }
/// <summary> /// Sets up invisible borders to prevent the player from moving off the edge of the map /// </summary> private void setupMapBounds() { mapBounds = new List<GameObject>(); //Build each bounding wall Sprite mapBoundsSprite = new Sprite(); mapBoundsSprite.image = new Image(); mapBoundsSprite.image.filename = "Resources/Textures/BoundingBox"; mapBoundsSprite.image.blocks = new Vector2(1.0f); mapBoundsSprite.visible = false; mapBoundsSprite.effect = "Resources/Effects/effects"; Vector2 lastPoint = map.bounds[0]; Vector2 currentPoint; GameObject mapBound; // Draw invisible game objects corresponding to the bounding points defined by the map. for (int i = 1; i < map.bounds.Count; i++) { currentPoint = map.bounds[i]; mapBoundsSprite = mapBoundsSprite.clone(); mapBound = new GameObject(); if (lastPoint.X == currentPoint.X) { mapBoundsSprite.image.size = new Vector2(10.0f, MathHelper.Distance(currentPoint.Y, lastPoint.Y)); mapBound.sprites.Add("BoundingBox", mapBoundsSprite); mapBound.size = mapBoundsSprite.image.size; mapBound.position = new Vector2(currentPoint.X, (currentPoint.Y + lastPoint.Y) / 2.0f); } else if (lastPoint.Y == currentPoint.Y) { mapBoundsSprite.image.size = new Vector2(MathHelper.Distance(currentPoint.X, lastPoint.X), 10.0f); mapBound.sprites.Add("BoundingBox", mapBoundsSprite); mapBound.size = mapBoundsSprite.image.size; mapBound.position = new Vector2((currentPoint.X + lastPoint.X) / 2.0f, currentPoint.Y); } mapBound.collidable = true; mapBounds.Add(mapBound); lastPoint = currentPoint; } }
/// <summary> /// Creates a game object to be used as an overlay when the game is paused /// </summary> private void setupPauseOverlay() { Sprite pauseSprite = new Sprite(); pauseSprite.image = new Image(); pauseSprite.image.filename = "Resources/Textures/PauseScreen"; pauseSprite.image.blocks = new Vector2(1.0f, 1.0f); pauseSprite.image.size = new Vector2(800.0f, 600.0f); pauseSprite.visible = false; pauseSprite.effect = "Resources/Effects/effects"; pause = new GameObject(); pause.sprites = new Dictionary<string, Sprite>(); pause.sprites.Add("Pause", pauseSprite); pause.position = new Vector2(0.0f, 0.0f); pause.layer = -300; pause.collidable = false; }
/// <summary> /// /// </summary> /// <param name="startPosition"></param> /// <param name="weaponName"></param> public Player(Vector2 startPosition, String weaponName, String nextLevel) : base() { if (allowLevelProgression) { this.nextLevel = nextLevel; } else { this.nextLevel = null; } saveObject = new GameObject(); saveObject.affectedByGravity = false; saveObject.collidable = false; save(startPosition); Weapon[] oldInventory = new Weapon[0]; if (helix != null) { oldInventory = helix.inventory; } helix = new Helix(startPosition, weaponName); if (SnailsPace.cheatAllWeapons) { helix.AddWeapon(Weapon.load("stinger")); helix.weapon.ammunition = 50; helix.weapon.cooldown *= 0.25; helix.AddWeapon(Weapon.load("grenadelauncher")); helix.weapon.ammunition = 20; helix.weapon.cooldown *= 1; helix.AddWeapon(Weapon.load("minigun")); helix.weapon.ammunition = 200; helix.weapon.cooldown *= 4; helix.AddWeapon(Weapon.load("flamethrower")); helix.weapon.ammunition = 20; helix.weapon.cooldown *= 1; helix.AddWeapon(Weapon.load("generic")); } load(); // Crosshair creation Sprite crosshairSprite = new Sprite(); crosshairSprite.image = new Image(); crosshairSprite.image.filename = "Resources/Textures/Crosshair"; crosshairSprite.image.blocks = new Vector2(1.0f, 1.0f); crosshairSprite.image.size = new Vector2(64.0f, 64.0f); crosshairSprite.visible = true; crosshairSprite.effect = "Resources/Effects/effects"; crosshair = new GameObject(); crosshair.sprites = new Dictionary<string, Sprite>(); crosshair.sprites.Add("Crosshair", crosshairSprite); crosshair.position = new Vector2(0.0f, 0.0f); crosshair.layer = 0; crosshair.collidable = false; // Weapon weapon = new GameObject(); weapon.sprites = new Dictionary<string, Sprite>(); weapon.sprites.Add("Weapon", helix.weapon.sprite); weapon.position = helix.position; weapon.layer = -5; weapon.collidable = false; strings = new List<Text>(); pointsText = new Text(); pointsText.position = new Vector2(400, 0); pointsText.font = SnailsPace.getInstance().Content.Load<SpriteFont>("Resources/Fonts/Score"); pointsText.color = Color.White; pointsText.scale = Vector2.One; pointsText.content = points.ToString(); strings.Add(pointsText); recalculatePoints(); Engine.player = this; Renderer.cameraTarget = helix; }
/// <summary> /// Action performed when this character collides with another object. /// </summary> /// <param name="otherObject">The object that this character collided with.</param> public override void collidedWith(GameObject otherObject) { if (otherObject is Objects.Helix) { ((Helix)otherObject).takeDamage(); } else { base.collidedWith(otherObject); } }
/// <summary> /// Action performed when Helix collides with another GameObject. /// </summary> /// <param name="otherObject">The GameObject that Helix collided with.</param> public override void collidedWith(GameObject otherObject) { if (otherObject is Objects.Character) { base.takeDamage(); } else { base.collidedWith(otherObject); } }
/// <summary> /// Decide if a collision can occur between Helix and another object. /// </summary> /// <param name="otherObject">The object that Helix collided with.</param> /// <returns>Whether or not a collision should occur.</returns> public override bool canCollideWith(GameObject otherObject) { if ((otherObject is Objects.Bullet) && (((Bullet)otherObject).isPCBullet)) return false; return base.canCollideWith(otherObject); }