Peg Solitaire tutorial/fi
│
English (en) │
suomi (fi) │
Tämä opetusohjelma on toinen Lazarus opetusohjelma, jonka tavoitteena on ottaa käyttöön Lazaruksen sovelluskehityksen perusteet. On parasta aloittaa tämän opetusohjelman ensimmäisellä osalla: (Howdy World (Hello World on steroids)/fi). Tämä opetusohjelma selittää vähän sitä, miten työskennellä grafiikan parissa ja miten tehdään modulaarinen ohjelma. Tämän opetusohjelman lopputuotteena on yksinkertainen, mutta toimiva versio Peg Solitaire pelistä ([1]). Jos kaikki menee hyvin niin lopulta se näyttää tältä:
Projektin aloitus
Kuten edellisessä opetusohjelmassakin niin on parasta aloittaa puhtaalta pöydältä. Tee erillinen hakemisto(tai kansio) kutakin projektia kohti. Nopea kertaus:
- Luo uusi hakemisto tähän peliin.
- Aloita uusi projekti (Projekti / Uusi projekti ... ja valitse Sovellus).
- Tallenna projekti nimellä PegSolitaire.
- Tallenna aloitusikkuna/lomake nimellä ufrmMain.
- Komponenttimuokkaimessa muuta lomakkeen nimeksi frmMain.
- Muuta Caption arvoon Lazarus Peg Solitaire.
- Valitse valikosta: Projekti / Projektikohtaiset asetukset ...
- Valitse Kääntäjän asetukset / Hakupolut (klikkaa solmun puunäkymää)
- Kirjoita teksti bin\ ennen kohdetiedostonimeä (tai bin/ esim. unix/linux ympäristössä)
Ja lisänä tässä projektissa:
- Avaa projekti valintaikkunan (⇧ Shift+Ctrl+F11).
- Valitse kääntäjän asetukset / Virheenjäljitys.
- Ota käyttöön ylivuoto- ja aluevirhe tarkistus (katso kuva alla).
Ensiaskeleet
On aina hyvä idea erottaa käyttöliittymään liittyvä koodi muusta esim. tietorakennetta määritelevästä koodista. Joten ensimmäinen askel on tehdä erillinen käännösyksikkö Solitaire tietorakenteita varten.
- Valitse valikosta Tiedosto / Uusi käännösyksikkö.
- Tallenna käännösyksikkö nimellä PegDatastructures.pas
Peruselementit PegSolitaire laudalla ovat marmorit, levyn rakenne ja tyhjät paikat. Simuloidaan yksinkertaisella matriisilla, jossa paikat ovat pelin solujen tyyppisiä (tyhjä, pelinappula ja ei pääse). Ja tämä kaikki kiteytetään luokkaan, joka käsittelee kaikkea tietojen manipulointia.
- Lisää seuraava koodi PegDatastructures käännösyksikköön ( uses-lausekkeen jälkeen juuri ennen implementation osiota):
const
C_MAX = 7; // Max board size: 7x7
type
TCellNums = 1..C_MAX;
TCellType = (ctNoAccess, ctEmpty, ctPeg);
TPegCells = array[TCellNums, TCellNums] of TCellType;
TPegSolitaire = class
private
Size: TCellNums;
PegCells: TPegCells;
public
constructor Create(const pSize: TCellNums);
end;
On kohtuullista olettaa, että muu koodi, joka aikoo käyttää tätä luokkaa tarvitsee pääsyn solujen sisältöön (eli PegCells). Tapa käsitellä tätä on joko määrittelemällä joukko funktioita jotka hakee ja vie soluihin tai määritellä ns taulukko-ominaisuus. Valitaan jälkimmäinen lähestymistapa ja lisätään seuraava rivi TPegSolitaire luokan public osioon:
property Cell[const pRow, pCol: TCellNums]: TCellType;
- Sijoita tekstikursori rakentajan eli constructor:n riville.
- Paina Ctrl+⇧ Shift+C: ohjelman kehitysympäristö luo rakentajan rungon (kuten aiemmissa esimerkeissä), mutta se myös luo tyhjän rungon kahdelle metodille, jotka antavat pääsyn Cell-ominaisuuden kautta PegCells taulukkoon.
- Funktio GetCell noutaa tiedot PegCells taulukosta. Lisää seuraava koodi tähän funktioon:
result := PegCells[pRow,pCol];
- Aliohjelma SetCell sijoittaa dataa PegCells taulukkoon. Lisää seuraava koodi tähän aliohjelmaan:
PegCells[pRow,pCol] := AValue;
- Ja viimeistellä rakentaja Create. Lisää tämä koodi sen runkoon:
var iRow,iCol: integer;
begin
// Store the size of the board locally
Size := pSize;
// Initialize all cells to 'not accessible'
for iRow := 1 to C_MAX do
for iCol := 1 to C_MAX do
Cell[iRow,iCol] := ctNoAccess;
Nyt kun rakenteen perustiedot ovat paikallaan, on aika tarkastella mitä grafiikka tarvitsee. On monia tapoja näyttää solitaire pöytä. Tähän voidaan käyttää TPaintbox- komponenttia. Se antaa täydellisen määräysvallan graafisiin ominaisuuksiin.
Ohjelman ikkunalomake aikoo käyttää datarakennetta joka on määritelty PegDatastructure käännösyksikköön.
- Siirry lähdekoodieditorissa ufrmmain-käännösyksikköön.
- Lisää PegDatastructures sen uses luetteloon joka on tiedoston yläosassa:
uses
PegDatastructures,
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
- Paina F12 (tämä tuo esiin lomakkeen).
- Komponenttipaletin Standard-välilehdeltä valitaan TButton ja pudota se lomakkeelle vasempaan yläkulmaan.
- Vaihda sen Caption ominaisuuten teksti Test paint.
- Komponenttipaletin Additional-välilehdeltä valitaan TPaintbox-komponentti ja tuodaan se lomakkeelle.
- Muuta sen Align arvoon alRight.
- Muuta BordSpacing.Around arvoon 4.
- Muuta Anchors.akLeft arvoon true.
- Kirjoita Name-ominaisuuteen teksti: pbPeg.
- Muuta lomakkeen kokoa, jotta se näyttää suunnilleen tältä:
Seuraava vaihe on kiinnittää solujen matriisi tähän TPaintbox-komponenttiin jakamalla se riveihin ja sarakkeisiin, jotka sisältävät levyn jokaisen solun. Jotta se olisi skaalautuva niin lasketaan leveys ja korkeus itsenäisesti. Tarvitaan pari muuttujaa pitämään tulosta. Ensin solujen leveys ja korkeus, jotta kaikki solut (7) sopivat täsmälleen lomakkeelle. Kehitetään piirtokoodi interaktiivisesti. Tähän käyttää painiketta, joka tuotiin lomakkeelle.
- Tuplaklikkaa Test paint-painiketta (tämä luo tapahtumakäsittelijän).
- Lisää kaksi muuttujaa:
var
CellWidth : integer;
CellHeight: integer;
Tarvitaan ylimääräisiä muuttujia pitämään välituloksia:
- Lisää kolme paikallista muuttujaa:
iRow, iCol: TCellNums;
CellArea : TRect;
CellArea käytetään rajoittamaan suorakulmaista aluetta näytöllä, johon solu piirretään.
Jotta käsittellään kaikki rivit ja sarakkeet niin tehdään se kahdella suoraviivaisella for-silmukalla.
- Lisää seuraava koodi tapahtumankäsittelijään:
// Calculate the width/height of each cell to accomodate for all cells in the paintbox
CellWidth := pbPeg.Width div 7;
CellHeight := pbPeg.Height div 7;
// Draw boxes for all cells
for iRow := 1 to 7 do
for iCol := 1 to 7 do
begin
// Calculate the position of the cell in the paintbox
CellArea.Top := (iRow-1) * CellHeight;
CellArea.Left := (iCol-1) * CellWidth;
CellArea.Right := CellArea.Left + CellWidth;
CellArea.Bottom := CellArea.Top + CellHeight;
// And now draw the cell
pbPeg.Canvas.Rectangle(CellArea);
end;
Canvas on nimensä mukaisesti on ohjaus, joka auttaa meitä piirtämään asioita, kuten viivoja, suorakaiteita, ympyröitä jne. TPaintbox-komponentti sisältää Canvas:n. Siksi rivillä pbPeg.Canvas.Rectangle (CellArea) ohjelma piirtää suorakulmion paintbox, rajattu alue on määritelty CellArea. Ja koska paintbox sijoitetaan lomakkeelle, voimme nähdä tuloksen siellä.
- Kääntää ja ajaa ohjelma (paina F9).
- Paina Test paint-painiketta.
- Suurenna lomaketta (solut häviävät mutta älä huoli se korjatuu).
- Paina Test paint-painiketta uudelleen
Tämä todistaa, että laskelmat olivat paikallaan ja että on nyt keino tehdä kaikki tarpeellisen oikealla paikalla. Yksi asia on tehtävä kuitenkin ennen lisäämällä piirustus toiminnallisuutta. Piirustus solujen ei mitenkään tärkein muodossa tai toisessa (tai minkäänlaista että asia). Ainoa asia, meidän piirtämiseen asiat on Canvas ja joitakin mittauksia soluille. Joten aiomme luoda tukea luokan siivota tärkeimmäksi.
- Luo uusi käännösyksikkö (Tiedosto / Uusi käännösyksikkö).
- Tallenna se nimellä PegSolPainter.pas (Tiedosto / Tallenna nimellä ...).
- Lisää uusi luokka tähän käännösyksikköön, se tekee kaiken piirtämisen (luokka tulee uses-lauseen jälkeen, ennen
implementation osiota).
type
TPegSolPainter = class
private
PegSol : TPegSolitaire;
Canvas : TCanvas;
public
constructor Create(pPegSol: TPegSolitaire; pCanvas: TCanvas);
end;
Huomaa, että TPegSolitaire muuttuja lisätään myös, koska käyttää tätä luokkaa käytetään noutamaan solujen tila.
- Sijoita tekstikursorin rakentajan eli constructor:n riville ja paina Ctrl+⇧ Shift+C.
- Rakentajan kaksi alustus parametriä on sijoitettava luokan private muuttujiin (Täydennä luokan rakentajaa siten että se näyttää tältä) :
constructor TPegSolPainter.Create(pPegSol: TPegSolitaire; pCanvas: TCanvas);
begin
PegSol := pPegSol;
Canvas := pCanvas;
end;
Yritettäessä kääntää koodia niin tulee virheilmoitus koska ei olla lisätty PegDatastrucures käännösyksikköä uses-lauseseen. Ja koska käytetään myös TCanvas-luokkaa niin täytyy lisätä Graphics käännösyksikkö samoin.
- Lisää PegDatastructures ja Graphics uses lauseen luetteloon.
uses
PegDatastructures,
Graphics,
Classes, SysUtils;
Syy tämän luokan rakentamiseen oli poistaa kaikki piirtäminen koodilla lomakkeella. Joten on luotava menetelmä, joka tekee piirtämisen. Ennen lisäystä että menetelmä on jotain, johon on puututtava: laskea leveys solun, jaamme paintbox leveyttä useissa solujen. Teoriassa voisimme käyttää ominaisuutta Canvas.Width tähän. Kuitenkin tämä ominaisuus ei aina anna oikeata leveyttä oikeaan aikaan. Joten piirrettäesä soluja täytyy tarjota piirtää menetelmälle oikeat Canvas arvot leveydelle ja korkeudelle.
Nyt tiedetään tämä, voidaan lisätä piirustusmetodi luokkaan.
- Lisää aliohjelma Repaint luokkaan (jolloin luokka määrittely näyttää tältä).
TPegSolPainter = class
private
PegSol : TPegSolitaire;
Canvas : TCanvas;
public
constructor Create(pPegSol: TPegSolitaire; pCanvas: TCanvas);
procedure Repaint(const pCanvasWidth, pCanvasHeight: integer);
end;
</sourcet>
* Luo aliohjelman Repaint runko. Paina {{keypress|Ctrl|Shift|C}}.
* Kopio koodi ''TfrmMain.Button1Click(Sender: TObject)''-aliohjelmasta tähän runkoon (Jolloin siitä tulee tälläinen)
<syntaxhighlight lang="pascal">
procedure TPegSolPainter.Repaint(const pCanvasWidth, pCanvasHeight: integer);
var
CellWidth : integer;
CellHeight : integer;
iRow, iCol : TCellNums;
CellArea : TRect;
begin
// Calculate the width of each cell to accomodate for all cells
CellWidth := pbPeg.Width div 7;
CellHeight := pbPeg.Height div 7;
// Draw boxes for all cells
for iRow := 1 to 7 do
for iCol := 1 to 7 do
begin
// Calculate the position of the cell in the paintbox
CellArea.Top := (iRow-1) * CellHeight;
CellArea.Left := (iCol-1) * CellWidth;
CellArea.Right := CellArea.Left + CellWidth;
CellArea.Bottom := CellArea.Top + CellHeight;
// And now draw the cell
pbPeg.Canvas.Rectangle(CellArea);
end;
end;
Koska ei enää tarvita TPaintbox-komponenttia pbPeg. Niin on poistettava viittaukset siihen. Joten kolmessa kohdassa tarvitaan muutoksia:
- Muutos CellWidth:n ja CellHeight:n laskennassa:
CellWidth := pCanvasWidth div 7;
CellHeight := pCanvasHeight div 7;
- Muutos suorakulmion piirtämisessä:
Canvas.Rectangle(CellArea);
Nyt on luokka, jolla voidaan tehdä haluttu piirtäminen, on aika käyttää sitä. Tätä piirtoluokkaa käytetään yhdessä PegSolitare luokan kanssa solujen piirtämiseen.
- Käännösyksikössä ufrmmain.pas, etsi Button1Click aliohjelma.
- Poista siitä kaikki lausekkeet ja muuttujat (Tyhjennä se).
- Lisätään kaksi uutta muuttujaa: peliluokan ja piirtämisluokan (Kaikkien näiden toimien tuloksena aliohjelma nyt näyttää tältä):
procedure TfrmMain.Button1Click(Sender: TObject);
var
pegsol : TPegSolitaire; // The game data
pegpaint: TPegSolPainter; // The paint class for the game
begin
end;
- Lisää PegSolPainter uses-lauseen luetteloon.
Piirtoluokan käyttö on yksinkertaista: Luodaan uusi esiintymä ja kutsutaan piirto aliohjelmaa:
- Lisää seuraava koodi Button1Click aliohjelmaan:
// Create a new game object
pegsol := TPegSolitaire.Create(7);
// Create a new painter object to paint this game
pegpaint := TPegSolPainter.Create(pegsol, pbPeg.Canvas);
// And paint the board
pegpaint.Repaint(pbPeg.Width, pbPeg.Height);
// Clean up
pegpaint.Free;
pegsol.Free
Suorita ja testta ohjelma. Nähdään että se toimii samaan tapaan kuin ennen. Tulos näyttää tältä:
Kyse tapahtumista
Mitä on saatu aikaan tähän mennessä? On tietorakenne, joka sisältää kaikki tiedot PegSolitaire peliä varten. On luokka, johon voi piirtää. Ja on melko yksinkertainen lomake, jossa on testipainike. Joten mitä seuraavaksi? Tapahtumat!
Kuten edellisessä osassa on nähty, solun matriisi joka tehtiin hävisi jos lomakkeen kokoa muutettiin. Näin tapahtuu, koska lomake ei tiedä mitään "pikku pelistä". Heti kun lomakkeen mielestä on aika piirtää itse, se tekee niin ja ohittaa "pikku pelin". Mitä se tekee? Se vain lähettää viestin lapsi kontrolleille, että virkistäminen on välttämätön. Tämä viesti on käytettävissämme tapahtumana: OnPaint-tapahtuma.
- Mene lomakkeelle (Valitse ufrmMain lähdekoodieditorissa ja paina F12).
- Valitse TPaintbox pbPeg.
- Siirry tapahtumat-välilehdelle komponenttimuokkaimessa.
- Yksi luetelluista tapahtumista on OnPaint.
Tätä tapahtumaa kutsutaan joka kerta kun TPaintbox tarvitsee uudelleenpiirtämistä. Se on paikka johon tehdään piirtäminen.
- Valitse OnPaint tapahtuma komponenttimuokkaimessa ja klikkaa pientä (...) painiketta kolmella pisteellä . Tämä luo tapahtumakäsittelijän rungon.
- Kopioi / Liitä tarkka koodi Button1Click tapahtumakäsittelystä tähän uuteen aliohjelmaan.
procedure TfrmMain.pbPegPaint(Sender: TObject);
var
pegsol : TPegSolitaire; // The game data
pegpaint: TPegSolPainter; // The paint class for the game
begin
// Create a new game object
pegsol := TPegSolitaire.Create(7);
// Create a new painter object to paint this game
pegpaint := TPegSolPainter.Create(pegsol, pbPeg.Canvas);
// And paint the board
pegpaint.Repaint(pbPeg.Width, pbPeg.Height);
// Clean up
pegpaint.Free;
pegsol.Free
end;
- Poista kaikki koodi ja muuttujat aliohjelmasta Button1Click. Muista: Ohjelmankehitysympäristö automaattisesti poistaa tämän tyhjän aliohjelman.
- Poista Test paint -painike lomakkeelta.
- Suorita ohjelma, katso mitä tapahtuu.
Nyt on selvää, että me luoda pelin objekti OnPaint menetelmässä, piirretään tyhjät solut ja sitten tuhota se. Mutta täytyy tallentaa peliolio kunnes peli on päättynyt. Sama koskee piirtämisoliota. Joten OnPaint tapahtuma ei ole loogisin paikka luoda näitä olioita. Lomakkeen luokan määrittely on parempi paikka tallentaa ne.
- Siirry lomakkeen määrittelyyn.
- Lisää kaksi muuttujaa jotka luotiin OnPaint tapahtumaan:
TfrmMain = class(TForm)
pbPeg: TPaintBox;
procedure pbPegPaint(Sender: TObject);
private
{ private declarations }
pegsol : TPegSolitaire; // The game data
pegpaint: TPegSolPainter; // The paint class for the game
public
{ public declarations }
end;
Nämä muuttujat pitää alustaa heti, kun lomake avataan (tai kun halutaan aloittaa uusi peli). Joten luodaan aliohjelma, joka tekee sen meille ja lisätään se private-osioon lomakkeelle.
- Lisää aliohjelma StartNewGame lomakkeelle.
private
{ private declarations }
pegsol : TPegSolitaire; // The game data
pegpaint: TPegSolPainter; // The paint class for the game
procedure StartNewGame;
- Luo aliohjelman runko. Paina Ctrl+⇧ Shift+C.
- Lisää alustus koodi:
procedure TfrmMain.StartNewGame;
begin
// Clean up the previous game
pegpaint.Free;
pegsol.Free;
// Start with a new game
pegsol := TPegSolitaire.Create(7);
pegpaint := TPegSolPainter.Create(pegsol, pbPeg.Canvas);
end;
Nyt kun alustuksen koodi on luotu, se on suoritettava. Looginen aika tehdä tämä on, kun lomake on luodaan (eli sovellus käynnistetään). Tähän toiseen tapahtumaan pääse kiinni: FormCreate-tapahtumassa. Se voidaan luoda kahdella eri tavalla: Komponenttimuokkaimessa löytää OnFormCreate tapahtuma ja klikata "..." -painiketta. Toinen tapa tuottaa se on tuplaklikata lomaketta.
- Mene lomakkeelle (Lähdekoodieditorissa paina F12).
- Tuplaklikkaa jossain vapaata aluetta. Älä klikkaa TPaintbox:a.
- Lisää sen runkoon aliohjelman StartNewGame kutsu:
procedure TfrmMain.FormCreate(Sender: TObject);
begin
StartNewGame;
end;
Nyt kun peli- ja piirto-oliot luodaan ohjelman alkaessa niitä ei enää tarvita OnPaint aliohjelmassa.
- Paikallista aliohjelma procedure TfrmMain.pbPegPaint (Sender: TObject);
- Poista paikalliset muuttujat ja kaikki koodi paitsi rivin joka tekee piirtämisen. (jolloin se on näin yksinkertainen)
procedure TfrmMain.pbPegPaint(Sender: TObject);
begin
// Paint the board
pegpaint.Repaint(pbPeg.Width, pbPeg.Height);
end;
- Suorita ohjelma ja katso mitä tapahtuu.
Intermezzo
Tähän mennessä ollaan keskittynyt rakentamaan ohjelmaa jolla pelata klassista pasianssi peliä 7x7 ruudukossa. Onko nyt mahdollista luoda pienempi tai suurempi ruudukko esim. muun tyyppiselle pelille? Testataan tätä.
- Mene käännösyksikköön ufrmmain.pas ja siellä aliohjelmaan StartNewGame.
- Muuta rivi
pegsol: = TPegSolitaire.Create (7);
riviksipegsol: = TPegSolitaire.Create (5);
. Joten pienempi lauta 5x5 neliöitä on luotu. - Suorita ohjelma ja katso mitä tapahtuu.
Kuten ruudulla nähdään niin vielä on 7x7 ruudukko! Tämä on seurausta kun käyttää (taika) numeroita, jota olisi voitu välttää (ks http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Unnamed_numerical_constants ).
Taikanumeroiden käyttäminen on suuri katastrofi: se on asia, kun, ei jos, ohjelma epäonnistuu.
- Avaa käännösyksikkö PegSolPainter.
- Paikanna aliohjelma TPegSolPainter.Repaint(const pCanvasWidth, pCanvasHeight: integer);.
Siellä nähdään numero 7 muutamia kertoja. Tämä on se maaginen numero, joka tekee temppuja. Laskettaessa solun leveys ja korkeus, tarvitsemme laudalle koon joka on tallennettu pegsol pelin Size muuttujaan. Ja sama pätee iRow ja iCol silmukoihin. Joten korjataan ne kerralla:
// Calculate the width of each cell to accomodate for all cells
CellWidth := pCanvasWidth div pegsol.Size;
CellHeight := pCanvasHeight div pegsol.Size;
// Draw boxes for all cells
for iRow := 1 to pegsol.Size do
for iCol := 1 to pegsol.Size do
begin
// Calculate the position of the cell in the paintbox
CellArea.Top := (iRow-1) * CellHeight;
CellArea.Left := (iCol-1) * CellWidth;
CellArea.Right := CellArea.Left + CellWidth;
CellArea.Bottom := CellArea.Top + CellHeight;
// And now draw the cell
Canvas.Rectangle(CellArea);
end;
Tässä varoitus: pegsol:ssa ei ole julkisesti saatavilla Size -muuttujaa. Ja näin se pitäisi olla: kaikki luokan muuttujat pitäisi olla yksityisiä. Tapa käyttää näitä yksityisiä arvoja on funktioiden tai ominaisuuksien (rajapinta luokan) kautta. Tähän yksinkertaiseen arvoon sovelletaan ominaisuutta.
- Mene käännösyksikköön PegDatastructures.
- Uudelleen nimeä luokan TPegSolitaire yksityisen muuttujan Size uudeksi nimeksi FSize.
- Lisää julkinen vain luku ominaisuus luokkaan: property Size: TCellNums read FSize;
Luokka nyt näyttää tältä:
TPegSolitaire = class
private
FSize: TCellNums;
PegCells: TPegCells;
function GetCell(const pRow, pCol: TCellNums): TCellType;
procedure SetCell(const pRow, pCol: TCellNums; const pValue: TCellType);
public
constructor Create(const pSize: TCellNums);
property Cell[const pRow, pCol: TCellNums]: TCellType read GetCell write SetCell;
property Size: TCellNums read FSize;
end;
On yleinen käytäntö käyttää muuttujien etuliitteenä kirjainta F joihin päästään käsiksi ominaisuuksilla. Julkinen pääsy ominaisuuteen Size on vain sen arvon lukeminen koska sitä ei pidä koskaan muuttaa kun peli käynnistetään. Ainoa paikka, jossa tämä yksityinen muuttuja pitäisi saada sen lopullinen arvon on Create rakentaja.
- Paikallista rakentaja (constructor).
- Muuta riviSize := pSize; riviksi FSize := pSize; (Jotta ei saada käännösvirhettä).
- Ja kun ollaan siellä niin alustusta tarvitsee vähäisen korjata. Ei tarvitse alustaa soluja joita ei aiota käyttää. Joten rakentaja tulisi näyttää tältä (C_MAX korvataan Size:lla):
constructor TPegSolitaire.Create(const pSize: TCellNums);
var iRow,iCol: integer;
begin
FSize := pSize;
for iRow := 1 to Size do
for iCol := 1 to Size do
Cell[iRow,iCol] := ctNoAccess;
end;
Olemme nyt valmis testaamaan ohjelman ja katso jos se piirtää nyt mukavan 5x5 matriisi.
- Suorita ohjelma ja katso mitä tapahtuu (5x5 matriisi piirretään).
Nyt kun pelin perusteet ovat paikoillaan, on aika täsmentää käyttöliittymää.
Ollaan taiteellisia
Hienoa. Nyt on peli luokka, tukiluokka piirtämiseen ja ruudullinen pelilauta lomakkeella. Pasianssipelin solu voi olla kolmessa (3) tilassa: ei saatavilla, tyhjä tai käytössä. Kuhunkin solutyyppiin halutaan erilainen graafinen esitys. Tehdään nyt se.
- Avaa lähdekoodieditorissa käännösyksikkö PegSolPainter (käännösyksikkö joka piirtää pelilaudan).
- Siirry Repaint aliohjelmaan.
- Etsi rivi, jossa solu on piirretään: Canvas.Rectangle (CellArea);.
Täällä täytyy tehdä muutoksia. Se riippuu solun tilasta mitä tarvitaan piirtämiseen (ei käytettävissä oleva solu, tyhjä solu tai pelinappula solu). Tapaus ... toiminta tulee pelastus.
- Muuta solujen piirtämistä:
// Draw boxes for all cells
for iRow := 1 to pegsol.Size do
for iCol := 1 to pegsol.Size do
begin
// Calculate the position of the cell in the paintbox
CellArea.Top := (iRow-1) * CellHeight;
CellArea.Left := (iCol-1) * CellWidth;
CellArea.Right := CellArea.Left + CellWidth;
CellArea.Bottom := CellArea.Top + CellHeight;
// And now draw the cell based on the cell's contents
case pegsol.Cell[iRow,iCol] of
ctNoAccess: // Draw cells that are not accessible
begin
Canvas.Brush.Color := clGray;
Canvas.Rectangle(CellArea);
end;
ctEmpty: // Draw cells that are currently empty
begin
Canvas.Brush.Color := clBlue;
Canvas.Rectangle(CellArea);
end;
ctPeg: // Draw cells that are occupied
begin
Canvas.Brush.Color := clBlue;
Canvas.Rectangle(CellArea); // Erase the background first
Canvas.Brush.Color := clGreen;
Canvas.Ellipse(CellArea); // Draw the pegs as green circles
end;
end;
end;
Ohjelmaa voitaisiin ajaa tässä vaiheessa (tai vain kokeilla sitä), mutta se on vain tylsä? (5x5) harmaa ruudukko. Tämä johtuu siitä ettei vielä ole määritelty mitään peliasetuksia. Korjataan sitä seuraavaksi.
- Avaa ufrmmain käännösyksikkö.
- Paikallista StartNewGame aliohjelma.
- Luo 5x5 pelin sijasta 7x7 peli.
- Alusta muutamia soluja. Esimerkiksi:
procedure TfrmMain.StartNewGame;
begin
// Clean up the previous game
pegpaint.Free;
pegsol.Free;
// Start with a new 7x7 game
pegsol := TPegSolitaire.Create(7);
pegpaint := TPegSolPainter.Create(pegsol, pbPeg.Canvas);
// Initialize some cells
pegsol.Cell[3,4] := ctEmpty;
pegsol.Cell[4,2] := ctPeg;
pegsol.Cell[4,3] := ctPeg;
pegsol.Cell[4,4] := ctEmpty;
pegsol.Cell[4,5] := ctPeg;
pegsol.Cell[4,6] := ctPeg;
pegsol.Cell[5,4] := ctEmpty;
end;
- Suorita ohjelma (sen tulee näyttää jokseenkin samanlaiselta kuin kuva alla).
Täytä pelilauta
Kuten tiedetään niin klassinen solitaire pelilaudan pitäisi näyttää tältä:
Jos täytetään kaikki solut yksitellen, niin se johtaisi suureen koodimäärään. Mitä jos voitaisiin alustaa peli vain tekstillä, joka symbolisesti kuvaa pelilaudan? Jotain tällaista:
// Initialize the cells to the classic game
pegsol.InitializeBoard( ' ooo ' + LineEnding +
' ooo ' + LineEnding +
'ooooooo' + LineEnding +
'ooo.ooo' + LineEnding +
'ooooooo' + LineEnding +
' ooo ' + LineEnding +
' ooo ' );
- o on solu jossa on pelinappula.
- . on tyhjä, mutta pelattavaa solu.
- Välilyönnit osoittavat solut, jotka eivät ole käytettävissä.
Oletetaan että tämä tulee toimimaan ja luodaan aliohjelma TPegSolitaire luokkaan joka voi hoitaa tämän.
- Ollaan optimistinen (kutsutaan myös Top down suunnitteluksi) ja lisätään edellä ollut koodi TfrmMain.StartNewGame:
procedure TfrmMain.StartNewGame;
begin
// Clean up the previous game
pegpaint.Free;
pegsol.Free;
// Start with a new 7x7 game
pegsol := TPegSolitaire.Create(7);
pegpaint := TPegSolPainter.Create(pegsol, pbPeg.Canvas);
// Initialize the cells to the classic game
pegsol.InitializeBoard( ' ooo ' + LineEnding +
' ooo ' + LineEnding +
'ooooooo' + LineEnding +
'ooo.ooo' + LineEnding +
'ooooooo' + LineEnding +
' ooo ' + LineEnding +
' ooo ' );
end;
- Avaa PegDatastructures lähdekooditiedosto.
- Lisää tämä aliohjelman esittely luokan public osioon: InitializeBoard(const pBoard: ansistring);
public
constructor Create(const pSize: TCellNums);
procedure InitializeBoard(const pBoard: ansistring);
property Cell[const pRow, pCol: TCellNums]: TCellType read GetCell write SetCell;
property Size: TCellNums read FSize;
- Luo aliohjelman InitializeBoard:n runko. Paina Ctrl+⇧ Shift+C.
Mitä tämä aliohjelman tarvitse tehdä? Se jakaa textstringin osiksi erillisiin riveihin ja sitten käsitellä nämä rivit, koska käytettiin LineEnding-vakiota erottamaan rivit niin voidaan käyttää TStringList luokkaa erottamaan ne.
- Lisää InitalizeBoard koodi:
procedure TPegSolitaire.InitializeBoard(const pBoard: ansistring);
var lst : TStringList;
iRow,iCol: integer;
s : string;
begin
// Create a list with the board text in it. This will split all lines
// into individual lines, because of the LineEnding 'splitter'.
lst := TStringList.Create;
lst.Text := pBoard;
// Process all lines one at a time
for iRow := 0 to lst.Count-1 do
if iRow < Size then // Make sure there is no overflow in the rows
begin
// Process a single line of text
s := lst[iRow];
for iCol := 1 to length(s) do
if iCol <= Size then // Make sure there is no overflow in the columns
case s[iCol] of
' ': Cell[iRow+1,iCol] := ctNoAccess;
'.': Cell[iRow+1,iCol] := ctEmpty;
'o': Cell[iRow+1,iCol] := ctPeg;
end;
end;
// Clean up the list
lst.Free;
end;
- TStringList käytetään puskurina. Tämä toimii, koska LineEnding:a käytettiin rivien erottimena.
- Count kertoo rivien määrän, mutta ne on numeroitu 0..Count-1. Ja solut on numeroitu aloittaen ykkösestä(1). Siksi on solua osoittaessa tehtävä iRow + 1.
Edellä esitetty menettely sisältää paljon ylimääräisiä muuttujia ja ei pitäisi olla kovin vaikea ymmärtää. On mahdollista vähentää aliohjelma minimiin kuten näin:
procedure TPegSolitaire.InitializeBoard(const pBoard: ansistring);
var iRow,iCol: integer;
begin
with TStringList.Create do
begin
Text := pBoard;
for iRow := 0 to Min(Count-1, Size-1) do
for iCol := 1 to Min(length(Strings[iRow]),Size) do
case Strings[iRow][iCol] of
' ': Cell[iRow+1,iCol] := ctNoAccess;
'.': Cell[iRow+1,iCol] := ctEmpty;
'o': Cell[iRow+1,iCol] := ctPeg;
end;
Free;
end;
end;
Tämä menettely tekee täsmälleen saman. Se käyttää kätevää ominaisuutta, sitä että voidaan käyttää With-lausetta kun luodaan dynaamisesti oliota. Toimiakseen tämä aliohjelma vaatii että lisätään Math käännösyksikkö uses lauseen luetteloon.
- Suorita ohjelma. Kaikki soluissa on pelinappula kuten klassisessa Peg Solitaire pelissä.
Tapahtumien uudelleenkäynti
Se ei kovin vaikuttava vielä, mutta tehty on jo paljon. Tärkein joka puuttuu tällä hetkellä on kyky hypätä solujen poistaa ne pelilaudalta. Ja että siihen käytetään hiirtä. Mutta miten? Tutkitaan mahdollisuuksia.
- Tuo TMemo-komponentti lomakkeelle, TPaintBox pbPeg:n vasemmalla puolen (TMemo löytyy Standard välilehdeltä komponenttipaletilta).
- Komponenttimuokkaimessa muuta Align arvoon alClient. Se vie kaiken jäljellä olevan tilan, joka on jäljellä TPaintbox.
- Vaihda BorderSpacing.Around arvoon 4.
- Tyhjennä Lines ominaisuus (klikkaa kolme pistettä painiketta (...) ja poista teksti). Tuloksena pitäisi näyttää tältä:
- Valitse TPaintBox pbPeg.
- Komponenttimuokkaimessa valitse Tapahtumat-välilehti.
On kaksi tapahtumaa, jotka ovat mielenkiintoisia: OnMouseDown ja OnMouseUp. Katsotaan mitä tapahtuu OnMouseDown tapahtumassa.
- Komponenttimuokkaimessa valitse OnMouseDown tapahtuma.
- Klikkaa kolme pisteettä painiketta (tämä luo rungon tapahtuman aliohjelmalle).
Syntyvään aliohjelman otsikkoon sisältyy useita muuttujia. Kaksi niistä ovat X ja Y. Ne sisältävät TPaintBox:n paikan jossa hiirtä painettiin. Tehdään ne näkyviksi lisäämällä joitakin tietoja TMemo-komponenttiin joka on lisätty lomakkeelle.
- Lisää seuraavat lauseet aliohjelmaan; se lisää muotoillun rivin TMemo-komponentin näyttämään paikkaa, missä hiirtä painettiin:
Memo1.Append( format('Mouse down @ %dx%d',[X,Y]) );
- Suorita ohjelma. (Paina F9).
- Klikkaa hiirellä pelilautaa useissa eri paikoissa. Muistion (TMemon) ruutu näyttää missä hiiri painettiin.
- Lopeta ohjelma.
Ilmeisesti ei voi käyttää "raakaa" X ja Y-koordinaatteja; ne ovat liian isoja 7x7 pelilaudalle. Ne täytyy sovittaa solun rivin ja sarakkeen koordinaattuin. Sitä varten tarvitaan toiminto, joka sovittaa X / Y-koordinaatin solun koordinaattiin. Tähän tarvitaan paintbox:n leveys ja korkeus, solujen lukumäärä ruudukossa ja yksittäisen solun leveys ja korkeus. On yksi paikka, jossa kaikki tämä tulee yhdessä ... pn Paint luokka. Tämä on siis selvä paikka tehdä tämä laskelma.
- Avaa PegDatastructures käännösyksikkö.
- Lisää TCellPosition tietuetyyppi johon tallennetaan solun koordinaatit, tyyppien esittelyosioon:
type
TCellNums = 1..C_MAX;
TCellType = (ctNoAccess, ctEmpty, ctPeg);
TPegCells = array[TCellNums, TCellNums] of TCellType;
TCellPosition = record
Row: TCellNums;
Col: TCellNums;
end;
- Avaa PegSolPainter käännösyksikkö.
- Lisää luokan julkiseen (public)osioon funktio CanvasXYtoCell (...):
public
constructor Create(pPegSol: TPegSolitaire; pCanvas: TCanvas);
procedure Repaint(const pCanvasWidth, pCanvasHeight: integer);
function CanvasXYtoCell(const pX, pY: integer): TCellPosition;
- Luo funktion runko. Paina Ctrl+⇧ Shift+C.
- Lisää seuraava koodi:
function TPegSolPainter.CanvasXYtoCell(const pX, pY: integer): TCellPosition;
begin
result.Col := (pX div CellWidth) + 1;
result.Row := (pY div CellHeight) + 1;
end;
Edellä koodi kartoittaa suuret X / Y-koordinaatit vastaamaan solun rivi ja sarake numeroita. Yksi ongelma on kuitenkin: Täällä CellWidth ja CellHeight ei ole saatavilla. Ne ovat välituloksia Repaint aliohjelman eikä niitä tallenneteta. Tämä pitää korjata.
- Lisää CellWidth ja CellHeight yksityisinä muuttujina TPegSolPainter luokkaan:
TPegSolPainter = class
private
PegSol : TPegSolitaire;
Canvas : TCanvas;
CellWidth : integer;
CellHeight : integer;
- Poista muuttujat CellWidth and CellHeight aliohjelman Repaint muuttujien esittelystä:
procedure TPegSolPainter.Repaint(const pCanvasWidth, pCanvasHeight: integer);
var
iRow, iCol : TCellNums;
CellArea : TRect;
begin
Nyt voidaan muuttaa aliohjelmaa, missä hiiren napin alaspainamisen koordinaattia käsiteltiin. Uuden funktion X / Y-paikka voidaan nyt sovittaa solun koordinaatteihin.
- Avaa ufrmmain käännösyksikkö.
- Paikallista pbPegMouseDown aliohjelma.
- Muuta koodi:
procedure TfrmMain.pbPegMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var CellRC: TCellPosition;
begin
CellRC := pegpaint.CanvasXYtoCell(X,Y);
Memo1.Append( format('Mouse down @ %dx%d',[CellRC.Row, CellRC.Col]) );
end;
Tehdään sama OnMouseUp tapahtumaan:
- Luo aliohjelman runko OnMouseUp tapahtumaan.
- Lisää tämä koodi:
procedure TfrmMain.pbPegMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
with pegpaint.CanvasXYtoCell(X,Y) do
Memo1.Append( format('Mouse up @ %dx%d',[Row, Col]) );
end;
Huomaa, että tämä menetelmä tekee täsmälleen sama kuin OnMouseDown, mutta käyttäen with-rakennetta säästetään vaivaa määrittää paikallinen muuttuja (vähemmän on enemmän ...).
Testataan tätä:
- Suorita ohjelma.
- Klikkaa ylhäällä vasemman puolista solua ja pidä hiiren nappi alhaalla.
- Siirrä hiiren kohdistin toiseen soluun.
- Vapauta hiiren painike.
Joka kerta kun painaa hiiren painiketta ja vapauttaa sen, niin solu, jossa hiiren painiketta painettiin tai vapautettiin näkyy muistiossa.
Liimataan asioita yhteen
Tämä ohjelma toimii vähän kuin laskin ohjelma:
- Käyttäjä valitsee pelinappulan jota siirretään painamalla hiiren painiketta.
- Käyttäjä siirtää pelinappulan toiseen pelinappulan yli tyhjään soluun (hyppy).
- Käyttäjä vapauttaa hiiren painikkeen ja siirto tapahtuu.
Tällä ohjelma ei muista mitä solua painettiin. Joten kun käyttäjä painaa hiiren painiketta solun päällä, meidän täytyy säilyttää tämä paikka jonnekin.
- Avaa ufrmmain käännösyksikkö.
- Lisää muuttuja FromCell lomakkeen yksityiseksi muuttujaksi:
private
{ private declarations }
pegsol : TPegSolitaire; // The game data
pegpaint: TPegSolPainter; // The paint class for the game
FromCell: TCellPosition; // The peg that is going to leap
- Mene OnMouseDown tapahtumankäsittelijään.
- Lisää seuraava rivi tähän aliohjelmaan:
FromCell := pegpaint.CanvasXYtoCell(X,Y);
Nyt kun käyttäjä vapauttaa hiiren painikkeen, on suoritettava hyppy (myös tarkistettava, onko se ok ja päivittää pelilautaa). Ollaan optimistisia (muista Top Down huomautus?) Ja olettaa, on aliohjelma, joka tekee sen meille: Leap (<FromCell>, <ToCell>).
- Paikallista OnMouseUp tapahtumakäsittelijä ja lisää seuraava rivi:
pegsol.Leap(FromCell, pegpaint.CanvasXYtoCell(X,Y));
FromCell oli jo pelinappula kun hiiren näppäintä painettiin. Kohdesolun lasketaan yksinkertaisesti kutsumalla pegpaint.CanvasXYToCell (...). Voisimme ottaa käyttöön paikallinen muuttuja sitä, mutta se ei ole välttämättä tarpeen.
- Lisää seuraava rivi aliohjelmaan:
pbPeg.Repaint;
Tämä pakottaa paintbox:n piirtämään itsensä, joten kaikki päivitetyt solut näyttävät oikean sisällön.
Täydellinen OnMouseDown aliohjelma nyt näyttää tältä:
procedure TfrmMain.pbPegMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
with pegpaint.CanvasXYtoCell(X,Y) do
Memo1.Append( format('Mouse up @ %dx%d',[Row, Col]) );
pegsol.Leap(FromCell, pegpaint.CanvasXYtoCell(X,Y));
pbPeg.Repaint;
end;
Ennenkuin mennään Leap aliohjelmaan niin vähän lisätoimintoja otetaan käyttöön. Muista, että voidaan noutaa solun tila (tyhjä, pelinappula tai ei pääsyä) TPegSolitaire:n Cell ominaisuuden kautta! Esimerkiksi pegsol.Cell [2,2] antaisi rivin 2 ja sarakkeen 2 solun tilan. Mutta tähän väliin, on otettu käyttöön uusi tietorakenne: TCellPosition. Niinpä esimerkiksi, jos halutaan saada tila FromCell:stä tarvitaan seuraava hankala lause:
state := pegsol.Cell[ FromCell.Row, FramCell.Col ]
Olisi mukavaa, jos voisi kirjoittaa jotain:
state := pegsol.GetCell(FromCell)
Lisätään tämä peli luokkaan.
- Avaa käännösyksikkö PegDatastructures.
- Lisää yksityinen (private) funktio:GetCell (const pPosition: TCellPosition): TCellType;
TPegSolitaire = class
private
FSize: TCellNums;
PegCells: TPegCells;
function GetCell(const pRow, pCol: TCellNums): TCellType;
procedure SetCell(const pRow, pCol: TCellNums; const pValue: TCellType);
function GetCell(const pPosition: TCellPosition): TCellType;
Nyt on kaksi samannimistä GetCell funktiota, mutta niiden parametriluettelo on erilainen. Joten Lazarus tietää tarkalleen, milloin kutsua kyseistä funktiota.
- Luo funktion runko (Paina Ctrl+⇧ Shift+C).
- Lisätään seuraava koodi:
result := Cell[pPosition.Row, pPosition.Col];
Takaisin "pääjuoneen". On otettu käyttöön aliohjelma Leap (...), mutta sitä ei vielä ole. Se on aliohjelma, joka muuttaa peliä pelilaudalla, joten paikka mihin laittaa se on TPegSolitaire luokka.
- Avaa käännösyksikkö PegDatastructures.
- Lisää aliohjelma Leap luokan TPegSolitaire public osioon.
public
constructor Create(const pSize: TCellNums);
procedure InitializeBoard(const pBoard: ansistring);
procedure Leap(const pFromCell, pToCell: TCellPosition);
- Luo aliohjelman runko (Paina Ctrl+⇧ Shift+C).
Nyt tämä aliohjelma tekee kaiken kovan työn: tarkistaa, että solut ovat voimassa ja onko hyppy on suinkin mahdollista. Tämä ei ole vaikeaa, mutta tarkastusten määrä on varsin laaja jotain, joka näyttää yksinkertaista. Koodi on melko itsestään selvä, ja jos ei, toivottavasti kommentit selittää mitä tapahtuu. Huomaa, että aliohjelma käyttää GetCell (<cellposition>) funktiota joka luotiin aikaisemmin.
procedure TPegSolitaire.Leap(const pFromCell, pToCell: TCellPosition);
var dx, dy: integer;
JumpedCell: TCellPosition;
begin
// Verify that the start cell is occupied and the target cell is empty
// If not, leave the procedure via the EXIT.
if (GetCell(pFromCell) <> ctPeg) or
(GetCell(pToCell) <> ctEmpty) then
EXIT;
// Calculate the horizontal and vertical distance between the cells
dx := abs(pFromCell.Col - pToCell.Col);
dy := abs(pFromCell.Row - pToCell.Row);
// A valid move has one direction equal to zero and the other equal to 2
if ((dx = 2) and (dy = 0))
or ((dx = 0) and (dy = 2)) then
begin
// Determine the position of the jumped cell; it's in the middle
JumpedCell.Col := (pFromCell.Col + pToCell.Col) div 2;
JumpedCell.Row := (pFromCell.Row + pToCell.Row) div 2;
// Final check: is there a peg in the jumped cell?
if GetCell(JumpedCell) = ctPeg then
begin
// Jump: clear the FromCell, empty the jumped cell and populate the ToCell
Cell[pFromCell.Row, pFromCell.Col] := ctEmpty;
Cell[JumpedCell.Row, JumpedCell.Col] := ctEmpty;
Cell[pToCell.Row, pToCell.Col] := ctPeg;
end;
end;
end;
Usko tai älä, mutta nyt on toimiva Peg Solitaire lautapasianssi peliohjelma!
- Suorita ohjelma.
- Paina hiiren painiketta yhden pelinappula solun päällä.
- Hyppää yli toisen pelinappula solun tyhjään soluun.
- Vapauta hiiren painike.
- Toista tätä...
Puhdistusta
Lomakkeelle meillä on vielä muistio komponentti, joka näyttää hiiren klikkauksella. Sitä enää todellakaan tarvita.
- Poista muistio lomakkeelta.
- Poista muistion päivitykset MouseDown ja MouseUp tapahtumat.
procedure TfrmMain.pbPegMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
FromCell := pegpaint.CanvasXYtoCell(X,Y);
end;
procedure TfrmMain.pbPegMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
pegsol.Leap(FromCell, pegpaint.CanvasXYtoCell(X,Y) );
pbPeg.Repaint;
end;
- Siirry lomakkeelle (F12).
- Valitse TPaintBox pbPeg
- Komponenttimuokkaimessa vaihda ominaisuus Align arvoon alClient (Ominaisuudet välilehti).
- Poista käännösyksikkö stdctrls uses luettelosta. Muuten kääntäjä antaa vihjeen "Kääntäjän viestit" ikkunassa ettei sitä käytetä (Se lisättiin automaattisesti kun TMemo komponentti tuotiin lomakkeelle).
Mitä seuraavaksi?
Jotta tämä olisi täysin toimiva peli tarvitsemme tapa muuttaa pelilautaa ilman että kääntää sovellus uudelleen. Ja grafiikka voitaisiin parantaa. aletaan käsitellä ensinmäistä kysymystä ensin.
Luodaan enemmän pelilautoja
Olisi mukavaa, jos voidaan lisätä pelilautoja peliin ilman että joutuisi kääntämään uudelleen sovelluksen. Ilmeinen paikka tallentaa pelilaudan kaavat olisi ulkoiset tekstitiedosto. Käyttäjä voi sitten aloittaa uuden pelin avaamalla jonkun tekstitiedoston. Hauska asia on, se on helppo lisätä, koska ollaan jo tehty suurimman osan kovasta työstä. Katsotaanpa ensin tehdä kaksi pelilaudan kaavoista.
- Valitse valikosta Tiedosto / Uusi ...; Tämä avaa "Uusi..."- ikkunan.
- Valitse vaihtoehto Moduuli / Teksti ja valitse OK.
- Kirjoita seuraava teksti (sen pitäisi näyttää tutulta :-)):
ooo
ooo
ooooooo
ooo.ooo
ooooooo
ooo
ooo
- Valitse valikosta Tiedosto / Tallenna.
- Tallenna tiedosto projektin bin kansioon nimellä classic.txt.
Lisää toinen tiedosto:
- Valitse valikosta Tiedosto / Uusi ....
- Valitse vaihtoehto Moduuli / Teksti ja valitse OK.
- Kirjoita seuraava teksti:
...
.o.
..ooo..
.ooooo.
ooooooo
...
...
- Valitse valikosta Tiedosto / Tallenna.
- Tallenna tiedosto bin kansioon nimellä triangle.txt.
On monia tapoja luoda ja avata uusia pelilautoja. Tässä opetusohjelmassa käytetään valikko lähestymistapaa (perusteellisempi kuvaus valikosta on jossain toisessa opetusohjelmassa).
- Avaa ufrmmain käännösyksikkö.
- Valitse TMainMenu-komponentti komponenttipaletin Standard-välilehdeltä.
- Klikkaa lomakkeella (sillä ei ole väliä, jos osut paintbox:n). Kuvake näkyy osoittaa, että valikko on nyt saatavilla olevat.
- Tuplaklikkaa MainMenu1 kuvaketta. Tämä avaa valikkomuokkaimen. Yksi kohde on jo käytettävissä, nimeltään New Item1.
- Komponenttimuokkaimella muutaa sen Caption tekstiksi &File (sisältyy et-merkillä, sitä käytetään vetämään alaviivat kun painat Alt päävalikkoon).
- Klikkaa hiiren oikealla File kohdasta valikkomuokkaimessa ja valitse Luo alivalikko ponnahdusikkunasta.
- Muuta Caption tekstiksi E&xit (tämä on tuttu sovelluksen lopeta kohta).
- Klikkaa hiiren oikealla File kohtaa.
- Valitse Lisää uusi kohta (jälkeen) ponnahdusikkunasta.
- Muuta Caption tekstiksi kyseisen kohtaan &Game.
- Klikkaa hiiren oikealla Game kohdasta valikkomuokkaimessa ja valitse Luo alivalikko ponnahdusikkunasta.
- Muuta Caption tekstiksi Load from file....
Valikko nyt näyttää tältä:
Valikko on myös näkyvissä lomakkeen yläosassa.
- Klikkaa File-valikosta kohtaa; Tämä avaa alivalikon ja Exit on näkyvissä.
- Tuplaklikkaa Exit. Kuten arvata saattaa tämä luo tapahtumakäsittelijän. Toisin sanoen aliohjelman, joka suoritetaan heti, kun käyttäjä klikkaa tätä valikkokohtaa.
- Lisää seuraava rivi luotuun koodin runkoon:
Close;
Tämä sulkee lomakkeen. Ja koska se on ainoa lomake niin myös sovellus loppuu.
Ok, nyt on aika tarkastella, miten avata pelilauta tiedostoja. On olemassa vakiokomponentteja ja valikko käytettävissä tehdä tämä, mutta tehdään se manuaalisesti.
- Mene lomakkeelle.
- Komponentti paletulta valitse Dialogs välilehti.
- Valitse TOpenDialog komponentti (klikkaa sitä).
- Klikkaa lomaketta (missä tahansa). OpenDialog1 komponentti on nyt näkyvissä lomakkeella. Tämä komponentti tarjoaa meille eräänlainen selailuikkunan, jolla voimme valita tiedostoja. Lomake näyttää nyt tältä:
- Komponenttimuokkaimessa klikkaa Filter ominaisuutta ja sitten 3-piste (...) painiketta. Tämä avaa suodattimen muokkain dialogi-ikkunan. Tämä on paikka, jossa kerrotaan minkä tyyppisiä tiedostoja käyttäjälle on mahdollisuus valita.
- Nyt halutaan vain tekstitiedostoja joten lisätään tälläinen suodatinrivi: Peg Solitaire TextFiles (* .txt) | * .txt (katso kuva alla).
- Paina OK.
- Komponenttimuokkaimessa muuta Options.ofFileMustExist totta. Tämä pakottaa dialogi sallii vain nykyiset tiedostot voidaan valita.
Hyvä niin. Nyt todella avata tiedoston, kun valikko on valittuna:
- Lomakkeella klikkaa Game valikosta. Tämä avaa pudotusvalikon.
- Klikkaa Load from file.... Odotetusti tämä luo .... tapahtumankäsittelijänä.
- Lisää seuraava koodi tapahtumakäsittelijään:
if OpenDialog1.Execute then
ShowMessage(OpenDialog1.FileName);
Muista: edetä pienin askelin. Nyt on lisätty valikko lomakkeelle josta voi poistua sovelluksesta tai ladata pelilaudan pohjan levyltä. Vielä ei ole ??koodattu latausosaa, mutta yritetään ensin testata koodia joka on jo tehty.
- Suorita sovellus.
- Valitse valikosta Game/Load from file....
- Valitse triangle.txt ja paina Avaa.
- Ikkuna ponnahtaa joka osoittaa meille valitun tiedostonimen.
- Lopeta ohjelma.
Nyt viimeistellään latauskoodi. Koodi on melko itsestään selvä ja käyttää uudelleen joitakin aliohjelmia jotka on jo luotu.
- Mene lomakkeella ja klikkaa Game valikosta. Tämä avaa pudotusvalikon.
- Klikkaa Load from file.... Odotetusti tämä avaa tapahtumankäsittelijän.
- Korvaa koodin tällä:
procedure TfrmMain.MenuItem4Click(Sender: TObject);
begin
// Open the pop up dialog
if OpenDialog1.Execute then
begin
// Start with a new empty board
StartNewGame;
// Dynamically create a stringlist to load the board layout
with TStringList.Create do
begin
// Load the board layout from the textfile
LoadFromFile(OpenDialog1.FileName);
// Initialize the board with the file's contents
pegsol.InitializeBoard(Text);
// Clean up the stringlist
Free
end;
// After loading the new board update the paintbox
pbPeg.Repaint;
end;
end;
Ja se kaikki on siinä!
Suorita ohjelma, avaa pelilaudan pohjatiedosto ja pelaa peliä!
Silmänruokaa
Nyt on toimiva Peg Solitaire sovellus. Siellä on vielä paljon asioita parannettavaksi, mutta peli itsessään toimii hienosti. Viimeinen osa tätä opetusohjelmaa on parantaa pelin ulkonäköä. Toki peli toimii, mutta hieman näyttävämpi grafiikka olisi mukava. Ja se on melko helppo lisätä. Kaikki mitä tarvitaan on kolme kuvaa solutyyppejä varten, päivittää TPegSolPainter-luokkaa ja tärkein muoto, kerro maalari käyttää kuvia. Joten lisää kolme kuvaa TPegSolPainter luokkaan:
- Avaa PegSolPainter käännösyksikkö.
- Lisää kolme (3) kuvamuuttujaa private osioon:
TPegSolPainter = class
private
PegSol : TPegSolitaire;
Canvas : TCanvas;
CellWidth : integer;
CellHeight : integer;
ImgNoAccess: TPicture;
ImgEmpty : TPicture;
ImgPeg : TPicture;
- Lisää kuvan latausaliohjelma public osioon.
public
constructor Create(pPegSol: TPegSolitaire; pCanvas: TCanvas);
procedure Repaint(const pCanvasWidth, pCanvasHeight: integer);
function CanvasXYtoCell(const pX, pY: integer): TCellPosition;
procedure LoadImage(const pCellType: TCellType; const pFilename: string);
- Luo aliohjelman runko.
- Lisää koodi:
procedure TPegSolPainter.LoadImage(const pCellType: TCellType; const pFilename: string);
procedure UpdateImage(var pImg: TPicture; const pNewImage: TPicture);
begin
pImg.Free;
pImg := pNewImage
end;
var pic: TPicture;
begin
// Make sure the file exists
if FileExists(pFilename) then
begin
// First load the picture in a local variable
pic := TPicture.Create;
pic.LoadFromFile(pFilename);
// Now update the required image based on the pCelltype parm
case pCelltype of
ctNoAccess: UpdateImage(ImgNoAccess, pic);
ctEmpty : UpdateImage(ImgEmpty, pic);
ctPeg : UpdateImage(ImgPeg, pic);
end;
end;
end;
Koodi on taas aika itsestään selvä. Huomaa, että käytetään lyhyttä paikallista aliohjelmaa päivittämään kuvaa. Jos emme tekisi sitä sitten case-lause ((case pCellType of) olisi monimutkaisempi (eli sisältäisi ylimääräistä koodia). Nyt kukin solutyyppi tarvitsee vain yhden rivin päivittäkseen oikean kuvan.
Nyt kun kuvia on lisätty luokkaan, täytyy käyttää niitä ja se tietenkin tapahtuu Repaint aliohjelmassa. Jokaista celltype tarkistetaan, onko kuva on saatavilla. Ja jos on niin käytetäään tavallisen piirustuksen sijasta kuvaa.
- Paikallista Repaint aliohjelma.
- Case lauseessa, muuta ctNoAccess, ctEmpty ja ctPeg osiot seuraavanlaiseksi:
// And now draw the cell based on the cell's contents
case pegsol.Cell[iRow,iCol] of
ctNoAccess: // Draw cells that are not accessible
if not assigned(ImgNoAccess) then
begin
Canvas.Brush.Color := clGray;
Canvas.Rectangle(CellArea);
end
else with ImgNoAccess do
Canvas.CopyRect(CellArea, Bitmap.Canvas, Rect(0,0,Width,Height));
ctEmpty: // Draw cells that are currently empty
if not assigned(ImgEmpty) then
begin
Canvas.Brush.Color := clBlue;
Canvas.Rectangle(CellArea);
end
else with ImgEmpty do
Canvas.CopyRect(CellArea, Bitmap.Canvas, Rect(0,0,Width,Height));
ctPeg: // Draw cells that are occupied
if not assigned(ImgPeg) then
begin
Canvas.Brush.Color := clBlue;
Canvas.Rectangle(CellArea); // Erase the background first
Canvas.Brush.Color := clGreen;
Canvas.Ellipse(CellArea);
end
else with ImgPeg do
Canvas.CopyRect(CellArea, Bitmap.Canvas, Rect(0,0,Width,Height));
end;
Lisätään kuva latauskoodi piirtoluokkaan.
- Avaa ufrmmain -käännösyksikkö.
- Paikallista aliohjelma StartNewGame.
- Lisää LoadImage kutsu pegpaint olion luomisen jälkeen:
// Start with a new 7x7 game
pegsol := TPegSolitaire.Create(7);
pegpaint := TPegSolPainter.Create(pegsol, pbPeg.Canvas);
pegpaint.LoadImage(ctNoAccess, 'tutpeg_cellforbidden.jpg');
pegpaint.LoadImage(ctEmpty, 'tutpeg_cellempty.jpg');
pegpaint.LoadImage(ctPeg, 'tutpeg_celloccupied.jpg');
Varmista, että kuvat ladataan ja laitetaan ohjelman bin kansioon, tai vielä parempaa, luot omat kuvat. Huom. kun lataat allaolevia kuvia, klikkaa niitä ensin.
- Nyt kun testataan Peg Solitaire peliä. Toivottavasti se näyttää suunnilleen samanlaiselta kuin kuva tämän opetusohjelman alussa ...
Viimeinen "helmi"
Useimmilla ohjelmilla on oma kuvake, joka näkyvät valikon ja tiedoston selaus ikkunoissa. On helppo lisätä oma kuvake sovellukseen.
- Valitse valikosta Projekti / Projektikohtaiset asetukset ....
- Klikkaa Projektin asetukset/ Sovellus. Tämä näyttää ikkunassa sovelluksen kuvaketta.
- Klikkaa Lataa kuvake ja lataa siihen sopiva kuvake tai ladata marmorikuvan joka on tässä alla:
- Paina OK.
Sovelluksella on nyt oma kuvake, joka näkyy valikossa ja tiedoston selaus ikkunassa.