Feedback wanted on my tetris source
Working on a tetris clone in C# and Managed DirectX.
So far I got: blocks dropping, rotation, row removal and game restart when board is full.
No menus, no scoring and nothing fancy yet -- and the source is VERY VERY unpolished.
However, I'm posting it here, looking for some feedback to learn from, what I'm mostly looking for is
- feedback on my use of directx
- feedback on the way I limit input speed
Feel free to post any feedback though, but don't flame me for the many very obvious points of polishing ;) (for instance the 4-5 instances of the exact same code copied and pasted etc)
Anyways, you can grab the source ziped with a VS2005 solution here:
zip - homepage
And for those who just want to read the source...here goes:
...will reply with source in a sec...
thanks!
/* * T-Clone * Another tetris clone * Author: Jens B (jens@the-bengtssons.com) * * -- Search for 'QQQ' for TODO's -- * */using System;using System.Collections.Generic;using System.Text;using System.Windows.Forms;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;using Microsoft.DirectX.DirectInput;using System.Drawing; // for Color.Blue....QQQusing D3D = Microsoft.DirectX.Direct3D;using DInput = Microsoft.DirectX.DirectInput;namespace tclone{ /* * tcloneWindow * Window / Form class for the main window */ class tcloneWindow : Form { // d3d device private D3D.Device device = null; // dinput device private DInput.Device inputDevice = null; // textures private Texture backgroundTexture = null; // playfield / background image / texture private Texture blocksTexture = null; // blocks image // sprites private Sprite background = null; // playfield / background sprite private Sprite blocks = null; // blocks // "pile" array private int[,] pileArray = new int[10, 20]; // the block arrays, with rotations, that is x,y coordinate for each piece of the block, // for all 4 rotations #region Block arrays with rotations /* * # | # | ### | # * ### | ## | # | ## * | # | | # */ private int[, ,] block1 = new int[4, 4, 2] { { {1,0},{0,1},{1,1},{2,1} }, { {1,0},{0,1},{1,1},{1,2} }, { {0,0},{1,0},{2,0},{1,1} }, { {0,0},{0,1},{1,1},{0,2} } }; /* * #### */ private int[, ,] block2 = new int[4, 4, 2] { { {0,0},{1,0},{2,0},{3,0} }, { {0,0},{0,1},{0,2},{0,3} }, { {0,0},{1,0},{2,0},{3,0} }, { {0,0},{0,1},{0,2},{0,3} } }; /* * ## * ## */ private int[, ,] block3 = new int[4, 4, 2] { { {0,0},{1,0},{0,1},{1,1} }, { {0,0},{1,0},{0,1},{1,1} }, { {0,0},{1,0},{0,1},{1,1} }, { {0,0},{1,0},{0,1},{1,1} } }; /* * ## | # * ## | ## * | # */ private int[, ,] block4 = new int[4, 4, 2] { { {0,0},{1,0},{1,1},{2,1} }, { {1,0},{1,1},{0,1},{0,2} }, { {0,0},{1,0},{1,1},{2,1} }, { {1,0},{1,1},{0,1},{0,2} } }; /* * ## | # * ## | ## * | # */ private int[, ,] block5 = new int[4, 4, 2] { { {1,0},{2,0},{0,1},{1,1} }, { {0,0},{0,1},{1,1},{1,2} }, { {1,0},{2,0},{0,1},{1,1} }, { {0,0},{0,1},{1,1},{1,2} } }; #endregion // current & next piece private int currentPiece = -1; private int nextPiece = -1; private int currentColor = -1; // indicates wich color to use from the texture private int nextColor = -1; private int currentRotation = 0; // top left corner of playing piece private int currentX = 4; private int currentY = 0; // speed value / level / difficulty private int speed = 4; private int tick = 0; // funkey variable to control fps....QQQ private int lastFrame = 0; // another funkey variable to control input rate....QQQ private int lastInput = 0; /* * Constructor to set up properties for the main window * Put here instead of in a form designer window to make it more readable */ public tcloneWindow() { Text = "T-Clone by Jens B"; Width = 800; // QQQ support fullscreen Height = 600; FormBorderStyle = FormBorderStyle.Fixed3D; MaximizeBox = false; // adjust size to have a 800x600 client area (QQQ probably there is a smarter way to do this :P) Width = 800 + (800 - ClientSize.Width); Height = 600 + (600 - ClientSize.Height); } /* * Initialize the playfield etc. */ private void InitializePlayfield() { // zero out playfied for (int i = 0; i < 10; ++i) { for (int j = 0; j < 20; ++j) { pileArray[i, j] = 0; } } // set up current and next piece Random rnd = new Random(); currentPiece = rnd.Next(5); nextPiece = rnd.Next(5); currentColor = rnd.Next(5); nextColor = rnd.Next(5); currentX = 4; currentY = 0; } /* * Moves the piece down */ private void MovePiece() { tick += speed; if (tick >= 100) { tick = 0; ++currentY; } if (CheckHit()) { if (--currentY < 0) // reverse move { // we lost!! InitializePlayfield(); return; } // add piece to pile AddPiece(); // set up a new piece currentY = 0; currentX = 4; currentPiece = nextPiece; currentColor = nextColor; Random rnd = new Random(); nextPiece = rnd.Next(5); nextColor = rnd.Next(5); } } /* * Adds the current piece at the current position to the pile */ private void AddPiece() { /* * This block of code is reproduced waaaay to many times, need to change the logic a bit * Most likely by making the currentblock an array describing it.... */ int[, ,] block = new int[4, 4, 2]; switch (currentPiece) { case 0: block = block1; break; case 1: block = block2; break; case 2: block = block3; break; case 3: block = block4; break; case 4: block = block5; break; } for (int i = 0; i < 4; i++) { pileArray[currentX + block[currentRotation, i, 0], currentY + block[currentRotation, i, 1]] = currentColor+1; // +1 just to confuse :P } // QQQ no overflow check :P } /* * Checks if the piece hit the stack */ public bool CheckHit() { int[, ,] block = new int[4, 4, 2]; switch (currentPiece) { case 0: block = block1; break; case 1: block = block2; break; case 2: block = block3; break; case 3: block = block4; break; case 4: block = block5; break; } for (int i = 0; i < 4; ++i) { if (currentX + block[currentRotation, i, 0] > 9 || currentY + block[currentRotation, i, 1] > 19) { // we got a hit! return true; } if (pileArray[currentX + block[currentRotation, i, 0], currentY + block[currentRotation, i, 1]] != 0) { // we got a hit! return true; } } return false; } /* * Check input */ public void CheckInput() { // doing a move takes about 1/5 of a second, so make sure we dont process to quickly // QQQ is this a bright idea, and is 1/5 fast enough? int now = System.Environment.TickCount; if (now - lastInput < 200) return; KeyboardState state = inputDevice.GetCurrentKeyboardState(); if (state[Key.Left]) { if (--currentX < 0) currentX = 0; lastInput = System.Environment.TickCount; } if (state[Key.Right]) { ++currentX; if (CheckHit()) --currentX; lastInput = System.Environment.TickCount; } if (state[Key.Up]) { if (++currentRotation > 3) currentRotation = 0; if (CheckHit()) { if (--currentRotation < 0) currentRotation = 3; } lastInput = System.Environment.TickCount; } } /* * D3D initialization function * Also initialzes DInput */ private void InitializeGraphics() { try { PresentParameters presentParams = new PresentParameters(); presentParams.Windowed = true; // QQQ support fullscreen presentParams.SwapEffect = SwapEffect.Discard; device = new D3D.Device(0, D3D.DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, presentParams); } catch (DirectXException e) { MessageBox.Show(null, "Error intializing graphics: " + e.Message, "Error"); Close(); } // QQQ set up callbacks for loosing devices etc // set up input device inputDevice = new DInput.Device(DInput.SystemGuid.Keyboard); inputDevice.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive); inputDevice.Acquire(); } /* * Load textures and create background sprite */ private void LoadTextures() { // load the background texture -- QQQ load from resource to make the exe as self contained as possible string file = "C:\\Documents and Settings\\Jens\\My Documents\\Visual Studio 2005\\Projects\\tclone\\playfield.bmp"; backgroundTexture = TextureLoader.FromFile(device, file, 800, 600, 24, 0, Microsoft.DirectX.Direct3D.Format.Unknown, Microsoft.DirectX.Direct3D.Pool.Default, Microsoft.DirectX.Direct3D.Filter.None, Microsoft.DirectX.Direct3D.Filter.None, 0); // load the blocks texture -- QQQ load from resource to make the exe as self contained as possible file = "C:\\Documents and Settings\\Jens\\My Documents\\Visual Studio 2005\\Projects\\tclone\\blocks.bmp"; blocksTexture = TextureLoader.FromFile(device, file, 800, 28, 24, 0, Microsoft.DirectX.Direct3D.Format.Unknown, Microsoft.DirectX.Direct3D.Pool.Default, Microsoft.DirectX.Direct3D.Filter.None, Microsoft.DirectX.Direct3D.Filter.None, 0); // create the sprites background = new Sprite(device); blocks = new Sprite(device); // QQQ this dont seem to read the dimensions from the file //backgroundTexture = TextureLoader.FromFile(device, file); // QQQ for some reason this comented out code dont work... //Bitmap background = new Bitmap( file); //backgroundTexture = new Texture(device, background,Microsoft.DirectX.Direct3D.Usage.None, Microsoft.DirectX.Direct3D.Pool.Default); } /* * Draws the current piece */ private void DrawCurrentPiece() { int textureStartX = currentColor * 28; int[, ,] block = new int[4, 4, 2]; switch (currentPiece) { case 0: block = block1; break; case 1: block = block2; break; case 2: block = block3; break; case 3: block = block4; break; case 4: block = block5; break; } int startx = 80 + (28 * currentX); int starty = 10 + (28 * currentY); blocks.Begin(SpriteFlags.None); for (int i = 0; i < 4; ++i) { blocks.Draw2D(blocksTexture, new Rectangle(textureStartX, 0, 27, 27), new SizeF(28f, 28f), new Point(startx + (block[currentRotation, i, 0] * 28), starty + (block[currentRotation, i, 1] * 28)), Color.White); } blocks.End(); } /* * Draws the "next" piece */ private void DrawNextPiece() { int textureStartX = nextColor * 28; int[, ,] block = new int[4, 4, 2]; switch (nextPiece) { case 0: block = block1; break; case 1: block = block2; break; case 2: block = block3; break; case 3: block = block4; break; case 4: block = block5; break; } int startx = 515; // QQQ Write some clever code to center this int starty = 245; blocks.Begin(SpriteFlags.None); for (int i = 0; i < 4; ++i) { blocks.Draw2D(blocksTexture, new Rectangle(textureStartX, 0, 27, 27), new SizeF(28f, 28f), new Point(startx + (block[0, i, 0] * 28), starty + (block[0, i, 1] * 28)), Color.White); } blocks.End(); } /* * Rendering function that draws the contens of the window * */ private void Render() { if (device == null) return; // no device = do nothing // limit to 70 fps -- QQQ is this really a good idea? int now = System.Environment.TickCount; while (now - lastFrame < 15) // aproxes to 70 fps....QQQ { now = System.Environment.TickCount; Application.DoEvents(); // QQQ do i need to acctually sleep? } // begin the scene device.BeginScene(); // draw the playfield as a sprite background.Begin(SpriteFlags.None); background.Draw2D(backgroundTexture, new Rectangle(0, 0, 800, 600), new SizeF(800f, 600f), new Point(0, 0), Color.White); background.End(); // draw current piece DrawCurrentPiece(); // draw "pile" DrawPile(); // draw "next" piece, if applicable DrawNextPiece(); // end the scene device.EndScene(); device.Present(); } /* * Draws the pile! */ private void DrawPile() { int textureStartX = 0; int startx = 80; int starty = 10; blocks.Begin(SpriteFlags.None); for (int i = 0; i < 10; ++i) { for (int j = 0; j < 20; ++j) { if (pileArray[i, j] > 0) { textureStartX = (pileArray[i, j] - 1) * 28; blocks.Draw2D(blocksTexture, new Rectangle(textureStartX, 0, 27, 27), new SizeF(28f, 28f), new Point(startx + (i * 28), starty + (j * 28)), Color.White); } } } blocks.End(); } /* * Check for row removal or loss * returns false for loss, true for "ok" */ private bool CheckWin() { for (int i = 19; i >= 0; --i) { bool rowFull = false; for (int j = 0; j < 10; ++j) { if (pileArray[j, i] > 0) rowFull = true; else { rowFull = false; break; } } if (rowFull) { for (int y = i; y > 0; --y) { for (int x = 0; x < 10; ++x) { pileArray[x, y] = pileArray[x, y - 1]; } } for (int x = 0; x < 10; ++x) { pileArray[x, 0] = 0; } } } return true; } /* * mog, the main function! */ static void Main() { // create our window and show it tcloneWindow win = new tcloneWindow(); win.InitializeGraphics(); win.LoadTextures(); win.InitializePlayfield(); win.Show(); // game loop while (win.Created) { // check inputs win.CheckInput(); // move piece (will check for collision) win.MovePiece(); // check "win" (remove line(s)) / loss win.CheckWin(); // render d3d win win.Render(); // let windows do message processing Application.DoEvents(); } } }}
I just realized I left out 2 of the normal shapes of Tetris :P Please dont rub my nose in that :P
This is not a comment, I'm sorry. I think people won't have enough time to surf through all your code. If you want some advices, please post more specific questions about problems you encountered (design issues for example). You've already had a running game, then the best critic in your design will be your future games, through projects you'll fine tune your design to suit your own needs.
You may also take a look at good game programming books, the GDNet Book section maybe a good place.
You may also take a look at good game programming books, the GDNet Book section maybe a good place.
I got: "Error intializing graphics: Error in the application."
Other then that, looking through the source is quite nice. It's bound to be my non-working video card that's the problem.
Other then that, looking through the source is quite nice. It's bound to be my non-working video card that's the problem.
Quote:Original post by Rob Loach
I got: "Error intializing graphics: Error in the application."
Other then that, looking through the source is quite nice. It's bound to be my non-working video card that's the problem.
Either that, or you are not using the latest DirectX drivers.
Quote:Original post by Rob Loach
I got: "Error intializing graphics: Error in the application."
Other then that, looking through the source is quite nice. It's bound to be my non-working video card that's the problem.
Or... the hardcoded path to the game's graphical resources [wink]
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement