/Samples/Breakout/Board.cs
C# | 469 lines | 336 code | 43 blank | 90 comment | 39 complexity | 8412313468fbabab2235ad7b231e9f23 MD5 | raw file
Possible License(s): Apache-2.0
- using Delta.ContentSystem.Rendering;
- using Delta.ContentSystem.Rendering.Helpers;
- using Delta.Engine;
- using Delta.InputSystem;
- using Delta.Rendering.Basics.Materials;
- using Delta.Rendering.Effects.Modifiers;
- using Delta.Rendering.Enums;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
- using Delta.Utilities.Math;
-
- namespace Breakout
- {
- /// <summary>
- /// Board, which handles most of the high level game logic for this game.
- /// </summary>
- public class Board
- {
- #region Constants
- private const int StartLives = 3;
- private const int StartLevel = 1;//2;//1;
- private const float StartBallSpeedY = -1.0f;
- private const float BallSpeedYIncreaseFactor = 1.015f;
- private const float BallSpeedXIncreaseFactor = 2.5f;
-
- /// <summary>
- /// Name of the move left command for player paddle.
- /// </summary>
- private const string PaddleMoveLeftDigital = "PaddleMoveLeftDigital";
- /// <summary>
- /// Name of the move right command for player paddle.
- /// </summary>
- private const string PaddleMoveRightDigital = "PaddleMoveRightDigital";
- /// <summary>
- /// Name of the move command for player paddle per mouse or touch.
- /// </summary>
- private const string PaddleMoveDirect = "PaddleMoveDirect";
- /// <summary>
- /// Name of the command to fire off the ball.
- /// </summary>
- private const string FireBall = "FireBall";
- #endregion
-
- #region Public
- /// <summary>
- /// Paddle of the player
- /// </summary>
- public Paddle PlayerPaddle
- {
- get;
- private set;
- }
-
- /// <summary>
- /// Ball for the game
- /// </summary>
- public Ball Ball
- {
- get;
- private set;
- }
-
- /// <summary>
- /// Number of bricks alive
- /// </summary>
- public int LivingBricksCount
- {
- get;
- private set;
- }
-
- /// <summary>
- /// Bricks array in 2D
- /// </summary>
- public Brick[,] Bricks
- {
- get;
- private set;
- }
-
- /// <summary>
- /// Level
- /// </summary>
- public int Level
- {
- get;
- private set;
- }
-
- /// <summary>
- /// Is game over?
- /// </summary>
- public bool GameOver;
- /// <summary>
- /// Number of lives left.
- /// </summary>
- public int Lives;
- /// <summary>
- /// Current score
- /// </summary>
- public int Score;
- /// <summary>
- /// Number of bricks destroyed so far.
- /// </summary>
- public int DestroyedBlocks;
- #endregion
-
- #region Private
- private float brickStepX;
- private float brickStepY;
- private EffectData effectTemplate;
- private float paddleInputX;
- private bool releaseBall;
- private long gameStartTimeMs;
- #endregion
-
- #region Constructor
- /// <summary>
- /// Create board
- /// </summary>
- public Board()
- {
- StartGame();
- // Setup the visual collision effect for the ball
- var collision = new Material2DColored("BreakoutEffect");
- effectTemplate = EffectData.Get("<BrickDestroyEffect>");
- EmitterData emitter = new EmitterData();
- emitter.Modifiers.Add(new SpawnOnceModifier());
- emitter.Modifiers.Add(new SizeModifier()
- {
- WidthRange = new Range(1.0f, 4.0f),
- HeightRange = new Range(1.0f, 4.0f),
- });
- emitter.Modifiers.Add(new FadeModifier()
- {
- AlphaFade = new Range(1.0f, 0.0f),
- });
- emitter.Modifiers.Add(new MaterialModifier()
- {
- BillboardMode = BillboardMode.Only2D,
- Material = collision,
- });
- effectTemplate.Emitters.Add(emitter);
-
- RegisterInputCommands();
- }
- #endregion
-
- #region RegisterInputCommands
- /// <summary>
- /// Register input commands
- /// </summary>
- private void RegisterInputCommands()
- {
- // Get the commands we have in the system already
- CommandManager commands = Input.Commands;
-
- #region Keyboard and GamePad steering
- commands[PaddleMoveLeftDigital].Add(this, delegate(CommandTrigger input)
- {
- paddleInputX -= 1.5f * Time.Delta;
- });
-
- commands[PaddleMoveRightDigital].Add(this, delegate(CommandTrigger input)
- {
- paddleInputX += 1.5f * Time.Delta;
- });
- #endregion
-
- #region Mouse and Touch steering
- commands[PaddleMoveDirect].Add(this, delegate(CommandTrigger input)
- {
- paddleInputX = input.Position.X;
- });
- #endregion
-
- #region Fire off the ball
- commands[FireBall].Add(this, delegate
- {
- // Quick fix to avoid that ball will be shoot automatically with the
- // same 'Released' state every time the "game restart" question was
- // confirmed
- if (Time.Milliseconds - gameStartTimeMs > 100)
- {
- releaseBall = true;
- } // if
- });
- #endregion
- }
- #endregion
-
- #region StartGame
- /// <summary>
- /// Start game
- /// </summary>
- public void StartGame()
- {
- Score = 0;
- DestroyedBlocks = 0;
- Lives = StartLives;
- Level = StartLevel;
- paddleInputX = 0.5f;
- NextLevel();
- SetupGame();
- gameStartTimeMs = Time.Milliseconds;
- }
- #endregion
-
- #region GetBrickByScreenPosition
- /// <summary>
- /// Get brick by screen position, used for collision handling.
- /// </summary>
- /// <param name="x">X screen position between 0.0 and 1.0</param>
- /// <param name="y">Y screen position between 0.0 and 0.5</param>
- /// <returns>Found brick (can return null if y is too much)</returns>
- public Brick GetBrickByScreenPosition(float x, float y)
- {
- if (y > 0.5f)
- {
- return null;
- }
-
- return Bricks[(int)(x / brickStepX), (int)(y / brickStepY)];
- }
- #endregion
-
- #region NextLevel
- /// <summary>
- /// Proceed to the next level
- /// </summary>
- public void NextLevel()
- {
- // Update application title with the current level
- Application.Window.Title = "Delta Engine - Breakout - Level " + Level;
-
- // Always increase the level size
- int rows = Level + 1;
- int columns = Level + 1;
- Bricks = new Brick[rows, columns];
- LivingBricksCount = rows * columns;
- brickStepX = 1f / rows;
- brickStepY = 0.5f / columns;
-
- for (int x = 0; x < Bricks.GetLength(0); x++)
- {
- for (int y = 0; y < Bricks.GetLength(1); y++)
- {
- Bricks[x, y] = new Brick();
- Bricks[x, y].Bounds = new Rectangle(x * brickStepX,
- y * brickStepY, brickStepX, brickStepY);
- }
- }
-
- LevelData.SetupLevelColors(Bricks, Level);
- // And finally increase the level number for next time
- Level++;
- }
- #endregion
-
- #region RemoveBrick
- /// <summary>
- /// Remove certain bricks (will set its IsDead and remove one from the
- /// LivingBricksCount).
- /// </summary>
- /// <param name="brick">Brick to remove</param>
- public void RemoveBrick(Brick brick)
- {
- brick.IsDead = true;
- LivingBricksCount--;
-
- //obs, too much: Effect.Spawn(effectTemplate, brick.Bounds.Center);
- }
- #endregion
-
- #region SpawnEffect
- /// <summary>
- /// Spawns a new 2D effect at the given position.
- /// </summary>
- /// <param name="position">Position to spawn 2D effect.</param>
- public void SpawnEffect(Point position)
- {
- //HACK: Disabled right now, causes too much headache
- //Effect.Spawn(effectTemplate, position);
- }
- #endregion
-
- #region SetupGame
- private void SetupGame()
- {
- PlayerPaddle = new Paddle();
- Ball = new Ball();
- }
- #endregion
-
- #region IncreaseScore
- private void IncreaseScore()
- {
- Score += DestroyedBlocks + Level;
- DestroyedBlocks++;
- SpawnEffect(Ball.Position);
- }
- #endregion
-
- #region UpdateGame
- /// <summary>
- /// This is the most complex method of this game, it handles all collision
- /// detection and most of the game logic, input and updating of the paddle,
- /// ball and bricks of the game.
- /// </summary>
- public void UpdateGame()
- {
- #region Paddle
- // We are using command delegates to change the paddle position
-
- // Clamp it to the screen, so the paddle won't move outside the window.
- paddleInputX = MathHelper.Clamp(paddleInputX, PlayerPaddle.Size.WidthHalf,
- 1.0f - PlayerPaddle.Size.WidthHalf);
-
- // And update the paddle position.
- PlayerPaddle.Position = new Point(paddleInputX, PlayerPaddle.Position.Y);
- #endregion
-
- if (Ball.IsOnPaddle)
- {
- #region Ball release
- Ball.Position = new Point(PlayerPaddle.Position.X,
- PlayerPaddle.Position.Y - Ball.Radius);
- // Should be Ok for both of them.
- if (releaseBall)
- {
- Ball.IsOnPaddle = false;
- if (Ball.Velocity.X == 0f &&
- Ball.Velocity.Y == 0f)
- {
- float randomXSpeed = RandomHelper.RandomFloat(-0.5f, 0.5f) / 5f;
- if (randomXSpeed == 0f)
- {
- randomXSpeed = 0.01f;
- }
- Ball.Velocity = new Point(randomXSpeed, StartBallSpeedY);
- }
- releaseBall = false;
- }
- #endregion
- }
- else
- {
- // Update the ball movement
- Ball.Position += (Ball.Velocity * Time.Delta);
-
- #region Ball wall collision
- if (Ball.Position.X >= 1f - Ball.Radius)
- {
- Ball.AbsXSpeed(true);
- SpawnEffect(Ball.Position);
- }
- else if (Ball.Position.X <= Ball.Radius)
- {
- Ball.AbsXSpeed(false);
- SpawnEffect(Ball.Position);
- }
- else if (Ball.Position.Y >= 1.0f - Ball.Radius)
- {
- Ball = new Ball();
-
- Lives--;
- DestroyedBlocks /= 2;
-
- if (Lives == 0)
- {
- GameOver = true;
- }
- }
- else if (Ball.Position.Y <= Ball.Radius)
- {
- Ball.AbsYSpeed(false);
- SpawnEffect(Ball.Position);
- }
- #endregion
- else
- {
- #region Paddle collision
- if (Ball.Position.Y + Ball.Radius > PlayerPaddle.Position.Y &&
- Ball.Velocity.Y > 0)
- {
- if (Ball.Position.X + Ball.Radius >
- PlayerPaddle.Position.X - 0.1f &&
- Ball.Position.X - Ball.Radius < PlayerPaddle.Position.X + 0.1f)
- {
- Score++;
-
- SpawnEffect(Ball.Position-new Point(0, 0.1f));
- Ball.AbsYSpeed(true);
-
- float distance = Ball.Position.X - PlayerPaddle.Position.X;
- Ball.Velocity = new Point(
- Ball.Velocity.X + distance * BallSpeedXIncreaseFactor,
- Ball.Velocity.Y * BallSpeedYIncreaseFactor);
-
- if (Ball.Velocity.X > 5.0f)
- {
- Ball.Velocity = new Point(5.0f, Ball.Velocity.Y);
- }
- if (Ball.Velocity.X < -5.0f)
- {
- Ball.Velocity = new Point(-5.0f, Ball.Velocity.Y);
- }
- if (Ball.Velocity.Y < -5.0f)
- {
- Ball.Velocity = new Point(Ball.Velocity.X, -5.0f);
- }
- }
- }
- #endregion
-
- #region Bricks collision
- Brick colPongUpper = GetBrickByScreenPosition(
- Ball.Position.X, Ball.Position.Y - Ball.Radius);
- if (colPongUpper != null &&
- colPongUpper.IsDead == false)
- {
- IncreaseScore();
- RemoveBrick(colPongUpper);
- Ball.AbsYSpeed(false);
- }
-
- Brick colPongLower = GetBrickByScreenPosition(
- Ball.Position.X, Ball.Position.Y + Ball.Radius);
- if (colPongLower != null &&
- colPongLower.IsDead == false)
- {
- IncreaseScore();
- RemoveBrick(colPongLower);
- Ball.AbsYSpeed(true);
- }
-
- Brick colPongRight = GetBrickByScreenPosition(
- Ball.Position.X + Ball.Radius, Ball.Position.Y);
- if (colPongRight != null &&
- colPongRight.IsDead == false)
- {
- IncreaseScore();
- RemoveBrick(colPongRight);
- Ball.AbsXSpeed(true);
- }
-
- Brick colPongLeft = GetBrickByScreenPosition(
- Ball.Position.X - Ball.Radius, Ball.Position.Y);
- if (colPongLeft != null &&
- colPongLeft.IsDead == false)
- {
- IncreaseScore();
- RemoveBrick(colPongLeft);
- Ball.AbsXSpeed(false);
- }
- #endregion
-
- if (LivingBricksCount == 0)
- {
- Lives++;
- NextLevel();
- SetupGame();
- }
- }
- }
- }
- #endregion
- }
- }