topical media & game development

talk show tell print

game-xna-intro-XnaTetris-TetrisGrid.cs / cs



  // Project: XnaTetris, File: TetrisGrid.cs
  // Namespace: XnaTetris, Class: TetrisGrid
  // Path: C:\code\XnaBook\XnaTetris, Author: Abi
  // Code lines: 16, Size of file: 298 Bytes
  // Creation date: 21.11.2006 03:56
  // Last modified: 26.11.2006 13:21
  // Generated with Commenter by abi.exDream.com
  
  #region Using directives
  using System;
  using System.Collections.Generic;
  using System.Text;
  using Microsoft.Xna.Framework.Graphics;
  using Microsoft.Xna.Framework;
  using XnaTetris.Helpers;
  using XnaTetris.Sounds;
  #endregion
  
  namespace XnaTetris
  {
  
<summary> TetrisGrid helper class to manage the grid and all block types for the tetris game. </summary> public class TetrisGrid : Microsoft.Xna.Framework.DrawableGameComponent { #region Constants public const int GridWidth = 12; public const int GridHeight = 20;

  
<summary> Block types we can have for each new block that falls down. </summary> public enum BlockTypes { Empty, Block, Triangle, Line, RightT, LeftT, RightShape, LeftShape, } // enum BlockTypes

  
<summary> Number of block types we can use for each grid block. </summary> public static readonly int NumOfBlockTypes = if XBOX360 8; else EnumHelper.GetSize(typeof(BlockTypes)); endif

  
<summary> Block colors for each block type. </summary> public static readonly Color[] BlockColor = new Color[] { new Color( 60, 60, 60, 128 ), // Empty, color unused new Color( 50, 50, 255, 255 ), // Line, blue new Color( 160, 160, 160, 255 ), // Block, gray new Color( 255, 50, 50, 255 ), // RightT, red new Color( 255, 255, 50, 255 ), // LeftT, yellow new Color( 50, 255, 255, 255 ), // RightShape, teal new Color( 255, 50, 255, 255 ), // LeftShape, purple new Color( 50, 255, 50, 255 ), // Triangle, green }; // Color[] BlockColor

  
<summary> Unrotated shapes </summary> public static readonly int[][,] BlockTypeShapesNormal = new int[][,] { // Empty new int[,] { { 0 } }, // Line new int[,] { { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } }, // Block new int[,] { { 1, 1 }, { 1, 1 } }, // RightT new int[,] { { 1, 1 }, { 1, 0 }, { 1, 0 } }, // LeftT new int[,] { { 1, 1 }, { 0, 1 }, { 0, 1 } }, // RightShape new int[,] { { 0, 1, 1 }, { 1, 1, 0 } }, // LeftShape new int[,] { { 1, 1, 0 }, { 0, 1, 1 } }, // LeftShape new int[,] { { 0, 1, 0 }, { 1, 1, 1 }, { 0, 0, 0 } }, }; // BlockTypeShapesNormal #endregion

                  #region Variables
  
<summary> Remember game for accessing textures and sprites </summary> TetrisGame game = null;

  
<summary> Precalculated Rotated shapes </summary> static int[,][,] BlockTypeShapes;

  
<summary> The actual grid, contains all blocks, including the currently falling block. </summary> BlockTypes[,] grid = new BlockTypes[GridWidth, GridHeight]; <summary> Use this simple array to see where the floating parts are. Simply check from bottom up and putting stuff in the next line. </summary> bool[,] floatingGrid = new bool[GridWidth, GridHeight];

  
<summary> When game is over, this is set to true, start over then! </summary> public bool gameOver = false;

  
<summary> Remember current block type and rotation </summary> int currentBlockType = 0; int currentBlockRot = 0; Point currentBlockPos;

  
<summary> Next block game component, does not only store the next block type, but also displays it. </summary> internal NextBlock nextBlock;

  
<summary> Grid rectangle for drawing. </summary> Rectangle gridRect; #endregion

                  #region Constructor
                  public TetrisGrid(TetrisGame setGame, Rectangle setGridRect,
                          Rectangle setNextBlockRect)
                          : base(setGame)
                  {
                          game = setGame;
                          gridRect = setGridRect;
  
                          nextBlock = new NextBlock(game, setNextBlockRect);
                          game.Components.Add(nextBlock);
                  } // TetrisGrid(game)
                  #endregion
  
                  #region Initialize
                  public override void Initialize()
                  {
                          // Precalculate Rotated shapes, for all types
                          BlockTypeShapes = new int[NumOfBlockTypes, 4][,];
                          for (int type = 0; type < NumOfBlockTypes; type++)
                          {
                                  int[,] shape = BlockTypeShapesNormal[type];
                                  int width = shape.GetLength(0);
                                  int height = shape.GetLength(1);
                                  // Init all precalculated shapes
                                  BlockTypeShapes[type, 0] = new int[height, width];
                                  BlockTypeShapes[type, 1] = new int[width, height];
                                  BlockTypeShapes[type, 2] = new int[height, width];
                                  BlockTypeShapes[type, 3] = new int[width, height];
                                  for (int x = 0; x < width; x++)
                                          for (int y = 0; y < height; y++)
                                          {
                                                  BlockTypeShapes[type, 0][y, x] = shape[(width - 1) - x, y];
                                                  BlockTypeShapes[type, 1][x, y] = shape[x, y];
                                                  BlockTypeShapes[type, 2][y, x] = shape[x, (height - 1) - y];
                                                  BlockTypeShapes[type, 3][x, y] =
                                                          shape[(width - 1) - x, (height - 1) - y];
                                          } // for for
                          } // for
  
                          Restart();
                          AddRandomBlock();
  
                          base.Initialize();
                  } // Initialize()
                  #endregion
  
                  #region Restart
                  public void Restart()
                  {
                          for ( int x=0; x<GridWidth; x++ )
                                  for ( int y=0; y<GridHeight; y++ )
                                  {
                                          grid[x,y] = BlockTypes.Empty;
                                          floatingGrid[x,y] = false;
                                  } // for for
  
                          //done automatically: AddRandomBlock();
                          Sound.Play(Sound.Sounds.Fight);
                  } // Restart()
                  #endregion
  
                  #region Add random block
  
<summary> Adds a random block in the top middle </summary> public void AddRandomBlock() { // Randomize block type and rotation currentBlockType = (int)nextBlock.SetNewRandomBlock(); currentBlockRot = RandomHelper.GetRandomInt(4);

                          // Get precalculated shape
                          int[,] shape = BlockTypeShapes[currentBlockType,currentBlockRot];
                          int xPos = GridWidth/2-shape.GetLength(0)/2;
                          // Center block at top most position of our grid
                          currentBlockPos = new Point(xPos, 0);
  
                          // Add new block
                          for ( int x=0; x<shape.GetLength(0); x++ )
                                  for ( int y=0; y<shape.GetLength(1); y++ )
                                          if ( shape[x,y] > 0 )
                                          {
                                                  // Check if there is already something
                                                  if (grid[x + xPos, y] != BlockTypes.Empty)
                                                  {
                                                          // Then game is over dude!
                                                          gameOver = true;
                                                          Sound.Play(Sound.Sounds.Lose);
                                                  } // if
                                                  else
                                                  {
                                                          grid[x + xPos, y] = (BlockTypes)currentBlockType;
                                                          floatingGrid[x + xPos, y] = true;
                                                  } // else
                                          } // for for if
  
                          // Add 1 point per block!
                          score++;
                  } // AddRandomBlock()
                  #endregion
  
                  #region Move block
                  public enum MoveTypes
                  {
                          Left,
                          Right,
                          Down,
                  } // enum MoveTypes
  
  
<summary> Remember if moving down was blocked, this increases the game speed because we can force the next block! </summary> public bool movingDownWasBlocked = false; <summary> Move current floating block to left, right or down. If anything is blocking, moving is not possible and nothing gets changed! </summary> <returns>Returns true if moving was successful, otherwise false</returns> public bool MoveBlock(MoveTypes moveType) { // Clear old pos for ( int x=0; x<GridWidth; x++ ) for ( int y=0; y<GridHeight; y++ ) if ( floatingGrid[x,y] ) grid[x,y] = BlockTypes.Empty; // Move stuff to new position bool anythingBlocking = false; Point[] newPos = new Point[4]; int newPosNum = 0; if ( moveType == MoveTypes.Left ) { for ( int x=0; x<GridWidth; x++ ) for ( int y=0; y<GridHeight; y++ ) if ( floatingGrid[x,y] ) { if ( x-1 < 0 || grid[x-1,y] != BlockTypes.Empty ) anythingBlocking = true; else if ( newPosNum < 4 ) { newPos[newPosNum] = new Point( x-1, y ); newPosNum++; } // else if } // for for if } // if (left) else if ( moveType == MoveTypes.Right ) { for ( int x=0; x<GridWidth; x++ ) for ( int y=0; y<GridHeight; y++ ) if ( floatingGrid[x,y] ) { if ( x+1 >= GridWidth || grid[x+1,y] != BlockTypes.Empty ) anythingBlocking = true; else if ( newPosNum < 4 ) { newPos[newPosNum] = new Point( x+1, y ); newPosNum++; } // else if } // for for if } // if (right) else if ( moveType == MoveTypes.Down ) { for ( int x=0; x<GridWidth; x++ ) for ( int y=0; y<GridHeight; y++ ) if ( floatingGrid[x,y] ) { if ( y+1 >= GridHeight || grid[x,y+1] != BlockTypes.Empty ) anythingBlocking = true; else if ( newPosNum < 4 ) { newPos[newPosNum] = new Point( x, y+1 ); newPosNum++; } // else if } // for for if if ( anythingBlocking == true ) movingDownWasBlocked = true; } // if (down)

                          // If anything is blocking restore old state
                          if ( anythingBlocking ||
                                  // Or we didn't get all 4 new positions?
                                  newPosNum != 4 )
                          {
                                  for ( int x=0; x<GridWidth; x++ )
                                          for ( int y=0; y<GridHeight; y++ )
                                                  if ( floatingGrid[x,y] )
                                                          grid[x,y] = (BlockTypes)currentBlockType;
                                  return false;
                          } // if
                          else
                          {
                                  if ( moveType == MoveTypes.Left )
                                          currentBlockPos = new Point( currentBlockPos.X-1, currentBlockPos.Y );
                                  else if ( moveType == MoveTypes.Right )
                                          currentBlockPos = new Point( currentBlockPos.X+1, currentBlockPos.Y );
                                  else if ( moveType == MoveTypes.Down )
                                          currentBlockPos = new Point( currentBlockPos.X, currentBlockPos.Y+1 );
  
                                  // Else we can move to the new position, lets do it!
                                  for ( int x=0; x<GridWidth; x++ )
                                          for ( int y=0; y<GridHeight; y++ )
                                                  floatingGrid[x,y] = false;
                                  for ( int i=0; i<4; i++ )
                                  {
                                          grid[newPos[i].X,newPos[i].Y] = (BlockTypes)currentBlockType;
                                          floatingGrid[newPos[i].X,newPos[i].Y] = true;
                                  } // for
                                  Sound.Play(Sound.Sounds.BlockMove);
  
                                  return true;
                          } // else
                  } // MoveBlock(moveType)
                  #endregion
  
                  #region Rotate block
                  public void RotateBlock()
                  {
                          // Clear old pos
                          for ( int x=0; x<GridWidth; x++ )
                                  for ( int y=0; y<GridHeight; y++ )
                                          if ( floatingGrid[x,y] )
                                                  grid[x,y] = BlockTypes.Empty;
                          // Rotate and check if new position is valid
                          bool anythingBlocking = false;
                          Point[] newPos = new Point[4];
                          int newPosNum = 0;
  
                          int newRotation = (currentBlockRot+1)%4;
                          int[,] shape = BlockTypeShapes[currentBlockType,newRotation];
                          for ( int x=0; x<shape.GetLength(0); x++ )
                                  for ( int y=0; y<shape.GetLength(1); y++ )
                                          if ( shape[x,y] > 0 )
                                          {
                                                  if ( currentBlockPos.X+x >= GridWidth ||
                                                          currentBlockPos.Y+y >= GridHeight ||
                                                          currentBlockPos.X+x < 0 ||
                                                          currentBlockPos.Y+y < 0 ||
                                                          grid[currentBlockPos.X+x,currentBlockPos.Y+y] != BlockTypes.Empty )
                                                          anythingBlocking = true;
                                                  else if ( newPosNum < 4 )
                                                  {
                                                          newPos[newPosNum] = new Point(
                                                                  currentBlockPos.X+x, currentBlockPos.Y+y );
                                                          newPosNum++;
                                                  } // else if
                                          } // for for if
  
                          // If anything is blocking restore old state
                          if ( anythingBlocking ||
                                  // Or we didn't get all 4 new positions?
                                  newPosNum != 4 )
                          {
                                  for ( int x=0; x<GridWidth; x++ )
                                          for ( int y=0; y<GridHeight; y++ )
                                                  if ( floatingGrid[x,y] )
                                                          grid[x,y] = (BlockTypes)currentBlockType;
                          } // if
                          else
                          {
                                  // Else we can rotate, lets do it!
                                  currentBlockRot = newRotation;
                                  for ( int x=0; x<GridWidth; x++ )
                                          for ( int y=0; y<GridHeight; y++ )
                                                  floatingGrid[x,y] = false;
                                  for ( int i=0; i<4; i++ )
                                  {
                                          grid[newPos[i].X,newPos[i].Y] = (BlockTypes)currentBlockType;
                                          floatingGrid[newPos[i].X,newPos[i].Y] = true;
                                  } // for
                                  Sound.Play(Sound.Sounds.BlockRotate);
                          } // else
                  } // RotateBlock()
                  #endregion
  
                  #region Update
                  // Current score we got
                  public int score = 0;
                  // Number of lines we crushed
                  public int lines = 0;
  
<summary> Update whole field, move current floating stuff down and check if any full lines are given. Note: Do not use the override Update(gameTime) method here, Update is ONLY called when 1000ms have passed (level 1), or 100ms for level 10. </summary> public void Update() { if (gameOver) return;

                          // Try to move floating stuff down
                          if (MoveBlock(MoveTypes.Down) == false ||
                                  movingDownWasBlocked)
                          {
                                  // Failed? Then fix floating stuff, not longer moveable!
                                  for ( int x=0; x<GridWidth; x++ )
                                          for ( int y=0; y<GridHeight; y++ )
                                                  floatingGrid[x,y] = false;
                                  Sound.Play(Sound.Sounds.BlockFalldown);
                          } // if
                          movingDownWasBlocked = false;
  
                          // Check if we got any moveable stuff,
                          // if not add new random block at top!
                          bool canMove = false;
                          for ( int x=0; x<GridWidth; x++ )
                                  for ( int y=0; y<GridHeight; y++ )
                                          if ( floatingGrid[x,y] )
                                                  canMove = true;
                          if (canMove == false)
                          {
                                  int linesKilled = 0;
                                  // Check if we got a full line
                                  for ( int y=0; y<GridHeight; y++ )
                                  {
                                          bool fullLine = true;
                                          for ( int x=0; x<GridWidth; x++ )
                                                  if ( grid[x,y] == BlockTypes.Empty )
                                                  {
                                                          fullLine = false;
                                                          break;
                                                  } // for if
                                          // We got a full line?
                                          if (fullLine)
                                          {
                                                  // Move everything down
                                                  for ( int yDown=y-1; yDown>0; yDown-- )
                                                          for ( int x=0; x<GridWidth; x++ )
                                                                  grid[x,yDown+1] = grid[x,yDown];
                                                  // Clear top line
                                                  for ( int x=0; x<GridWidth; x++ )
                                                          grid[0,x] = BlockTypes.Empty;
                                                  // Add 10 points and count line
                                                  score += 10;
                                                  lines++;
                                                  linesKilled++;
                                                  Sound.Play(Sound.Sounds.LineKill);
                                          } // if
                                  } // for
                                  // If we killed 2 or more lines, add extra score
                                  if (linesKilled >= 2)
                                          score += 5;
                                  if (linesKilled >= 3)
                                          score += 10;
                                  if (linesKilled >= 4)
                                          score += 25;
  
                                  // Add new block at top
                                  AddRandomBlock();
                          } // if
                  } // Update()
                  #endregion
  
                  #region Draw
                  public override void Draw(GameTime gameTime)
                  {
                          // Show next block
                          nextBlock.Draw(gameTime);
  
                          // Calc sizes for block, etc.
                          int blockWidth = gridRect.Width / GridWidth;
                          int blockHeight = gridRect.Height / GridHeight;
                          if ( blockWidth < 2 )
                                  blockWidth = 2;
                          if ( blockHeight < 2 )
                                  blockHeight = 2;
  
                          for ( int x=0; x<GridWidth; x++ )
                                  for ( int y=0; y<GridHeight; y++ )
                                  {
                                          game.BlockSprite.Render(new Rectangle(
                                                  gridRect.X + x * blockWidth,
                                                  gridRect.Y + y * blockHeight,
                                                  blockWidth-1, blockHeight-1 ),
                                                  BlockColor[(int)grid[x,y]]);
                                  } // for for
                  } // Draw(gameTime)
                  #endregion
          } // class TetrisGrid
  } // namespace XnaTetris.TetrisGrid
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.