Like Tree3Likes

Need help with rotation in Tetris game

This is a discussion on Need help with rotation in Tetris game within the C++ Programming forums, part of the General Programming Boards category; I need help getting started with rotation of shapes in tetris c++ game made with darkGDK. Here is the current ...

  1. #1
    Registered User
    Join Date
    Feb 2012
    Posts
    26

    Post Need help with rotation in Tetris game

    I need help getting started with rotation of shapes in tetris c++ game made with darkGDK.

    Here is the current code:
    Code:
    //Dark GDK - The Game Creators - www.thegamecreators.com
    
    //the wizard has created a very simple project that uses Dark GDK
    //it contains the basic code for a GDK application
    
    //whenever using Dark GDK you must ensure you include the header file
    #include "DarkGDK.h"
    
    
    //Global
    int Block_Size=20;
    int grid[20][10];
    //colors
    const DWORD Red = dbRGB(255,0,0);
    const DWORD Green = dbRGB(0,255,0);
    const DWORD Blue = dbRGB(0,0,255);
    const DWORD Magenta = dbRGB(255,0,255);
    const DWORD Black = dbRGB(0,0,0);
    const DWORD White = dbRGB(255,255,255);
    const DWORD Yellow = dbRGB(255,255,0);
    const DWORD Cyan = dbRGB(0,255,255);
    const DWORD Orange = dbRGB(255,165,0);
    const DWORD Grey = dbRGB(177,177,177);
    //end colors
    //end global
    
    //Classes
    
    class Block {
    private:
        DWORD color;
        //int blocks[4];
    public:
        int x,y;
        Block();
        Block(int, int, DWORD);
        void draw();
        void move(int, int);
        void clear();
        //bool col();
    
    };
    Block::Block(){
    x = 0;
    y =0;
    color = White;
    }
    //Methods (Block)
    
    Block::Block(int X, int Y, DWORD COLOR)
    {
        x=X;
        y=Y;
        color=COLOR;
    }
    
    void Block::draw()
    {
        int x1, y1, x2, y2;
        x1=x*Block_Size;
        y1=y*Block_Size;
        x2=x1+Block_Size;
        y2=y1+Block_Size;
    
    //    dbInk(color, Black);
        dbBox(x1,y1,x2,y2);
    }
    
    void Block::clear()
    {
    
        dbInk(Black, Black);
        //dbBox(x1,y1,x2,y2);
        draw();
    }
    
    void Block::move(int dx, int dy)
    {
        x=x+dx;
        y=y+dy;
        dbInk(color, Black);
        draw();
    }
    
    //End Method (Block)
    
    class Shape {
    
    private:
        Block blocks[4];
        //int pos[8];
    public:
        int pos[8];
        DWORD color;
        Shape();
        void make_ishape();
        void make_oshape();
        void make_jshape();
        void make_lshape();
        void make_tshape();
        void make_zshape();
        void make_sshape();
        void move_shape(int,int);
        void draw_shape();
        bool col(int,int); 
    
        
    };
    //Methods (Shape)
    Shape::Shape() {
    blocks[0] = Block(0,0, White);
    blocks[1] = Block(0,0, White);
    blocks[2] = Block(0,0, White);
    blocks[3] = Block(0,0, White);
    }
    bool Shape::col(int dx, int dy)
    {
        for (int i=0; i<=4; i++)
        {
            if (blocks[i].y + dy > 19 || blocks[i].x + dx < 0 || blocks[i].x +dx > 9)
            {
                return false;
            }
            
        }
        return true;
    }
    
    
    void Shape::make_ishape()
    {
        blocks[0] = Block(pos[0],pos[1],Blue);
        blocks[1] = Block(pos[2],pos[3],Blue);
        blocks[2] = Block(pos[4],pos[5],Blue);
        blocks[3] = Block(pos[6],pos[7],Blue);
        
    }
    void Shape::make_oshape()
    {
        blocks[0] = Block(pos[0],pos[1],Green);
        blocks[1] = Block(pos[2],pos[3],Green);
        blocks[2] = Block(pos[4],pos[5],Green);
        blocks[3] = Block(pos[6],pos[7],Green);
        
    }
    void Shape::make_jshape()
    {
        blocks[0] = Block(pos[0],pos[1],Yellow);
        blocks[1] = Block(pos[2],pos[3],Yellow);
        blocks[2] = Block(pos[4],pos[5],Yellow);
        blocks[3] = Block(pos[6],pos[7],Yellow);
        
    }
    void Shape::make_lshape()
    {
        blocks[0] = Block(pos[0],pos[1],Orange);
        blocks[1] = Block(pos[2],pos[3],Orange);
        blocks[2] = Block(pos[4],pos[5],Orange);
        blocks[3] = Block(pos[6],pos[7],Orange);
        
    }
    void Shape::make_tshape()
    {
        blocks[0] = Block(pos[0],pos[1],Cyan);
        blocks[1] = Block(pos[2],pos[3],Cyan);
        blocks[2] = Block(pos[4],pos[5],Cyan);
        blocks[3] = Block(pos[6],pos[7],Cyan);
        
    }
    void Shape::make_zshape()
    {
        blocks[0] = Block(pos[0],pos[1],Red);
        blocks[1] = Block(pos[2],pos[3],Red);
        blocks[2] = Block(pos[4],pos[5],Red);
        blocks[3] = Block(pos[6],pos[7],Red);
        
    }
    void Shape::make_sshape()
    {
        blocks[0] = Block(pos[0],pos[1],Magenta);
        blocks[1] = Block(pos[2],pos[3],Magenta);
        blocks[2] = Block(pos[4],pos[5],Magenta);
        blocks[3] = Block(pos[6],pos[7],Magenta);
        
    }
    void Shape::draw_shape()
    {
        for (int i=0; i<4; i++)
        {
            blocks[i].draw();
        }
    }
    
    void Shape::move_shape(int dx, int dy)
    {
        for (int i=0; i<4; i++)
        {
            blocks[i].clear();
        }
        for (int i=0; i<4; i++)
        {
            blocks[i].move(dx,dy);
        }
    }
    
    
    class I_Shape: public Shape{
    public:
        I_Shape(int,int);
        
    private:
        DWORD color;
        //int pos[8];
    };
    
    //Methods (I_Shape)
    
    
    I_Shape::I_Shape(int x, int y):Shape()
    {
    
        color=Blue;
        pos[0]=x-1;
        pos[1]=y;
        pos[2]=x;
        pos[3]=y;
        pos[4]=x+1;
        pos[5]=y;
        pos[6]=x+2;
        pos[7]=y;
        make_ishape(); //makes I_Shape
    }
    
    
    class O_Shape: public Shape{
    public:
        O_Shape(int,int);
    private:
        DWORD color;
    };
    O_Shape::O_Shape(int x, int y):Shape()
    {
        color=Red;
        pos[0]=x;
        pos[1]=y;
        pos[2]=x;
        pos[3]=y-1;
        pos[4]=x+1;
        pos[5]=y-1;
        pos[6]=x+1;
        pos[7]=y;
        make_oshape(); //makes O_Shape
    }
    
    class J_Shape: public Shape{
    public:
        J_Shape(int,int);
    private:
        DWORD color;
    };
    J_Shape::J_Shape(int x, int y):Shape()
    {
        color=Yellow;
        pos[0]=x-1;
        pos[1]=y;
        pos[2]=x;
        pos[3]=y;
        pos[4]=x+1;
        pos[5]=y;
        pos[6]=x+1;
        pos[7]=y-1;
        make_jshape(); //makes I_Shape
    }
    
    class L_Shape: public Shape{
    public:
        L_Shape(int,int);
    private:
        DWORD color;
    };
    L_Shape::L_Shape(int x, int y):Shape()
    {
        color=Orange;
        pos[0]=x+1;
        pos[1]=y;
        pos[2]=x;
        pos[3]=y;
        pos[4]=x-1;
        pos[5]=y;
        pos[6]=x-1;
        pos[7]=y-1;
        make_lshape(); //makes L_Shape
    }
    
    class T_Shape: public Shape{
    public:
        T_Shape(int,int);
    private:
        DWORD color;
    };
    T_Shape::T_Shape(int x, int y):Shape()
    {
        color=Cyan;
        pos[0]=x-1;
        pos[1]=y;
        pos[2]=x;
        pos[3]=y;
        pos[4]=x;
        pos[5]=y-1;
        pos[6]=x+1;
        pos[7]=y;
        make_tshape(); //makes T_Shape
    }
    
    class Z_Shape: public Shape{
    public:
        Z_Shape(int,int);
    private:
        DWORD color;
    };
    Z_Shape::Z_Shape(int x, int y):Shape()
    {
        color=Red;
        pos[0]=x-1;
        pos[1]=y;
        pos[2]=x;
        pos[3]=y;
        pos[4]=x;
        pos[5]=y+1;
        pos[6]=x+1;
        pos[7]=y+1;
        make_zshape(); //makes Z_Shape
    }
    
    /*/class S_Shape: public Shape{
    public:
        S_Shape(int,int);
    private:
        DWORD color;
    };
    S_Shape::S_Shape(int x, int y):Shape()
    {
        color=Magenta;
        pos[0]=x-1;
        pos[1]=y;
        pos[2]=x;
        pos[3]=y;
        pos[4]=x;
        pos[5]=y+1;
        pos[6]=x-1;
        pos[7]=y+1;
        make_sshape(); //makes S_Shape
    }/*/
    
    // the main entry point for the application is this function 
    //(MAIN)
    void DarkGDK ( void )
    {
        dbInk(White,Black);
        dbBox(0,0,200,400);
        
        //I_Shape First(3,1);
        //O_Shape First(3,1);
        //J_Shape First(3,1);
        //L_Shape First(3,1);
        //T_Shape First(3,1);
        Z_Shape First(3,1);
        
        // turn on sync rate and set maximum rate to 1 fps
        dbSyncOn   ( );
        dbSyncRate (2);
        First.draw_shape();
        //First.move_shape(5,4);
        
    
        // our main loop
        while ( LoopGDK ( ) )
        { 
            
            
                if (dbLeftKey())
                {
                    if (First.col(-1,0))
                    {    
                    First.move_shape(-1,0);
                    }
                }
                if (dbRightKey())
                {
                    if (First.col(1,0))
                    {
                    First.move_shape(1,0);
                    }
                }    
                if (dbDownKey())
                {
                    if (First.col(0,1))
                    {
                    First.move_shape(0,1);        
                    }
                }
            // update the screen
            dbSync ( );
            dbWaitKey();
        }
        
        
        // return back to windows
        return;
    }

  2. #2
    Registered User antred's Avatar
    Join Date
    Apr 2012
    Location
    Germany
    Posts
    255
    PLEASE, for the love of God, get in the habit of precisely stating what exactly the problem is. Just saying "I need help with xyz" and dumping half a dozen pages of code just doesn't cut it.
    manasij7479 likes this.

  3. #3
    Registered User
    Join Date
    Feb 2012
    Posts
    26
    Quote Originally Posted by antred View Post
    PLEASE, for the love of God, get in the habit of precisely stating what exactly the problem is. Just saying "I need help with xyz" and dumping half a dozen pages of code just doesn't cut it.
    I said in the last post that I need help getting started with setting up rotation of the shapes. I have no idea how set it up or where to put it.

  4. #4
    Registered User
    Join Date
    May 2012
    Location
    Bonn, Germany
    Posts
    16
    Do you need help with:
    - actually rotating the shapes?
    - checking if a rotation is "allowed" by the game?
    - displaying the rotated shapes?

    ________________
    Visit my project: Derivative Calculator

  5. #5
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,308
    Quote Originally Posted by Dylan Metz View Post
    I said in the last post that I need help getting started with setting up rotation of the shapes. I have no idea how set it up or where to put it.
    Then you have a design problem, not a coding problem. You haven't thought through how you are going to make it work, yet you've jumped in and started coding anyway. Step away from the problem and give yourself some time to think about it. My most satisfying and memorable breakthroughs in programming when I was a kid, were when I was away from the computer on a two-week holiday.

    I'm sorry, I don't want to rob you of the satisfaction of having figured it out for yourself.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  6. #6
    Registered User
    Join Date
    Feb 2012
    Posts
    26
    My c++ teacher gave a lecture of rotating the pieces. but I am so confuse. I do not know where to put the code or how to call it. Do I put in one of the classes?

    My teacher gave us something like this: x'=px+x-y (or something like that).

  7. #7
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    If this is 2D a 2D rotation is essentially a rotation about the Z axis or the axis going into or coming out of the screen.

    pixel.x = origPixel.x * cosf(angle) - origPixel.y * sinf(angle)
    pixel.y = origPixel.x * sinf(angle) + origPixel.y * cosf(angle)

    I may have the signs mixed up on x and y but this should get you close.

  8. #8
    Registered User
    Join Date
    May 2012
    Location
    Bonn, Germany
    Posts
    16
    However, in Tetris only 90 rotations are allowed, which kind of simplifies this formula because all the sin and cos terms become -1, 0 or 1!

    ___________
    Visit my project: Online symbolic derivative calculator

  9. #9
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Then do not use that formula. I would personally create a shape rotated 0 degrees and store in an array or image. Simply change the way to iterate the array to draw the image to rotate it or for a more graphics oriented approach alter the u,v coordinates of the corners of the quad to get different 90 degree orientations.

    0 degrees =(up) u,v,v1,v2
    90 degrees = (right) swap u,v
    180 degrees (down) subtract 1.0 from v or swap v2,v
    270 degrees (left) swap u,v - swap u2,u or subtract 1 from u,u2

    0 = u,v,v1,v2
    90 = v,u,v2,u2
    180 = u,v2,u,v
    270 = v,u2,v2,u

    Note that if you replace u,v with i,j you also have the basis for the solution if you are using text mode graphics and drawing inside of a loop.
    Last edited by VirtualAce; 05-18-2012 at 06:55 PM.

  10. #10
    Registered User
    Join Date
    Feb 2012
    Posts
    26
    Thank-you for all the help in getting me started. I have one more question right now. Where to put all the code? Do I make a function or do I add it to a class?

  11. #11
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,308
    There's a lot more than simply rotating the coordinate system to worry about.
    You also have to pick the center of rotation sensibly.
    e.g. You could do it like this:
    Code:
    .... .... .XX. ....
    .X.. XXX. ..X. ...X
    .X.. X... ..X. .XXX
    .XX. .... .... ....
    or like this:
    Code:
    .X.. .... .... ....
    .X.. .XXX .XX. ..X.
    .XX. .X.. ..X. XXX.
    .... .... ..X. ....
    or this:
    Code:
    .X. ... XX. ..X
    .X. XXX .X. XXX
    .XX X.. .X. ...
    or several other ways.

    Most of which might seem okay initially, but when you play it you'll notice that the rotations feel unnatural.

    Also, you have to worry about other issues like pushing the block away from the edge if it would rotate into the sides.
    e.g. if you take an L shape hard up againse the left hand wall and rotate it about the middle block of its 3-blocks-in-a-row part, then you also need to implicitly push it one column to the right.

    I can hardly believe they're forcing people to write tetris clones nowdays. To me, tetris was always one of those standard thing that a rookie writes because they have reached the level where they think they can do it and want to give it a go. I remember getting carried away with mine and adding an AI self-playing mode, sounds, music, flashy graphics, highscores, custom skins etc.
    If your only reason to do it is because you've been forced to do it, then I'd say that's the problem right there. There's a tremendous amount that you learn through making a tetris clone, and it's all stuff that is never beyond the skill level of someone who really wants to make it. Being forced to make it just does not sit well with me.

    I advise you to draw out what you think the rotations for each block should look like. Then also think about what else you would have to do when the block is rotated in practice, to stop it going through the wall.
    Also, consider how you represent your blocks very carefully, keeping in mind what you're going to need to do with them. For example, you'll need to test when the block comes to rest upon another.
    Last edited by iMalc; 05-18-2012 at 08:05 PM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  12. #12
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    Center of rotation can be changed in tetris by storing each block of each piece as on offset from the center of the piece. This is exactly the same as creating a model in local space around 0,0,0. Your blocks then are represented in local coordinates and you can translate the entire block to any world x,y and render it correctly at that location.

  13. #13
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,308
    Quote Originally Posted by VirtualAce View Post
    Center of rotation can be changed in tetris by storing each block of each piece as on offset from the center of the piece. This is exactly the same as creating a model in local space around 0,0,0. Your blocks then are represented in local coordinates and you can translate the entire block to any world x,y and render it correctly at that location.
    That's not an idea I've considered before, and as much as it sounds good, to be honest I didn't, and probably wouldn't do it that way. But maybe that's just me. The thing about that idea is that your center might not be on a block. E.g. for the 2x2 square block, the offsets would be:
    (-0.5, -0.5) (-0.5, 0.5) (0.5, -0.5) (0.5, 0.5)
    You also need an offset from that coordinate system to an integer block boundary. Of course it could make for a nice rotation animation doing it that way.

    I originally did it by making a 4x4 table for each block in each position, with the origin implicitly being in the same spot for all of them. (1, 1) I think it was. There are only 19 unique tables since several pieces have symmetries. This also makes it easier to see what it looks like when coding. It can literally look like what I posted above if you store each piece in a string.

    I.e.
    Code:
    static const char *L1 = 
    	".X.."
    	".X.."
    	".XX."
    	"....";
    static const char *L2 = 
    	"...."
    	"XXX."
    	"X..."
    	"....";
    Last edited by iMalc; 05-18-2012 at 09:21 PM.
    Salem likes this.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  14. #14
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    The problem with the center not being on a block is more an issue of using text mode graphics instead of true pixel based graphics. Regardless of what you do in text mode your block can only ever be centered around one of the columns and rows available in text mode. If that is not the dead center of the object there is nothing you can do to fix it. The 'bitmap' to draw on in text mode is only 80 x 25 or 80 x 40 compared to 1024x768 or 1680x1050 or 1920x1080 or 1600x900. Clearly the graphical approach is the better one.

  15. #15
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,409
    O_o

    I've done it both ways.

    If you want smooth vertical dropping, certain shadow effects, or continuous rotations you are ultimately going to have to deal with the same mathematics you would have to deal with to rotate around a true center in any event so you may as well start with that.

    That said, the majority of "Tetris" clones appear to rotate around center by rotating around a specific anchor block and then adjusting the horizontal position. I'd recommend emulating that regardless of your ultimate approach because it is what the majority of players will expect. It is also completely necessary if you allow "z spins" (Allowing the players to "spin" the piece through impossible positions.) that some "Tetris" clones now allow.

    text mode is only 80 x 25 or 80 x 40
    Well, graphic mode is still obviously better but being that blocks are the core graphic element in "Tetris" anyway you can use the "half block" character to get "80x50" which after a few lines for score and level is exactly enough for a good horizontally played "Tetris" field.

    Soma

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need help with movement in tetris c++ game
    By Dylan Metz in forum C++ Programming
    Replies: 8
    Last Post: 05-23-2012, 10:56 AM
  2. problem with tetris game...
    By Sokay in forum C Programming
    Replies: 4
    Last Post: 10-11-2009, 04:16 AM
  3. Tetris game
    By Da-Nuka in forum Game Programming
    Replies: 1
    Last Post: 01-16-2005, 09:27 AM
  4. My tetris game 2.0
    By PJYelton in forum Game Programming
    Replies: 12
    Last Post: 04-25-2003, 04:40 AM
  5. tetris game
    By mrt in forum Game Programming
    Replies: 2
    Last Post: 04-08-2002, 11:01 AM

Tags for this Thread


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21