📄 game.cs
字号:
using System;
using System.Drawing;
using Microsoft.Win32;
namespace AnotherBlock
{
/// <summary>
/// Represents a Tetris game engine.
/// </summary>
public class Game
{
/// <summary>
/// Constant with the image width of the block unit (in pixels).
/// </summary>
public const int BlockImageWidth = 21;
/// <summary>
/// Constant with the image height of the block unit (in pixels).
/// </summary>
public const int BlockImageHeight = 21;
/// <summary>
/// Constant with the playing field width (in block units).
/// </summary>
public const int PlayingFieldWidth = 10;
/// <summary>
/// Constant with the playing field height (in block units).
/// </summary>
public const int PlayingFieldHeight = 20;
/// <summary>
/// Constante with the number of lines for each level.
/// </summary>
public const int LevelEveryLines = 12;
/// <summary>
/// Private attribute that holds the current game score.
/// </summary>
private int score = 0;
/// <summary>
/// Private attribute that holds the current game level.
/// </summary>
private short level = 1;
/// <summary>
/// Private attribute that holds the current number of completed lines.
/// </summary>
private int lines = 0;
/// <summary>
/// Private attribute that holds the current game state.
/// </summary>
private GameState gameState;
/// <summary>
/// Private attribute that holds the level where the game started.
/// </summary>
private short startLevel = 1;
/// <summary>
/// Private attribute that holds the current game pile.
/// </summary>
private Brick[,] pile = new Brick[(PlayingFieldWidth + 1), (PlayingFieldHeight + 1)];
/// <summary>
/// Private attribute that holds the current block.
/// </summary>
private Block currentBlock = new Block();
/// <summary>
/// Private attribute that holds the next block.
/// </summary>
private Block nextBlock = new Block();
/// <summary>
/// Class constructor that creates a game.
/// </summary>
public Game()
{
// Clears the game pile.
ClearPile();
}
/// <summary>
/// Class constructor that creates a game in a given level.
/// </summary>
/// <param name="level">The level where the game should start.</param>
public Game(short level)
{
// Sets the level attribute to the game level where the game should start.
this.level = level;
// Sets the startLevel attribute to the game level where the game should start.
startLevel = level;
// Clears the game pile.
ClearPile();
}
/// <summary>
/// Readonly property that holds the score of the current game.
/// </summary>
public int Score
{
get
{
// Returns the value in the score attribute.
return score;
}
}
/// <summary>
/// Readonly property that holds the level of the current game.
/// </summary>
public short Level
{
get
{
// Returns the value in the level attribute.
return level;
}
}
/// <summary>
/// Readonly property that holds the number of complete lines in the current game.
/// </summary>
public int Lines
{
get
{
// Returns the value in the lines attribute.
return lines;
}
}
/// <summary>
/// Property that holds and sets the current game state.
/// </summary>
/// <remarks>
/// The game state can be "Running", "Paused" or "Over".
/// </remarks>
public GameState GameState
{
get
{
// Returns the value in the gameState attribute.
return gameState;
}
set
{
// Checks if the current game state is "Over", and if the value to change is not "Over".
if ((gameState == GameState.Over) && (value != GameState.Over))
{
// The current game state is "Over", and it's changing to something else than "Over".
// Resets the score, level and lines.
score = 0;
level = startLevel;
lines = 0;
// Creates a new current block, and a new next block.
currentBlock = new Block();
nextBlock = new Block();
// Clears the game pile.
ClearPile();
}
// Sets the gameState attribute to the value.
gameState = value;
}
}
/// <summary>
/// Method that moves the current block down one position.
/// </summary>
/// <returns>True if there was a hit on the ground or on the pile, false if there wasn't.</returns>
public bool MoveCurrentBlockDown()
{
// Creates a "hit" flag, to check if the block has hit the pile or the ground.
bool hit = false;
// Increases the Top of the current block.
currentBlock.Top++;
// Checks if the current block has hit the ground.
// COLLISION DETECTION
if ((currentBlock.Top + currentBlock.Height) > PlayingFieldHeight)
{
// Current block has hit the ground.
// Sets the "hit" flag to "true".
hit = true;
}
else
{
// Checks if the current block has hit the pile.
// COLLISION DETECTION
for(int i = 0; i < currentBlock.Width; i++)
{
for(int j = 0; j < currentBlock.Height; j++)
{
int fx, fy;
fx = currentBlock.Left + i;
fy = currentBlock.Top + j;
if ((currentBlock.Shape[i, j].Filled == true) && (pile[fx, (fy + 1)].Filled == true))
{
// Current block has hit the pile.
// Sets the "hit" flag to "true".
hit = true;
}
}
}
}
// Checks if there was a hit.
if (hit)
{
// There was a hit.
// Puts the current block in the pile.
MoveBlockToPile();
// Checks if the current game state is not "Over".
if (this.GameState != GameState.Over)
{
// Current game state is not "Over".
// Creates a new block.
CreateNewBlock();
}
}
// Returns if there was a hit or not.
return hit;
}
/// <summary>
/// Method that moves the current block down until there is a hit.
/// </summary>
public void MoveCurrentBlockCompletelyDown()
{
// Moves the current block down until it has a hit.
while(!MoveCurrentBlockDown());
}
/// <summary>
/// Method that rotates the current block.
/// </summary>
/// <param name="clockwise">True if the block will be rotated clockwise, false if counterclockwise.</param>
public void RotateCurrentBlock(bool clockwise)
{
// Creates a "canRotate" flag, to check if the block can be rotated.
bool canRotate = true;
// Rotates the current block.
// This should be different. There should be an easy way to check FIRST if the block could
// be rotated, and then rotate it. I'll study this later.
currentBlock.Rotate(clockwise);
// Checks if the current block could be rotated.
// COLLISION DETECTION
for(int i = 0; i < currentBlock.Width; i++)
{
for(int j = 0; j < currentBlock.Height; j++)
{
int fx, fy;
fx = currentBlock.Left + i;
fy = (currentBlock.Top + 1) + j;
if ((currentBlock.Shape[i, j].Filled == true) && (pile[fx, fy].Filled == true))
{
// Current block can't be rotated.
// Sets the "canRotate" flag to "false".
canRotate = false;
}
}
}
// Checks if the block can't be rotated.
if (!canRotate)
{
// Block can't be rotated.
// Rotates the block back to its first position.
currentBlock.Rotate(!clockwise);
}
}
/// <summary>
/// Method that moves the current block to the right or to the left.
/// </summary>
/// <param name="left">True if the block will be moved to the left, false if to the right.</param>
public void MoveCurrentBlockSide(bool left)
{
// Creates a "canMove" flag, to check if the block can be moved to the sides.
bool canMove = true;
// Checks if the block is to be moved to the left.
if (left)
{
// The block is to be moved to the left.
// Checks if the block is not already at the most left of the playing field.
if (currentBlock.Left > 0)
{
// Block is not at the most left of the playing field.
// Checks if the current block can move to the left.
// COLLISION DETECTION
for(int i = 0; i < currentBlock.Width; i++)
{
for(int j = 0; j < currentBlock.Height; j++)
{
int fx, fy;
fx = currentBlock.Left + i;
fy = (currentBlock.Top + 1) + j;
if ((currentBlock.Shape[i, j].Filled == true) && (pile[(fx - 1), fy].Filled == true))
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -