[ Srki_82 @ 13.05.2007. 20:10 ] @
Ovih dana imam dosta slobodnog vremena, a nadam se da ce tako i ostati pa bih mogao da napisem neki tutorial o pravljenju multiplatform igara u Pascal-u (Free Pascal Compiler + Lazarus). Poenta bi bila da napisem prvo tekst i potom postavim kod, ali nekompletan... cim ga neko zavrsi, postavim svoje resenje (mozda se na kraju ispostavi da je moje resenje pogresno ) i idemo dalje. Interesuje me samo da li ima ljudi koje bi to interesovalo da ne pisem bezveze?

Isli bi od pocetka (tekstualni prikaz i osnovni tipovi podataka) pa do OOP igara koje koriste razne API-je za kreiranje prozora, crtanje, pustanje zvuka, fiziku...

[ Nemanja_666 @ 13.05.2007. 22:19 ] @
Bilo bi dobro da uradimo igru cisto u free pascal-u nekoristeci nikih biblioteka iz lazarus-a.
[ PeroGenijalac @ 13.05.2007. 22:44 ] @
evo jedna igra koju je jedan moj prijatelj (drug kako god) napisao drugom prijatelju jer mu je to trebalo za školu, i sad je ovaj upao u živo blato, njemu profesor kaže da će ići na natjecanje, a nema zapravo pojma.... Inače u igri se radi o tome da treba u posudu hvatati slova koja padaju:

Code:


(*
========================
Napravio Matija *****
travanj 2007
========================
*)
PROGRAM Igra;                                                                  {Naslov Programa}
USES Crt,Dos,Graph;                                                            {Potrebno za neke funckcije kao KeyPressed,ReadKey,GotoXY...}
TYPE postavke=RECORD                                                           {Zapis,sadrži postavke slova}
posx,posy,slova : integer;                                                     {X,Y pozicija slova i sam broj koji preko chr naredbe pretvaramo u slovo}
END;                                                                           {Kraj zapisa}
slovo=ARRAY[1..40] OF postavke;                                                {Niz slova}
postavke2 = RECORD                                                             {Zapis,sadrži postavke za highscore}
ime :STRING;                                                                   {ime}
bodovi:integer;                                                                {bodovi}
END;                                                                           {kraj zapisa}
scores=ARRAY[1..40] of postavke2;                                              {niz zapisa imena i rezultata}
var KrajIgre,kraj,Pauza : boolean;                                             {Dali je igra gotova,kraj repeat petlje u Kraj_Igre procedure, i provjera pauze}
Key : LONGINT;                                                                 {Služi za provjeru koja je tipka pritisnuta}

(*varijable i,j služe za popunjavanje podataka kroz for petlju,padslova služi za spremanje koliko je slova palo zaredom,
maxslova koliko slova pada,TimeCount služi za određivanje brzine padanja umjesto Delay Funkcije,zivot služi za spremanje broja života,
bodovi za spremanje broja bodova,MaxRez-potreban broj bodova za novi "nivo",DelayTime zajedno sa TimeCount služi za ubrzavanje ili usporavnja slova
iako to nije prava vremenska funkcija već više ovisi o brzini procesora*)
i,j,padslova,maxslova,TimeCount,zivot,bodovi,MaxRez,DelayTime :integer;
kosaricax,kosaricay : byte;                                                    {GotoXY Traži Byte tip, iako radi i sa običnim integer tipom,sadrži x i y poziciju košarice}
s: slovo;                                                                      {Spremanje postavki slova}
sc :scores;                                                                    {Spremanje imena i bodova iz datoteke i u datoteku}
izbor: char;                                                                   {Spremanje Početnog Izbora}
data:text;                                                                     {Tex Tip}
ime: string;                                                                   {Ovdje Spremamo ime}
(*
==============================================================
Procedura koja resetira poziciju slova i samo slovo          |
==============================================================
*)
PROCEDURE gReset;                                                              {Naziv procedure}
BEGIN                                                                          {Početak procedure}
             s[i].slova:=65;                                                   {Po ASCII Kodu slovo A je na 65->trebamo obična slova}
             s[i].slova:=s[i].slova+random(25);                                {Do 90 su velika slova(65+25=90), dodajmo nasumičan broj da dobijemo nasumično slovo}
             s[i].posx:=random(75)+1;                                          {Sirina do 76(75+1) +1 zato da uvijek bude veće od 1}
             s[i].posy:=0;                                                     {Pozicija 0}
             s[i].posy:=s[i].posy-(5*i);                                       {Da slova ne bi bila u istom redu}
END;                                                                           {Kraj procedure}
(*
===============================================================
Procedura koja se poziva na kraju igre, sprema svaki          |
rezultat koji igrač napravi  u  datoteku i omogućuje          |
ponovno igranje ili izlaz iz igre        |                    |
===============================================================
*)
PROCEDURE Kraj_Igre;                                                           {Naziv procedure}
BEGIN                                                                          {Početak procedure}
    IF bodovi<0 THEN bodovi:=0;                                                {U slučaju da su nam bodovi ispod 0}
    assign(data,'Scores.txt');                                                 {Zadano ime datoteke}
    {$I- iskljuci I/O provjeru}
    append(data);                                                              {Dodaj rezultat na kraj}
    IF IOresult<>0 THEN rewrite(data);                                         {Ako datoteka ne postoji, napravi novu, IOresult vraća 0 ako je sve prošlo u redu}
    close(data);                                                               {Zatvori datoteku}
    {$I+ ukljuci I/O provjeru}
    append(data);                                                              {Dodavanje teksta na kraj datoteke}
    writeln(data,ime);                                                         {Piše ime u datoteku}
    writeln(data,bodovi);                                                      {Piše bodove u datoteku}
    close(data);                                                               {Zatvara datoteku}
    clrscr;                                                                    {Čisti ekran}
    kraj:=false;                                                               {Da nebi došlo do beskonačne petlje}
    REPEAT                                                                     {Repeat petlja}
         gotoxy(30,10);                                                        {Oko sredine ekrana}
         writeln('KRAJ IGRE');                                                 {Ispiši poruku}
         gotoxy(10,11);                                                        {Oko sredine}
         writeln('Pritisnite P ako zelite igrati ponovo ili Q za izlaz: ');    {Ispiši poruku}
         IF KeyPressed THEN                                                    {Ako je tipka pritisnuta, procesuiraj}
           BEGIN                                                               {Početak if naredbe}
             Key:=ORD(READKEY);
             IF Key=0 THEN Key:=ORD(READKEY) SHL 8;                            {Funkcijska tipka}
             CASE Key OF                                                       {Ako želimo igrati ponovo resetirajmo postavke}
                  ORD('p'): BEGIN
                            MaxRez:=500; bodovi:=0;
                            kosaricay:=23; kosaricax:=23;
                            zivot:=1;
                            maxslova:=1; DelayTime:=100;
                            greset; kraj:=true;
                            END;
                  ORD('q'): halt(1);                                           {Direktan izlaz iz programa}
             END;                                                              {End case}
            END;                                                               {end if}
     UNTIL kraj=true;                                                          {Sve dok kraj nije true}
END;                                                                           {Kraj procedure}

{Procedura za zamjenu brojeva}
PROCEDURE zamijeni(var x,y : integer);
var t:integer;
begin
t:=x;
x:=y;
y:=t;
end;
{Procedura za zamjenu stringova}
PROCEDURE zamijeni2(var x,y : string);
var t:string;
begin
 t:=x;
 x:=y;
 y:=t;
 end;
(*
========================================================================================================================
Procedura za ispis rezultata                                                                                           |
========================================================================================================================
*)
PROCEDURE HighScore;                                                           {Naziv procedure}
BEGIN                                                                          {Početak procedure}
assign(data,'Scores.txt');                                                     {Zadano ime datoteke}
{$I- iskljuci I/O provjeru}
reset(data);                                                                   {Pokušava otvoriti datoteku}
IF IOresult<>0 THEN                                                            {Ako nije uspjelo}
               BEGIN                                                           {Početak if naredbe}
                   rewrite(data);                                              {Napravi datoteku}
                   writeln('Rezultati 15 najboljih');                          {Ispiši poruku}
                   writeln;                                                    {Jedan prazan red}
                   FOR i:=1 TO 15 DO                                           {Popuni datoteku i ispiši na ekran}
                                 BEGIN                                         {Početak for naredbe}
                                      writeln(data,'Igrac',i);                 {Spremi ime igrača u datoteku}
                                      write('igrac',i);                        {Ispiši ime na ekran}
                                      writeln(data,' ',1000 div i);            {Spremi broj bodova u datoteku}
                                      writeln(' ',1000 div i);                 {Ispiši na ekran}
                                 END;                                          {Kraj for petlje}
               END                                                             {Kraj if naredbe}
ELSE                                                                           {Ako je uspjelo}
BEGIN                                                                          {Početak else naredbe}
         writeln('Rezultati 15 najboljih');                                     {Ispiši poruku}
         writeln;                                                              {Jedan prazan red}
         FOR i:=1 TO 40 DO                                                     {Za i=1 do 9 radi}
         BEGIN                                                                 {Početak for petlje}
              IF (IOresult=0)AND NOT eof(data) THEN                            {Ako nema greške i nije kraj datoteke}
              BEGIN                                                            {Početak if naredbe}
                   readln(data,sc[i].ime);                                     {Učitaj ime}
                   readln(data,sc[i].bodovi);                                  {Učitaj bodove}
              END;                                                             {Kraj if naredbe}
         END;                                                                  {Kraj for petlje}
(*
=====================
Sortiraj podatke    |
=====================
*)
    FOR i:=1 TO 39 DO                                                          {Za i=1 do 9 radi}
    FOR j:=i+1 TO 40 DO                                                        {Za j=i+1 do 10 radi}
                    IF sc[i].bodovi<sc[j].bodovi THEN                          {Ako su bodovi manji}
                    BEGIN                                                      {Početak if petlje}
                         zamijeni(sc[i].bodovi,sc[j].bodovi);                  {Zamijeni bodove}
                         zamijeni2(sc[i].ime,sc[j].ime);                       {Zamijeni imena}
                    END;                                                       {Kraj If naredbe}
    FOR i:=1 TO 15 DO                                                          {Od i=1 do 9 radi}
    BEGIN                                                                      {Početak for petlje}
        IF sc[i].ime<>'' THEN writeln(sc[i].ime,' ',sc[i].bodovi);             {Ako ime nije prazno ispiši ga i ispiši bodove}
    END;                                                                       {Kraj for petlje}
END;                                                                           {Kraj else}
close(data);                                                                   {Zatvori datoteku}
readln                                                                         {Čekaj pritisak tipke}
END;                                                                           {Kraj procedure}
(*
==============================================
Procedura koja se poziva na početku programa |
==============================================
*)
PROCEDURE Upute;
BEGIN
writeln('Za svako ulovljeno slovo igrac dobiva 25 bodova a za svako izgubljeno gubi');
writeln('30 bodova. Svakih 500 bodova dobiva 1 zivot, igra se ubrzava i pada jedno');
writeln('slovo vise. Kada izgubi sve bodove gubi 1 zivot. Ako nema zivota igra zavrsava ');
writeln('Ako padnu 3 slova zaredom takoder gubi zivot.');
TextColor(14);                                                                 {Boja teksta}
TextBackground(9);                                                             {Boja pozadine}
for i:=0 to 76 do                                                              {Za i=1 do 76 radi}
write('=');                                                                    {Nacrtaj marginu}
writeln;                                                                       {Jedan prazan red}
writeln('Unesite S za pocetak, Q za izlaz ili H za High Score :');             {Ispiši poruku}
readln (izbor);                                                                {Sprema izbor}
END;                                                                           {Kraj procedure}

(*
=====================================================================================================================
Glavna funckija
=====================================================================================================================
*)

BEGIN                                                                          (*Početak glavne funkcije*)
(*
====================
Početne definicije |
====================
*)
randomize;                                                                     {Nasumični brojevi}
TimeCount:=0;                                                                  {Brojač vremena}
MaxRez:=500;                                                                   {Svakih 500 bodova "novi nivo"}
padslova:=0;                                                                   {Spremamo koliko slova padne zaredom van košarice}
DelayTime:=100;                                                                {Vrijeme odgode kod padanja slova}
zivot:=1;                                                                      {Početni broj života}
bodovi:=0;                                                                     {Početni bodovi}
maxslova:=3;                                                                   {Koliko slova pada}
kosaricax:=25;                                                                 {X pozicija košarice}
kosaricay:=23;                                                                 {Y pozicija košarice}
kraj:=false;                                                                   {Kod procedure Kraj_Igre za Repeat petlju}
KrajIgre:=false;                                                               {U glavnoj funkciji repeat petlja}
Pauza:=false;                                                                  {Spremamo Status pauze}
(*
=============================
Popunjavanje postavki slova |
=============================
*)
FOR i:=1 TO maxslova DO
gReset;                                                                        {Resetirajmo poziciju svakog slova}
(*
=========================
Sama igra               =
=========================
*)
TextColor(15);                                                                 {Čista bijela boja}
Upute;                                                                         {Pozivanje procedure upute}
CASE izbor OF                                                                  {Ako u proceduri upute unesemo H pozovi HighScore Proceduru}
'H','h':
BEGIN
     clrscr;                                                                   {Očistimo ekran}
     HighScore;                                                                {Pozovimo proceduru HighScore}
END;                                                                           {Kraj case izbora of h}
'S','s' :                                                                      {Ako unesemo S pokreni igru}
BEGIN
      clrscr;                                                                  {Očistimo ekran}
      write('Unesite ime:');                                                   {Ispiši poruku}
      readln(ime);                                                             {spremi unos u varijablu ime}
      REPEAT                                                                   {Repeat petlja}
        GotoXY(1,1);                                                           {Lijevi gornji kut}
        writeln('Bodovi: ',bodovi,' Zivoti: ',zivot,'       Pritisnite P za Pauzu, Q za Izlaz');
        writeln('-------------------------------------------------------------------------------');
        IF KeyPressed THEN                                                      {Pritisak tipke}
           BEGIN
             Key:=ORD(READKEY);
             IF Key=0 THEN
                  Key:=ORD(READKEY) SHL 8;
             CASE Key OF
                  ORD('j'):IF pauza=false THEN kosaricax:=kosaricax-1;          {Ako nismo u pauzi pomakni kosaricu lijevo}
                  ORD('k'):IF pauza=false THEN kosaricax:=kosaricax+1;          {Ako nismo u pauzi pomakni kosaricu desno}
                  ORD('p'): BEGIN                                               {U slučaju tipke p}
                            IF pauza=false then                                 {Ako pauza nije uključena}
                            pauza:=true                                         {Uključi ju}
                            ELSE                                                {inače}
                            pauza:=false;                                       {isključi pauzu}
                            END;
                  ORD('q'): Kraj_Igre;                                          {Ako pritisnemo q pozovi proceduru za kraj igre}
             END;                                                               {End Case}
           END;                                                                 {End if}
  (*Za makivanje "dopplerovog" efekta kosarice maknuti komentare*)
(*GotoXY(1,23);
writeln('                                                                                  ');*)
     GotoXY(kosaricax,kosaricay);                                               {Postavi pokazivac na poziciju kosarice}
     writeln('\__/');                                                           {"Nacrtaj" kosaricu}
     (* Naš Brojač umjesto delay funkcije*)
      FOR i:=1 to maxslova DO                                                   {Procesuiraj svako slovo}
      BEGIN
      TimeCount:=TimeCount+1;                                                   {Povečaj brojač svaki krug}
           IF s[i].posy>2 THEN                                                  {Samo ako je y kordinata slova veća od 2-> da ne pišemo preko bodova}
           BEGIN
                GotoXY(s[i].posx,s[i].posy);                                    {postavi slovo i ispiši ga na ekran}
                writeln(chr(s[i].slova));
           END;                                                                 {End if}
      IF pauza=false THEN                                                       {Ako nije pauzirano}
      IF TimeCount>DelayTime THEN BEGIN                                         {Ako je prosao određen period vremena}
      TimeCount:=0;                                                             {Postavi brojač na 0}
      s[i].posy:=s[i].posy+1;                                                   {Simulacija padanja}
      clrscr;                                                                   {Očisti ekran}
      END;
      IF (s[i].posy=kosaricay) AND (s[i].posx<kosaricax) OR (s[i].posy=kosaricay) AND (s[i].posx>kosaricax+3) then {Ako slovo ne padne u košaricu gubimo bodove}
         BEGIN
              bodovi:=bodovi-30;                                                {Smanji bodove za 30}
              padslova:=padslova+1;                                             {Brojac koliko slova pada zaredom povećaj za 1}
              gReset;                                                           {Ponovno postavi slovo}
              IF bodovi<0 THEN zivot:=zivot-1;                                  {Ako su bodovi ispod 0 gubimo život}
              IF padslova>2 THEN                                                {Ako su pala 3 slova zaredom također gubimo život}
              BEGIN
              zivot:=zivot-1;
              padslova:=0;
              END;

         END;
      IF (kosaricay=s[i].posy) AND (s[i].posx>=kosaricax) AND (s[i].posx<=kosaricax+3)THEN {Ako su košarica u slovo na istoj poziciji, povećajmo bodove i ponovno postavimo slovo na random poziciju}
         BEGIN bodovi:=bodovi+25;                                              {Povečaj bodove}
         IF padslova>0 THEN padslova:=0;                                       {Resetiraj brojac koji broji koliko je slova palo zaredom}
         gReset;                                                               {Resetiraj poziciju slova}
         END;                                                                  {End if}
      END;                                                                     {End For}
      IF kosaricax<1 THEN kosaricax:=1;                                        {Da nebi mogli sa košaricom previše lijevo}
      IF kosaricax>76 THEN kosaricax:=76;                                      {Niti previše desno}
      IF bodovi>=MaxRez THEN                                                   {Ako imamo potreban broj bodova}
      BEGIN
           zivot:=zivot+1;                                                     {Dodaj novi život}
           DelayTime:=DelayTime-10;                                            {Ubrzaj padanje slova}
           maxslova:=maxslova+1;                                               {Dodaj slovo više}
           MaxRez:=MaxRez+500;                                                 {Povećaj potreban broj bodova}
      END;                                                                     {End if }
      IF pauza=true then                                                       {Ako je pauzirano}
      BEGIN
       GotoXY(25,10);                                                          {Oko sredine ekrana}
       TextColor(8);                                                           {Boja teksta}
       writeln('PAUZA');                                                       {Ispiši poruku}
       TextColor(14);                                                          {Vrati boju teksta}
       END;
        IF zivot=0 THEN                                                        {Ako nemamo života}
      Kraj_Igre;                                                               {Poziva proceduru za kraj programa}
    UNTIL KrajIgre=true;                                                       {Repeat until}
END;                                                                           {End izbor 'P','p'}
END;                                                                           {End Case}
clrscr;                                                                        {Očistimo ekran}
GotoXY(30,10);                                                                 {Postavimo pokazivač na zadanu poziciju}
writeln('KRAJ IGRE');                                                          {Ispišemo poruku o kraju igre}
readln                                                                         {Čeka pritisak tipke}
END.                                                                           (*Kraj glavne funkcije *) 

[ Srki_82 @ 13.05.2007. 22:45 ] @
Pisanje multiplatform igre bez upotrebe third party biblioteka je prilicno tesko. Ja licno odlicno poznajem WIN API, medjutim, nemam pojma kako da kreiram prozor u Linuxu ili MAC OSu. Gde je zvuk, gde je rad sa periferijama...
[ Nemanja Avramović @ 13.05.2007. 23:13 ] @
AFAIK, GLScene radi i na Lazarusu (Win/Lin)... zato, kreni(mo)
[ Srki_82 @ 13.05.2007. 23:39 ] @
Ne planiram da koristim GLScene... od third party stvari planiram da koristim sto manje... SDL za prozore, Vampyre za rad sa texturama, FMOD za zvuk, Newton za fiziku... mozda jos nesto, ali sigurno ne neki gotov 3D ili game engine, ali korak po korak... pocetak ce da bude cista konzola. Kada se nauci kako bi trebalo da se cuvaju podaci vezani za igru, onda malo Lazarus i LCL da se malo upozna sa keydown, keyup, mousemove i slicnim stvarima, a kad to prodje onda SDL and friends.
[ Nemanja_666 @ 14.05.2007. 10:54 ] @
Nisam vidio da mislis raditi za vise platformi igru, mislio sam samo za WIN. Volio bi ucestovati, a nekoristim lazarus vec Delphi. Nije problem instaliracu ga na Linux samo ajmo se dogovoriti sta cemo raditi. GLScene ne moramo koristi, u GLSceni cu ja pokrenuti temu na ljeto, jer imam jednu ideju za jednu igru koju bas necu moci sam da uradim.
[ Srki_82 @ 14.05.2007. 11:19 ] @
Kao sto rekoh, tutorial ce biti tipa ja napisem tekst, postavim veci deo koda i cim neko zavrsi kod idemo dalje. Sve sto budem objasnjavao ce biti za Lazarus. Ako neko zeli moze da ispravi kod i da dodatna objasnjenja za Delphi.

Prvi tutorial bi bilo nesto kao Hello World... X-O za 2 igraca u konzoli. Bice objasnjene neke osnovne stvari kao sto su inicijalizacija, game loop, nacin cuvanja i pristupanja podataka vezanih za igru i slicno... kod sam vec napisao, sad samo cekam da vidim da li ima dovoljno zainteresovanih ljudi... trenutno ste tu Nemanja_666 i Nemanja Avramović... ako se javi jos 2-3 zainteresovana korisnika pocecu sa pisanjem teksta.
[ Nikolavlasotince @ 14.05.2007. 11:52 ] @
Ja sam zainteresovan, mada ne znam bas nesto mnogo. Mozda bih mogao da pomognem u necemu, a usput i nesto da naucim.
[ Bojan Kopanja @ 14.05.2007. 15:05 ] @
Srki, znas da ja ovakve teme uvek pratim iz prikrajka ;). Znaci i ja sam tu, a ako stignem ( zbog kolokvijuma koji sada napadaju ) i aktivno cu da ucestvujem, a radicu u Lazarusu pod Linuxom ;).
[ Srki_82 @ 15.05.2007. 12:56 ] @
Ok... pocecu sa pisanjem texta kad stignem kuci pa cemo da vidimo kako sve to ide
[ anon28907 @ 15.05.2007. 12:59 ] @
Evo jos jednog zainteresovanog
[ Srki_82 @ 15.05.2007. 19:18 ] @
Pocecemo od vrlo jednostavne igre - X-O u konzoli. Nema kreiranja prozora, inicijalizacije za crtanje 3D objekata, pustanja zvuka i slicnih stvari. Pozabavicemo se osnovnim operacijama koje uglavnom svaka igra ima.

Za pocetak cemo sve da uprostimo i prikazemo kako ce program izgledati:
Code:
program helloxo;
begin
  Inicijalizacija promenljivih

  Izvrsavanje glavne petlje

  Oslobadjanje resursa
end.

Posto je glavna petlja cesto velika i komplikovana, praksa je da se ona i pomocne funkcije nalaze u drugom unit-u, a da se u programu sve inicijalizuje i samo pozove funkcija koja pokrece glavnu petlju. Tako cemo i mi uraditi bez obzira sto je XO vrlo jednostavno napraviti.

Napravicemo nov unit u kojem cemo postaviti promenljive i sve potrebne funkcije. Pocecemo od promenljivih... tabela u XO je velicine 3x3 i svako polje moze imati 3 moguce verdnosti: X, O ili prazno. Za takav podatak nam dvodimenzionalni niz (3x3) sasvim odgovara. Posto svako polje moze da ima 3 razlicite vrednosti, tip podatka za niz takodje mora da bude u stanju da ima bar 3 vrednosti. Integer moze da mnogo razlicitih vrednosti i operacije nad njim se vrlo brzo izvrsavaju pacemo napraviti dvodimenzionalni niz celobrojnih vrednosti.
Code:
Table: array[1..3, 1..3] of Integer;

Da nam se u kodu ne bi desilo da zaboravimo da li X predstavlja broj 1 ili broj 2 ili neki treci, nije lose da napravimo konstante koje ce nam pomoci i da se lakse snadjemo u kodu i da ne moramo da pamtimo sta koji broj znaci:
Code:
CX = 1;
CO = 2;

Dobro... imamo tabelu... bilo bi lepo da mozemo da zapamtimo imena igraca koji igraju i da mozemo kasnije da kazemo na redu je Pera umesto na redu je igrac koji igra sa znakom X. Niz od dva clana (jedan za X jedan za O) nam je sasvim dovoljan za to:
Code:
Players: array[CX..CO] of String;

Sta nam je jos potrebno... potrebno nam je da znamo koji igrac trenutno igra. Posto postoje samo X i O igrac bilo bi nam dovoljno da uzmemo Boolean tip i da True predstavlja igraca X, a False igraca O, ali da bi kod bio citljiviji i laksi za trazenje greske, bolje je da ovaj podatak cuvamo u tipu Integer i da ga postavimo na konstantu CX kada igra igrac X ili na CO kada igra igrac O:
Code:
CurrentPlayer: Integer;

Za kraj nam jos trebaju 2 promenljive u koje ce igrac upisati na koje polje zeli da igra (X i Y koordinatu polja):
Code:
CurrentX, CurrentY: Integer;


Imamo sve potrebne promenljive, sada krecemo na proceduru koja ce da predstavlja glavnu petlju u igri i na pomocne procedure i funkcije. U nasem slucaju bi glavna petlja trebala da radi sledece:
Code:
procedure Run;
begin
  Nacrtaj trenutno stanje na tabeli

  Uzmi od igraca polje na koje zeli da igra i postavi ga na
  vrednost koja zavisi od igraca koji je na potezu (X ili O)

  Promeni igraca

  Ako nije kraj igre, ponovi sve
end;

Kod koji uzima od igraca koordinatu polja i postavlja znak bi mogao da izgleda ovako:
Code:
Write('Postavi znak na (X Y): ');
ReadLn(CurrentX, CurrentY);

Table[CurrentX, CurrentY] := CurrentPlayer;

Sve deluje lepo, ali sta ako igrac pokusa da odigra na vec zauzeto polje ili da odigra na polje 10, 10 koje ne postoji? Treba nam neka provera. Da ne bi bez potrebe komplikovali glavnu petlju, tu proveru cemo napraviti u pomocnoj funkciji:
Code:
function GetField(aX, aY: Integer): Integer;
begin
  if (aX in [1..3]) and (aY in [1..3]) then
    Result := Table[aX, aY]
  else
    Result := -1;
end;

Ova funkcija ce nam vratiti -1 ako igrac bira nepostojece polje ili vrednost iz polja koja se trenutno tamo nalazi. Dakle, prolje je prazno ako nije -1, CX ni CO. Da ne bi morali da proveravamo sva tri uslova, prilikom inicijalizacije promenljivih cemo celu tabelu popuniti nulama i ona ce nam predstavljati prazno polje (nula i nil uglavnom uvek znace da je nesto prazno pa necemo definisati konstantu za prazno polje).
Sada kod za postavljanje znaka izgleda ovako:
Code:
repeat
  Write('Postavi znak na (X Y): ');
  ReadLn(CurrentX, CurrentY);
until GetField(CurrentX, CurrentY) = 0;

Table[CurrentX, CurrentY] := CurrentPlayer;


Imamo deo glavne petlje... idemo dalje... kod za promenu igraca je prilicno jednostavan i ne treba ga objasnjavati... ako nekom nije jasan neka uzme knjigu i prvo nauci Pascal pa neka nastavi sa citanjem ovog tutoriala:
Code:
Inc(CurrentPlayer);
if CurrentPlayer > CO then
  CurrentPlayer := CX;


Kod za proveru da li je igra zavrsena je takodje velik da bi ga ostavili u proceduri za glavnu petlju pa cemo i njega izvuci u pomocnu funkciju. Funkcija bi mogla da vracam samo True ili False, ali onda ne bi znali da li je igra gotova zato sto je neko pobedio ili je nereseno. Zato mozemo da uradimo funkciju tako da vrati -1 ako jos moze da se igra, 0 ako je nereseno, CX ako je pobedio igrac X ili CO ako je pobedio igrac O. Kako bi trebalo da izgleda ta funkcija... to je na vama da odlucite. Dacu vam samo malu pomoc. Imate polje na kojem je poslednji znak postavljen, dovoljno je da pogledate gore, dole i ukoso od tog polja i vidite da li imate 3 spojena znaka, ako nema 3 znaka ostaje samo jos da se proveri da li je tabela puna. Resenje koja ja trenutno imam je da svaki put prodjem kroz celu tabelu i vidim da li su sva polja puna, ali to moze da se resi na mnogo bolji i jednostavniji nacin... ako ima samo 9 polja koliko puta moze da se postavi znak do se tabla ne popuni

Jos jedna funkcija koju vam prepustam je funkcija za crtanje tabele. Ova funkcija je prilicno jednostavna... ja sam je uradio tako da ona ispisuje znak iz tabele i pravi razmak posle njega i, poels svakog reda ubacuje jedan prazan, a vi mozete da se igrate i da probate da nacrtate okvir i linije izmedju.

Kada imamo sve ovo, program je jednostavno napisati... prvo inicijalizujemo promenljive:
Code:
FillChar(Table, SizeOf(Table), 0);
  
Write('Unesite ime prvog igraca: ');
ReadLn(Players[CX]);
Write('Unesite ime drugog igraca: ');
ReadLn(Players[CO]);

Posle toga pozivamo glavnu petlju:
Code:
Run;

To je dovoljno da bi igra radila... ali, igraci bi voleli da vide ko je pobedio. Jedno od resenja je da u programu ponovo pozovemo funkciju koja nam je rekla zbog cega je doslo do kraja igre i na osnovu toga ispisemo poruku (tako sam uradio u kodu koji cu kasnije postovati)... ali zasto pozivati istu funkciju 2 puta kad mozemo da uradimo nesto drugo Ko se seti neka kaze.

Jos jedna stvar koja nervira igrace... zavrse igru i ona se zatvori bez pitanja da li zelis da igras ponovo. Funckija koja pita igraca bi mogla da izgleda ovako:
Code:
function PlayAgain: Boolean;
var
  YesNo: Char;
begin
  Write('Da li zelite da igrate ponovo (D/N): ');
  ReadLn(YesNo);

  Result := upCase(YesNo) = 'D';
end;

Kod glavnog programa bi onda ovako izgledao:
Code:
repeat
  FillChar(Table, SizeOf(Table), 0);
  
  Write('Unesite ime prvog igraca: ');
  ReadLn(Players[CX]);
  Write('Unesite ime drugog igraca: ');
  ReadLn(Players[CO]);
    
  Run;

until not PlayAgain;


E, sad... na nekom od vas je da napise ceo kod i da ga postuje. Cim dobijemo funkcionalnu igru, postovacu svoj kod i onda cemo na kraju zajedno da doteramo kod tako da bude lak za pregled, jednostavan, da ima sve sto treba da ima od funckionalnosti (bio bi lepo da i u sred igre da se izadje iz programa, zar ne) i da korisniku izgleda sto je lepse moguce
[ Srki_82 @ 15.05.2007. 22:17 ] @
Dok ovo neko radi da najavim sta je sledece. Idalje ostajemo u textualnom rezimu i idalje cemo da se zezamo s nacinom cuvanja i pristupanja podataka. Ovog puta cemo napraviti 2 programa. Jedan za kreiranje nivoa i jedan za igranje igre. Igra ce biti tipa idi na sever, idi na jug, pogledaj i slicno. Nivo koji cemo kreirati ce izgledati ovako:



Znaci svaka soba ima 4 moguca prolaza. Neki vode u druge sobe, a neki su zatvoreni. Postoje sobe koje mogu da imaju portale i da vode u sobe sa kojima naizgled nisu povezane (iz jedne sobe se ide na sever, a posle ne moze da se vrati na jug ili se vracanjem na jug ne vrati u istu sobu neko u neku trecu i slicno).

Sta nam treba... treba da se osmisli mehanizam koji ce cuvati podatke o sobama, ali i da podaci mogu da se snime u fajl, a kasnije i procitaju iz njega.

Svaka soba treba da ima 5 podataka:
Description: Opis sobe (to ce se videti kad igrac ukuca POGLEDAJ).
N, W, S, E: Podatak koji kaze u koju sobu dospemo kad podjemo na sever, zapad, jug ili istok.
[ Nemanja_666 @ 16.05.2007. 10:59 ] @
Nisam ni siguran dali radi najbolje, uradio sam za koji minut. Trebalo bi da radi. Nije bas kako Srki objasni, ja vise folim klase. Kompajlirano je sa FreePascal-om.

Code:

program XO;
{$mode objfpc}
uses
  Crt;

const
  PlayerX = 1;
  PlayerO = 2;    
  
type
  TGame = class
    private
      FTable : array[1..3, 1..3] of integer;
      FPlayerX : string;
      FPlayerO : string;
      FTurn : integer;
      FPuts : integer;
      procedure Paint;
      function Over : integer;
      function Valid(X, Y : integer) : boolean;
    public
      constructor Create;
      procedure Run;
  end;
  
var
  Game : TGame;  
  
{class TGame}

constructor TGame.Create;
begin
  FillChar(FTable, SizeOf(FTable), 0);
  TextColor(Random(14) + 2);
  Writeln('XO by Nemanja Tatic');
  Writeln;
  Writeln('PlayerX, vase ime:');
  Readln(FPlayerX);
  Writeln('PlayerO, vase ime:');
  Readln(FPlayerO);  
  FPuts := 0;
  FTurn := 1
end;  

procedure TGame.Paint;
var
  i, j : integer;
begin
  ClrScr;
  TextColor(Random(14) + 2);
  Writeln('XO by Nemanja Tatic');
  for i := 1 to 3 do
    begin
      Writeln();
         for j := 1 to 3 do
        case FTable[j, i] of
          0: Write(' ' : 2);
          1: Write('X' : 2);
          2: Write('O' : 2);
        end;
    end;
  Writeln;  
  Writeln;
  case FTurn of
    1: Writeln(FPlayerX, ' je na redu!');
    2: Writeln(FPlayerO, ' je na redu!');
  end;  
  Writeln;
end;

function TGame.Over : integer;
var
  X, Y : integer;
  i, Player : integer;
begin
  Result := -1;
  repeat
    Writeln('Unesite poziciju(x, y):');
    Readln(X, Y);
  until Valid(X, Y);
  FTable[X, Y] := FTurn;
  Inc(FPuts);
  Player := FTurn;
  Inc(FTurn); if FTurn = 3 then FTurn := 1;
  //Vertkalno
  Result := Player;
  for i := 1 to 3 do
    if FTable[i, Y] <> Player then Result := -1; 
  if Result = Player then exit;
  //Horizontalno
  Result := Player;  
  for i := 1 to 3 do
    if FTable[X, i] <> Player then Result := -1;
  if Result = Player then exit;  
  //Dijagonalno
  if ((FTable[1, 1] = FTable[2, 2]) and (FTable[2, 2] = FTable[3, 3])) or
     ((FTable[3, 1] = FTable[2, 2]) and (FTable[2, 2] = FTable[1, 3])) and (FTable[2, 2] <> 0) then
    begin
      Result := Player;
      exit;
    end;  
  if FPuts = 9 then 
    begin
      Result := 0;
      exit;
    end;   
end;

function TGame.Valid(X, Y : integer) : boolean;
begin
  if (X in [1..3]) and (Y in [1..3]) and (FTable[X, Y] = 0) then result := true
  else
    begin
      Writeln('Pogresan ulaz!');
      result := false;
    end;
end;

procedure TGame.Run;
var
  TempInt : integer;
begin
  repeat
    Paint;
    TempInt := Over;
  until TempInt > -1;
  Paint;
  if TempInt = 1 then Writeln(FPlayerX, ' JE POBJEDNIK!');
  if TempInt = 2 then Writeln(FPlayerO, ' JE POBJEDNIK!');
  if TempInt = 0 then Writeln('NERESENO!');
end;

function Play : boolean;
var
  TempStr : string;
begin
  result := false;
  Writeln('Unesite "d" za pocetak igre ili "n" za izlaz:');
  Readln(TempStr);
  if TempStr = 'd' then result := true;
end;
    
begin
  While Play do
    begin
      Game := TGame.Create;
      Game.Run;
      Game.Free; 
    end;
  Writeln('KRAJ PROGRAMA');
  Readln;
end.


[Ovu poruku je menjao Nemanja_666 dana 16.05.2007. u 14:40 GMT+1]
[ Srki_82 @ 16.05.2007. 13:15 ] @
Lepo, lepo... moras samo paziti kod dinamickog kreiranja objekata. Vidim da kreiras nov objekat TGame svaki put kad igrac zeli da pocne novu igru, a nikad ga ne oslobadjas.

Postovacu danas svoj kod kad se vratim kuci pa cemo uporediti kodove i iskomentarisati sta je moglo bolje, sta bi trebalo drugacije i dati konacnu verziju programa.

BTW jos uvek cekamo da neko da ideju kako bismo trebali da cuvamo podatke o sobama za sledeci tutorial.
[ Nemanja_666 @ 16.05.2007. 13:50 ] @
Ono sam radio u skoli na odmoru. Sad sam popravio.

Za ovu drugi problem. Zasto se muciti kad je najlakse:

Code:

program sobe;
{$mode objfpc}
const
  North = 1;
  West = 2;
  South = 3;
  East = 4;
  NoRoom = 0;
  RoomsCount = 1000;
  
type
  TRoom = record
    Decription : string[20];
    Doors : array[North..East] of word;
  end;
  
var
  Rooms : array[1..RoomsCount] of TRoom;
  
procedure SaveData(FileName : string);
var
  fRoom : file of TRoom;
  i : integer;
begin
  AssignFile(fRoom, FileName);
  Rewrite(fRoom);
  for i := 1 to RoomsCount do
    Write(fRoom, Rooms[i]);
  CloseFile(fRoom);
end;

procedure LoadData(FileName : string);
var
  fRoom : file of TRoom;
  i : integer;
begin
  AssignFile(fRoom, FileName);
  Reset(fRoom);
  i := 0;
  repeat
    Inc(i);
    Read(fRoom, Rooms[i]);
  until FilePos(fRoom) = FileSize(fRoom);  
  CloseFile(fRoom);
end;

begin
  SaveData('c:\room1.dat');
  LoadData('c:\room1.dat')
end.
 
[ Srki_82 @ 16.05.2007. 14:00 ] @
Odlicno je... doradi samo tako da je moguce imati neogranicen broj soba (memorija treba da bude jedino ogranicenje) i da svaka soba moze da ima opis bilo koje duzine (tesko da ces opisati mracnu pecinu, svetlost koja se probja sa vrha, pacove u vodi i slicno u samo 20 slova).

To bi znacilo da moras malo da modifikujes kod za upis i citanje iz fajla.
[ viking13 @ 16.05.2007. 14:01 ] @
Verujem da većina vas zna šta je magični kvadrat.

Za one koji ne znaju, to je kvadrat sa ciframa, gde je zbir cifara horizontalno, vodoravno ili ukoso isti, odnosno 15 u najprostijem slučaju.

Najprostiji magični kvadrat izgleda ovako:

4 9 2
3 5 7
8 1 6

Kakve sad to ima veze sa ovom pricom?

Postoji nešto što se zove teorija igra. Jedna od osnovnih stvari je upravo igra "tic-tac-toe" ili što bi mi rekli "iks oks".

Cilj igre je imati 3 spojena znaka horizontalno, vodoravno ili ukoso.

Da li sad vidite vezu? :)

Znači posle svakog odigranog poteza treba sabrati cifre koje se nalaze na pozicijama na koje su igraci igrali.

Pobednik je onaj ko prvi skupi 15.

Kod nek napravi neko drugi.

Toliko od mene. :)
[ Jovan Prokopović @ 16.05.2007. 14:20 ] @
U kodu za cuvenje podataka o sobama fali deo koji kaze u koju se sobu ulazi kad se prodje kroz vrata, zato bi ja umesto informacije koja vrata postoje cuvao informaciju u koju sobu vode neka vrata (kad nema vrata vrednost nekog prolaza je 0, a u suprotnom slucaju redni broj sobe), redni broj sobe je ustvari redni broj u nizu.

Otprilike ovako

Code:

TRoom = record
    Decription : string;
    North, West, South, East: integer;
  end;


Time resavamo i portale koji u jednom smeru vode u jednu sobu, a u drugom u neku drugu sobu.
[ Srki_82 @ 16.05.2007. 14:22 ] @
Pretpostavljam da mislis da tim princiom racunamo da li neko ima 3 povezana znaka.

Zamisli ovu situaciju:

x o -
- o o
x - x

X je na redu... kad saberem 3 polja na kojem je o dobijem 21, kad saberem polja na kojima je X dobijem 18... vec oba igraca imaju vise od 15. To bi znacilo da mora da se uradi kombinacija za svaka 3 razlicita polja i da se vidi da li neka od tih kombinacija daje zbir 15.

U svakom slucaju, zanimljivo resenje
[ Srki_82 @ 16.05.2007. 14:37 ] @
Znaci... da uzmemo najbolje iz oba predlozena tipa podatka za cuvanje sobe:
Code:
const
  CNorth = 1;
  CWest = 2;
  CSouth = 3;
  CEast = 4;

type
  TRoom = record
    Decription: String;
    Doors: array[CNorth..CEast] of Integer;
  end;

  TRooms = array of TRoom;

Podatke o tome gde vode koja vrata cuvamo u nizu pa cemo na osnovu komande koju korisnik izabere (idi na sever) moci lako da nadjemo gde treba da idemo (umesto da pisemo if Command = CWest then Room.West... bice dovoljno da napisemo Room.Doors[CWest]...).

Treba nam samo jos snimanje i ucitavanje.

Edit: Zaboravih da napomenem, rad sa fajlovima lako moze da podje naopako (korisnik nema prava pristupa, disk je read-only, disk je pun,...). Kod koji upisuje u fajl ili cita iz fajla ne bi trebalo jednostavno da pukne ako dodje do greske, nego treba lepo da zatvori fajl ako je otvoren, oslobodi sve zauzete resurse i vrati vrednost koja oznacava da je doslo do greske. Imajte to u vidu kada budete pisali kod.

[Ovu poruku je menjao Srki_82 dana 16.05.2007. u 15:50 GMT+1]
[ Nemanja_666 @ 16.05.2007. 15:06 ] @
Jovane to sto si napisao je isto sto i moje uz razliku sto ja informacije cuvam u nizu a ti u cetiri varijable.

Srki ne kontam kako mislis da memorija samo bude ogranicenje. Ako je tako smjestanje u jedan veci niz i mjenjanje RoomsCount sa 1000 u neki jako velik broj nije dovoljno. Trebalo bi:

Code:

type
  TRoomPok = ^TRoom;
  TRoom = record
    Decription : string[255];
    Doors : array[North..East] of TRoomPok;
  end;


Ali nemam ideju kako ovo sacuvati u fajl, tj. pokazivace nikad nisam cuvao u neki fajl. Druga mogucnost je kreiranje neke baze sta je veoma tesko uraditi samo u FreePascal(Barem ja mislim tako).

EDIT: Nisam video tvoj zadni post kad sam pisao ovo. Moze i tako(dinamicki niz).
[ Jovan Prokopović @ 16.05.2007. 15:39 ] @
Da, shvatio sam kad sam poslao odgovor, samo sam na brznu pogledao tvoj kod, i pogresno zakljucio zato sto sam video da si kreira konstante za pravce i pretpostavio da ces njih da cuvas u nizu.

Sto se tice cuvanja podataka cinimi se da je najlakse da se ovo sacuva kao textualni fajl (npr. kao ini fajl).
Mozda nije efikasno u smislu zauzeca prostora na disku, ali je jednostavno za implementaciju.
[ Srki_82 @ 16.05.2007. 15:46 ] @
Probaj da napravis... ja vec imam implementaciju koja lepo radi (podaci ne moraju da pocinju na samom pocetku fajla tako da je moguce cuvati bilo sta ispred i iza podataka o sobi, lepo se snalazi sa greskama).

Vezano za prvi tutorial, evo mog koda. Vec sam rekao da nije savrsen i namerno sam ga ostavio takvog... pa ko zeli moze da izvuce ono najbolje iz Nemanjinog i mog programa i napravi jedan bolji od oba
[ viking13 @ 16.05.2007. 15:59 ] @
Citat:
Srki_82: Pretpostavljam da mislis da tim princiom racunamo da li neko ima 3 povezana znaka.

Zamisli ovu situaciju:

x o -
- o o
x - x

X je na redu... kad saberem 3 polja na kojem je o dobijem 21, kad saberem polja na kojima je X dobijem 18... vec oba igraca imaju vise od 15. To bi znacilo da mora da se uradi kombinacija za svaka 3 razlicita polja i da se vidi da li neka od tih kombinacija daje zbir 15.

U svakom slucaju, zanimljivo resenje :)


Niko nije rekao da je 15 maksimalna vrednost. Igra se dok neko od igrača ne skupi 15 ili dok se ne popuni cela tabela.

Znači za pobedu je potrebno TAČNO 15! To je jedini način.

Potrebne su dve provere posle svakog poteza da bi znao na čemu si:

1. Provera da li bilo koji igrač ima 15.
2. Provera da li ti je tabela puna.

Zašto je logika ispravna?

Ne postoji način da oba igrača imaju 15 u isto vreme.

Ako jedan ima 3 spojena po dijagonali drugi nema teorije da skupi 3 na bilo koji drugi način, jel tako?

Jedino bi obojica mogli da imaju po 15 u ovoj situaciji:

x x x
- - -
o o o

A ovo je već gotova igra potez pre toga. :)

Zaključak

Posle prve provere znaš da li imaš pobednika, a posle druge da li je kraj igre sa nerešenim rezultato ili se igra sledeći potez.
[ Srki_82 @ 16.05.2007. 16:12 ] @
@viking13
Mislio sam da pricas o tome kako da znamo da li je u X-O 3 u nizu, a ne o novoj igri
[ viking13 @ 16.05.2007. 16:18 ] @
Citat:
Srki_82: @viking13
Mislio sam da pricas o tome kako da znamo da li je u X-O 3 u nizu, a ne o novoj igri :)


Pa o tome i pričam sve vreme, ako neko ima TAČNO 15, onda ima 3 u nizu i on je POBEDNIK!

Aj malo se koncetriši, please. :)

[ Srki_82 @ 16.05.2007. 16:29 ] @
@viking13

Idemo korak po korak...:
x - -
- - -
- - -
X ima 4, O ima 0 - Niko nema 15, niko nije pobedio

x o -
- - -
- - -
X ima 4, O ima 9 - Niko nema 15, niko nije pobedio

x o -
- - -
- - x
X ima 10, O ima 9 - Niko nema 15, niko nije pobedio

x o -
- o -
- - x
X ima 10, O ima 14 - Niko nema 15, niko nije pobedio

x o -
- o -
x - x
X ima 18, O ima 14 - Niko nema 15, niko nije pobedio

x o -
- o o
x - x
X ima 18, O ima 21 - Niko nema 15, niko nije pobedio

x o -
- o o
x x x
X ima 19, O ima 21 - Niko nema 15, niko nije pobedio

Vidis... ta situacija mi nije jasna... zato kazem da mora da se odradi provera za svaku kombinaciju u kojoj su 3 razlicita polja da bi videli da li je neko pobedio, tj da li neko ima 15.

Mozda samo ne razumem kako to treba da radi.
[ Srki_82 @ 16.05.2007. 22:06 ] @
Dok cekamo da neko napise kod za snimanje i ucitavanje sobe, neko sa bujnom mastom moze da napise opise za svaku sobu. Da se podsetimo kako su sobe rasporedjene:



Ako niko ne napise nista, moracete da se zadovoljite tekstom koji mi bude pao na pamet kad budemo pisali kod za unos soba sa tastature
[ Bojan Kopanja @ 16.05.2007. 22:21 ] @
Ljudi pa vi ste ludi :D... Nema me 1 dan zbog faxa, a vi sve skoro zavrsili :D. Idem da analiziram sta je napisano danas pa cu se sutra posle faxa verovatno i prikljuciti ;).
[ Nemanja_666 @ 18.05.2007. 22:41 ] @
Posto nitko nije napisao evo pisem ja kod za ulaz/izlaz. Nisam mastovit pa necu moci napisati opise soba.

Code:

program sobe;
{$mode objfpc}
const
  CNorth = 1;
  CWest = 2;
  CSouth = 3;
  CEast = 4;

type
  TRoom = record
    Decription: String;
    Doors: array[CNorth..CEast] of Integer;
  end;

  TRooms = array of TRoom;
  
var
  Rooms : TRooms;
  RoomsCount : longint;
  
  
procedure SaveData(FileName : string);
var
  fRoom : file of TRoom;
  i : integer;
begin
  AssignFile(fRoom, FileName);
  Rewrite(fRoom);
  for i := 0 to RoomsCount - 1 do
    Write(fRoom, Rooms[i]);
  CloseFile(fRoom);
end;

procedure LoadData(FileName : string);
var
  fRoom : file of TRoom;
begin
  AssignFile(fRoom, FileName);
  Reset(fRoom);
  RoomsCount := 0;
  repeat
    Inc(RoomsCount);
    SetLength(Rooms, RoomsCount);
    Read(fRoom, Rooms[RoomsCount - 1]); 
  until FilePos(fRoom) = FileSize(fRoom);  
  CloseFile(fRoom);
end;

begin
  RoomsCount := 10;      //stavio sam ovo kao da postoji 10 soba
  SetLength(Rooms, 10);
  //cuvanje
  try
    SaveData('c:\test.dat');
  except
    Writeln('Greska prilikom cuvanja!');
    
  end;
  //ucitavanje
  try
    LoadData('c:\test.dat');
  except
    Writeln('Greska prilikom ucitavanja!');
  end;    
end.
 
[ Srki_82 @ 19.05.2007. 08:45 ] @
@Nemanja_666
Rekli smo da necemo nikakva ogranicenja osim memorije za cuvanje podataka o sobama. Ovog puta nisi ogranicio niz, ali si iskljucio podrsku za velike stringove (stringovi u tvom programu mogu da imaju najvise 255 slova jer si iskljucio {$H+} direktivu). Nacin na koji ti snimas podatke je ok kada znas koliko ce mesta zauzeti koji podatak, ali fora je bas u tome da jedna soba moze da ima opis od 10 slova, a druga od 10000000 slova. Ukljuci podrsku za velike stringove.

BTW kada se koriste veliki stringovi onda je string tip ustvari pointer na memorijsku lokacijku na kojoj se nalazi string i obicno snimanje recorda samo snimi taj pointer. Moras snimiti ono sto se nalazi na memorijskoj lokaciji na koju string pokazuje, a ne sam pointer.
[ Srki_82 @ 19.05.2007. 16:02 ] @
Fora kod upisivanja podataka ciju velicinu ne znate prilikom kompajliranja programa je da pre podatka upisete i njegovu velicinu. Tako ce kod koji bude citao podatke znati koliko mesta je potrebno da bi se podatak uspesno ucitao.

Evo kako sam napisao funkcije za snimanje i ucitavanje soba:
Code:
const
  // Header fajla u kojem se snimaju sobe (ROOM)
  CROOM_HEADER = $4D4F4F52;

// Ucitavanje soba iz fajla
function LoadRooms(FileName: String; var Rooms: TRooms): Integer;
var
  HFile: File;
begin
  // Pretpostavimo da ce doci do greske
  Result := -1;
  
  // Otvaramo fajl
  Assign(HFile, FileName);
  try
    Reset(HFile, 1);
    // Ucitaj iz fajla
    Result := LoadRooms(HFile, Rooms);
  finally
    // Zatvori fajl
    Close(HFile);
  end;
end;

function LoadRooms(var HFile: File; var Rooms: TRooms): Integer; overload;
var
  Header: Cardinal;
  // Broj fajlova procitanih iz fajla
  ReadCount: Cardinal;
  Count: Integer;
  I: Integer;
  Len: Integer;
begin
  // Pretpostavimo da ce doci do greske
  Result := -1;

  // Ucitavanje headera
  BlockRead(HFile, Header, SizeOf(Header), ReadCount);
  if (ReadCount <> SizeOf(Header)) or (Header <> CROOM_HEADER) then
    Exit;
    
  // Citamo broj soba
  BlockRead(HFile, Count, SizeOf(Count), ReadCount);
  if ReadCount <> SizeOf(Count) then
    Exit;
    
  // Postavljamo velicinu niza
  SetLength(Rooms, Count);
    
  // Ucitavanje soba
  for I := 0 to Count - 1 do
  begin
    // Citamo duzinu opisa
    BlockRead(HFile, Len, SizeOf(Len), ReadCount);
    if ReadCount <> SizeOf(Len) then
      Exit;

    // Postavljamo duzinu opisa
    SetLength(Rooms[I].Decription, Len);
    
    // Citamo opis sobe
    BlockRead(HFile, Rooms[I].Decription[1], Len, ReadCount);
    if ReadCount <> Len then
      Exit;

    // Citamo prolaze do drugih soba
    BlockRead(HFile, Rooms[I].Doors, SizeOf(Rooms[I].Doors), ReadCount);
    if ReadCount <> SizeOf(Rooms[I].Doors) then
      Exit;
  end;

  // Sve je uspesno zavrseno
  Result := Count;
end;

// Snimanje soba u fajl
function SaveRooms(FileName: String; var Rooms: TRooms; Count: Integer): Integer; overload;
var
  HFile: File;
begin
  // Pretpostavimo da ce doci do greske
  Result := -1;

  // Otvaramo fajl
  Assign(HFile, FileName);
  try
  Rewrite(HFile, 1);
  // Snimi u fajl
  Result := SaveRooms(HFile, Rooms, Count);
  finally
  // Zatvori fajl
  Close(HFile);
  end;
end;

function SaveRooms(var HFile: File; var Rooms: TRooms; Count: Integer): Integer; overload;
var
  // Broj bajtova upisanih u fajl
  WriteCount: Cardinal;
  I: Integer;
  Len: Integer;
begin
  // Pretpostavimo da ce doci do greske
  Result := -1;
  
  // Upisemo header
  BlockWrite(HFile, CROOM_HEADER, SizeOf(CROOM_HEADER), WriteCount);
  if WriteCount <> SizeOf(CROOM_HEADER) then
    Exit;
    
  // Uzimamo broj soba iz niza ako korisnik nije nista uneo
  if Count = 0 then
    Count := High(Rooms) + 1;
    
  // Upisujemo broj soba
  BlockWrite(HFile, Count, SizeOf(Count), WriteCount);
  if WriteCount <> SizeOf(Count) then
    Exit;

  // snimanje soba
  for I := 0 to Count - 1 do
  begin
    // Upisujemo duzinu opisa
    Len := Length(Rooms[I].Decription);
    BlockWrite(HFile, Len, SizeOf(Len), WriteCount);
    if WriteCount <> SizeOf(Len) then
      Exit;

    // Upisujemo opis sobe
    BlockWrite(HFile, Rooms[I].Decription[1], Len, WriteCount);
    if WriteCount <> Len then
      Exit;
      
    // Upisujemo prolaze do drugih soba
    BlockWrite(HFile, Rooms[I].Doors, SizeOf(Rooms[I].Doors), WriteCount);
    if WriteCount <> SizeOf(Rooms[I].Doors) then
      Exit;
  end;
  
  // Sve je uspesno zavrseno
  Result := Count;
end;

Na ovaj nacin je moguce snimiti podatke u bilo koji fajl (korisnik moze da prosleti funkciji otvoren handle fajla i podaci ce tu biti upisani). Posto se u fajl upisuje broj soba funkcija za citanje lako moze da odredi koliko iz fajla treba da procita. Na pocetku fajla se upisuje header (ovo nije uvek potrebno) da bi se lakse detektovao ostecen fajl.

Kada imamo ove funkcije, treba nam editor za sobe. Neki najjednostavniji editor bi trebalo da moze da snimi i ucita sobe sa diska, doda nove i promeni ili izbrise postojece.

Snimanje i ucitavanje smo vec sredili.
Kreiranje novih soba je prilicno lako. Dovoljno je da povecamo niz i ubacimo podatke za sobu. Ovakav nacin je definitivno spor jer prilikom prosirenja niza FPC mora da kreira nov niz i da u njega prekopira podatke iz onog koji smo vec napunili. Zbog toga se obicno zauzme malo vise prostora kad god se popuni niz, a treba jos da se doda. Npr: niz je prazan, a mi hocemo da dodamo jednu sobu. Program bi mogao da zauzme mesta dovoljno za 5 soba, a da popuni samo prvu. U nekoj internoj promenljivoj cuva da ima samo jednu popunjenu sobu. Dodavanje druge, trece, cetvrte i pete sobe se svodi samo na popunjavanje podataka i povecavanja interne promenljive za 1. Kada budemo hteli da ubacimo sestu sobu, program bi opet mogao da zauzme mesta za jos 5 i opet iz pocetka...
Menjanje sobe je lakse nego kreiranje. Sve sto treba je da zamenimo vrednosti novim i to je to.
Brisanje sobe je najkomplikovanije. Pre nego sto uopste obrisemo sobu, moramo u svim ostalim sobama da podesimo gde vrata vode. Ako neka vrata vode u sobu koju cemo da obrisemo, ta vrata ce voditi u nepostojecu sobu i moramo da postavimo da ta vrata vise nikuda ne vode. Posto cemo izbaciti jedan element iz niza, svi koji se nalaze iza njega ce se pomeriti na jednu poziciju nize i time ce indexi prostorija na koje vrata pokazuju biti pogresni pa i njih moramo smanjiti za 1 za sva vrata koja vode u sobe koje imaju index veci od sobe koju brisemo. Kada sve to zavrsimo, mozemo da obrisemo sobu. Prvo sve sobe koje se nalaze iza sobe za brisanje pomerimo za jedno mesto u nazad i na kraju smanjimo niz za jedno msto. Naravno, svako pomeranje elemenata i smanjivanje niza je prilicno sporo i zato te operacije treba sto manje koristiti. Postoje specijalni nizovi kod kojih je brisanje vrlo brzo (povezane liste), ali je pozicioniranje na neki slucajni element prilicno sporo, ili nizovi koji brzo mogu da dodaju ili brisu elemente samo na pocetku ili kraju niza... na programeru je da odluci kakav niz mu najvise odgovara za odredjeni problem.

Napisao sam jedan vrlo jednostavan editor koji radi u textualnom modu. Nema nikakvih optimizacija i kod je vrlo lak za razumevanje. Sledece sto nas ocekuje je pravljenje editora u grafuckom okruzenju (GUI). Od sada cemo da radimo sa Lazarusom i da koristimo njegov LCL za kreiranje programa.

Cekamo da neko napravi editor za sobe u grafickom okruzenju pa cemo da predjemo na pravljenje igre koja ce koristiti fajl sa sobama koji cemo kreirati u editoru.

[ Nemanja_666 @ 19.05.2007. 16:56 ] @
Ovdje opet postoji ogranicenje. Ako imamo vise od 264 + 1 probijamo ogranicenje tipa int64, ti si ovde koristio integer(razumnije bi bilo da si koristio word jer nam negativni brojevi netrebaju) koji ima jos manji opseg. Uz to mnogo si zakomplicirao, puno lakse bi bilo da si naprvavio jos jedan niz karaktera i sa njim cuvao opis.

Napravi bez niza, da se sve cuva u dinamickim promjenjive.
[ Srki_82 @ 19.05.2007. 17:15 ] @
Kad pogledas da je velicina TRoom sa praznim opisom 5 * 4 (4 integera i jedan pointer... to je kad nema ni jedan znak u opisu), a MaxInt je 2147483647, to znaci da mozemo da kreiramo skoro 40Gb promenljivih tipa TRoom (opet naglasavam da je to ako nema ni jedan znak u opisu). Ako uzmemo da svaka soba u proseku ima vise od 10 slova to znaci da sa MaxInt mozemo da popunimo oko 400Gb memorije. Sad... ne znam da li cemo do ogranicenja stici zbog toga sto koristimo Integer ili zato sto ce nam nestati memorije.

BTW Integer u FPC o objfpc modu moze da prikaze broj 2147483647 sto je mnogo vise nego sto moze da prikaze WORD (65535). Mogao sam koristiti Cardinal, ali je i Integer sasvim dovoljan.

PS stvarno ne znam sta sam to zakomplikovao? Mislis na snimanje?
[ Srki_82 @ 24.05.2007. 14:17 ] @
Je l' odustajemo od tutoriala? Javite mi da znam... bezveze pisem dalje ako vas ovo ne zanima.
[ Bojan Kopanja @ 24.05.2007. 15:19 ] @
Mene naravno zanima, ali ne stizem puno da uradim posto imam sad 2 kolokvijuma i 3 projekta da spremam tako da ne znam ni sam gde mi je glava, a gde drugi kraj :(.

U svakom slucaju ja za neke 2 nedelje zavrsavam sa predavanjima na faxu tako da od onda imam vise vremena, pa mozes ako hoces ( ako se niko vise ne interesuje za ovu temu ) da stopiras sve do tada posto pretpostavljam da vecini koji idu u neku skolu/fax vise odgovara taj termin zbog slobodnog vremena :).
[ Nemanja_666 @ 24.05.2007. 16:30 ] @
Slicno i kod mene. Necu imati vremena najmanje dvije i po sedmice. Do tada pozz.
[ Nikolavlasotince @ 29.05.2007. 16:20 ] @
I ja sam zainteresovan, samo sto sam trenutno zauzet. Skola se zavrsava za neki dan i prijemni takodje tako da cu se za jedno nedelju dana i ja prikljuciti.

Pozdrav
[ vladaboy93 @ 29.05.2007. 21:52 ] @
Nemoj odustati Srki mnogo nas je zainteresovano ukljucujuci i mene ali nas jeb... skola za***....
pa se nestigne meni se takodje skola zavrsava za 2 dana! ali sam slobodan tek za 2 nedelje
[ Nemanja_666 @ 08.06.2007. 10:51 ] @
Ja sam spreman za nastavak projekta. Vidio sam da je srki vec napisao editor u konzoli. Nisam pogledao kod, ali ubro cu. Sto se tice da neko uradi u vizuelnom okruzenju, ja sam mislio da ce i ova igra biti u konzoli, a poslije nje cemo se baciti na nesto ozbiljnije.

Pozz
[ Srki_82 @ 08.06.2007. 16:54 ] @
Ovaj program je bas fin za prelazak iz konzole u graficko okruzenje. Posto vecina, ako ne i svi koji citaj ovo, vec znaju da rade sa VCL/LCL odlucio sam da pisem kompletan program za konzolu, a da neko od vas taj kod modifikuje kako bi radio u grafickom okruzenju. Osim "uglavljivanja" koda u graficko okruzenje, nije zabranjeno modifikovanje koda da bi radio brze, bolje, jace :D
[ Nemanja_666 @ 08.06.2007. 17:12 ] @
Prokrama (Igre) samo u konzoli nisam radio odavno, pa mi se sad vracaju stara sjecanja pa zato sam mislio da ovo odradimo u konzoli.
[ Srki_82 @ 08.06.2007. 17:31 ] @
Slobodno uradi jos jednu verziju u konzoli, ali ne idemo dalje dok neko ne uradi verziju u grafickom okruzenju :)
[ Srki_82 @ 21.06.2007. 08:38 ] @
Odoh na godisnji odmor... svracacu na forum, ali verovatno necu imati racunar na kojem bih mogao da pisem programcice pa cak i ako iko odluci da uradi editor, necu moci da nastavim sa pisanjem.

BTW da li ste svi odustali ili jos nisu zavrsene obaveze u skolama?
[ Nikolavlasotince @ 22.06.2007. 12:12 ] @
Ja sam juce zavrsio sve obaveze i sada sam konacno SLOBODAN. :)

Pozzzzzzzz
[ Elmer-Davez @ 04.08.2007. 18:49 ] @
Pre neki dan sam naleteo na ovu temu slucajno i malo me zaintersovala, pa sam uradio taj editor u grafickom okruzenju(nisam koristio lazarus nego delphi). Nadam se da ce sad srki nastaviti sa ovim.
Mozda je srki planirao da neko namesti editor tako da se odma vide sve sobe i strelice od vrata jedne sobe do vrata druge sobe, a tu bi verovatno morao neki pathfinding da se uradi, a ja to ne znam. Mozda postoji neki bug, jer ga nisam jos skroz testirao, pa ako naletite na neki javite mi.
[ Srki_82 @ 08.08.2007. 11:44 ] @
Sad sam u nekoj guzvici pa jedno vreme necu moci da pisem, ali cu sigurno nastaviti. Kao sto sam ranije rekao, sledi jednostavna igra koja ce koristiti fajl sa sobama.
[ Srki_82 @ 30.08.2007. 15:03 ] @
Posle duze pauze, nastavak teksta je zavrsen. Kao sto sam ranije rekao, napravicemo malu igru koja ce koristiti fajl sa informacijama o sobama i dozvoliti korisniku da se krece kroz njih. Cilj igre je da se od prve sobe stigne do poslednje.

Posto vec imamo napisan ceo kod za ucitavanje soba, jedino sto cemo morati da napisemo je kod koji ce na osnovu komandi da pokrece korisnika kroz sobe, i nesto malo koda da bi GUI lepo radio.

Forma koju cemo napraviti izgleda ovako:



Na formi se nalazi par komponenti koje bi trebalo vec da poznajete. ActionList, MainMenu, ImageList, OpenDialog, SaveDialog... ove komponente ce nam posluziti da napravimo lepsi GUI i dodamo minimalnu funkcionalnost koja se ocekuje od igre (pokretanje nove igre, snimanje i ucitavanje pozicije, i izlaz iz igre).

Memo kontrola ce sluziti za ispis opisa sobe, informacije o komandama, i svemu ostalom o cemu igra treba da nas obavesti.

Edit box i button kontrole ce igracu omoguciti da zadaje komande.

Da krenemo... potrebne su nam dve promenljive koje ce cuvati podatke o svim sobama i o trenutnoj sobi u kojoj se igrac nalazi:
Code:
var
  Rooms: TRooms;
  CurrentRoom: Integer;

Kada korisnik izabere novu igru to znaci da treba da ucitamo podatke o sobama i da trenutnu sobu postavimo na prvu... i to je to.
Kraj igre je kada je trenutna soba posledanj soba.
Snimanje i ucitavanje igre je takodje jednostavno... sve sto treba je da snimimo u kojoj se sobi igrac nalazi, i, kasnije, taj podatak da ucitamo iz fajla.

Jedino sto je malo teze od svega ostalog je sistem koji ce da uzima komande od korisnika i da izvrsava kod vezan za tu komandu. StringList je prilicno zgodan za ovakve stvari... tekst koji igrac unese se rastavi na razmacima i popuni se lista. Kada bi korisnik uneo:
Code:
komanda parametar1 parametar2

U listi bi imali 3 reda:
Code:
Index  Tekst
0        komanda
1        parametar1
2        parametar2

Dakle, komanda se nalazi na prvom mestu, a posle nje idu parametri.
Komande koje cemo implementirati u igri su sledece:
HELP
QUIT
LOOK
GO {NORTH | WEST | SOUTH | EAST}

HELP komanda ce da izlista sve komande i da da kratak opis svake.
QUIT prekida igru i zatvara je.
LOOK ispisuje opis sobe u kojoj se trenutno nalazi igrac.
GO komanda ima jedan parametar i to je smer u kojem igrac zeli da ide.

Za svaku komandu cemo napisati posebnu proceduru koja ce biti zaduzena za njeno izvrsavanje, koja ce imati sledeci oblik:
Code:
procedure Command(ACommand: TStringList; AStatus: TMemo);

Na taj nacin ce svaka komanda imati pristup listi parametara i memo kontroli u koju, ako je potrebno, ispisuje tekst.

Kompletan kod za izvrsavanje komandi igraca izgleda ovako:
Code:
procedure WaitingForCommand(AStatus: TMemo);
begin
  if AStatus.Lines[AStatus.Lines.Count - 1] <> 'Waiting for command: ' then
    AStatus.Append('Waiting for command: ');
end;

procedure UnknownCommand(ACommand: TStringList; AStatus: TMemo);
begin
  AStatus.Append('Unknown command ' + ACommand.DelimitedText + '. Type HELP for command list.');
  AStatus.Append('');
end;

procedure QuitCommand(ACommand: TStringList; AStatus: TMemo);
begin
  Application.MainForm.Close;
end;

procedure HelpCommand(ACommand: TStringList; AStatus: TMemo);
begin
  AStatus.Append('Command list');
  AStatus.Append('  HELP - Display this text');
  AStatus.Append('  QUIT - Quit game');
  AStatus.Append('  LOOK - Look around you');
  AStatus.Append('  GO {NORTH | WEST | SOUTH | EAST} - move to other room');
  AStatus.Append('');
end;

procedure LookCommand(ACommand: TStringList; AStatus: TMemo);
var
  I: Integer;
  Exits: String = '';
begin
  // Ispisuje opis sobe
  AStatus.Text := AStatus.Text + Rooms[CurrentRoom].Decription;

  // Trazi moguce izlaze ako nisi u poslednjoj sobi
  if CurrentRoom < Length(Rooms) -1 then
  begin
    for I := CNorth to CEast do
      if Rooms[CurrentRoom].Doors[I] > 0 then
        Exits := Exits + DirectionNames[I] + ', ';
    SetLength(Exits, Length(Exits) - 2);

    AStatus.Append('');
    AStatus.Append('Available exits: ' + Exits);
  end
  else
  begin
    AStatus.Append('');
    AStatus.Append('YOU DID IT!!!');
    AStatus.Append('You have found the exit!');
  end;
  AStatus.Append('');
end;

procedure GoCommand(ACommand: TStringList; AStatus: TMemo);
var
  Direction: Integer = 0;
  I: Integer;
begin
  // Drugi parametar je smer kretanja
  if ACommand.Count < 2 then
  begin
    AStatus.Append('Where do you want to go?');
    AStatus.Append('');
  end
  else
  begin
    // Pronadji smer
    for I := CNorth to CEast do
      if ACommand[1] = DirectionNames[I] then
        Direction := I;
    // Ako uneti smer ne postoji, izbaci gresku
    if Direction = 0 then
      UnknownCommand(ACommand, AStatus)
    else
    begin
      // Ako je nemoguce ici zadatim smerom, izbaci obavestenje
      if Rooms[CurrentRoom].Doors[Direction] = 0 then
      begin
        AStatus.Append('You cann''t go to the ' + LowerCase(ACommand[1]) + '.');
        AStatus.Append('');
      end
      else
      begin
        // Predji u novu sobu i pogledaj je
        CurrentRoom := Rooms[CurrentRoom].Doors[Direction] - 1;
        ExecuteCommand('LOOK', AStatus);
      end;
    end;
  end;
end;

procedure ExecuteCommand(ACommand: String; AStatus: TMemo);
var
  strCommand: TStringList;
begin
  ACommand := UpperCase(ACommand);
  strCommand := TStringList.Create;
  strCommand.Delimiter := ' ';
  strCommand.DelimitedText := ACommand;
  
  // Pokreni proceduru u zavisnosti od komande
  if strCommand[0] = 'HELP' then
    HelpCommand(strCommand, AStatus)
  else if strCommand[0] = 'QUIT' then
    QuitCommand(strCommand, AStatus)
  else if strCommand[0] = 'LOOK' then
    LookCommand(strCommand, AStatus)
  else if strCommand[0] = 'GO' then
    GoCommand(strCommand, AStatus)
  else
    UnknownCommand(strCommand, AStatus);

  if CurrentRoom < Length(Rooms) - 1 then
    WaitingForCommand(AStatus);
end;

Kao sto vidite komande kada se program kompajlira, nemoguce je menjati komande, dodatavi nove ili slicno. U mnogim slucajevima je ovo sasvim dovoljno, ali u slucajevima kada svaki nov nivo treba da ima nove komande, kada je potrebno da korisnik kreira sam svoje komande od vec postojecih komandi (macro), ovaj pristup nije dobar. U takvim situacijama je moguce napraviti listu komandi i uz svaku komandu vezati opis i proceduru koja treba da bude pozvana. Na taj nacin je moguce naknadno dodavati komande, procedure za komande drzati u samom kodu, dinamickoj biblioteci ili nekom skriptu, menjati naziv i opis komande...

Vas "domaci zadatak" je da napravite da igrac moze da napravi sam svoje komande (alias) koje ce pozivati neke od predefinisanih... npr:
igrac definise komandu POGLEDAJ koja poziva komandu LOOK
igrac definise komandu NA koja poziva komandu GO
.
.
.
Nacin na koji cete dozvoliti igracu da definise te nove komande zavisi od vas (da li ce biti dovoljno da korisnik samo napise POGLEDAJ = LOOK ili ce postojati nova forma koja ce omoguciti kreiranje novih komandi, ili ce biti nesto trece...). Snimanje i ucitavanje novih komandi ne mora da bude implementirano.

PS
Opisi soba su trenutno "Soba 1", "Soba 2", "Soba 3",... trebalo bi da se uskoro tu pojavi neki pravi opis
[ Srki_82 @ 30.08.2007. 21:57 ] @
Dok cekamo da neko napravi potrebne izmene u igri sa sobama, pogledajte sta sledi...



Snake, tj. zmijica. Igra ce da ima svoj editor (slika koju vidite) koji ce omoguciti lako kreiranje nivoa. Igra i editor ce koristiti LCL za prikaz svega. Ovo ce biti poslednja igra koja koristi LCL. Sledecih par projekata verovatno nece biti igre nego ce pokazivati kako se kreira prozor bez LCL, kako se crtaju neki osnovni objekti u OpenGL i slicno...
[ Elmer-Davez @ 14.09.2007. 18:07 ] @
Ja sam trenutno u Novom Sadu, gde jos uvek nemam pristup internetu(ovo sad saljem iz internet caffea), pa ne mogu da se prikljucim ovome, ali nadam se da cu uskoro uvesti adsl, pa cu se pozabaviti ovim novim, a i onim malo starijim problemom.

Aj pozzz
[ Elmer-Davez @ 14.10.2007. 18:29 ] @
Evo me, najzad. Uradio sam i ono sa sobama i snake(i editor i igru), a naisao sam na par banalnih problema. Problemi su kod snake i glase:
-Kad pokrenem editor za snake ne pojavi mi se odma status bar, nego tek kad stisnem na "Editor" na mejn meniju. Ne znam da li je to do mog kompa ili do delphija ali javi mi da li se to i tebi desava na ovoj igrici koju sam okacio tu.
-Kako da uklonim ekstenziju u TFileListBox, tj. da mi ne pise ono .dat, a da prikaze samo fajlove koji su .dat

Inace, interesuje me koja je razlika izmedju lazarusa i delphija i da li mozes da koristis ovde delphi(ili bar da stavis exe svojih igara) posto ja nemam lazarus, pa ne mogu da kompajliram tvoje projekte da vidim kako izgledaju.

Pozzz
[ Srki_82 @ 27.10.2007. 20:45 ] @
@Elmer-Davez
Citat:
Kad pokrenem editor za snake ne pojavi mi se odma status bar, nego tek kad stisnem na "Editor" na mejn meniju. Ne znam da li je to do mog kompa ili do delphija ali javi mi da li se to i tebi desava na ovoj igrici koju sam okacio tu.

Kad iskompajliram tvoj kod u Delphi 2007 tog problema vise nema, ali se polja ne icrtavaju kad kliknem na njima (nisam mnogo zagledao, ali ima veze sa DoubleBuffered := True)
Citat:
Kako da uklonim ekstenziju u TFileListBox, tj. da mi ne pise ono .dat, a da prikaze samo fajlove koji su .dat

Nisam bas najbolje razumeo pitanje jer ekstenzija moze da se vidi na 3 mesta... u listi gde su fajlovi, u polju gde mozes da ukucas naziv fajla i u combo box-u iz kojeg mozes da biras filter. Ako je problem sa listom u kojoj se nalaze fajlovi, to je zato sto ti je u explorer-u podeseno da se prikazuju ekstenzije, ako moras da upises *.dat u polju gde mozes uneti ime fajla, znaci da nisi izabrao filter koji ce ti prokazati samo *.dat fajlove, ako ti se u combo box-u vidi ektenzija, samo je izbrisi iz naziva filtera. To je sve sto mi trenutno pada na pamet.

Lazarus je besplatna alternativa Delphi-u. Treba jos dugo da se razvija da bi bio stabilan i koristan kao Delphi, ali se vec sada mogu napraviti neke aplikacije. Bitna razlika je to sto Lazarus moze da radi na razlicitim operativnim sistemima i da kompajlira programe za njih. To je moguce jer kao kompajler koristi FreePascal.
Lazarus mozes preuzeti ovde: http://sourceforge.net/project/showfiles.php?group_id=89339

@All
Za Snake Editor cemo napraviti malu formicu sa par jednostavnih procedura koje ce se pozivati kada korisnik klikne na odredjene njene odredjene delove. Forma ce izgledati ovako:



Osnovni meni, toolbar, akcije, status bar, slicice i dialozi su vam vec poznati od ranije i na tome se necemo zadrzavati. Kreirane su osnovne akcije (novo, otvori, snimi i izadji) i vezane su za stavke u meniju i toolbar-u. Status bar sluzi samo da objasni sta koji taster na misu znaci (kao mali help).

Na sredini forme se nalazi komponente tipa TImage. Ona ce sluziti za iscrtavanje i za "hvatanje" korisnickih akcija vezanih za crtanje.

Sad na programiranje... napravicemo da polje po kojem se igrac krece ne mora da bude fiksne sirine i visine (prvi nivo moze biti 10*10, drugi moze biti 30*30, itd...). Trebace nam tri promenljive. Jedna za polje po kojem ce igrac da se krece i jos dve za sirinu i visinu polja da ne bi morali svaki put da proveravamo velicinu polja. Bilo bi lepo i da imamo neke konstante koje ce obelezavati sta se na kom polju nalazi:
Code:
const
  C_Empty = 0;
  C_Wall = 1;

var
  Field: array of array of Byte;
  FieldWidth, FieldHeight: Integer;

Sad kad imamo gde da smestimo podatke, dodacemo i funkciju koja kreira prazno polje za igru:
Code:
procedure CreateField(AFieldWidth, AFieldHeight: Integer);
var
  X, Y: Integer;
begin
  SetLength(Field, AFieldWidth, AFieldHeight);
  FieldWidth := AFieldWidth;
  FieldHeight := AFieldHeight;
  
  for Y := 0 to FieldHeight - 1 do
    for X := 0 to FieldWidth - 1 do
      Field[X, Y] := C_Empty;
end;

Sve sto nam je jos potrebno, osim crtanja, je snimanje i ucitavanje polja. Posto polje moze biti razlicite sirine i visine, trebalo bi i te podatke upisati u fajl, pa kod za snimanje i ucitavanje izgleda ovako:
Code:
function SaveField(AFilename: TFilename): Boolean;
var
  S: TFileStream;
  X, Y: Integer;
begin
  Result := False;
  try
    S := TFileStream.Create(AFilename, fmCreate);
    S.Write(FieldWidth, SizeOf(FieldWidth));
    S.Write(FieldHeight, SizeOf(FieldHeight));
    for Y := 0 to FieldHeight - 1 do
      for X := 0 to FieldWidth - 1 do
        S.Write(Field[X, Y], SizeOf(Field[X, Y]));
    Result := True;
  finally
    S.Free;
  end;
end;

function LoadField(AFilename: TFilename): Boolean;
var
  S: TFileStream;
  X, Y: Integer;
begin
  Result := False;
  try
    S := TFileStream.Create(AFilename, fmOpenRead);
    S.Read(FieldWidth, SizeOf(FieldWidth));
    S.Read(FieldHeight, SizeOf(FieldHeight));
    CreateField(FieldWidth, FieldHeight);
    for Y := 0 to FieldHeight - 1 do
      for X := 0 to FieldWidth - 1 do
        S.Read(Field[X, Y], SizeOf(Field[X, Y]));
    Result := True;
  finally
    S.Free;
  end;
end;

Da se bacimo sad na iscrtavanje. Nazovimo TImage komponentu imgField. Funkcija za crtanje ce iscrtavati sve na imgField, a imgField ce se pobrinuti o iscrtavanju svega na formi kad god to bude bilo potrebno:
Code:
procedure TfrmMain.DrawField;
var
  X, Y: Integer;
  
begin
  for Y := 0 to FieldHeight - 1 do
    for X := 0 to FieldWidth - 1 do
    begin
      if Field[X, Y] = C_Empty then
        imgField.Canvas.Brush.Color := clWhite
      else
        imgField.Canvas.Brush.Color := clGray;
      imgField.Canvas.Rectangle(Trunc(X * CellWidth), Trunc(Y * CellHeight),
        Trunc((X + 1) * CellWidth), Trunc((Y + 1) * CellHeight));
    end;
end;

Primecujete dve promenljive koje ranije nismo spomenuli, CellWidth i CellHeight. One nemaju veze sa logikom igre nego sa iscrtavanjem. Prozor u kojem se sve iscrtava i dimenzije polja nisu fiksne vrednosti i one uticu na dimenzije pojedinacne celije u polju. To znaci da prilikom kreiranja novog polja, ucitavanja ili menjanja velicine prozora moramo izracunati nove dimenzije celija i iscrtati polje s novim vrednostima. Kod koji racuna dimenzije celije izgleda ovako:
Code:
CellWidth := imgField.Width / FieldWidth;
CellHeight := imgField.Height / FieldHeight;

Ostalo je jos samo da "saslusamo korisnika" i menjamo celije onako kako on zeli. Zato cemo hvatati MouseDown, MouseUp i MouseMove dogadjaje. Na MouseDown i MouseUp cemo postavljati jednu promenljivu koja ce oznacavati sta treba da se iscrta na odredjenoj celiji, a na MouseMove cemo raditi iscrtavanje tako da korisnik moze da pritisne taster i da pomera misa, i da se celije menjaju gde god se mis nalazi:
Code:
procedure TfrmMain.imgFieldMouseDown(Sender: TOBject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    CellValue := C_Wall
  else
    CellValue := C_Empty;

  imgFieldMouseMove(Sender, Shift, X, Y);
end;

procedure TfrmMain.imgFieldMouseUp(Sender: TOBject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  CellValue := -1;
end;

procedure TfrmMain.imgFieldMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if CellValue <> -1 then
  begin
    Field[GetCell(X, Y).X, GetCell(X, Y).Y] := CellValue;
    DrawField;
  end;
end;


To je to za sada. Posto sad nista ne cekamo jer je Elmer-Davez vec odradio sve, uskoro cu zavrsiti i igru koja ce koristiti podatke iz ovog editora i krecemo na SDL + OpenGL