Java-tworzenie gry, Pacman

Java-tworzenie gry, Pacman
AU
  • Rejestracja:około 13 lat
  • Ostatnio:prawie 12 lat
  • Postów:21
0

Witam, chciałabym napisać prostą grę pacman. Na razie zaprogramowałam menu, statystyki, generowanie mapy z pliku .txt oraz proste poruszanie się pacmanem. Teraz chciałabym stworzyć kolizję ze ścianami oraz stworzenie poruszających się przeciwników oraz ich AI. Nie mam jednak za bardzo pomysłu jak to zrealizować. Bardzo proszę o jakieś wskazówki dotyczące realizacji tych elementów. Z góry dziękuje:)

Antoniossss
  • Rejestracja:prawie 16 lat
  • Ostatnio:ponad 10 lat
0

AI? Po co AI ;]
Opisz plansze na grafie i poruszaj się po najkrótszej ścieżce :)


www.toptraker.pl
TopTraker! - I wiesz co jest grane!
Mój własny projekt w Javie - najnowsza wersja już jest!
AU
  • Rejestracja:około 13 lat
  • Ostatnio:prawie 12 lat
  • Postów:21
0

Okej dziękuje, kolizje już w sumie zrobiłam, ale nie działa ona idealnie,ponieważ poruszanie ludzika odbywa się poprzez przesuwanie obrazka o x pikseli. Chciałabym uzależnić ruch pacmana od czasu, ale nie bardzo wiem jak mam to zrobić. Na każdy kierunek ruchu pacmana(animacje) mam oddzielny watek, wygląda to następująco:
klasa abstrakcyjna ruchu:

Kopiuj
 
public abstract class RuchPacman implements Runnable{
    
    public RuchPacman(Pacman p)
    {
        panel=Plansza.getmappanel();
        ludek=p;
        inicjalizacja();
    }
    
    public void inicjalizacja(){}
  
    public void aktualizacjawsp(){}
  
    public synchronized void run() 
    {
        int ib=0;
        while(true)
        {   
            try
            {       
            ludek.setimg(img[ib]);
            aktualizacjawsp();
            panel.repaint();
  
            Thread.sleep(80);
            } 
            catch(InterruptedException ex) 
            {
                ex.printStackTrace();
            }     
            ib++;       
            if(ib==4)
            {
                ib=0;
            }

        }
       
    }
    
    public JPanel panel;            //zmienne public dla przejrzystosci podklas
    public Pacman ludek;
    public Image[] img;
    
}

A teraz np: ruch pacmana w prawo:

Kopiuj
 
public class RuchPacmanPrawo extends RuchPacman{
    
    public RuchPacmanPrawo(Pacman p)
    {
        super(p);
    }
    
    public void inicjalizacja()
    {
        img=new Image[4];
        try{
        img[0]=ImageIO.read(new File("/DataG/pacpelny.png"));
        img[1]=ImageIO.read(new File("/DataG/pacprawo0.png"));
        img[2]=ImageIO.read(new File("/DataG/pacprawo1.png"));
        img[3]=ImageIO.read(new File("/DataG/pacprawo2.png"));
        }
        catch(IOException ev)
        {
          ev.printStackTrace();  
        }
    }
    
    public void aktualizacjawsp()
    {        
        if(Kolizja.SprawdzKolizjeMapa(ludek.getx()+ludek.getpredkosc(), ludek.gety(), 'p',ludek)==false)
        {     
            ludek.setx(ludek.getx()+ludek.getpredkosc());                   
        }
        else
            Thread.currentThread().suspend();
          
    }
    
}

A tutaj obsługa klawiatury:

Kopiuj
public class Klawiatura implements KeyListener {

    public Klawiatura()
    {
      ludek=Plansza.getpac();
      ilepac=ludek.length;
      r=new RuchPacman[4*ilepac];
      
      r[0]=new RuchPacmanPrawo(ludek[0]);
      r[1]=new RuchPacmanLewo(ludek[0]);
      r[2]=new RuchPacmanGora(ludek[0]);
      r[3]=new RuchPacmanDol(ludek[0]);
      
      if(ilepac==2)
      {
        r[4]=new RuchPacmanPrawo(ludek[1]);
        r[5]=new RuchPacmanLewo(ludek[1]);
        r[6]=new RuchPacmanGora(ludek[1]);
        r[7]=new RuchPacmanDol(ludek[1]);
      }
      
      t=new Thread[4*ilepac];
      for(int i=0;i<t.length;i++)
      {
          t[i]=new Thread(r[i]);
          t[i].start();
          t[i].suspend();
      }
       
    }
    
    public synchronized void aktualizacjawatkow()
    {
        for(int i=0;i<t.length;i++)
        {
            if(i!=pac1||i!=pac2)
                t[i].suspend();
        }
        if(activepac1)
            t[pac1].resume();
        if(activepac2)
            t[pac2].resume();
    }
    
    public void keyTyped(KeyEvent e) {
        
    }

    public void keyPressed(KeyEvent e) {
       if(ilepac==2)
        {
            if(e.getKeyCode()==e.VK_D)
            {
                if(Kolizja.SprawdzKolizjeMapa(ludek[1].getx()+ludek[1].getpredkosc(), ludek[1].gety(), 'p',ludek[1])==false)
                {
                    activepac2=true;
                    pac2=4;
                    aktualizacjawatkow();
                }
            }
            else if(e.getKeyCode()==e.VK_A)
            {
                if(Kolizja.SprawdzKolizjeMapa(ludek[1].getx()-ludek[1].getpredkosc(), ludek[1].gety(), 'l',ludek[1])==false)
                {
                    activepac2=true;
                    pac2=5;
                    aktualizacjawatkow();
                }
            }
            else if(e.getKeyCode()==e.VK_W)
            {
                if(Kolizja.SprawdzKolizjeMapa(ludek[1].getx(), ludek[1].gety()-ludek[1].getpredkosc(), 'g',ludek[1])==false)
                {
                    activepac2=true;
                    pac2=6;
                    aktualizacjawatkow();
                }
            }
            else if(e.getKeyCode()==e.VK_S)
            {
                if(Kolizja.SprawdzKolizjeMapa(ludek[1].getx(), ludek[1].gety()+ludek[1].getpredkosc(), 'd',ludek[1])==false)
                {
                    activepac2=true;
                    pac2=7;
                    aktualizacjawatkow();
                }
            }      
        }  
        if(e.getKeyCode()==e.VK_RIGHT)
        {
            if(Kolizja.SprawdzKolizjeMapa(ludek[0].getx()+ludek[0].getpredkosc(), ludek[0].gety(), 'p',ludek[0])==false)
            {
                activepac1=true;
                pac1=0;
                aktualizacjawatkow();
            }
        }
        else if(e.getKeyCode()==e.VK_LEFT)
        {
            if(Kolizja.SprawdzKolizjeMapa(ludek[0].getx()-ludek[0].getpredkosc(), ludek[0].gety(), 'l',ludek[0])==false)
            {
                activepac1=true;
                pac1=1;
                aktualizacjawatkow();
            }
        }
        else if(e.getKeyCode()==e.VK_UP)
        {
            if(Kolizja.SprawdzKolizjeMapa(ludek[0].getx(), ludek[0].gety()-ludek[0].getpredkosc(), 'g',ludek[0])==false)
            {
                activepac1=true;
                pac1=2;
                aktualizacjawatkow();   
            }
        }
        else if(e.getKeyCode()==e.VK_DOWN)
        {
             if(Kolizja.SprawdzKolizjeMapa(ludek[0].getx(), ludek[0].gety()+ludek[0].getpredkosc(), 'd',ludek[0])==false)
             {
                activepac1=true;
                pac1=3;
                aktualizacjawatkow();
             }
        }
       
        if(e.getKeyCode()==e.VK_ESCAPE)
        {
            System.exit(1);
        }
           
    }

    public void keyReleased(KeyEvent e) {

    }
    
    private RuchPacman[] r;
    private Thread[] t; 
    private Pacman[] ludek;
    private int pac1,pac2,ilepac;
    private boolean activepac1,activepac2;
} 

I moje pytanie, to jak zorganizować to aby ruch był zależny od czasu, według wzoru: v=s*t?

Antoniossss
a to teraz nie masz tego zrobionego? tam jakiegoś sleepa widziałem. A ciebie to mi sie wydaje zależność d=vt interesuje ;] Jeżeli chodzi natomiast chodzi o prędkość, to wynalazłeś nową jednostkę prędkości i możesz ją nazwać 1 Auss w wymiarze [Auss]=[ms] haha :D
Antoniossss
  • Rejestracja:prawie 16 lat
  • Ostatnio:ponad 10 lat
0

także gratuluje osiągnięcia :D nie no śmieje się tylko, ale po prostu rozbawiło mnie to bardzo.
A na poważnie mówiąc - ruch postaci masz stały. Czas też za bardzo nie chce ani przyspieszać, ani zwalniać i upływa w czasie constans jednej sekundy na sekundę (no teraz to dowaliłem do pieca ;P Twoja wina!;P) więc przesunięcie postaci można by śmiało wyliczać np co 100ms prostego wzoru - przesunięcie w kierunku ruchu [np px] = 0.1 * prędkość_postaci; I już masz zależność od czasu i prędkości. Możesz sobie bawić się również w ruch nieliniowy modyfikując współczynnik prędkości postaci (też w czasie). Powodzonka!


www.toptraker.pl
TopTraker! - I wiesz co jest grane!
Mój własny projekt w Javie - najnowsza wersja już jest!
xxx_xx_x
  • Rejestracja:prawie 13 lat
  • Ostatnio:8 dni
  • Postów:365
1
Antoniossss napisał(a):

także gratuluje osiągnięcia :D nie no śmieje się tylko, ale po prostu rozbawiło mnie to bardzo.
A na poważnie mówiąc - ruch postaci masz stały. Czas też za bardzo nie chce ani przyspieszać, ani zwalniać i upływa w czasie constans jednej sekundy na sekundę (no teraz to dowaliłem do pieca ;P Twoja wina!;P) więc przesunięcie postaci można by śmiało wyliczać np co 100ms prostego wzoru - przesunięcie w kierunku ruchu [np px] = 0.1 * prędkość_postaci; I już masz zależność od czasu i prędkości. Możesz sobie bawić się również w ruch nieliniowy modyfikując współczynnik prędkości postaci (też w czasie). Powodzonka!

Nie dość że sie naśmiewasz z niego to jeszcze głupoty gadasz =/ co z tego ze masz sleep(80)?? jezeli wątek składa sie z operacji :

  1. aktualizacja_pozycji();
  2. render()
  3. sleep(x)
    To czas trwania klatki jest równy sumie czasów każdej z tych składowych, mało tego czas pkt. 1. i 2. może być zmienny. Co doprowadza do sytuacji, w której aktualizacja pozycji będzie wykonywana w różnych odstępach czasowych i sprawi że postać nie będzie poruszać się płynnie. Nigdy nie widziałeś w grze jak przy złapaniu laga, czy to sieciowego czy graficznego twoja postać niesamowicie szybko sie przemieściła? Właśnie dla tego że wszystkie profesjonalne gry mają uwzględnienie czasu gdyż fps sie zmienia w czasie, co oznacza że czas trwania jednej klatki jest też zmienny w czasie a co za tym idzie formuła typu 0.1 * prędkość_postaci; jest brednią bo zakłada że czas wywołania klatki jest stały.

Wracając do tematu zwykle robi się to tak(pseudokod):

Kopiuj
long start_t = (pobranie czasu w ms)     // początkowa inicjalizacja zmiennej pomocniczej
while(true) {
    long cur_t = (pobranie czasu w ms)   // pobranie czasu rozpoczęcia i-tej klatki
    float dt  = (float)(cur_t - start_t) / 1000.0;  // w start_t mamy mieć czas startu poprzedniej klatki
                                                                 // różnica miedzy cur_t a start_t to czas trwania ostatniej klatki
    start_t = cur_t;   // czas startu tej klatki przepisujemy do start_t, przygotowujemy zmienną pod kolejną klatkę

    // teraz możemy wykonać aktualizacje pozycji uwzględniając dt.
    aktualizacja_pozycji(dt);

    render();
    sleep(x);
}

// dt czas w sekundach
void aktualizacja_pozycji(float dt) {
    x += vx * dt;
    y += vy * dt;
}

Jedna mała uwaga nie v = s * t tylko S = vt lub dokładniej S = całka 0->t z (vdt), każde wywołanie funkcji aktualizacja_pozycji(dt) to tak naprawdę właśnie wywołanie v * dt, natomiast twoja pozycja to właśnie suma(całka) takich składowych.
Gdybyś teraz chciał np uwzględnić przyśpieszenie to musiałbyś tylko modyfikować v w każdej klatce w analogiczny czyli np
v = a*t czyli znowu v = a * dt; wygląda to ładniej kiedy postać stopniowo przyśpiesza i zwalnia ;-)

Zobacz pozostały 1 komentarz
Antoniossss
Ojojoj Pan moralny. U wanna know why? u know why:>. Śmiesznie napisał to się z tego pośmiałem i co? problem jakiś masz w związku z tym?
xxx_xx_x
@Aussie strzelił sie tylko co do wzoru, poza tym jasno napisał o co chodzi, to ty nie zrozumiałeś i napisałes jakieś swoje dziwne wnioski. Nawet widać że @Aussie przynajmniej był świadomy tego ze to wcale nie wykonuje się ze stałym dt.
Antoniossss
a ja się ze wzoru śmiałem, dlatego sam widzisz, że Twoje wywody są zbędne ;]
xxx_xx_x
i co z tego skoro później po wyśmiewaniu jego błędu sam strzelasz niewiele lepszy?
Antoniossss
No to jak jest z czego to się też śmiej, nie "filozuj" :)
Antoniossss
  • Rejestracja:prawie 16 lat
  • Ostatnio:ponad 10 lat
0

Przy takiej "skomplikowanej" grze wymagającej potężnej mocy obliczeniowej, może jednak wystarczyłoby w zamknąć wszystko w dwóch wątkach - logikę gry w jednym a renderowanie w osobnym co ?? :>
No właśnie postacie przyspieszają bo czas masz stały a komp przyciął..... <- jeżeli mówimy o grach sieciowych - normalnie mogłoby się bez tego obyć i zrobić można teleport. Jak mówisz o lagu lokalnym, to błagam, wszystko zamarza wtedy
Więc Władziu... co Ty mi tu....


www.toptraker.pl
TopTraker! - I wiesz co jest grane!
Mój własny projekt w Javie - najnowsza wersja już jest!
xxx_xx_x
  • Rejestracja:prawie 13 lat
  • Ostatnio:8 dni
  • Postów:365
1
Antoniossss napisał(a):

Przy takiej "skomplikowanej" grze wymagającej potężnej mocy obliczeniowej, może jednak wystarczyłoby w zamknąć wszystko w dwóch wątkach - logikę gry w jednym a renderowanie w osobnym co ?? :>
No właśnie postacie przyspieszają bo czas masz stały a komp przyciął..... <- jeżeli mówimy o grach sieciowych - normalnie mogłoby się bez tego obyć i zrobić można teleport. Jak mówisz o lagu lokalnym, to błagam, wszystko zamarza wtedy
Więc Władziu... co Ty mi tu....

No to sie popisałeś... przykład, jedną klatkę GPU ci wyrenderuje 10ms drugą np. w 50ms(mowa o lagu GPU NIE ZAWIESZENIU kompa, przypominam że lag == latencja == opóźnienie, poza tym nie musi to być nawet lag, a zwykła zmiana liczby wyświetlanych obiektów na ekranie) i juz proste x+=v jest do bani. Do tego render nie jest w pełni niezależny od zmian, musi gdzieś nastąpić jego synchronizacja, mało tego, sleep(x), gwarantuje tylko tyle ze wątek poczeka minimum x ms, a nie dokładnie x(do tego nawet to nie jest takie 100% pewne bo może pojawić się przerwanie). Nie zapominaj także o tym że wątek uśpiony musi jeszcze poczekać aż zostanie wybudzony, a to oznacza ze musi czekać aż inny wątek będzie można wywłaszczyć. Do tego zakładasz fizyczną równoległość wątków co też jest nieprawdą... co powiesz chociażby o procesorze z jednym rdzeniem? Podobnie moze sie zdażyć gdy wielordzeniowy procek ma wiele wątków do obsłużenia. Nie wprowadzaj go w błąd.

Antoniossss
Ale Ty nadal przy pacmanie jesteś prawda ?
xxx_xx_x
a jaka jest różnica? nieprawidłowe rozwiązanie będzie tylko mniej odczuwalne z reszta do czasu aż app zacznie sie bardziej rozrastać... z resztą wystarczy że czas wykonania klatki będzie tego samego rzędu jednostki co czas podany w sleep i już to co napisałeś nie będzie działac prawidłowo. poza tym moze autor za jakis czas będzie chciał zrobić wiekszy program i to rozwiązanie będzie błędne. prosty przykład dajesz sleep(10), klatka trwa 5ms, już masz błąd rzędu 50%... na mapie pojawiają sie stworki i wicej operacji jest wykonywanych, czas klatki wzrasta do 10ms.. błąd 100%
Antoniossss
no fascynujące ile potrafisz włożyć energii w wypacanie swoich wywodów an temat tego, że nie miałem racji. Gratuluje zapału i tak trzymaj!
0

Chętnie mogę pomóc, gg - 940289.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)