Setelah berhasil membuat project pada tutorial sebelumnya, selanjutnya kita akan membuat dokumen, Dokumen / view arsitektur adalah paradigma yang menarik di mana kita memisahkan data aplikasi yang sebenarnya dari menampilkan dari data kepada pengguna. Dokumen ini berisi semua data sementara pandangan mendap...

Membuat Game Mencocokan Objek dengan C++ (Part 2)

Setelah berhasil membuat project pada tutorial sebelumnya, selanjutnya kita akan membuat dokumen, Dokumen / view arsitektur adalah paradigma yang menarik di mana kita memisahkan data aplikasi yang sebenarnya dari menampilkan dari data kepada pengguna. Dokumen ini berisi semua data sementara pandangan mendapat data dari dokumen dan menampilkannya kepada pengguna beberapa fashion. Berikut data kami adalah papan permainan yang sebenarnya, waktu yang dibutuhkan untuk menyelesaikan permainan dan informasi terkait lainnya. Pandangan kami menampilkan permainan papan sebagai blok berwarna dan memungkinkan pengguna untuk mengklik mereka. Pandangan menangani interaksi pengguna dan memodifikasi data permainan dalam dokumen yang sesuai, maka pandangan diperbarui untuk mencerminkan perubahan dan siklus terus.

Dengan memilih opsi arsitektur dokumen / view di MFC Application Wizard, basis kode yang dihasilkan bersama dengan semua mekanik yang berkaitan dua.

Hal ini akhirnya waktu untuk memulai coding. Sebelum kita dapat menampilkan sesuatu di layar kita membutuhkan data untuk kembali ke atas sehingga kita akan mulai dengan mengembangkan bagian dokumen dari aplikasi diikuti oleh tampilan data itu.

Pertama kita akan membuat sebuah kelas yang mewakili papan permainan kami, sebut saja CSameGameBoard. Buat kelas baru dengan mengklik kanan pada proyek SameGame di Solution Explorer dan memilih "Add -> Kelas ..." atau "Tambah Kelas ..." dari menu Project. Kami ingin C ++ class dari C ++ kelompok dan klik "Add". Ini akan memunculkan Generic C ++ Kelas Wizard. Mengisinya dengan nama kelas yang kami memilih seperti yang muncul di bawah ini.

 

Sekarang mari kita mengisi di kelas game board. Berikut adalah source code untuk file header.

#pragma once

class CSameGameBoard
{
public:
  /*  Default Constructor */
  CSameGameBoard(void);
  /*  Destructor */
  ~CSameGameBoard(void);
  /*  Function to randomly setup the board */
  void SetupBoard(void);
  /*  Get the color at a particular location */
  COLORREF GetBoardSpace(int row, int col);
  /*  Accessor functions to get board size information */
  int GetWidth(void) const { return m_nWidth; }
  int GetHeight(void) const { return m_nHeight; }
  int GetColumns(void) const { return m_nColumns; }
  int GetRows(void) const { return m_nRows; }
  /*  Function to delete the board and free memory */
  void DeleteBoard(void);
private:
  /*  Function to create the board and allocate memory */
  void CreateBoard(void);
  /*  2D array pointer */
  int** m_arrBoard;
  /*  List of colors, 0 is background and 1-3 are piece colors */
  COLORREF m_arrColors[4];
  /*  Board size information */
  int m_nColumns;
  int m_nRows;
  int m_nHeight;
  int m_nWidth;
};

Kelas ini secara konsept cukup sederhana.  berisi pointer, yang disebut m_arrBoard, untuk array dua dimensi bilangan bulat yang mewakili kosong (0) atau salah satu dari tiga warna (1-3). Kami menambahkan variabel anggota untuk melacak baris (m_nRows), kolom (m_nColumns), lebar pixel (m_nHeight) dan tinggi (m_nHeight). Ada juga berfungsi untuk membuat, mengatur dan menghapus papan.

Untuk membuat method perlu mengalokasikan array dua dimensi untuk menyimpan game board dan menginisialisasi semua blok kosong. Metode setup akan me-reset papan permainan dengan secara acak memilih warna untuk setiap ruang di papan tulis. Akhirnya metode menghapus de-mengalokasikan memori yang kita gunakan untuk papan permainan untuk menghilangkan kebocoran memori.

Di dalam board ada juga jenis berbagai COLORREF. Sebuah COLORREF hanya 32-bit unsigned integer yang berisi nilai warna RGBA untuk aplikasi MFC. Array ini berisi warna untuk latar belakang, pada indeks nol, dan warna blok di indeks satu sampai tiga. Indeks ini adalah nomor yang sama yang diadakan di array dua dimensi bilangan bulat. Dalam konstruktor di bawah ini kita menggunakan RGB makro untuk menciptakan nilai COLORREF dari tiga bilangan bulat yang mewakili nilai-nilai merah, hijau dan biru.

 

Berikut ini adalah implementasi kelas CSameGameBoard, di SameGameBoard.cpp

#include "StdAfx.h"
#include "SameGameBoard.h"

CSameGameBoard::CSameGameBoard(void)
: m_arrBoard(NULL),
  m_nColumns(15), m_nRows(15),
  m_nHeight(35),  m_nWidth(35)
{
  m_arrColors[0] = RGB(  0,  0,  0);
  m_arrColors[1] = RGB(255,  0,  0);
  m_arrColors[2] = RGB(255,255, 64);
  m_arrColors[3] = RGB(  0,  0,255);
}

CSameGameBoard::~CSameGameBoard(void)
{
  //  Simply delete the board
  DeleteBoard();
}

void CSameGameBoard::SetupBoard(void)
{
  //  Create the board if needed
  if(m_arrBoard == NULL)
    CreateBoard();
  //  Randomly set each square to a color
  for(int row = 0; row < m_nRows; row++)
    for(int col = 0; col < m_nColumns; col++)
      m_arrBoard[row][col] = (rand() % 3) + 1;
}

COLORREF CSameGameBoard::GetBoardSpace(int row, int col)
{
  //  Check the bounds of the array
  if(row < 0 || row >= m_nRows || col < 0 || col >= m_nColumns)
    return m_arrColors[0];
  return m_arrColors[m_arrBoard[row][col]];
}

void CSameGameBoard::DeleteBoard(void)
{
  //  Don't delete a NULL board
  if(m_arrBoard != NULL)
  {
    for(int row = 0; row < m_nRows; row++)
    {
      if(m_arrBoard[row] != NULL)
      {
        //  Delete each row first
        delete [] m_arrBoard[row];
        m_arrBoard[row] = NULL;
      }
    }
    //  Finally delete the array of rows
    delete [] m_arrBoard;
    m_arrBoard = NULL;
  }
}

void CSameGameBoard::CreateBoard(void)
{
  //  If there is already a board, delete it
  if(m_arrBoard != NULL)
    DeleteBoard();
  //  Create the array of rows
  m_arrBoard = new int*[m_nRows];
  //  Create each row
  for(int row = 0; row < m_nRows; row++)
  {
    m_arrBoard[row] = new int[m_nColumns];
    //  Set each square to be empty
    for(int col = 0; col < m_nColumns; col++)
      m_arrBoard[row][col] = 0;
  }
}

 

Sekarang kita punya game board yang dienkapsulasi menjadi objek,  kita dapat membuat sebuah instance dari objek yang di kelas dokumen. Ingat bahwa kelas dokumen berisi semua data permainan kami dan itu dipisahkan dari tampilan atau display kode. Kemudian kita akan menyiapkan dokumen sebagai berikut. Berikut adalah file header, SameGameDoc.h (dicetak tebal).

#pragma once

#include "SameGameBoard.h"

class CSameGameDoc : public CDocument
{
protected: // create from serialization only
  CSameGameDoc();
  virtual ~CSameGameDoc();
  DECLARE_DYNCREATE(CSameGameDoc)

// Attributes
public:

  // Operations
public:

  /*  Functions for accessing the game board */
  COLORREF GetBoardSpace(int row, int col)
  { return m_board.GetBoardSpace(row, col); }
  void SetupBoard(void)   { m_board.SetupBoard(); }
  int GetWidth(void)      { return m_board.GetWidth(); }
  int GetHeight(void)     { return m_board.GetHeight(); }
  int GetColumns(void)    { return m_board.GetColumns(); }
  int GetRows(void)       { return m_board.GetRows(); }
  void DeleteBoard(void)  { m_board.DeleteBoard(); }


  // Overrides
public:
  virtual BOOL OnNewDocument();

protected:

  /*  Instance of the game board */
  CSameGameBoard m_board;


  // Generated message map functions
protected:
  DECLARE_MESSAGE_MAP()
};

 

Sebagian besar kode ini akan terlihat akrab bagi Anda kecuali untuk beberapa hal yang MFC tertentu. Untuk saat ini kita dapat mengabaikan DECLARE_DYNCREATE dan DECLARE_MESSAGE_MAP garis, mereka arahan MFC diulang.

Pada poin ini sebenarnya dokumen  adalah pembungkus yang sangat simple untuk kelas game board. Dalam artikel kemudian kami akan menambahkan fungsionalitas lebih yang akan memerlukan perubahan ke dokumen tapi untuk saat ini cukup sederhana. Kita akan  menambahkan sebuah instance dari kelas game board dan kemudian tujuh fungsi yang memanggil fungsi yang sama di papan tulis. Hal ini akan memungkinkan view untuk mengakses informasi melalui papan dokumen. File sumber untuk dokumen (SameGameDoc.cpp) juga sangat simple karena semua fungsi yang kita menambahkan memiliki diimplementasikan pada baris di bawah ini (yang dicetak tebal)

#include "stdafx.h"
#include "SameGame.h"

#include "SameGameDoc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CSameGameDoc
IMPLEMENT_DYNCREATE(CSameGameDoc, CDocument)
BEGIN_MESSAGE_MAP(CSameGameDoc, CDocument)
END_MESSAGE_MAP()

// CSameGameDoc construction/destruction
CSameGameDoc::CSameGameDoc()
{
}

CSameGameDoc::~CSameGameDoc()
{
}

BOOL CSameGameDoc::OnNewDocument()
{
  if (!CDocument::OnNewDocument())
    return FALSE;

  //  Set (or reset) the game board
  m_board.SetupBoard();

  return TRUE;
}

.Sebenarnya semua yang kita ditambahkan adalah panggilan untuk fungsi SetupBoard dalam penangan OnNewDocument dalam dokumen. Semua hal ini adalah memungkinkan pengguna untuk memulai permainan baru dengan built-in akselerator Ctrl + N atau dari menu File-> New.

Karena kami terus melalui serangkaian artikel kami akan menambahkan fungsi baru untuk kedua papan permainan dan dokumen untuk mengimplementasikan fitur yang berbeda untuk permainan tetapi untuk sekarang kita selesai dengan dokumen dan siap untuk menampilkan informasi ini dalam view..

Sekarang dokumen berisi objek game board diinisialisasi kita perlu untuk menampilkan informasi ini kepada pengguna. Di sinilah kita benar-benar dapat mulai melihat permainan hidup.

Langkah pertama adalah menambahkan kode untuk mengubah ukuran jendela ke ukuran yang benar. Sekarang jendela adalah ukuran standar yang tidak apa yang kita inginkan. Kami akan melakukan ini di override OnInitialUpdate. Pandangan kelas mewarisi OnInitialUpdate default yang mendirikan pandangan dan kami ingin menimpanya sehingga kita dapat mengubah ukuran jendela ketika pandangan awalnya diperbarui. Hal ini dapat dicapai dengan membuka Properties Window dari file header CSameGameView (yang akan benar-benar disebut SameGameView.h). Lakukan ini dengan menekan Alt + Enter atau dari menu View-> Properties Window (pada beberapa versi dari Visual Studio, maka akan View-> Windows yang lainnya -> Properties Window). Di bawah ini adalah apa yang akan Anda lihat di jendela properti.

 

Pada screenshoot kursormengarah pada bagian"Override", lalu klik di atasnya. Carilah opsi OnInitialUpdate, klik di atasnya, klik dropdown seperti yang ditunjukkan pada gambar di bawah ini dan pilih "<Add> OnInitialUpdate".

 

Kita akan menambahkan override OnInitialUpdate ke dalam viewkita dengan beberapa kode default di dalamnya untuk memanggil fungsi implementasi dari CViewi. Kemudian kita hanya menambahkan call ke fungsi ResizeWindow bahwa kita akan menulis. Lalu ketikkan kode berikut dalam file header 

#pragma once

class CSameGameView : public CView
{
protected: // create from serialization only
  CSameGameView();
    DECLARE_DYNCREATE(CSameGameView)

  // Attributes
public:
  CSameGameDoc* GetDocument() const;
  // Overrides
public:
  virtual void OnDraw(CDC* pDC);  // overridden to draw this view
  virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:

  // Implementation
public:

  void ResizeWindow();

  virtual ~CSameGameView();
#ifdef _DEBUG
  virtual void AssertValid() const;
  virtual void Dump(CDumpContext& dc) const;
#endif

  // Generated message map functions
protected:
  DECLARE_MESSAGE_MAP()
public:
  virtual void OnInitialUpdate();
};

#ifndef _DEBUG  // debug version in SameGameView.cpp
inline CSameGameDoc* CSameGameView::GetDocument() const
{ return reinterpret_cast<CSameGameDoc*>(m_pDocument); }
#endif

 

Sementara kita  mengubah ukuran pada kode, kita juga perlu menambahkan kode gambar untuk kelas CSameGameView. Header dan source file untuk tampilan yang sudah mengandung override fungsi yang disebut OnDraw. Di sinilah kita akan menempatkan kode gambar. Berikut adalah full source code untuk view.

#include "stdafx.h"
#include "SameGame.h"

#include "SameGameDoc.h"
#include "SameGameView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CSameGameView
IMPLEMENT_DYNCREATE(CSameGameView, CView)
BEGIN_MESSAGE_MAP(CSameGameView, CView)
END_MESSAGE_MAP()

// CSameGameView construction/destruction
CSameGameView::CSameGameView()
{
}

CSameGameView::~CSameGameView()
{
}

BOOL CSameGameView::PreCreateWindow(CREATESTRUCT& cs)
{
  return CView::PreCreateWindow(cs);
}

// CSameGameView drawing

void CSameGameView::OnDraw(CDC* pDC) // MFC will comment out the argument name by default; uncomment it
{
  //  First get a pointer to the document
  CSameGameDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if(!pDoc)
    return;
  //  Save the current state of the device context
  int nDCSave = pDC->SaveDC();
  //  Get the client rectangle
  CRect rcClient;
  GetClientRect(&rcClient);
  //  Get the background color of the board
  COLORREF clr = pDoc->GetBoardSpace(-1, -1);
  //	Draw the background first
  pDC->FillSolidRect(&rcClient, clr);
  //  Create the brush for drawing
  CBrush br;
  br.CreateStockObject(HOLLOW_BRUSH);
  CBrush* pbrOld = pDC->SelectObject(&br);
  //	Draw the squares
  for(int row = 0; row < pDoc->GetRows(); row++)
  {
    for(int col = 0; col < pDoc->GetColumns(); col++)
    {
      //  Get the color for this board space
      clr = pDoc->GetBoardSpace(row, col);
      //  Calculate the size and position of this space
      CRect rcBlock;
      rcBlock.top = row * pDoc->GetHeight();
      rcBlock.left = col * pDoc->GetWidth();
      rcBlock.right = rcBlock.left + pDoc->GetWidth();
      rcBlock.bottom = rcBlock.top + pDoc->GetHeight();
      //  Fill in the block with the correct color
      pDC->FillSolidRect(&rcBlock, clr);
      //  Draw the block outline
      pDC->Rectangle(&rcBlock);
    }
  }
  //  Restore the device context settings
  pDC->RestoreDC(nDCSave);
  br.DeleteObject();
}


// CSameGameView diagnostics
#ifdef _DEBUG
void CSameGameView::AssertValid() const
{
  CView::AssertValid();
}

void CSameGameView::Dump(CDumpContext& dc) const
{
  CView::Dump(dc);
}

//  non-debug version is inline
CSameGameDoc* CSameGameView::GetDocument() const
{
  ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSameGameDoc)));
  return (CSameGameDoc*)m_pDocument;
}
#endif //_DEBUG

void CSameGameView::OnInitialUpdate()
{
  CView::OnInitialUpdate();

  //  Resize the window
  ResizeWindow();

}


void CSameGameView::ResizeWindow()
{
  //  First get a pointer to the document
  CSameGameDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if(!pDoc)
    return;
  //  Get the size of the client area and the window
  CRect rcClient, rcWindow;
  GetClientRect(&rcClient);
  GetParentFrame()->GetWindowRect(&rcWindow);
  //  Calculate the difference
  int nWidthDiff = rcWindow.Width() - rcClient.Width();
  int nHeightDiff = rcWindow.Height() - rcClient.Height();
  //  Change the window size based on the size of the game board
  rcWindow.right = rcWindow.left +
    pDoc->GetWidth() * pDoc->GetColumns() + nWidthDiff;
  rcWindow.bottom = rcWindow.top +
    pDoc->GetHeight() * pDoc->GetRows() + nHeightDiff;
  //  The MoveWindow function resizes the frame window
  GetParentFrame()->MoveWindow(&rcWindow);
}

Hal ini sangat simple untuk menarik game board, kita hanya akan loop melalui setiap baris, kolom dengan kolom, dan menggambar persegi panjang berwarna. Ada satu argumen untuk fungsi OnDraw dan itu adalah pointer CDC. CDC kelas kelas dasar untuk semua konteks perangkat. Sebuah konteks perangkat adalah antarmuka generik untuk perangkat seperti layar atau printer. Di sini kita akan menggunakannya untuk menarik ke layar.

Pertama kita akan memulai fungsi dengan mendapatkan pointer ke dokumen sehingga kita bisa mendapatkan informasi papan. Berikutnya kita sebut fungsi SaveDC dari konteks perangkat. Fungsi ini menyimpan keadaan konteks perangkat sehingga kita dapat mengembalikannya setelah kami selesai.

//  Get the client rectangle
CRect rcClient;
GetClientRect(&rcClient);
//  Get the background color of the board
COLORREF clr = pDoc->GetBoardSpace(-1, -1);
//  Draw the background first
pDC->FillSolidRect(&rcClient, clr);

Selanjutnya kita akan memberi  warna hitam pada background sehingga kita mendapatkan dimensi dari wilayah klien dengan memanggil GetClientRect. Panggilan untuk GetBoardSpace (-1, -1) pada dokumen akan mengembalikan warna latar belakang dan FillSolidRect akan mengisi area klien dengan warna background

//  Create the brush for drawing
CBrush br;
br.CreateStockObject(HOLLOW_BRUSH);
CBrush* pbrOld = pDC->SelectObject(&br);

...

//  Restore the device context settings
pDC->RestoreDC(nDCSave);
br.DeleteObject();

Sekarang saatnya untuk menggambar persegi panjang.  Caranya yaitu dengan menggambar persegi panjang berwarna dan kemudian menggambar garis hitam di sekitarnya. Kita sekarang akan membuat objek brush untuk melakukan garis besar. Kita buat dari jenis brush, HOLLOW_BRUSH, disebut hollow karena ketika kita menggambar persegi panjang MFC akan ingin mengisi di tengah-tengah dengan beberapa pola. Kita tidak menginginkannya jadi kita akan menggunakan sikat berongga sehingga persegi panjang berwarna ditarik . Buat kuas dan alokasikan pada memori GDI lalu  hapus sehingga kita sumber GDI tidak bocor.

//  Draw the squares
for(int row = 0; row < pDoc->GetRows(); row++)
{
  for(int col = 0; col < pDoc->GetColumns(); col++)
  {
    //  Get the color for this board space
    clr = pDoc->GetBoardSpace(row, col);
    //  Calculate the size and position of this space
    CRect rcBlock;
    rcBlock.top = row * pDoc->GetHeight();
    rcBlock.left = col * pDoc->GetWidth();
    rcBlock.right = rcBlock.left + pDoc->GetWidth();
    rcBlock.bottom = rcBlock.top + pDoc->GetHeight();
    //  Fill in the block with the correct color
    pDC->FillSolidRect(&rcBlock, clr);
    //  Draw the block outline
    pDC->Rectangle(&rcBlock);
  }
}

 Nested loop yang sangat simple,di iiterasikan baris demi baris, kolom dengan kolom,dan  mendapatkan warna yang sesuai dari dokumen dengan menggunakan fungsi GetBoardSpace dari dokumen,hitung ukuran persegi panjang dengan warna dan kemudian menggambar blok . Menggambar menggunakan dua fungsi, FillSolidRect () untuk mengisi bagian berwarna dari blok dan Rectangle () untuk menggambar garis besar blok. Inilah  semua yang kita gambar pada blok view.

Fungsi terakhir yang kita dimasukkan ke dalam view adalah salah satu untuk mengubah ukuran jendela berdasarkan dimensi dari papan permainan. Dalam artikel kemudian kami akan memungkinkan pengguna untuk mengubah jumlah blok dan ukuran blok sehingga fungsi ini akan berguna nanti. Sekali lagi kita arahkan pointer ke dokumen untuk mendapatkan ukuran pada klien dan window sekarang.

//  Get the size of the client area and the window
CRect rcClient, rcWindow;
GetClientRect(&rcClient);
GetParentFrame()->GetWindowRect(&rcWindow);

Temukan perbedaan antara keduanya  dan memberi kita jumlah ruang yang digunakan oleh judul bar, menu dan border window.  Lalu kita dapat menambahkan perbedaan kembali ke ukuran area klien yang diinginkan  untuk mendapatkan ukuran window yang baru.

//  Calculate the difference
int nWidthDiff = rcWindow.Width() - rcClient.Width();
int nHeightDiff = rcWindow.Height() - rcClient.Height();
//  Change the window size based on the size of the game board
rcWindow.right = rcWindow.left +
  pDoc->GetWidth() * pDoc->GetColumns() + nWidthDiff;
rcWindow.bottom = rcWindow.top +
  pDoc->GetHeight() * pDoc->GetRows() + nHeightDiff;
//  The MoveWindow function resizes the frame window

Akhirnya fungsi GetParentFrame kembali dari pointer ke kelas CMainFrame jendela yang sebenarnya untuk game kita dan kita akan ubah ukuran jendela dengan memanggil MoveWindow.

GetParentFrame()->MoveWindow(&rcWindow);

Tampilan Game kita sekarang terlihat seperti berikut

 

Pada tutorial kali ini kita sudah melewati beberapa dasar dari Microsoft Foundation Classes dan Dokumen /View arsitektur . Kitai telah mengumpulkan objek game board yang berisi data permainan kami dan dibangun oleh view yang membuat data kepada pengguna. Dalam artikel berikutnya kita akan pergi ke pemrograman event driven, menanggapi peristiwa dari pengguna seperti klik mouse dan selesaikan dengan versi "dimainkan" dari permainan kami.


About Author

Sendy PK

Saya adalah Programmer yang memiliki impian untuk menguasai dunia kunjungi situs pribadi saya di www.spk.my.id dan Online Shop saya di www.spkshop.web.id


Comment & Discussions

    Please LOGIN before if you want to give the comment.