Otwieramy Visual Studio 2008
Wybieramy z menu File - New Project
Wybieramy typ projektu MFC, oraz szablon (template) MFC Application
Wpisujemy nazwe projektu np. Labiryncik, klikamy OK
Proszę skompilować (F7) oraz uruchomić aplikację (F5)
Zaglądamy do widoku plików (Solution Expolorer lub File View). Widać że Visual C++ wygenerował dla nas DUŻO klas, dużo kodu źródłowego.
Teraz, aplikacja wygenerowana przez Visual C++ składa się między innymi z następujących klas:
Klasa Dokumentu
CLabiryncikDoc - w klasie tej należy umieścić wszelkiego rodzaju dane które będą używane przez nasz program
Klasa Widoku
CLabiryncikView - w klasie tej należy umieścić wszelkiego rodzaju procedury które będą prezentować (wypisywać, rysować) nasze dane
W jaki sposób rozdzielić nasz labirynt pomiędzy klasę widoku i klasę dokumentu?
Zaglądnijmy do pliku nagłówkowego Labirynt.h który już kiedyś napisaliśmy
class Labirynt
{
public:
Labirynt(void);
//tablica z danymi labiryntu
char* m_labirynt[13];
//polozenie ludzika
int m_wiersz;
int m_kolumna;
//metoda wypiujace labirynt
void WypiszLabirynt();
};
oraz do pliku z kodem źródłowym Labirynt.cpp
#include <iostream>
using namespace std;
Labirynt::Labirynt(void)
{
m_labirynt[0] = "*************";
m_labirynt[1] = "* * * *";
m_labirynt[2] = "* *** *** * *";
m_labirynt[3] = "* * *";
m_labirynt[4] = "* * * *******";
m_labirynt[5] = "* * * * ";
m_labirynt[6] = "*** *** * ***";
m_labirynt[7] = "* * * * *";
m_labirynt[8] = "* *** * *** *";
m_labirynt[9] = "* * * * *";
m_labirynt[10] = "* * * * *** *";
m_labirynt[11] = "* * * *";
m_labirynt[12] = "*************";
m_wiersz=9;
m_kolumna=1;
}
void Labirynt::WypiszLabirynt()
{
for(int i=0;i<13;i++) {
if(i==m_wiersz) {
for(int j=0;j<m_kolumna;j++) cout << m_labirynt[i][j];
cout << "X";
for(int j=m_kolumna+1;j<13;j++) cout << m_labirynt[i][j];
}
else
cout << m_labirynt[i];
}
}
Jak rozdzielić tą klasę pomiędzy Klasę Dokumentu i Klasę Widoku?
Co stanowi tutaj dane, a co stanowi procedurę prezentowania tych danych?
W pliku nagłówkowym CLabiryncikDoc.h klasy dokumentu CLabiryncikDoc proszę znaleźć sekcję attributes (atrybuty) oraz dopisać dane labiryntu:
// Attributes
public:
//tablica z danymi labiryntu
char* m_labirynt[13];
//polozenie ludzika
int m_wiersz;
int m_kolumna;
Proszę uważać żeby nie zmienić niczego innego w pliku !
W pliku z kodem zródłowym CLabiryncikDoc.cpp klasy dokumentu CLabiryncikDoc proszę znaleźć konstruktor klasy i dopisać w nim wypełnianie klasy labiryntu:
// CLabiryncikDoc construction/destruction
CLabiryncikDoc::CLabiryncikDoc()
{
m_labirynt[0] = "*************";
m_labirynt[1] = "* * * *";
m_labirynt[2] = "* *** *** * *";
m_labirynt[3] = "* * *";
m_labirynt[4] = "* * * *******";
m_labirynt[5] = "* * * * ";
m_labirynt[6] = "*** *** * ***";
m_labirynt[7] = "* * * * *";
m_labirynt[8] = "* *** * *** *";
m_labirynt[9] = "* * * * *";
m_labirynt[10] = "* * * * *** *";
m_labirynt[11] = "* * * *";
m_labirynt[12] = "*************";
m_wiersz=9;
m_kolumna=1;
}
Proszę uważać żeby nie zmienić niczego innego w pliku !
Proszę w pliku nagłówkowym CLabiryncikView.h klasy widoku CLabiryncikView
znaleźć sekcję z operacjami (procedurami) i dopisać nagłówek procedury WypiszLabirynt
// Operations
public:
void WypiszLabirynt();
Proszę uważać żeby nie zmienić niczego innego w pliku !
Proszę w pliku z kodem źródłowym CLabiryncikView.cpp klasy widoku CLabiryncikView odnaleźć konstruktor i destruktor klasy CLabiryncikView i dopisać za nimi procedurę WypiszLabirynt.
void CLabiryncikView::WypiszLabirynt()
{
for(int i=0;i<13;i++) {
cout << m_labirynt[i];
}
}
Proszę zwrócić uwagę że procedura WypiszLabirynt jest teraz składową klasy CLabiryncikView.
Proszę uważać żeby nie zmienić niczego innego w pliku !
Proszę spróbować skompilować program.
Między innymi otrzymujemy następujące błędy:
1>c:\users\maciek\documents\visual studio 2008\projects\labiryncik\labiryncik\labiryncikview.cpp(42) : error C2065: 'm_labirynt' : undeclared identifier
Na czym polega problem?
Nie kompiluje się procedura WypiszLabirynt.
W jakiej klasie umieściliśmy procedurę WypiszLabirynt?
W jakiej klasie umieszczone zostały zmienne (atrybuty) m_wiersz, m_kolumna, oraz tablica m_labirynt??? W tej samej?
Musimy umieć dostać się z klasy widoku do klasy dokumentu.
Jeśli jesteśmy w klasie widoku, możemy dostać wskaźnik do klasy dokumentu pisząc
CLabiryncikDoc* pDoc = GetActiveDocument();
Jeśli jesteśmy natomiast w klasie dokumentu, i chcemy dostać wskaźnik do klasy widoku, musimy najpierw dostać wskaźnik do obiektu reprezentującego nasze okienko, i dopiero na końcu wskaźnik do obiektu widoku.
CMainFrame *pFrame = (CMainFrame*) AfxGetMainWnd();
CLabiryncikView *pView =
(CLabiryncikView*) pFrame->GetActiveView();
Proszę w procedurze WypiszLabirynt, dopisać na początku pobranie wskaźnika do klasy dokumentu.
Następnie przed odwołaniem do m_labirynt, proszę dopisać pDoc->, np. zamieniamy m_labirynt na pDoc->m_labirynt
Proszę skompilować program. Należy jeszcze na początku pliku CLabiryncikView.cpp dopisać
#include <iostream>
using namespace std;
najlepiej przed
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
Ponadto należy znaleźć w pliku CLabiryncikView.cpp procedurę OnDraw i dopisać w niej wywołanie WypiszLabirynt:
// CLabiryncikView drawing
void CLabiryncikView::OnDraw(CDC* /*pDC*/)
{
CLabiryncikDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
WypiszLabirynt();
}
Proszę spróbować skompilować i uruchomić program.
Wszystko działa tylko nic się nie rysuje!
Dlaczego ? Bo teraz mamy okienko graficzne a nie okienko tekstowe.
W okienku graficznym pisze / rysuje się w zupełnie inny sposób.
Musimy napisać procedurę WypiszLabirynt od nowa:
void CLabiryncikView::WypiszLabirynt(CDC* pDC)
{
CLabiryncikDoc* pDoc = GetDocument();
int poczatek_x = 5;
int poczatek_y = 5;
for(int i=0;i<13;i++) {
CString tekst(pDoc->m_labirynt[i]);
CSize size = pDC->GetTextExtent(tekst);
CRect rect(poczatek_x,poczatek_y,
poczatek_x+size.cx,poczatek_y+size.cy);
int format = DT_CENTER;
pDC->DrawText(tekst,&rect,format);
poczatek_y += size.cy;
}
}
Proszę skompilować i uruchomić program.
Czy wszystko jest w porządku?
Musimy przekazać nowy parametr CDC* pDC - to jest rysowacz który służy do rysowania w okienku. Musimy zmienić program w trzech miejscach:
W procedurze OnDraw należy odkomentować pDC oraz przekazać do procedury WypiszLabirynt
void CLabiryncikView::OnDraw(CDC* pDC)
{
CLabiryncikDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
WypiszLabirynt(pDC);
}
W pliku nagłówkowym CLabiryntView.h klasy CLabiryntView należy dopisać pDC do nazwy procedury WypiszLabirynt:
// Operations
public:
void WypiszLabirynt(CDC* pDC);
Proszę skompilować i uruchomić program.
Czy wszystko jest w porządku?
Problem polega na tym, że spacje i gwiazdki mają różną długość w trybie graficznym, i właściwie zamiast wypisywać labirynt powinniśmy go narysować...Musimy napisać nową procedurę NarysujLabirynt która naprawdę bedzie rysowała labirynt.
Proszę w pliku nagłówkowym CLabiryncikView.h klasy widoku CLabiryncikView
znaleźć sekcję z operacjami (procedurami) i dopisać nagłówek procedury NarysujLabirynt
// Operations
public:
void WypiszLabirynt();
void NarysujLabirynt(CDC *pDC);
Proszę w pliku z kodem źródłowym CLabiryncikView.cpp klasy widoku CLabiryncikView odnaleźć konstruktor i destruktor klasy CLabiryncikView i dopisać za nimi procedurę NarysujLabirynt.
void CLabiryncikTestView::NarysujLabirynt(CDC* pDC)
{
CLabiryncikTestDoc* pDoc = GetDocument();
//pobieramy rozmiar okna
CRect okno;
GetClientRect(&okno);
//szerokosc pola = 1/30 szerokosci okno
int dx = okno.right/30;
//wysokosc pola = 1/30 wysokosci okna
int dy = okno.bottom/30;
//punkt w ktorym bedzie znajdowac sie srodek pierwszego pola labiryntu
CPoint pierwsze_pole(2*dx,2*dy);
//petla po wierszach z tablicy labiryntu
for(int i=0;i<13;i++)
//petla po kolumnach z tablicy labiryntu
for(int j=0;j<13;j++) {
//punkt w ktorym znajduje sie srodek pola [i][j] labiryntu
CPoint pole(pierwsze_pole.x+j*2*dx, pierwsze_pole.y+i*2*dy);
if(pDoc->m_labirynt[i][j]=='*'){
//rozmiar cegielki
CRect cegielka(pole.x-dx,pole.y-dy,pole.x+dx,pole.y+dy);
//pedzel do zamalowania cegielki
CBrush pedzel(RGB(255,0,0));
//rysujemy cegielke
pDC->FillRect(&cegielka,&pedzel);
}
}
}
Proszę dorysować ludzika w labiryncie. Dopisujemy w procedurze RysujLabirynt
if(pDoc->m_wiersz==i && pDoc->m_kolumna==j){
//rozmiar elipsy
CRect ludzik(pole.x-dx/2,pole.y-dy/2,pole.x+dx/2,pole.y+dy/2);
//rysujemy elipse
pDC->Ellipse(&ludzik);
pDC->MoveTo(pole.x,pole.y);
pDC->LineTo(pole.x+dx/2,pole.y+dy/2);
pDC->MoveTo(pole.x,pole.y);
pDC->LineTo(pole.x-dx/2,pole.y+dy/2);
pDC->MoveTo(pole.x,pole.y);
pDC->LineTo(pole.x,pole.y+dy/2);
pDC->MoveTo(pole.x,pole.y+dy/2);
pDC->LineTo(pole.x+dx/2,pole.y+dy);
pDC->MoveTo(pole.x,pole.y+dy/2);
pDC->LineTo(pole.x-dx/2,pole.y+dy);
}
Proszę w klasie dokumentu CLabiryncikDoc dodać procedury WLewo, WPrawo, WGore, WDol które będą sprawdzały czy da się przesunąć ludzika w odpowiednią stronę, i jeśli się da, to będą go przesuwały, np.
void CLabiryncikTestDoc::WLewo()
{
if(m_labirynt[m_wiersz][m_kolumna-1]==' ')m_kolumna--;
}
Dodamy teraz przyciski do kierowania ludzikiem.
Proszę otworzyć z lewej strony Solution Explorer (lub File View) kliknąć dwa razy na Labiryncik.rc
Otworzy się przeglądarka zasobów.
Proszę kliknąć prawym przyciskiem myszy na projekcie i wybrać Add - Resource - Dialog, kliknąć New.
Proszę na Toolbox'ie wybrać Button (przycisk) i narysować cztery nowe buttony na okienku dialogowym.
Proszę kliknąć prawym przyciskiem myszy na przycisku, wybrać Properties i zmienić Caption na Góra, Dół, Lewo i Prawo.
Proszę kliknąć dwa razy myszą na dowolnym przycisku. Pojawi się okienko kojarzenia nazwy klasy z oknem dialogowym. Proszę wpisać nazwę klasy Sterownik.
Proszę teraz otworzyć nagłówek Sterownik.h i dopisać:
Sterownik(CWnd* pParent,CDocument* pDoc,
CView *pView); // standard constructor
//atributes
CDocument* m_pDoc;
CView *m_pView;
Proszę otworzyć plik źródłowy Sterownik.cpp i dopisać w konstruktorze:
Sterownik::Sterownik(CWnd* pParent, CDocument* pDoc, CView* pView)
: CDialog(Sterownik::IDD, pParent)
{
m_pView=pView;
m_pDoc=pDoc;
}
Proszę teraz dwu-klikać kolejno na wszystkich czterech przyciskach - otworzy
nam się wówczas program który uruchomiony zostanie gdy ktoś wciśnie przycisk podczas działania programu. Proszę wpisać odpowiednio:
((CLabiryncikTestDoc*)m_pDoc)->WGore();
((CLabiryncikTestView*)m_pView)->Invalidate();
lub
((CLabiryncikTestDoc*)m_pDoc)->WDol();
((CLabiryncikTestView*)m_pView)->Invalidate();
lub
((CLabiryncikTestDoc*)m_pDoc)->WLewo();
((CLabiryncikTestView*)m_pView)->Invalidate();
lub
((CLabiryncikTestDoc*)m_pDoc)->WPrawo();
((CLabiryncikTestView*)m_pView)->Invalidate();
Proszę też na początku tego pliku dopisać:
#include "MainFrm.h"
#include "LabiryncikTestDoc.h"
#include "LabiryncikTestView.h"
Musimy wreszcie podczas uruchamiania programu stworzyć nasze okno dialogowe do kierowania ludzikiem.
Proszę otworzyć z lewej strony Solution Explorer (lub File View) kliknąć dwa razy na Labiryncik.rc
Otworzy się przeglądarka zasobów.
Wchodzimy do pola Menu, wybieramy IDR_LABIRYNCIK i dopisujemy do menu pole Start.
Z przyciskiem tym bedzie skojarzone zdarzenie ID_FILE_START
Musimy teraz dodać procedurę obsługi zdarzenia związanego z wybraniem tej opcji menu (chcemy wtedy otworzyć okno dialogowe do kierowania ludzikiem)
Klikamy prawym przyciskiem myszy na menu Start, i wybieramy AddEventHandler (dodaj procedurę obsługi zdarzenia)
Na liście klas wybieramy CLabiryncikView.
Pojawi się nam procedura która zostanied uruchomiona po wybraniu menu Start.
Odpalamy w niej nasze okienko do kierowania ludzikiem:
void CLabiryncikView::OnFileStart()
{
Sterownik okno(NULL,GetDocument(),this);
okno.DoModal();
}
Pamiętamy też o dopisaniu z przodu pliku:
#include "Sterownik.h"
Proszę skompilować i uruchomić program
Proszę zmienić sposób rysowania ludzika.
Proszę podmienić labirynt na swój własny.
Chcemy teraz zmienić sposób rysowania labiryntu, tak aby jego ściany miały zerową grubość.
W tym celu musimy dodać nowy sposób kodowania labiryntu - za pomocą tablicy kodów.
W pliku nagłówkowym CLabiryncikDoc.h klasy dokumentu CLabiryncikDoc dodajemy tablice dwuwymiarowa liczb
// Attributes
public:
//tablica z danymi labiryntu
char* m_labirynt[13];
//tablica z kodami scian
int m_kody[6][6];
//polozenie ludzika
int m_wiersz;
int m_kolumna;
Musimy wypełnić tą tablicę kodami. UWAGA - zrobimy to automatycznie, na podstawie tablicy tekstów kodującej labirynt. Napiszemy procedurę WypelnijKodyLabiryntu(). W pliku nagłówkowym CLabiryncikDoc.h klasy CLabirynicikDoc szukamy sekcji Operations i dodajemy nagłówek procedury
// Operations
public:
//procedura oblicza kody scian dla pol labiryntu
void WypelnijKodyLabiryntu();
W pliku z kodem źródłowym CLabiryncikDoc.cpp klasy CLabirynicikDoc szukamy konstruktora i dopisujemy za nią procedurę WypelnijKodyLabiryntu
void CLabiryncikTestDoc::WypelnijKodyLabiryntu()
{
//petla po wierszach z tabeli kodow
for(int i=0;i<6;i++)
//petla po kolumnach z tabeli kodow
for(int j=0;j<6;j++)
{
int w = 2*i+1; //wiersz w tablicy tekstow
int k = 2*j+1; //kolumna w tablicy tekstow
if(m_labirynt[w][k+1]==' ' && m_labirynt[w+1][k]==' ' &&
m_labirynt[w][k-1]==' ' && m_labirynt[w-1][k]==' ')
m_kody[i][j]=0;
if(m_labirynt[w][k+1]=='*' && m_labirynt[w+1][k]==' ' &&
m_labirynt[w][k-1]==' ' && m_labirynt[w-1][k]==' ')
m_kody[i][j]=1;
if(m_labirynt[w][k+1]==' ' && m_labirynt[w+1][k]=='*' &&
m_labirynt[w][k-1]==' ' && m_labirynt[w-1][k]==' ')
m_kody[i][j]=2;
PROSZE TUTAJ DOPISAC USTAWIANIE POZOSTALYCH KODOW
}//koniec petli po kolumnach
}
Gdzie należy zawołać procedurę WypełnijKodyLabiryntu?
W konstruktorze klasy dokumentu CLabiryncikDoc dopisujemy
CLabiryncikTestDoc::CLabiryncikTestDoc()
{
m_labirynt[0] = "*************";
m_labirynt[1] = "* * * *";
m_labirynt[2] = "* *** *** * *";
m_labirynt[3] = "* * *";
m_labirynt[4] = "* * * *******";
m_labirynt[5] = "* * * * ";
m_labirynt[6] = "*** *** * ***";
m_labirynt[7] = "* * * * *";
m_labirynt[8] = "* *** * *** *";
m_labirynt[9] = "* * * * *";
m_labirynt[10] = "* * * * *** *";
m_labirynt[11] = "* * * *";
m_labirynt[12] = "*************";
m_wiersz=9;
m_kolumna=1;
WypelnijKodyLabiryntu();
}
Proszę teraz ustawić pułapkę (klawisz F9) w procedurze WypelnijKodyLabiryntu,
skompilować i uruchomić program klawiszem F7 i potem F5, i sprawdzić czy procedura dobrze wypełnia tablicę kodów (proszę sprawdzić przynajmniej 2 wiersze).
Kody powinny wyglądać następująco:
{ 5,13, 5,10, 8,12},
{ 3, 4, 1,11, 2, 6},
{14, 9, 7, 8, 5,13},
{ 5, 6,12, 9, 7, 8},
{ 9,12, 9, 3,10, 1},
{ 7, 6, 7, 6,11, 6}
Proszę w pliku nagłówkowym CLabiryncikView.h klasy widoku CLabiryncikView
znaleźć sekcję z operacjami (procedurami) i dopisać nagłówek procedury NarysujCienkiLabirynt
// Operations
public:
void WypiszLabirynt();
void NarysujCienkiLabirynt(CDC *pDC);
Proszę w pliku z kodem źródłowym CLabiryncikView.cpp klasy widoku CLabiryncikView odnaleźć konstruktor i destruktor klasy CLabiryncikView i dopisać za nimi procedurę NarysujCienkiLabirynt.
void CLabiryncikTestView::NarysujCienkiLabirynt(CDC* pDC)
{
CLabiryncikTestDoc* pDoc = GetDocument();
//szerokosc pola
int dx = 30;
//wysokosc pola
int dy = 30;
//punkt w ktorym bedzie znajdowac sie srodek pierwszego pola labiryntu
CPoint pierwsze_pole(2*dx,2*dy);
//petla po wierszach z tablicy kodow
for(int i=0;i<6;i++)
//petla po kolumnach z tablicy kodow
for(int j=0;j<6;j++) {
//punkt w ktorym znajduje sie srodek pola [i][j] labiryntu
CPoint pole(pierwsze_pole.x+j*2*dx, pierwsze_pole.y+i*2*dy);
int kod = pDoc->m_kody[i][j];
//przypadki dla ktorych trzeba narysowac gorna sciane
if(kod==4 ||kod==5 ||kod==8 ||kod==10 ||kod==11 ||kod==12 ||kod==13)
{
pDC->MoveTo(pole.x-dx,pole.y-dy);
pDC->LineTo(pole.x+dx,pole.y-dy);
}
//przypadki dla ktorych trzeba narysowac prawa sciane
if(kod==1 ||kod==6 ||kod==8 ||kod==9 ||kod==12 ||kod==13 ||kod==14)
{
PROSZE TUTAJ DOPISAC RYSOWANIE PRAWEJ SCIANY
}
//przypadki dla ktorych trzeba narysowac dolna sciane
PROSZE TUTAJ DOPISAC SPRAWDZANIE I RYSOWANIE DOLNEJ SCIANY
//przypadki dla ktorych trzeba narysowac lewa sciane
PROSZE TUTAJ DOPISAC SPRAWDZANIE I RYSOWANIE LEWEJ SCIANY
}
}
Proszę skompilować i uruchomić program. Czy labirynt dobrze się rysuje?
Jeśli nie, to proszę ustawić pułapkę w procedurze NarysujLabirynt (klawisz F9), proszę skompilować i uruchomić program klawiszami F7 i F5. Proszę poprawić ewentualne błędy rysowania, tak żeby labirynt dobrze się rysował.
Proszę dodać rysowanie ludzika w nowej procedurze NarysujCienkiLabirynt
Proszę zmodyfikować odpowiednio procedury WGore, WDol, WLewo, WPrawo (jak to zrobić?)