/// <summary> /// A streak of lightning is drawn simply by piecing together a long string of small line segments, /// each of which is slightly rotated. /// </summary> private void DrawLightningStreak(LineBatch lineBatch, Color color, Vector2 start, Vector2 end, int numSegments, float deviation) { Random randomGen = new Random(); Vector2 disp = end - start; Vector2 unitDisp = Vector2.Normalize(disp); Vector2 stepVector = unitDisp * disp.Length() / (float)numSegments; Vector2 perpVec = new Vector2(-stepVector.Y, stepVector.X); perpVec.Normalize(); Vector2 startPoint = start; Vector2 endPoint = new Vector2(); for (int i = 0; i < numSegments; ++i) { endPoint = start + stepVector * (i + 1); // If this is an intermediate segment, apply an offset perpendicular to the line connecting start to end. if (i < numSegments - 1) { float random = (float)randomGen.NextDouble(); float offset = (random >= 0.5f ? 1 : -1) * random * deviation; endPoint += perpVec * offset; } lineBatch.DrawLine(startPoint, endPoint, color); startPoint = endPoint; } }
/// <summary> /// This draws the box with streaks of lightning representing its edges. /// </summary> public void Draw(LineBatch lineBatch, Color color, int numSegments, float deviation) { // Let's compute the coordinates of all the vertices to make the line drawing simpler. Vector2 upperLeft = new Vector2(rectangle.Left, rectangle.Top); Vector2 upperRight = new Vector2(rectangle.Right, rectangle.Top); Vector2 lowerLeft = new Vector2(rectangle.Left, rectangle.Bottom); Vector2 lowerRight = new Vector2(rectangle.Right, rectangle.Bottom); // Now draw the lightning streaks. DrawLightningStreak(lineBatch, color, upperLeft, upperRight, numSegments, deviation); DrawLightningStreak(lineBatch, color, upperRight, lowerRight, numSegments, deviation); DrawLightningStreak(lineBatch, color, lowerRight, lowerLeft, numSegments, deviation); DrawLightningStreak(lineBatch, color, lowerLeft, upperLeft, numSegments, deviation); }
/// <summary> /// Draws the grid, showing the springs connecting neighboring nodes as line segments. The color of each /// line segment is interpolated linearly between dimColor and brightColor depending on the length /// of the segment. /// </summary> public void Draw(LineBatch lineBatch, Color dimColor, Color brightColor) { float dx = (bounds.Right - bounds.Left) / gridSize; float dy = (bounds.Bottom - bounds.Top) / gridSize; Vector4 vecDimColor = new Vector4(dimColor.R, dimColor.G, dimColor.B, dimColor.A); Vector4 vecBrightColor = new Vector4(brightColor.R, brightColor.G, brightColor.B, brightColor.A); for (int x = 0; x < gridSize; ++x) { for (int y = 0; y < gridSize; ++y) { if (x < gridSize - 1) { float lineLength = (grid[x + 1, y].Position - grid[x, y].Position).Length(); float t = (lineLength - dx) / (5 * (float)Math.Sqrt(2) * dx); Vector4 modulatedColorVec = Vector4.Lerp(vecDimColor, vecBrightColor, t); Color modulatedColor = new Color((byte)modulatedColorVec.X, (byte)modulatedColorVec.Y, (byte)modulatedColorVec.Z, (byte)modulatedColorVec.W); lineBatch.DrawLine(grid[x, y].Position, grid[x + 1, y].Position, modulatedColor); } if (y < gridSize - 1) { float lineLength = (grid[x, y + 1].Position - grid[x, y].Position).Length(); float t = (lineLength - dy) / (5 * (float)Math.Sqrt(2) * dx); Vector4 modulatedColorVec = Vector4.Lerp(vecDimColor, vecBrightColor, t); Color modulatedColor = new Color((byte)modulatedColorVec.X, (byte)modulatedColorVec.Y, (byte)modulatedColorVec.Z, (byte)modulatedColorVec.W); lineBatch.DrawLine(grid[x, y].Position, grid[x, y + 1].Position, modulatedColor); } } } }
/// <summary> /// Frees graphics content /// </summary> public override void UnloadGraphicsContent(bool unloadAllContent) { if (unloadAllContent) { if (spriteBatch != null) { spriteBatch.Dispose(); spriteBatch = null; } if (lineBatch != null) { lineBatch.Dispose(); lineBatch = null; } content.Unload(); actorManager.Unload(); } }
/// <summary> /// Loads graphics content needed for the game. /// </summary> public override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { if (content == null) content = new ContentManager(ScreenManager.Game.Services, "Content"); grid = new MassSpringGrid(60, 5f, 100f, 0.5f, new Rectangle(safeAreaWidth, safeAreaHeight, screenSafeWidth, screenSafeHeight)); actorManager = new ActorManager(); #region Allocate graphics content spriteBatch = new SpriteBatch(ScreenManager.GraphicsDevice); lineBatch = new LineBatch(ScreenManager.GraphicsDevice); starTexture = content.Load<Texture2D>("blank"); Animation countdownAnim = new Animation(content, 300, 100); countdownAnim.AddFrame(new AnimationFrame(TimeSpan.FromSeconds(2.0f / 3.0f), "ready")); countdownAnim.AddFrame(new AnimationFrame(TimeSpan.FromSeconds(2.0f / 3.0f), "set")); countdownAnim.AddFrame(new AnimationFrame(TimeSpan.FromSeconds(2.0f / 3.0f), "go")); countdownSprite = new Sprite(); countdownSprite.AddAnimation("default", countdownAnim); countdownSprite.SetCurrentAnimation("default"); #endregion #region Set up lightning boxes playerLightningBox = new LightningBox(new Rectangle(safeAreaWidth + 10, screenHeight - safeAreaHeight - 38 - 150, screenSafeWidth - 30, 150)); computerLightningBox = new LightningBox(new Rectangle(safeAreaWidth + 10, safeAreaHeight + 38, screenSafeWidth - 30, 150)); #endregion #region Collision Categories for physics // Collision Categories: // // 1 = player paddle // 10 = ball // 11 = gravity ball // 12 = obstacle // 13 = computer paddle // 14 = bullet // 20 = screen borders // 21 = player lightning box // 22 = borders for obstacles // 23 = computer lightning box #endregion #region Create Player Paddle Actor Template Actor playerPaddleTemplate = new Actor(ScreenManager.Game, new ActorBehavior[] { new GamePadPaddleController(actorManager), new DistortGridBehavior(actorManager, grid, DistortGridDetail.High), new ConstrainToRectangleBehavior(actorManager, playerLightningBox.Rectangle, Physics.Enums.CollisionCategories.Cat21) }, content, "paddle", 100, 100 * 0.174089069f); playerPaddleTemplate.PhysicsBody = Physics.Dynamics.BodyFactory.Instance.CreateRectangleBody(playerPaddleTemplate.Sprite.Width, playerPaddleTemplate.Sprite.Height, 1); playerPaddleTemplate.PhysicsGeom = Physics.Collisions.GeomFactory.Instance.CreateRectangleGeom(playerPaddleTemplate.PhysicsBody, playerPaddleTemplate.Sprite.Width, playerPaddleTemplate.Sprite.Height); playerPaddleTemplate.PhysicsGeom.CollisionCategories = Physics.Enums.CollisionCategories.Cat1; playerPaddleTemplate.PhysicsGeom.CollidesWith = Physics.Enums.CollisionCategories.Cat1 | Physics.Enums.CollisionCategories.Cat10 | Physics.Enums.CollisionCategories.Cat11; playerPaddleTemplate.PhysicsBody.LinearDragCoefficient = 0.01f; playerPaddleTemplate.PhysicsBody.RotationalDragCoefficient = 300.0f; actorManager.AddActorTemplate(playerPaddleTemplate, "Player Paddle"); #endregion #region Create Computer Paddle Actor Template Actor computerPaddleTemplate = new Actor(ScreenManager.Game, new ActorBehavior[] { new DistortGridBehavior(actorManager, grid, DistortGridDetail.High), new ConstrainToRectangleBehavior(actorManager, computerLightningBox.Rectangle, Physics.Enums.CollisionCategories.Cat23), new AIPaddleController(actorManager) }, content, "paddle2", 100, 100 * 0.174089069f); computerPaddleTemplate.PhysicsBody = Physics.Dynamics.BodyFactory.Instance.CreateRectangleBody(playerPaddleTemplate.Sprite.Width, playerPaddleTemplate.Sprite.Height, 1); computerPaddleTemplate.PhysicsGeom = Physics.Collisions.GeomFactory.Instance.CreateRectangleGeom(playerPaddleTemplate.PhysicsBody, playerPaddleTemplate.Sprite.Width, playerPaddleTemplate.Sprite.Height); computerPaddleTemplate.PhysicsGeom.CollisionCategories = Physics.Enums.CollisionCategories.Cat13; computerPaddleTemplate.PhysicsGeom.CollidesWith = Physics.Enums.CollisionCategories.Cat13 | Physics.Enums.CollisionCategories.Cat10 | Physics.Enums.CollisionCategories.Cat11; computerPaddleTemplate.PhysicsBody.LinearDragCoefficient = 0.01f; computerPaddleTemplate.PhysicsBody.RotationalDragCoefficient = 300.0f; actorManager.AddActorTemplate(computerPaddleTemplate, "Computer Paddle"); #endregion #region Create Obstacle Actor Template Actor obstacleActorTemplate = new Actor(ScreenManager.Game, new ActorBehavior[] { new DistortGridBehavior(actorManager, grid, DistortGridDetail.High), new ConstrainToRectangleBehavior(actorManager, new Rectangle( computerLightningBox.Rectangle.X, computerLightningBox.Rectangle.Y + computerLightningBox.Rectangle.Height, computerLightningBox.Rectangle.Width, playerLightningBox.Rectangle.Y - (computerLightningBox.Rectangle.Y + computerLightningBox.Rectangle.Height)), Physics.Enums.CollisionCategories.Cat22), new ShootOffRandomlyBehavior(actorManager, 300, 350), new InteractWithBlackHolesBehavior(actorManager) }, content, "obstacle", 45.0f, 45.0f); obstacleActorTemplate.PhysicsBody = Physics.Dynamics.BodyFactory.Instance.CreateRectangleBody(obstacleActorTemplate.Sprite.Width, obstacleActorTemplate.Sprite.Height, 0.001f); obstacleActorTemplate.PhysicsGeom = Physics.Collisions.GeomFactory.Instance.CreateRectangleGeom(obstacleActorTemplate.PhysicsBody, obstacleActorTemplate.Sprite.Width, obstacleActorTemplate.Sprite.Height); obstacleActorTemplate.PhysicsGeom.CollisionCategories = Physics.Enums.CollisionCategories.Cat12; obstacleActorTemplate.PhysicsGeom.CollidesWith = Physics.Enums.CollisionCategories.Cat1 | Physics.Enums.CollisionCategories.Cat10 | Physics.Enums.CollisionCategories.Cat11 | Physics.Enums.CollisionCategories.Cat12; obstacleActorTemplate.PhysicsBody.LinearDragCoefficient = 0.0f; actorManager.AddActorTemplate(obstacleActorTemplate, "Obstacle"); #endregion #region Create Ball Actor Template float ballDiameter = 25.0f; Actor ballTemplate = new Actor(ScreenManager.Game, new ActorBehavior[] { new DistortGridBehavior(actorManager, grid, DistortGridDetail.High), new ConstrainToRectangleBehavior(actorManager, new Rectangle(safeAreaWidth, safeAreaHeight, screenSafeWidth, screenSafeHeight), Physics.Enums.CollisionCategories.Cat20), new ShootOffRandomlyBehavior(actorManager, 300, 301.0f), new InteractWithBlackHolesBehavior(actorManager) }, content, "ball", ballDiameter, ballDiameter); ballTemplate.PhysicsBody = Physics.Dynamics.BodyFactory.Instance.CreateCircleBody(ballDiameter / 2.0f, 0.001f); ballTemplate.PhysicsGeom = Physics.Collisions.GeomFactory.Instance.CreateCircleGeom(ballTemplate.PhysicsBody, ballDiameter / 2.0f, 16); ballTemplate.PhysicsBody.LinearDragCoefficient = 0.0f; ballTemplate.PhysicsGeom.CollisionCategories = Physics.Enums.CollisionCategories.Cat10; ballTemplate.PhysicsGeom.CollidesWith = Physics.Enums.CollisionCategories.Cat1 | Physics.Enums.CollisionCategories.Cat10 | Physics.Enums.CollisionCategories.Cat13; actorManager.AddActorTemplate(ballTemplate, "Ball"); #endregion #region Create Gravity Ball Actor Template float gravityBallDiameter = 55.0f; Actor gravBallTemplate = new Actor(ScreenManager.Game, new ActorBehavior[] { new BlackHoleBehavior(actorManager, grid), new ConstrainToRectangleBehavior(actorManager, new Rectangle(safeAreaWidth, safeAreaHeight, screenSafeWidth, screenSafeHeight), Physics.Enums.CollisionCategories.Cat20), new LiveTemporarilyBehavior(actorManager, TimeSpan.FromSeconds(2.9f), TimeSpan.FromSeconds(0.5)) }, content, "GravityBall", gravityBallDiameter, 2.0f); gravBallTemplate.PhysicsBody = Physics.Dynamics.BodyFactory.Instance.CreateCircleBody(gravityBallDiameter / 2.0f, 3.0f); gravBallTemplate.PhysicsGeom = Physics.Collisions.GeomFactory.Instance.CreateCircleGeom(gravBallTemplate.PhysicsBody, gravityBallDiameter / 2.0f, 16); gravBallTemplate.PhysicsBody.LinearDragCoefficient = 0.0f; gravBallTemplate.PhysicsGeom.CollisionCategories = Physics.Enums.CollisionCategories.Cat11; gravBallTemplate.PhysicsGeom.CollidesWith = Physics.Enums.CollisionCategories.Cat11 | Physics.Enums.CollisionCategories.Cat10 | Physics.Enums.CollisionCategories.Cat1 | Physics.Enums.CollisionCategories.Cat13; actorManager.AddActorTemplate(gravBallTemplate, "Gravity Ball"); #endregion #region Create Bullet Actor Template float bulletDiameter = 5.0f; Actor bulletTemplate = new Actor(ScreenManager.Game, new ActorBehavior[] { new ConstrainToRectangleBehavior(actorManager, new Rectangle(safeAreaWidth, safeAreaHeight, screenSafeWidth, screenSafeHeight), Physics.Enums.CollisionCategories.Cat20), new DieUponCollisionBehavior(actorManager), new InteractWithBlackHolesBehavior(actorManager) }, content, "projectile", bulletDiameter, bulletDiameter); bulletTemplate.PhysicsBody = Physics.Dynamics.BodyFactory.Instance.CreateCircleBody(bulletDiameter / 2.0f, 0.001f); bulletTemplate.PhysicsGeom = Physics.Collisions.GeomFactory.Instance.CreateCircleGeom(bulletTemplate.PhysicsBody, bulletDiameter / 2.0f, 16); bulletTemplate.PhysicsBody.LinearDragCoefficient = 0.0f; bulletTemplate.PhysicsGeom.CollisionCategories = Physics.Enums.CollisionCategories.Cat14; bulletTemplate.PhysicsGeom.CollidesWith = Physics.Enums.CollisionCategories.Cat1 | Physics.Enums.CollisionCategories.Cat10 | Physics.Enums.CollisionCategories.Cat11 | Physics.Enums.CollisionCategories.Cat12 | Physics.Enums.CollisionCategories.Cat13; actorManager.AddActorTemplate(bulletTemplate, "Bullet"); #endregion #region Place basic gameplay objects playerPaddle = actorManager.InstantiateTemplate("Player Paddle"); playerPaddle.PhysicsBody.Position = new Vector2(safeAreaWidth + screenSafeWidth / 2.0f, screenHeight - safeAreaHeight - 38 - 150 / 2); computerPaddle = actorManager.InstantiateTemplate("Computer Paddle"); computerPaddle.PhysicsBody.Position = new Vector2(safeAreaWidth + screenSafeWidth / 2.0f, safeAreaHeight + 38 + 150 / 2); ball = actorManager.InstantiateTemplate("Ball"); ball.PhysicsBody.Position = new Vector2(safeAreaWidth + screenSafeWidth / 2, safeAreaHeight + screenSafeHeight / 2); PauseBallForNewRound(); ShootOffRandomlyBehavior shootOffBehavior = (ShootOffRandomlyBehavior)ball.GetBehavior(typeof(ShootOffRandomlyBehavior)); shootOffBehavior.JustStarting = false; AIPaddleController ai = (AIPaddleController)computerPaddle.GetBehavior(typeof(AIPaddleController)); ai.Ball = ball; PopulateArenaWithObstacles(); #endregion } // Set the projection matrix for the line batch lineBatch.SetProjection(Matrix.CreateOrthographicOffCenter( 0.0f, ScreenManager.GraphicsDevice.Viewport.Width, ScreenManager.GraphicsDevice.Viewport.Height, 0.0f, 0.0f, 1.0f)); }