Skocz do zawartości

Zarchiwizowany

Ten temat jest archiwizowany i nie można dodawać nowych odpowiedzi.

zizou666

Snake - java

Polecane posty

Witajcie. Postanowiłem napisać sobie w Javie tradycyjnego Snake'a. Ale napotkałem problem już na początku. Otóż wczytuje sobie obrazek i nie działa mi jego przesuwanie. Wyobrażałem to sobie tak, że wczytuje sobie obrazek (głowę mojego węża) później wywoływałem metodę paintComponent z mojej klasy obrazek, która rysuje mi go w okreslonym przeze mnie miejscu. Wszystko to robiłem w metodzie actionPerformed:

ActionListener listener_start = new ActionListener()
    {
        public void actionPerformed(ActionEvent event)
        {
                    validate();
            head = new Obrazek("src/Snake/snake_head.png", 1);
            add(head);
            validate();
                      
        }
    };

Ten obiekt nasluchujący łącze z przyciskiem start. No i tutaj jeszcze wszystko działa. Po naciśnięciu klawisza startu wczytuje mi głowę węża. Teraz jednak zrobiłem sobie w klasie Obrazek taką funkcję:

public void changePosition(int kierunek)
{
    boolean warunek = true;
    while(warunek)
    {
        try {
              Thread.sleep(500);
            }
            catch (InterruptedException e) { }
        
        if(kierunek==1)       //idzie w prawo
            x += 15;
        else if(kierunek==2)         //idzie w lewo
            x -=15;
        else if(kierunek == 3)        //idzie w gore
            y -= 15;
        else if(kierunek ==4)        //idzie w dol
            y += 15;
        
        
        paintComponent(object);
        revalidate();
        
    }
}

Jednak szczerze powiedziawszy nie wiem czy spełnia ona swoje zadanie oraz nie wiem gdzie ją umieścic (w konstruktorze ramki?). W actionPerfermed obiektu listener_start chyba nie bo to bez sensu. Jeśli ją tam umieszczę to wyskakują mi exceptiony.

Nie wiem czy jest to zrozumiałe, jakby co to podeślę więcej kodu. Chodzi mi głównie o jakieś pseudokody/algorytmy jak to przesuwanie zrobić. Z góry dzięki za pomoc.

Link do komentarza
Udostępnij na innych stronach

Ehhh, Java! W jakimś ludzkim języku, to nie łaska ;)

A tak całkiem serio, to źle się do tego zabierasz. Czemu klasa obrazek ma pozycję? Jak będziesz chciał narysować dwa razy ten sam obrazek to załadujesz go dwa razy? Przecież to bez sensu.

Zrób sobie klasę węzła i niech ten węzeł ma pozycję. Przy rysowaniu węzła bierzesz jego pozycję i tam rysujesz obrazek. Głowa też jest węzłem tylko, że specyficznym.

Nie umieszczaj logiki gry w węźle. Zrób jeszcze wyższą klasę gry, która będzie pobierać wejście od gracza i analizować stan gry.

Link do komentarza
Udostępnij na innych stronach

No dobra, zrobiłem jak mówisz, mam klasę węzeł i przesyłam tam wczytany osobno obrazek, zmieniając jego pozycję. Zacząlem sobie to mniej więcej wyobrażać, ale dalej nie mam pojęcia co zrobić, żeby z jakimś opóźnieniem rysować mój obrazek cały czas zmieniając jego pozycję. Kiedy robię to w listenerze odpowiedzialnym za przycisk start rysuję sobie mój obrazek za pomoc ą metody paintComponent z klasy węzeł. Póżniej dalej w tym listenerze w pętli próbuje usypiać, zmieniać położenie węzła, i rysować go od nowa. Jednak kolejność wykonywania poszczególnych rzeczy w tej pętli jest całkowicie inna niż ja sobie tego życzę. Nie mam pojęcia czemu tak się dzieje:

ActionListener listener_start = new ActionListener()
    {
        public void actionPerformed(ActionEvent event)
        {
            try          
            {
                head1 = ImageIO.read(new File("src/Snake/snake_head.png"));
                        }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            
            try
            {
                basic = new Wezel(head1);
                
                remove(menu);
                for(int i=0; i<10; ++i)
                {
                    add(basic);
                    basic.setX(100);
                    basic.repaint();
                    Thread.sleep(500);
                }
                
            }
            catch (InterruptedException e) {
                
                e.printStackTrace();
            }
                validate();
    }
};

Zawsze najpierw wykonywana jest pętla zamiast rysowania. Tak samo menu, chociaż mam je dużo wcześniej w kolejności, jest kasowane na końcu, po wszystkich obrotach pętli. Potrafi to ktoś wyjaśnić?

A w Javie pisze, bo miałem zamiar użyć tego programu na laboratoria z Javy, ale wydaje mi się, że chyba trzeba będzie zrobić coś innego...

Link do komentarza
Udostępnij na innych stronach

Rozumiem, że to jest obsługa wciśnięcia klawisza start. A gdzie obsługa innych klawiszy? Do snake'a to chociaż ze dwa guziki by się przydały.

Czemu do obsługi start dodałeś tą pętle? Start to chyba powinien ustawić warunki początkowe gry. Czyli przygotować plansze, ustawić pozycję węża, ustawić stan zmiennych itp.

Rysowanie zrób w pętli głównej.

class SnakeGame
{
   public static int main()
   {
       SnakeGame game = new SnakeGame();

       game.Init();

       game.Start();      
   }
};

W metodzie init dajesz sobie inicjalizację obiektu SnakeGame, rejestrujesz te actionlistenery, tworzysz menusy, ładujesz obrazki itp.

Metoda start to tak w pętli analiza stanu wejścia, stanu gry i rysowanie.

Link do komentarza
Udostępnij na innych stronach

Właśnie jeśli chodzi o inne klawisze miałem zamiar zająć się tym później, najpierw chciałem żeby po prostu ta głowa zaczęła się poruszać ;)

A właśnie nie miałem pojęcia gdzie to umieścić, ale chciałem zrobić to po włączeniu przycisku start dlatego wrzuciłem to do actionListenera dotyczącego tego przycisku. Ale dzięki za rady, jutro spróbuje co nieco wcielić w życie.

Link do komentarza
Udostępnij na innych stronach

Powoli ruszyłem dalej i napotkałem problem przy próbie obracania głowy. Otóż samo obracanie wiem jak zrobić i działałoby, gdyby nie to, że kiedy zmieniam sobie obrazek i rysuje go od nowa stary obrazek zostaje a nowy się nakłada. Nie wiem za bardzo jak ten stary usunąć. Rysuje oczywiście na obiekcie typu Graphics metodą paintComponent. Próby zasłaniania starego obrazka poprzez prostokąt w kolorze tła się nie udały( (może coś źle robię). Nie wierze, żeby w klasie Graphics nie istniała metoda pozwalająca na wyczyszczenie całego obiektu. Niemniej jednak ja na razie takowej nie znalazłem. Jeśli ktoś wie jak to rozwiązać to będe wdzięczny.

Link do komentarza
Udostępnij na innych stronach

Jest coś takiego co się nazywa clearRect.

Aczkolwiek prawie jestem pewien, że to niezbyt eleganckie rozwiązanie i że kiedyś, kiedy miałem podobny problem to go rozwiązałem jakoś nieco inaczej (coś z BufferedImage mi świta, ale nie chcę mówić o czymś czego nie jestem pewien).

W każdym razie - Graphics ma metodę clearRect(Rectangle r) która po prostu czyści contentPane aż do tła - czyli dokładnie to, czego chcesz. Na laboratoria starczy :)

Link do komentarza
Udostępnij na innych stronach

Dzięki wielkie teraz już działa ;) Ale mam inny problem. Otóż kiedy używam metody sleep tzn. Thread.sleep() bądź Thread.currentThread().sleep(), to np przy wypisywaniu konsolowym wszystko działa w pętli z przerwami czasowymi, natomiast kiedy próbuje robić coś takiego z ramką (np dodawanie panelu z przyciskiem z rosnącą na nim wartością), to aplikacja czeka do końca pętli i wyświetla mi panel po ostatnim przebiegu pętli. Czy tutaj konieczne jest zastosowanie wielu wątków? Przyznam, że jak narazie średnio je ogarniam ;) Czy można to jakoś inaczej rozwiązać?

Link do komentarza
Udostępnij na innych stronach

Odpowiedź: być może jest jakiś sposób, by nie robić to poprzez dodanie wątku. Ja jednak nie widzę żadnych innych rozwiązań.

Sam wątek jest jednak de facto bajecznie prosty.

Tutaj niżej masz taki prosty niby wątek:

public class MyThread implements Runnable {
    private boolean running = true;

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    @Override
    public void run() {
        while(running){
            // tu wpisz co ci się żywnie podoba
        }
    }
}

Tworzysz go prosto - na początek tworzysz obiekt tej klasy, np:

MyThread mt = new MyThread();

a później rozpoczynasz wątek:

new Thread(mt).start();

Ot i cała filozofia, jedyny problem to po prostu jakoś tak przerobić wątek, żeby on sterował twoją aplikacją.

Są jeszcze klasy Timer i TimerTask, ale tych nie polecam bo kiedyś próbowałem i coś mi słabo wyszło.

Link do komentarza
Udostępnij na innych stronach

Problemów ciąg dalszy ;) Otóż chodzi mi tym razem konkretnie o rysowanie mojego jedzonka na planszy. Otóż mam zapętlony watek, gdzie cały czas zmieniam pozycję głowy zgodnie z kierunkiem, który zmieniam poprzez strzałki na klawiaturze, no i przerysowuje głowę i tutaj jest wszystko ok. Ale zrobiłem sobie osobną klasę odpowiedzialną za moje jedzenie, dla której losuje sobie pozycje i kiedy współrzędna głowy jest równa wylosowanej współrzędnej mojego jedzonka to wywołuje metodę repaint dla jedzenia. Problem w tym, że kiedy w wątku wywołuje metodę repaint dla głowy (przy każdym obrocie pętli) to równiez niewiadomo dlaczego przerysowuje mi moją mysz (jedzenie). Nigdzie w wątku nie wywołuje metody repaint dla myszy. Nie mam pojęcia dlaczego tak się dzieje. Dorzucam fragment kodu:

Wątek:

class game implements Runnable
    {
        boolean end_condition = false;
        @Override
        public void run()
        {    
            
            try
            {
            while(end_condition == false)        
            {
            points.setText("Points: " + punkty);
            add(points_panel,BorderLayout.SOUTH);
            Thread.sleep(1000);
            basic.changePosition();
            basic.checkCollision();
            basic.repaint();
            
            if(basic.getX() == jedzenie.getX() && basic.getY() == jedzenie.getY())
            {
            jedzenie.repaint();
            punkty += 10;
            }
            
            }
                
            }
            catch (InterruptedException e)
            { }
            
        }
        
    }

I jego obsługa:

public void setStart()
    {                        
        boolean game = false;
        start_game = true;
        basic = new Wezel(head1);
        rest = new Wezel(body);
        jedzenie = new Feed(mouse);
        remove(menu);
        add(basic);
        validate();
        add(jedzenie);
        validate();
        game = true;
        KeyActions();
        
        try {
            gra(game);
        } catch (InterruptedException e) {
        
            e.printStackTrace();
        }
    }
    
    public void gra(boolean game) throws InterruptedException
    {
        if (game == true)
        {
            points.setSize(50,10);
            points_panel.add(points);
            
            
                game ng = new game();
                new Thread(ng).start();
                
            }
    }

Nie wiem czy jest to całkiem zrozumiałe. Widać jednak że tylko w metodzie setStart(), która wykonuje sie tylko jeden raz tworze sobie obiekt jedzenia i dodaje go do ramki i w wątku w ifie, do którego narazie wogóle mi nie wchodzi go przerysowuje. Więc nie mam pojęcia dlaczego za każdym razem w while'u w wątku mi przerysowuje to jedzenie.

Link do komentarza
Udostępnij na innych stronach

Projekt poszedł troche do przodu, później miałem od niego przerwę bo musiałem zająć sie innymi rzeczami, no a teraz wróciłem no i... tak tak, napotkałem kolejny problem ;) Otóż mam problem z wątkami, a konkretnie ze sterowaniem nimi. Opisać będzie to niemiłosiernie ciężko, więc zamieszczę cały mój kod może tutaj i po odpaleniu gry każdy zobaczyłby co jest nie tak. Chodzi mianowicie o to, że mam wątek głowny odpowiedzialny za głowę. Przy zjedzeniu myszy dodaje za głowę kolejny element i uruchamiam dla niego nowy wątek ustawiając w nim położenie w którym ma sie pojawić i kierunek ruchu. Potem w tym wątku sekwencyjnie zmieniam położenie na podstawie elementu stojącego wczesniej. Problem w tym, że o ile dla pierwszego elementu działa mi to ok, to kiedy uruchamiam nowy wątek dla kolejnego węzła na podstawie danych z wcześniejszego zmiana jego położenia następuje natychmiast po zmianie ruchu głowy. Wiem dlaczego tak sie dzieje, po prostu kiedy zmienie położenie pierwszego elementu w pierwszym wątku to natychmiast zmieniaja się następne, a chodzi o to żeby działało to z opóźnieniem dla każdego elementu.

Jak wspomniałem cholernie ciężko to wyjaśnić ;D Dlatego zamieszczam cały kod, żeby jeśli jakaś dobra dusza będzie mogła to pobierze i powie mi jak moge zrobić to co chcę ;) Z góry przepraszam za być może trochę pogmatwany kod.

Snake.zip

Link do komentarza
Udostępnij na innych stronach



  • Kto przegląda   0 użytkowników

    • Brak zalogowanych użytkowników przeglądających tę stronę.
×
×
  • Utwórz nowe...