Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Kết quả 1 đến 2 của 2
  1. #1
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    Học hỏi và trao đổi: viết trò chơi Tetris bằng visual c++.


    phần code được viết bằng visual studio 2010, bạn nào muốn chạy thử thì build nhé.
    Mình sẽ giải thích từng ngày để các bạn hiểu thêm, ai có vốn tiếng anh tốt thì cứ vào đọc code sẽ hiểu
    vì mình giải thích hết trong code.
    Tetris.rar[IMG]images/smilies/thinking.gif[/IMG]

    Phần mềm:
    Visual Studio 10, nếu bạn nào có nhu cầu thì inbox mình thì mình sẽ upload lên đây.
    Extension :
    Whole Tomato visual assist: có thể lấy trên forum này.


    Ngày 1: Yêu cầu về trò chơi
    1. Trò chơi này bao gồm 7 biểu tượng khác nhau và mỗi biểu tượng có một màu.
    2. Nhiệm vụ của người chơi là di chuyển và xoay biểu tượng sao nhiều dòng được san lấp.
    3. Khi một dòng san lấp thì dòng đó sẽ bị xoá và cộng 1 điểm cho một dòng.
    4. Cửa sổ window được chia làm 2: bên trái là game tetris và bên phải là điểm của người chơi và biểu tượng kế tiếp.

    Lớp FigureInfo:
    định nghĩa 7 màu cho từng biểu tượng: dùng lớp COLORREF

    const COLORREF RED = RGB(255, 0, 0); //màu đỏ cho hình vuông.
    const COLORREF BROWN = RGB(255, 128, 0); // màu nâu cho hình chữ nhật dài.
    const COLORREF TURQUOISE = RGB(0, 255, 255); //màu ngọc lam cho hình turquoise xuôi theo kim dồng hồ.
    const COLORREF GREEN = RGB(0, 255, 0); // màu xanh lá cây cho hình turquoise ngược chiều đòng hồ.
    const COLORREF BLUE = RGB(0, 0, 255); // màu xanh biển cho hình chữ L xuôi theo kim đồng hồ
    const COLORREF PURPLE = RGB(255, 0, 255); // màu vàng cho hình chữ L ngược chiều kim đồng hồ.
    const COLORREF YELLOW = RGB(255, 255, 0); // màu vành là hình nửa chữ thập.


    //tất cả các hình đều có thể xoay theo hướng đông nam tây bắc : tương ứng với đống hồ ợ vị trí 3h, 6h,9h,12h. Tại sao dùng hướng đông tây nam bắc, vì người chơi phải quay hình để lấy điểm.
    //theo qui định là hình vuông X có vị trí (x,y) = (0,0) trên trục toạ độ màn hình X, Y. Qui định vị trí (0,0) là góc trái trên cùng màn hình, nếu hình vuông ở bên phài (0,0) thì + 1, bên dưới (0,0) là + 1, ben trái (0,0) thi -1 và bên trên (0,0) là -1
    //square là một lớp hướng đối tượng bao gồm vị trí iRow, và iCol trên Grid.
    //FigureShape là một mảng bao gồm 4 Squares
    typedef Square FigureShape[4];


    //hình vuông: có chữ X: chứa 4 hình vuông nhỏ để tạo thành 1 hình vuông lớn và màu đỏ.
    FigureShape RedGeneric = {Square(0, 0), Square(0, 1),
    Square(1, 1), Square(1, 0)};

    //mỗi figureInfo có 4 hình theo 4 hướng xoay giống nhau.
    typedef FigureShape* FigureInfo[4];

    FigureInfo RedInfo = {&RedGeneric, &RedGeneric,
    &RedGeneric, &RedGeneric};

    //biểu tượng màu nâu, màu xanh lam, màu xanh lá chỉ có thể xoay 2 hướng, hướng ngang và hướng dọc,
    //điều này có nghĩa là hướng bắc và hướng nam đều là một biểu tượng,
    //hướng đông và hướng tây cũng là một biểu tượng.

    FigureShape BrownVertical = {Square(0, 0), Square(-1, 0),
    Square(1, 0), Square(2, 0)};
    FigureShape BrownHorizontal = {Square(0, 0), Square(0, -1),
    Square(0, 1),Square(0, 2)};
    FigureInfo BrownInfo = {&BrownVertical, &BrownHorizontal,
    &BrownVertical, &BrownHorizontal};
    FigureShape TurquoiseVertical = {Square(0, 0), Square(-1, 0),
    Square(0, 1), Square(1, 1)};
    FigureShape TurquoiseHorizontal = {Square(0, 0), Square(1, -1),
    Square(1, 0), Square(0, 1)};
    FigureInfo TurquoiseInfo = {&TurquoiseVertical, &TurquoiseHorizontal,
    &TurquoiseVertical,&TurquoiseHorizontal};

    FigureShape GreenVertical = {Square(0, 0), Square(1, -1),
    Square(0, -1), Square(-1, 0)};
    FigureShape GreenHorizontal = {Square(0, 0), Square(0, -1),
    Square(1, 0), Square(1, 1)};
    FigureInfo GreenInfo = {&GreenVertical, &GreenHorizontal,
    &GreenVertical, &GreenHorizontal};

    //Các biểu tượng còn lại thì đều có hình dáng khác nhau theo 4 hướng
    //bao gồm biểu tượng màu vàng, màu xanh da trời, màu tím.
    FigureShape YellowNorth = {Square(0, 0), Square(0, -1),
    Square(-1, 0), Square(0, 1)};
    FigureShape YellowEast = {Square(0, 0), Square(-1, 0),
    Square(0, 1), Square(1, 0)};
    FigureShape YellowSouth = {Square(0, 0), Square(0, -1),
    Square(1, 0), Square(0, 1)};
    FigureShape YellowWest = {Square(0, 0), Square(-1, 0),
    Square(0, -1), Square(1, 0)};

    FigureInfo YellowInfo = {&YellowNorth, &YellowEast,
    &YellowSouth, &YellowWest};

    FigureShape BlueNorth = {Square(0, 0), Square(0, -2),
    Square(0, -1),Square(-1, 0)};
    FigureShape BlueEast = {Square(0, 0), Square(-2, 0),
    Square(-1, 0), Square(0, 1)};
    FigureShape BlueSouth = {Square(0, 0), Square(1, 0),
    Square(0, 1), Square(0, 2)};
    FigureShape BlueWest = {Square(0, 0), Square(0, -1),
    Square(1, 0), Square(2, 0)};

    FigureInfo BlueInfo = {&BlueNorth, &BlueEast,
    &BlueSouth, &BlueWest};

    FigureShape PurpleNorth = {Square(0, 0), Square(-1, 0),
    Square(0, 1), Square(0, 2)};
    FigureShape PurpleEast = {Square(0, 0), Square(1, 0),
    Square(2, 0), Square(0, 1)};
    FigureShape PurpleSouth = {Square(0, 0), Square(0, -2),
    Square(0, -1), Square(1, 0)};
    FigureShape PurpleWest = {Square(0, 0), Square(0, -1),
    Square(-2, 0), Square(-1, 0)};

    FigureInfo PurpleInfo = {&PurpleNorth, &PurpleEast,
    &PurpleSouth, &PurpleWest};


    Trình bày lớp ColorGrid:
    Lớp ColorGrid được tạo ra bằng 20 dòng và 10 cột.Mỗi hình vuông trong biểu tương có chiều dài bằng 1 và chiều rộng bằng 1 và có một màu tùy thuộc vào màu của biểu tượng.
    Mỗi khi trò chơi được bắt đầu, chương trình sẽ bao gồm 200 ô vuông (20 X 10) màu trắng.(màu mặc định).
    Những chức năng của lớp:

    hàm clear : hàm này sẽ tạo ra 200 ô vuông màu trắng.
    void ColorGrid::Clear()
    {
    for (int iRow = 0; iRow < ROWS; ++iRow)
    {
    for (int iCol = 0; iCol < COLS; ++iCol)
    {
    Index(iRow, iCol) = DEFAULT_COLOR;
    }
    }
    }
    hàm index: có giá trị trả về là tham chiếu:hàm này được dùng để gán màu COLORREF vào thành phần của mảng.
    Chú ý: giá trị trả về phải là một biến thành viên và là một thành phần của mảng COLORREF 200 thành phần.
    Để truy xuất thành phần của mảng một chiều, chương trình cung cấp dòng thứ mấy và cột thứ mấy, và dùng công thức
    dòng thứ mấy nhân với số lượng cột và cộng với cột thứ mấy.
    COLORREF& ColorGrid::Index(int iRow, int iCol)
    {
    check((iRow >= 0) && (iRow < ROWS));
    check((iCol >= 0) && (iCol < COLS));
    return m_buffer[iRow * COLS + iCol];
    }
    hàm index: có giá trị trả về là hằng số COLORREF:hàm này được dùng để lấy màu của thành phần của mảng.
    tương tự như hàm trên.
    const COLORREF ColorGrid::Index(int iRow, int iCol) const
    {
    check((iRow >= 0) && (iRow < ROWS));
    check((iCol >= 0) && (iCol < COLS));
    return m_buffer[iRow * COLS + iCol];
    }

    hàm serialize: hàm này dùng để lưu màu của thành phần của mảng và gán màu cho thành phần của mảng. 200 ô vuông.
    Chú ý: CArchive cần thiết để lưu và nạp màu của thành phần mảng một cách đồng bộ, có nghĩa là chương trình sẽ có luu màu giống như những kiểu dữ liệu cơ bản như int, long...

    void ColorGrid::Serialize(CArchive& archive)
    {
    if (archive.IsStoring())
    {
    for (int iRow = 0; iRow < ROWS; ++iRow)
    {
    for (int iCol = 0; iCol < COLS; ++iCol)
    {
    archive << Index(iRow, iCol);
    }
    }
    }

    if (archive.IsLoading())
    {
    for (int iRow = 0; iRow < ROWS; ++iRow)
    {
    for (int iCol = 0; iCol < COLS; ++iCol)
    {
    archive >> Index(iRow, iCol);
    }
    }
    }
    }

    3. Lớp đối tượng Figure
    - Có 2 hàm khởi tạo:
    1. Hàm khơi tạo không có tham số: được dùng trong việc Serialize (đồng bộ), do đó không được gọi trực tiếp.

    Figure::Figure(int iDirection, COLORREF rfColor,
    const FigureInfo& figureInfo)
    :m_iRow(0),
    m_iCol(COLS / 2),
    m_iDirection(iDirection),
    m_rfColor(rfColor),
    m_pColorGrid(NULL)
    {
    ::memcpy(&m_figureInfo, &figureInfo, sizeof m_figureInfo);
    }

    2. Hàm khởi tạo có tham số: mỗi một figure(gồm 4 hình vuông nhỏ) có một hướng (dựa vào hình dáng của figure), màu, và thông tin của figure(tức là vị trí của 4 ô vuông nhỏ trong grid.)
    và điểm X trong figure luôn là bắt đầu tại dòng 0 và cột 5 trong grid. Thông tin của figure là một mảng 4 con trỏ, mỗi con trỏ trỏ đến một FigureShape(tức là 4 ô vuông nhỏ). Con trỏ đầu tiên thì trỏ tới một figureShape có hướng là Bắc,
    con trỏ thứ 2 trỏ đến mot figureshape có hướng là Đông, con trỏ thứ 3 trỏ đến một figureshape có hướng là Nam, con trỏ thứ 4 trỏ đến một figureshape có hướng là Tây.
    Lưu ý: chương trình vẽ figureshape dưa vào 2 biến thành viên m_iRow và m_iCol và hướng của figure. Khi người dùng đổi hướng chương trình sẽ lấy tọa độ (x,y) của 4 ô vuông (1 figureInfo) và cộng với m_iRow, m_iCol để xác định vị trí của figure trên grid.

    Figure::Figure(int iDirection, COLORREF rfColor,
    const FigureInfo& figureInfo)
    :m_iRow(0),
    m_iCol(COLS / 2),
    m_iDirection(iDirection),
    m_rfColor(rfColor),
    m_pColorGrid(NULL)
    {
    ::memcpy(&m_figureInfo, &figureInfo, sizeof m_figureInfo);
    }

    3. Hàm toán tử gán: (=):
    Được dùng để sao chép thông tin của một đối tương figure vào một đối tượng figure khác.

    Figure Figure:perator=(const Figure& figure)
    {
    if (this != &figure)
    {
    m_iRow = figure.m_iRow;
    m_iCol = figure.m_iCol;
    m_iDirection = figure.m_iDirection;
    m_rfColor = figure.m_rfColor;
    m_pColorGrid = figure.m_pColorGrid;

    ::memcpy(&m_figureInfo, &figure.m_figureInfo, sizeof m_figureInfo);
    }

    return *this;
    }

    4. Đôi lúc người chơi muốn xoay figure gần lề trái của grid, chương trình phải kiểm tra xem các ô vuông của hình
    có hơp lệ không, thí dụ Figure L không thể xoay khi ở bên lề trái vì khi xoay vị trí x,y của 4 ô vuông không hợp lệ.

    BOOL Figure::IsSquareValid(int iRow, int iCol) const
    {
    return (iRow >= 0) && (iRow < ROWS) &&
    (iCol >= 0) && (iCol < COLS) &&
    (m_pColorGrid->Index(iRow, iCol) == DEFAULT_COLOR);
    }

    BOOL Figure::IsFigureValid() const
    {
    FigureShape* pFigureShape = m_figureInfo[m_iDirection];

    for (int iIndex = 0; iIndex < SQUARE_ARRAY_SIZE; ++iIndex)
    {
    Square& square = (*pFigureShape)[iIndex];

    if (!IsSquareValid(m_iRow + square.Row(), m_iCol + square.Col()))
    {
    return FALSE;
    }
    }

    return TRUE;
    }

    4. Chương trình cho phép biểu tượng (figure) xoay theo bốn hướng (thí dụ chia hình vuông ra 4 phần, một lần xoay là 1/4 hình vuông) theo chiều kim dồng hồ và ngược chiều kim đồng hồ.
    Theo chiều kim đồng hồ Bắc -> Đông -> Nam -> Tây và ngược chiều kim đồng hồ Bắc ->Tây -> Nam -> Đông.

    void Figure::RotateClockwiseOneQuarter()
    {
    switch (m_iDirection)
    {
    case NORTH:
    m_iDirection = EAST;
    break;

    case EAST:
    m_iDirection = SOUTH;
    break;

    case SOUTH:
    m_iDirection = WEST;
    break;

    case WEST:
    m_iDirection = NORTH;
    break;
    }
    }

    void Figure::RotateAnticlockwiseOneQuarter()
    {
    switch (m_iDirection)
    {
    case NORTH:
    m_iDirection = WEST;
    break;

    case WEST:
    m_iDirection = SOUTH;
    break;

    case SOUTH:
    m_iDirection = EAST;
    break;

    case EAST:
    m_iDirection = NORTH;
    break;
    }
    }

    //trong qua trình xoay hình figure, đầu tiên chương trình giả sử là figure hợp lệ (tức là 4 ô vuông đều nằm trong grid) nên cho phép xoay, nhưng sau đó,
    //chương trình phát hiện ra là figure không hợp lệ, chương trình sẽ xoay ngươc lại. Do đó chương trình cần 2 hàm xoay ngựoc và xuôi kim đồng hồ để bổ
    //xung cho nhau.
    BOOL Figure::RotateClockwise()
    {
    RotateClockwiseOneQuarter();

    if (IsFigureValid())
    {
    return TRUE;
    }
    else
    {
    RotateAnticlockwiseOneQuarter();
    return FALSE;
    }
    }

    BOOL Figure::RotateAnticlockwise()
    {
    RotateAnticlockwiseOneQuarter();

    if (IsFigureValid())
    {
    return TRUE;
    }

    else
    {
    RotateClockwiseOneQuarter();
    return FALSE;
    }
    }

    5. Chương trình cho phép người dùng di chuyển sang trái, sang phải, và di chuyển xuống nhanh hơn.
    Đầu tiên chương giả sử là figure hợp lệ nên cho phép di chuyển, nhưng sau đó kiểm tra nếu không hợp lệ chương trình sẽ đảo ngược lại.
    lưu ý: chương trình thay đôi m_iCol khi di chuyen sang trái và phải và thay đổi i_Row khi di chuyển xuống.

    BOOL Figure::MoveLeft()
    {
    --m_iCol;

    if (IsFigureValid())
    {
    return TRUE;
    }

    else
    {
    ++m_iCol;
    return FALSE;
    }
    }

    BOOL Figure::MoveRight()
    {
    ++m_iCol;

    if (IsFigureValid())
    {
    return TRUE;
    }

    else
    {
    --m_iCol;
    return FALSE;
    }
    }

    BOOL Figure::MoveDown()
    {
    ++m_iRow;

    if (IsFigureValid())
    {
    return TRUE;
    }

    else
    {
    --m_iRow;
    return FALSE;
    }
    }

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    bài viết có ích mà sao không thấy ai comment vậy [IMG]images/smilies/smile.png[/IMG]

 

 

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •