/// <summary> /// Get the y value of the first line of two lines where they intersect. /// The lines must not be perfectly horizontal, vertical, or have zero length. /// </summary> /// <param name="line1">Two points that define the first line</param> /// <param name="line2">Two points that define the second line</param> /// <returns>The y value of line1 where x in line1 = x in line2</returns> public static float Line1YWhereXTheSameInBothLines(LineSegment2 line1, LineSegment2 line2) { float j = (line1.point2.Y - line1.point2.Y) / (line1.point2.X - line1.point1.X); float k = (line2.point2.X - line2.point1.X) / (line2.point2.Y - line2.point1.Y); float l = line2.point1.X; float q = line2.point1.Y; return (j * (q - (k * l))) / (1 - (j * k)); }
/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) { this.Exit(); } SnakeDirection oldDirection = snakeDirection; Vector2 oldSnakePosition = snakePosition; bool justInitialized = false; // Get keyboard input KeyboardState state = Keyboard.GetState(); if (IsKeyReleased(Keys.Space, state)) { TogglePaused(); } if (IsKeyReleased(Keys.R, state)) { InitializeSnake(); justInitialized = true; } if (!paused && !justInitialized) { if (state.IsKeyDown(Keys.Left)) { if (snakeDirection != SnakeDirection.Right) { snakeDirection = SnakeDirection.Left; } } if (state.IsKeyDown(Keys.Right)) { if (snakeDirection != SnakeDirection.Left) { snakeDirection = SnakeDirection.Right; } } if (state.IsKeyDown(Keys.Up)) { if (snakeDirection != SnakeDirection.Down) { snakeDirection = SnakeDirection.Up; } } if (state.IsKeyDown(Keys.Down)) { if (snakeDirection != SnakeDirection.Up) { snakeDirection = SnakeDirection.Down; } } } if (IsKeyReleased(Keys.G, state)) { GrowSnake(); } if (state.IsKeyDown(Keys.OemPlus)) { snakeSpeed += 5; } if (state.IsKeyDown(Keys.OemMinus)) { snakeSpeed -= 5; if (snakeSpeed < 0) { snakeSpeed = 0; } } if (state.IsKeyDown(Keys.OemCloseBrackets)) { snakeLength += 1.0f; } if (state.IsKeyDown(Keys.OemOpenBrackets)) { snakeLength -= 1.0f; if (snakeLength < initialSnakeLength) { snakeLength = initialSnakeLength; } } if (state.IsKeyDown(Keys.W)) { cameraPitch += 1.0f; if (cameraPitch > 360.0f) { cameraPitch -= 360.0f; } UpdateViewMatrix(); } if (state.IsKeyDown(Keys.S)) { cameraPitch -= 1.0f; if (cameraPitch < 0) { cameraPitch += 360.0f; } UpdateViewMatrix(); } if (state.IsKeyDown(Keys.D)) { cameraDistance += 1.0f; UpdateViewMatrix(); } if (state.IsKeyDown(Keys.A)) { cameraDistance -= 1.0f; if (cameraDistance < 0) { cameraDistance = 0; } UpdateViewMatrix(); } if (IsKeyReleased(Keys.V, state)) { if (cameraType == CameraType.FromAbove) { SetCameraType(CameraType.Angled); } else { SetCameraType(CameraType.FromAbove); } } if (IsKeyReleased(Keys.T, state)) { Toggle2DSnake(); } if (IsKeyReleased(Keys.I, state)) { ToggleOverlay(); } if (IsKeyReleased(Keys.C, state)) { ToggleIgnoreCollisions(); } if (IsKeyReleased(Keys.B, state)) { if (arenaBoundaryType == ArenaBoundaryType.WrapAround) { arenaBoundaryType = ArenaBoundaryType.Collision; } else if (arenaBoundaryType == ArenaBoundaryType.Collision) { arenaBoundaryType = ArenaBoundaryType.NoBoundary; } else { arenaBoundaryType = ArenaBoundaryType.WrapAround; } } if (state.IsKeyDown(Keys.Q) || state.IsKeyDown(Keys.Escape)) { Exit(); } oldKeyboardState = state; if (!paused && !justInitialized) { // Check if snake switched direction if (snakeDirection != oldDirection) { // Add position to list of joints snakePositions.Add(new Vector2(oldSnakePosition.X, oldSnakePosition.Y)); disconnectedToPreviousPoint.Add(false); } // Move the snake float displacement = (float)gameTime.ElapsedGameTime.TotalSeconds * snakeSpeed; switch (snakeDirection) { case SnakeDirection.Up: snakePosition.Y -= displacement; break; case SnakeDirection.Down: snakePosition.Y += displacement; break; case SnakeDirection.Left: snakePosition.X -= displacement; break; case SnakeDirection.Right: snakePosition.X += displacement; break; } bool hit = false; // Check if snake went out of bounds if (arenaBoundaryType == ArenaBoundaryType.Collision) { if (snakePosition.X < 0 || snakePosition.X > graphics.GraphicsDevice.Viewport.Width - 1 || snakePosition.Y < 0 || snakePosition.Y > graphics.GraphicsDevice.Viewport.Height - 1) { hit = true; } } else if (arenaBoundaryType == ArenaBoundaryType.WrapAround) { if (snakePosition.X < 0) { oldSnakePosition = new Vector2(graphics.GraphicsDevice.Viewport.Width, snakePosition.Y); snakePositions.Add(new Vector2(0, snakePosition.Y)); disconnectedToPreviousPoint.Add(false); snakePositions.Add(oldSnakePosition); disconnectedToPreviousPoint.Add(true); snakePosition = new Vector2(graphics.GraphicsDevice.Viewport.Width + snakePosition.X, snakePosition.Y); } else if (snakePosition.X > graphics.GraphicsDevice.Viewport.Width) { oldSnakePosition = new Vector2(0, snakePosition.Y); snakePositions.Add(new Vector2(graphics.GraphicsDevice.Viewport.Width, snakePosition.Y)); disconnectedToPreviousPoint.Add(false); snakePositions.Add(oldSnakePosition); disconnectedToPreviousPoint.Add(true); snakePosition = new Vector2(snakePosition.X - graphics.GraphicsDevice.Viewport.Width, snakePosition.Y); } else if (snakePosition.Y < 0) { oldSnakePosition = new Vector2(snakePosition.X, graphics.GraphicsDevice.Viewport.Height); snakePositions.Add(new Vector2(snakePosition.X, 0)); disconnectedToPreviousPoint.Add(false); snakePositions.Add(oldSnakePosition); disconnectedToPreviousPoint.Add(true); snakePosition = new Vector2(snakePosition.X, graphics.GraphicsDevice.Viewport.Height + snakePosition.Y); } else if (snakePosition.Y > graphics.GraphicsDevice.Viewport.Height) { oldSnakePosition = new Vector2(snakePosition.X, 0); snakePositions.Add(new Vector2(snakePosition.X, graphics.GraphicsDevice.Viewport.Height)); disconnectedToPreviousPoint.Add(false); snakePositions.Add(oldSnakePosition); disconnectedToPreviousPoint.Add(true); snakePosition = new Vector2(snakePosition.X, snakePosition.Y - graphics.GraphicsDevice.Viewport.Height); } } // Trim the snake tail, making sure the length of the snake is correct. // Go backwards from current point through each point, adding up the displacement. // Stop when you reach the right length, removing history of non relevant points. float length = 0; Vector2 lastPosition = snakePosition; Vector2 vector = new Vector2(); int i; for (i = snakePositions.Count - 1; i >= 0 && length <= snakeLength; --i) { Vector2 position = snakePositions[i]; if (i == snakePositions.Count - 1 || !disconnectedToPreviousPoint[i + 1]) { vector = lastPosition - position; length += vector.Length(); } lastPosition = position; } if (length > snakeLength) { // Modify the tail end position float changeAmount = length - snakeLength; vector.Normalize(); vector = vector * changeAmount; Vector2 newPosition = snakePositions[i + 1] + vector; snakePositions[i + 1] = newPosition; } if (i >= 0) { snakePositions.RemoveRange(0, i + 1); disconnectedToPreviousPoint.RemoveRange(0, i + 1); // Make sure the first element (the tail end point) is set to false disconnectedToPreviousPoint[0] = false; } // Check if snake intersected with itself LineSegment2 recentMovement = new LineSegment2(oldSnakePosition, snakePosition); if (!hit && !ignoreSnakeCollisions) { if (snakePositions.Count > 1) { lastPosition = snakePositions[snakePositions.Count - 2]; i = snakePositions.Count - 3; for (; i >= 0 && !hit; --i) { Vector2 position = snakePositions[i]; LineSegment2 snakeSegment = new LineSegment2(position, lastPosition); if (!disconnectedToPreviousPoint[i + 1]) { hit = LineSegment2.SegmentsIntersect(recentMovement, snakeSegment); } lastPosition = position; } } } if (hit) { InitializeSnake(); } else { // Check if snake reached a goal if (LineSegment2.SegmentsIntersect(recentMovement, goalLeftSide) || LineSegment2.SegmentsIntersect(recentMovement, goalRightSide) || LineSegment2.SegmentsIntersect(recentMovement, goalBottomSide) || LineSegment2.SegmentsIntersect(recentMovement, goalLeftSide)) { // intersection GrowSnake(); RepositionGoal(); } Update3DSnakeData(); } } base.Update(gameTime); }
/// <summary> /// Reposition the goal to a random location. /// </summary> protected void RepositionGoal() { // TODO: make sure it gets repositioned somewhere the snake is not at Random rand = new Random(System.DateTime.Now.Millisecond); goalPosition.X = rand.Next(0, GraphicsDevice.Viewport.Width - (int)blockSize.X); goalPosition.Y = rand.Next(0, GraphicsDevice.Viewport.Height - (int)blockSize.Y); // Check if snake reached a goal goalUpperLeft = new Vector2(goalPosition.X - blockSize.X / 2.0f, goalPosition.Y - blockSize.Y / 2.0f); goalUpperRight = new Vector2(goalPosition.X + blockSize.X / 2.0f, goalPosition.Y - blockSize.Y / 2.0f); goalLowerLeft = new Vector2(goalPosition.X - blockSize.X / 2.0f, goalPosition.Y + blockSize.Y / 2.0f); goalLowerRight = new Vector2(goalPosition.X + blockSize.X / 2.0f, goalPosition.Y + blockSize.Y / 2.0f); goalLeftSide = new LineSegment2(goalUpperLeft, goalLowerLeft); goalTopSide = new LineSegment2(goalUpperLeft, goalUpperRight); goalRightSide = new LineSegment2(goalUpperRight, goalLowerRight); goalBottomSide = new LineSegment2(goalLowerLeft, goalLowerRight); // Update 3D data goalVertices = new VertexPositionNormalTexture[4]; goalVertices[0] = new VertexPositionNormalTexture(Snake2DTo3DVector(goalUpperLeft), new Vector3(0, 1, 0), new Vector2(0, 0)); goalVertices[1] = new VertexPositionNormalTexture(Snake2DTo3DVector(goalUpperRight), new Vector3(0, 1, 0), new Vector2(0, 0)); goalVertices[2] = new VertexPositionNormalTexture(Snake2DTo3DVector(goalLowerRight), new Vector3(0, 1, 0), new Vector2(0, 0)); goalVertices[3] = new VertexPositionNormalTexture(Snake2DTo3DVector(goalLowerLeft), new Vector3(0, 1, 0), new Vector2(0, 0)); goalVertexBuffer = new VertexBuffer(graphics.GraphicsDevice, typeof(VertexPositionNormalTexture), goalVertices.Length, BufferUsage.None); goalVertexBuffer.SetData<VertexPositionNormalTexture>(goalVertices); goalIndexBuffer = new IndexBuffer(graphics.GraphicsDevice, typeof(int), goalIndices.Length, BufferUsage.None); goalIndexBuffer.SetData<int>(goalIndices); }
/// <summary> /// Determine whether two line segments intersect. /// Segments may be points, but must not have infinite length. /// </summary> /// <param name="segment1"></param> /// <param name="segment2"></param> /// <returns></returns> public static bool SegmentsIntersect(LineSegment2 segment1, LineSegment2 segment2) { bool doIntersect = false; LineSegment2[] segments = { segment1, segment2 }; float[] changeInX = new float[2]; float[] changeInY = new float[2]; SegmentRelationship segmentRelationship; List<int> horizontalSegmentIndices = new List<int>(); List<int> verticalSegmentIndices = new List<int>(); List<int> pointSegmentIndices = new List<int>(); List<int> tiltedSegmentIndices = new List<int>(); Vector2 intersectPoint; for (int i = 0; i < 2; ++i) { changeInX[i] = segments[i].point2.X - segments[i].point1.X; changeInY[i] = segments[i].point2.Y - segments[i].point1.Y; if (changeInX[i] == 0 && changeInY[i] == 0) { pointSegmentIndices.Add(i); } else if (changeInX[i] == 0) { verticalSegmentIndices.Add(i); } else if (changeInY[i] == 0) { horizontalSegmentIndices.Add(i); } else { tiltedSegmentIndices.Add(i); } } if (pointSegmentIndices.Count >= 1) { if (pointSegmentIndices.Count == 2) { segmentRelationship = SegmentRelationship.Points; } else if (verticalSegmentIndices.Count == 1) { segmentRelationship = SegmentRelationship.VerticalPoint; } else if (horizontalSegmentIndices.Count == 1) { segmentRelationship = SegmentRelationship.HorizontalPoint; } else { segmentRelationship = SegmentRelationship.TiltedPoint; } } else if (verticalSegmentIndices.Count >= 1) { if (verticalSegmentIndices.Count == 2) { segmentRelationship = SegmentRelationship.Verticals; } else if (horizontalSegmentIndices.Count == 1) { segmentRelationship = SegmentRelationship.VerticalHorizontal; } else { segmentRelationship = SegmentRelationship.VerticalTilted; } } else if (horizontalSegmentIndices.Count >= 1) { if (horizontalSegmentIndices.Count == 2) { segmentRelationship = SegmentRelationship.Horizontals; } else { segmentRelationship = SegmentRelationship.HorizontalTilted; } } else { segmentRelationship = SegmentRelationship.Tilted; } if (segmentRelationship == SegmentRelationship.Points) { // check that the points are identical if (segment1.point1.X == segment2.point1.X && segment1.point1.Y == segment2.point1.Y) { doIntersect = true; intersectPoint = segment1.point1; } } else if (segmentRelationship == SegmentRelationship.Verticals || segmentRelationship == SegmentRelationship.Horizontals) { // no intersection } else if (segmentRelationship == SegmentRelationship.VerticalHorizontal) { LineSegment2 vertical = segments[verticalSegmentIndices[0]]; LineSegment2 horizontal = segments[horizontalSegmentIndices[0]]; if (vertical.point1.X >= horizontal.LowestX() && vertical.point1.X <= horizontal.GreatestX() && horizontal.point1.Y >= vertical.LowestY() && horizontal.point1.Y <= vertical.GreatestY()) { doIntersect = true; intersectPoint = new Vector2(vertical.point1.X, horizontal.point1.Y); } } else if (segmentRelationship == SegmentRelationship.VerticalPoint) { // check if point is in line segment LineSegment2 vertical = segments[verticalSegmentIndices[0]]; LineSegment2 point = segments[pointSegmentIndices[0]]; if (point.point1.X == vertical.point1.X && point.point1.Y >= vertical.LowestY() && point.point1.Y <= vertical.GreatestY()) { doIntersect = true; intersectPoint = new Vector2(vertical.point1.X, point.point1.Y); } } else if (segmentRelationship == SegmentRelationship.HorizontalPoint) { // check if point is in line segment LineSegment2 horizontal = segments[horizontalSegmentIndices[0]]; LineSegment2 point = segments[pointSegmentIndices[0]]; if (point.point1.Y == horizontal.point1.Y && point.point1.X >= horizontal.LowestX() && point.point1.X <= horizontal.GreatestX()) { doIntersect = true; intersectPoint = new Vector2(point.point1.X, horizontal.point1.Y); } } else if (segmentRelationship == SegmentRelationship.VerticalTilted) { LineSegment2 vertical = segments[verticalSegmentIndices[0]]; LineSegment2 tilted = segments[tiltedSegmentIndices[0]]; // Check if they intersect horizontally if (vertical.point1.X >= tilted.LowestX() && vertical.point1.X <= tilted.GreatestX()) { // get y value at x = vertical.point1.X float y = YAtXEqualsNInLine(tilted, vertical.point1.X); // Check if they intersect vertically if (y >= vertical.LowestY() && y <= vertical.GreatestY()) { doIntersect = true; intersectPoint = new Vector2(vertical.point1.X, y); } } } else if (segmentRelationship == SegmentRelationship.HorizontalTilted) { LineSegment2 horizontal = segments[horizontalSegmentIndices[0]]; LineSegment2 tilted = segments[tiltedSegmentIndices[0]]; // Check if they intersect vertically if (horizontal.point1.Y >= tilted.LowestY() && horizontal.point1.Y <= tilted.GreatestY()) { // get x value at y = horizontal.point1.Y float x = XAtYEqualsNInLine(tilted, horizontal.point1.Y); // Check if they intersect horizontally if (x >= horizontal.LowestX() && x <= horizontal.GreatestX()) { doIntersect = true; intersectPoint = new Vector2(x, horizontal.point1.Y); } } } else if (segmentRelationship == SegmentRelationship.TiltedPoint) { LineSegment2 point = segments[pointSegmentIndices[0]]; LineSegment2 tilted = segments[tiltedSegmentIndices[0]]; // get y value at x = point.point1.X float y = YAtXEqualsNInLine(tilted, point.point1.X); if (y == point.point1.Y) { doIntersect = true; intersectPoint = point.point1; } } else if (segmentRelationship == SegmentRelationship.Tilted) { LineSegment2 line1 = segments[tiltedSegmentIndices[0]]; LineSegment2 line2 = segments[tiltedSegmentIndices[1]]; // get y value where both lines have the same x float y = Line1YWhereXTheSameInBothLines(line1, line2); if (y >= line1.LowestY() && y <= line1.GreatestY()) { doIntersect = true; intersectPoint = new Vector2(XAtYEqualsNInLine(line1, y), y); } } return doIntersect; }
/// <summary> /// Get the y value of a line where x = n. /// The line must not be perfectly horizontal or vertical or have zero length. /// </summary> /// <param name="line">Two points that define the line</param> /// <param name="n">The value of x</param> /// <returns></returns> public static float YAtXEqualsNInLine(LineSegment2 line, float n) { return ((line.point2.Y - line.point1.Y) / (line.point2.X - line.point1.X)) * (n - line.point1.X) + line.point1.Y; }
/// <summary> /// Get the x value of a line where y = n. /// The line must not be perfectly horizontal or vertical or have zero length. /// </summary> /// <param name="line">Two points that define the line</param> /// <param name="n">The value of y</param> /// <returns></returns> public static float XAtYEqualsNInLine(LineSegment2 line, float n) { return ((line.point2.X - line.point1.X) / (line.point2.Y - line.point1.Y)) * (n - line.point1.Y) + line.point1.X; }