Feedback wanted on my tetris source

Started by
6 comments, last by darookie 18 years, 3 months ago
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!
// Jens
Advertisement
/* * 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();            }        }    }}
// Jens
I just realized I left out 2 of the normal shapes of Tetris :P Please dont rub my nose in that :P
// Jens
*drops a needle* anyone? :P
// Jens
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.
--> The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones <--
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.
Rob Loach [Website] [Projects] [Contact]
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.
// Jens
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