Post: C# XNA - Puzzle Game 1/2 Code Finished - Game Base Complete
01-20-2013, 06:19 PM #1
Pichu
RIP PICHU.
(adsbygoogle = window.adsbygoogle || []).push({});
You must login or register to view this content.


Game1
    using System;using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Flood_Control
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;


//Creates a texture of Tile_Sheet.png, Background.png, and TitleScreen.png
Texture2D playingPieces, backgroundScreen, titleScreen;


GameBoard gameBoard;
Vector2 gameBoardDisplayOrigin = new Vector2(70, 89); //Vector Point where the board will be drawn


int playerScore = 0;


enum GameStates { Titlescreen, Playing };
GameStates gamestate = GameStates.Titlescreen;


Rectangle EmptyPiece = new Rectangle(1, 247, 40, 40);


const float MinTimeSinceLastinput = 0.25f;
float timeSinceLastInput = 0.0f;


public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}


/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
this.IsMouseVisible = true;
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 600;
graphics.ApplyChanges();
gameBoard = new GameBoard();
base.Initialize();
}


/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
playingPieces = Content.Load<Texture2D>(@"Textures\Tile_Sheet");
backgroundScreen = Content.Load<Texture2D>(@"Textures\Background");
titleScreen = Content.Load<Texture2D>(@"Textures\TitleScreen");

}


/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}


/// <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();


switch (gamestate)
{
case GameStates.Titlescreen:
if (Keyboard.GetState().IsKeyDown(Keys.Space))
{
gameBoard.ClearBoard();
gameBoard.GenerateNewPieces(false);
playerScore = 0;
gamestate = GameStates.Playing;
}break;


case GameStates.Playing:
timeSinceLastInput += (float)gameTime.ElapsedGameTime.TotalSeconds;


if (timeSinceLastInput >= MinTimeSinceLastinput)
{
HandleMouseInput(Mouse.GetState());
}
gameBoard.ResetWater();


for (int y = 0; y < GameBoard.GameBoardHeight; y++)
{
CheckScoringChain(gameBoard.GetWaterChain(y));
}
gameBoard.GenerateNewPieces(true);
break;
}
base.Update(gameTime);
}


/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);


if (gamestate == GameStates.Titlescreen)
{
spriteBatch.Begin();
//Draws the titlescreen
spriteBatch.Draw(titleScreen, new Rectangle(0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height), Color.White);
spriteBatch.End();
}


if (gamestate == GameStates.Playing)
{
spriteBatch.Begin();
spriteBatch.Draw(backgroundScreen, new Rectangle(0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height), Color.White);


for (int x = 0; x < GameBoard.GameBoardWidth; x++)
{
for (int y = 0; y < GameBoard.GameBoardHeight; y++)
{
int pixelX = (int)gameBoardDisplayOrigin.X + (x * GamePiece.PieceWidth);
int pixelY = (int)gameBoardDisplayOrigin.Y + (y * GamePiece.PieceHeight);


spriteBatch.Draw(playingPieces, new Rectangle(pixelX, pixelY, GamePiece.PieceWidth, GamePiece.PieceHeight), EmptyPiece, Color.White);
spriteBatch.Draw(playingPieces, new Rectangle(pixelX, pixelY, GamePiece.PieceWidth, GamePiece.PieceHeight), gameBoard.GetSourceRect(x, y), Color.White);
}
}
this.Window.Title = playerScore.ToString();
spriteBatch.End();
}
base.Draw(gameTime);
}


private int DetermineScore(int SquareCount)
{
return (int)((Math.Pow((SquareCount / 5), 2) + SquareCount) * 10);
}


private void CheckScoringChain(List<Vector2> WaterChain)
{
if (WaterChain.Count > 0)
{
Vector2 LastPipe = WaterChain[WaterChain.Count - 1];
if (LastPipe.X == GameBoard.GameBoardWidth - 1)
{
if (gameBoard.hasConnector((int)LastPipe.X, (int)LastPipe.Y, "Right"))
{
playerScore += DetermineScore(WaterChain.Count);
foreach (Vector2 ScoringSquare in WaterChain)
{
gameBoard.SetSquare((int)ScoringSquare.X, (int)ScoringSquare.Y, "Empty"); ;
}
}
}
}
}


private void HandleMouseInput(MouseState mouseState)
{
int x = ((mouseState.X - (int)gameBoardDisplayOrigin.X) / GamePiece.PieceWidth);
int y = ((mouseState.Y - (int)gameBoardDisplayOrigin.Y) / GamePiece.PieceHeight);


if ((x >= 0) && (x < GameBoard.GameBoardWidth) && (y >= 0) && (y < GameBoard.GameBoardHeight))
{
if (mouseState.LeftButton == ButtonState.Pressed)
{
gameBoard.RotatePiece(x, y, false);
timeSinceLastInput = 0.0f;
}


if (mouseState.RightButton == ButtonState.Pressed)
{
gameBoard.RotatePiece(x, y, true);
timeSinceLastInput = 0.0f;
}
}
}
}
}


GamePiece.cs

    using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace Flood_Control
{
class GamePiece
{
//Names each of the different types of game pieces that will be added to the game board
public static string[] PieceTypes = {
"Left,Right", "Top,Bottom", "Left,Top", "Top,Right", "Right,Bottom", "Bottom,Left", "Empty"
};
//Declares the Height and the Width of the sprite
public const int PieceHeight = 40;
public const int PieceWidth = 40;


//Index of the last piece that can be placed on the board
public const int MaxPlayablePiecesIndex = 4;
//Declares the position of an empty piece
public const int EmptyPieceIndex = 6;


//Describes the layout of the texture file.
//Offsets pixels left and top edge of the texture and adds a single pixel of padding betweene ach sprite on the sprite sheet
private const int textureOffsetX = 1;
private const int textureOffsetY = 1;
private const int texturePaddingX = 1;
private const int texturePaddingY = 1;


//Stores the associated values of a piece
private string pieceType = "";
private string pieceSuffix = "";


public string PieceType { get { return pieceType; } }
public string Suffix { get { return pieceSuffix; } }


public GamePiece(string type, string suffix)
{
pieceType = type;
pieceSuffix = suffix;
}


public GamePiece(string type)
{
pieceType = type;
pieceSuffix = "";
}


//An overload, Can Pass both type and piece
public void SetPiece(string type, string suffix)
{
pieceType = type;
pieceSuffix = suffix;
}


//An overload, Can Pass the type
public void SetPiece(string type)
{
SetPiece(type, "");
}


//Checks to see if the piece is already contains the suffix
//If it does, nothing happens.
//If not, the suffix value is passed to the method and added to the pieceSuffix member variable
public void AddSuffix(string suffix)
{
if (!pieceSuffix.Contains(suffix)) { pieceSuffix += suffix; }
}


//Uses the Replace method of the string class to remove the passed suffix from the pieceSuffix variable
public void RemoveSuffix(string suffix)
{
pieceSuffix = pieceSuffix.Replace(suffix, "");
}


public void RotatePiece(bool Clockwise)
{
switch (pieceType)
{
case "Left,Right":
pieceType = "Top,Bottom";
break;
case "Top,Bottom":
pieceType = "Left,Right";
break;
case "Left,Top":
if (Clockwise)
pieceType = "Top,Right";
else
pieceType = "Bottom,Left";
break;
case "Top,Right":
if (Clockwise)
pieceType = "Right,Bottom";
else
pieceType = "Left,Top";
break;
case "Right,Bottom":
if (Clockwise)
pieceType = "Bottom,Left";
else
pieceType = "Top,Right";
break;
case "Bottom,Left":
if (Clockwise)
pieceType = "Left,Top";
else
pieceType = "Right,Bottom";
break;
case "Empty":
break;
}
}


//Creates an empty List object for holding the ends we want to return to the calling code
//Uses the split() method of the string class to get each end listed in the pieceType
//EX Top,Bottom will return two elements, Top and Bottom
public string[] GetOtherEnds(string startingEnd)
{
List<string> opposites = new List<string>();
foreach (string end in pieceType.Split(','Winky Winky)
{
if (end != startingEnd) { opposites.Add(end); }
}
return opposites.ToArray();
}


//Returns 'true' if the pieceType string contains the string value passed in as the direction parameter
//This allowed the GamePiece class to determine if the piece has a connector facing in particular direction
public bool HasConnector(string direction)
{
return pieceType.Contains(direction);
}


public Rectangle GetSourceRect()
{
int x = textureOffsetX;
int y = textureOffsetY;


if (pieceSuffix.Contains("W"))
{
x += PieceWidth + texturePaddingX;
}


y += (Array.IndexOf(PieceTypes, pieceType) * (PieceHeight + texturePaddingY));


return new Rectangle(x, y, PieceWidth, PieceHeight);
}




}
}




GameBoard

    using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;


namespace Flood_Control
{
class GameBoard
{
//Generate Random Numbers
Random rand = new Random();


//Size of the playing board
public const int GameBoardWidth = 8;
public const int GameBoardHeight = 10;


//Arrays provide the storage mechanism for the GamePiece objects that make up the 8 by 10 piece board
private GamePiece[,] boardSquares = new GamePiece[GameBoardWidth, GameBoardHeight];
//Identify scoring pipe combinations.
private List<Vector2> WaterTracker = new List<Vector2>();


public GameBoard()
{
ClearBoard();
}


public void ClearBoard()
{
for (int x = 0; x < GameBoardWidth; x++)
{
for (int y = 0; y < GameBoardHeight; y++)
{
boardSquares[x, y] = new GamePiece("Empty");
}
}
}


//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public void RotatePiece(int x, int y, bool clockwise)
{
boardSquares[x, y].RotatePiece(clockwise);
}


//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public Rectangle GetSourceRect(int x, int y)
{
return boardSquares[x, y].GetSourceRect();
}


//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public string GetSquare(int x, int y)
{
return boardSquares[x, y].PieceType;
}


//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public void SetSquare(int x, int y, string pieceName)
{
boardSquares[x, y].SetPiece(pieceName);
}


//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public bool hasConnector(int x, int y, string direction)
{
return boardSquares[x, y].HasConnector(direction);
}


//Gets a random value from the PieceTypes array and assigns it to a GamePiece.
//With the Rnadom.NExt() method overload used here, the second parameter is non-inclusive
//In order to generate a number from 0 to 5, the second parameter needs to be 6
public void RandomPiece(int x, int y)
{
boardSquares[x, y].SetPiece(GamePiece.PieceTypes[rand.Next(0, GamePiece.MaxPlayablePiecesIndex+1)]);
}


//Looks at the piece above to see if it makred as Empty. If it is, the method will subtract one from rowLookup and start over until
//it reaches the top of the board. If no non-empty pieces are found when the top of the board is reached, the method does nothign and exits.


//When a non-empty piece is found, it is copies to the destination square
//The copy is changed to an empty piece. The rowLookup variable is set to -1 to ensure that the loop does not continue to run
public void FillFromAbove(int x, int y)
{
int rowLookUp = y - 1;


while (rowLookUp >= 0)
{
if (GetSquare(x, rowLookUp) != "Empty")
{
SetSquare(x, y, GetSquare(x, rowLookUp));
SetSquare(x, rowLookUp, "Empty");
rowLookUp = -1;
}
rowLookUp--;
}
}


//When called with 'true' passed as dropSquares, the loping logic processes one column at a time from the bottom up.
//When it finds and empty square it called FillFromAbove() to pull a filled square from above into that locaiton.


//The Reason the processing order is imporant here is that, by filling a lower square from a higher position,
//That higher position will become empty. It in turn will need to be filled from above


//After the holes are filled or dropSquares is set to false, GenerateNewPieces() examines each square in BoardSquares and asks it to generate a random piece for each square
//that contains an empty piece
public void GenerateNewPieces(bool dropSquares)
{
if (dropSquares)
{
for (int x = 0; x < GameBoard.GameBoardWidth; x++)
{
for (int y = GameBoard.GameBoardHeight - 1; y >= 0; y--)
{
if (GetSquare(x, y) == "Empty")
{
FillFromAbove(x, y);
}
}
}
for (int y = 0; y < GameBoard.GameBoardHeight; y++)
{
for (int x = 0; x < GameBoard.GameBoardWidth; x++)
{
if (GetSquare(x, y) == "Empty")
{
RandomPiece(x, y);
}
}
}
}
}


//Loops through each item in the boardSquares array and removes the W suffix from the GamePiece.
//To fill a piece with water, the FillePiece() method adds the W suffix to the GamePiece
//Recall that by having a W suffix, the GetSourceRect() method of the GamePiece shifts the source rectangle one tile to the right on the sprite sheet,
//returning the image for a pipe filled with water instead of an empty pipe
public void ResetWater()
{
for (int y = 0; y < GameBoardHeight; y++)
{
for (int x = 0; x < GameBoardWidth; x++)
{
boardSquares[x, y].RemoveSuffix("W");
}
}
}


public void FillPiece(int X, int Y)
{
boardSquares[X, Y].AddSuffix("W");
}


public void PropagateWater(int x, int y, string fromDirection)
{
if ((y >= 0) && (y < GameBoardHeight) && (x >= 0) && (x < GameBoardWidth))
{
if (boardSquares[x, y].HasConnector(fromDirection) && !boardSquares[x, y].Suffix.Contains("W"))
{
FillPiece(x, y);
WaterTracker.Add(new Vector2(x, y));
foreach (string end in boardSquares[x, y].GetOtherEnds(fromDirection))
{
switch (end)
{
case "Left": PropagateWater(x - 1, y, "Right"); break;
case "Right": PropagateWater(x + 1, y, "Left"); break;
case "Top": PropagateWater(x, y - 1, "Bottom"); break;
case "Bottom": PropagateWater(x, y + 1, "Top"); break;


}
}
}
}
}


public List<Vector2> GetWaterChain(int y)
{
WaterTracker.Clear();
PropagateWater(0, y, "Left");
return WaterTracker;
}
}
}


(adsbygoogle = window.adsbygoogle || []).push({});

The following user thanked Pichu for this useful post:

01-21-2013, 05:32 AM #2
Rath
Today Will Be Different
At one point I was working on making a little game in C#... however I gave up because I just ran out of time. And nice looking game.. it reminds me of hacking on BioShock lol.
01-24-2013, 02:53 PM #3
Pichu
RIP PICHU.
Originally posted by RathHavoc View Post
At one point I was working on making a little game in C#... however I gave up because I just ran out of time. And nice looking game.. it reminds me of hacking on BioShock lol.


This isn't mine; it's from a book. I've modified a couple things here and there and added my own comments but other than that 99% of this is the book's source.

I'm still learning and I felt like sharing this with others to use as an example. School has been keeping me busy from practicing as now I'm having to relearn Java and manage C# XNA as well as study for all my classes.

I'm still planning on learning XNA to the point where I can make a game; I'll most likely start out on smaller indie games like simple character puzzle maze games with things like simple enemies, jumps, etc then when I'm comfortable with animation, and the necessecities of a game; I'll try and move onto the production of a simple RPG with some friends..

Copyright © 2026, NextGenUpdate.
All Rights Reserved.

Gray NextGenUpdate Logo