Membuat Game Mencocokan Objek dengan C++ (Part 2) Oleh: Sendy PK
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...
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 "
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(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.
Tentang Penulis 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