Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: jou222 02. 12. 2014, 21:30:41

Název: Java - Canvas a tlačítka
Přispěvatel: jou222 02. 12. 2014, 21:30:41
Zdravím,

dělám hru a potřebuji poradit, dělám tlačítka a chci až zmáčknu třeba R, tak se spustí metoda, která prohazuje dva šálky. Metodu už jsem otestoval přímo v kódu takže když jsem ji volal přímo v třída, tak to fungovalo.

Ale když jsem to dal na tlačítko a zavolal, provede se to, ale není vidět, jak se objekty pohybuji. Jde jen vidět, že na konci se objekty pohnuli.

Prosím o pomoc. Děkuji

Kód: [Vybrat]
package game;

import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import graphics.Canvas;

import javax.swing.JFrame;
import javax.swing.JLabel;

import objects.Background;
import objects.Ball;
import objects.Cup;

public class Game extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;

private static final String TITLE = "Skořápky";
private static final int WIDTH = 800;
private static final int HEIGHT = 600;

private static final int countCups=3 ;

private Canvas canvas;
private Background background = new Background();
private Ball ball;
private Cup[] cups;

private boolean isRunning = false;
private boolean isRotate = false;


//vytvoření okna
public Game() {
this.setTitle(TITLE);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(WIDTH, HEIGHT);     
        this.setLocationRelativeTo(null);
        this.setResizable(false);
        this.setVisible(true);     
        this.add(this.canvas=new Canvas(WIDTH,HEIGHT));       
        gameStart();   
        prepareGame();
        //runningGame(); <-- když dám metodu tady, tak to funguje
        this.addKeyListener(this);
}

//Uvodní stránka
public void gameStart() {
this.canvas.addObject(this.background);
}

//vytvoří objekty, nastaví pozadí hrací plochy
public void prepareGame() {
this.background.setImageString("res/table_texture.jpg");
createCupsOnTable();
this.canvas.repaint();
}

//vymaže z listu všechny objekty krom pozadí
public void removeAllObject() {
this.canvas.removeObject(ball);
for(int x=0; x<countCups; x++)
this.canvas.removeObject(this.cups[x]);
}


public void runningGame() {
int rotate=generatedCountRotate();
for(int r=0; r<rotate;r++) {
int numSecondCup=generatedCupForRotate(-1);
changeCups(cups[generatedCupForRotate(numSecondCup)], cups[numSecondCup]);
}
}


//nastaví velikost pole,vytvoří šálky na stůl a prostřednímu dá kuličku a všechny objekty přidá  do listu
public void createCupsOnTable() {
int pom=(countCups/2)-1;

this.cups=new Cup[countCups+1];
if(countCups>=2 && countCups<=8) {
for(int x=0; x<countCups; x++) {
this.cups[x]=new Cup(((widthCreateCup()+(40/countCups))*x)+40,(HEIGHT/2)-heightCreateCup(), widthCreateCup(),heightCreateCup(), null);
if(x==pom) {
this.cups[x].setHasBall(true);
// this.ball=new Ball(widthCreateCup()/2, heightCreateCup()/4, 0, 0, null);
// this.ball.setPositionToCup(this.cups[x]);
// this.canvas.addObject(this.ball);
}
this.canvas.addObject(this.cups[x]);
}
}

}

private int heightCreateCup() {
int result=widthCreateCup()*2;
if(result>200)
result=200;
return result;
}

private int widthCreateCup() {
int result=(WIDTH-100)/countCups;
if(result>100)
result=100;
return result;
}

public void changeCups(Cup cup1, Cup cup2) {

int Cup1X =cup1.getPositionX();
int Cup2X =cup2.getPositionX();

int diff=0;
if(cup1.getPositionX()>cup2.getPositionX())
diff=cup1.getPositionX()-cup2.getPositionX();
else
diff=cup2.getPositionX()-cup1.getPositionX();


for(int y=0; y<diff; y++) {
    if(Cup1X<Cup2X) {
    cup1.moveRight();
    cup2.moveLeft();
    if(y<(diff/2)) {
    cup1.moveUp();
    cup2.moveDown();
    } else {
    cup1.moveDown();
    cup2.moveUp();
    }
    }
    else {
    cup1.moveLeft();
    cup2.moveRight();
    if(y<(diff/2)) {
    cup1.moveUp();
    cup2.moveDown();
    } else {
    cup1.moveDown();
    cup2.moveUp();
    }
    }
    this.canvas.repaint();    
    System.out.println(y);
    timeSleep(generatedTimeSleep());
           
    }
}

private int generatedCountRotate() {
int x;
x=countCups+(int)(Math.random()*(5*countCups));
return x;
}

private int generatedCupForRotate(int firstIs) {
int cup;
cup=(int)(Math.random()*(countCups));
if(cup==firstIs) {
while(cup!=firstIs) {
cup=(int)(Math.random()*(countCups));
}
}
return cup;
}

private int generatedTimeSleep() {
int sleep;
sleep=5+(int)(Math.random()*(5));
return sleep;
}



private void showCup() {
    for(int i=0;i<50; i++) {
    cups[countCups/2-1].moveUp();
    timeSleep(10);
    this.canvas.repaint();
        }
   
}

private void timeSleep(int time) {
try {
    Thread.sleep(time); //1000 milliseconds is one second.
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
}

@Override
     public void keyPressed(KeyEvent e) { 
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
System.out.println("ENTER");
this.ball=new Ball(50, 50, 50, 50, null);
this.canvas.addObject(this.ball);
repaint();
       
      }
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
System.out.println("RIGHT");
this.ball.setPositionX(this.ball.getPositionX()+5);
this.canvas.repaint();
      }

if (e.getKeyCode() == KeyEvent.VK_R) {
System.out.println("X");
runningGame();
}
     }

     @Override
     public void keyReleased(KeyEvent e) {

     }

     @Override
     public void keyTyped(KeyEvent e) {

     }
}
Název: Re:Java - Canvas a tlačítka
Přispěvatel: vyvojar 03. 12. 2014, 00:30:11
No obecně požadavky na překreslení (v tvém případě volání repaint) jsou slučovány, takže když už ve frontě událostí je jeden požadavek na repaint a přijde další, tak se sloučí do jednoho.
Ty děláš to, že v cyklu vždycky zavoláš repaint a pak sleep.
V tom případě, kdy to máš jako handler na klik tlačítka, tak se ti to neprovede, protože ty do té fronty událostí
nasypeš všechny ty repainty naráz, takže se sloučí do jednoho. Pokud se ptáš, proč se ti neprovedou
v tom mezičase, co voláš Thread.sleep, tak to bude podle mě kvůli tomu, že to zpracování událostí ví, že ten objekt, na kterém se má zavolat překreslení, má ještě nedokončené zpracování nějaké předchozí události (ten cyklus, ve kterém voláš repaint a sleep).
Že ti to funguje v konstruktoru je asi tím, že ten kód není spuštěn v nějaké obsluze události, jako když to provádíš v obsluze kliku na tlačítko.

Řešení:
Neměl by jsi používat Thread.sleep, ale použít timer a nedělat to v žádném cyklu v rámci jedné obsluhy události.
Název: Re:Java - Canvas a tlačítka
Přispěvatel: vyvojar 03. 12. 2014, 00:33:16
sorry že ti to nedám přímo v javě.

Pseudcode:

function handleTimer() {
 moveObjects(); // logika toho presouvani
 repaint(); // pozadavek na prekresleni
 scheduleTimer(100ms, handleTimer); // dalsi presouvani za dalsich 100ms
}
Název: Re:Java - Canvas a tlačítka
Přispěvatel: podlesh 03. 12. 2014, 08:17:31
Klasický problém, a nejenom začátečnický - čekání (ať už sleep nebo třeba i diskové/síťové operace) prostě přímo do swing handleru nepatří, nikdy.
Zde je to popsáno:
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html

A to co ty konkrétně potřebuješ pro animace je:
https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html

Název: Re:Java - Canvas a tlačítka
Přispěvatel: jou222 03. 12. 2014, 14:29:47
Zdravím,

děkuji za rady a pomoc. Předělám to tak a už aspoň do budoucna vím, že se to nepoužívá.

Děkuji
Název: Re:Java - Canvas a tlačítka
Přispěvatel: jou222 03. 12. 2014, 15:12:44
Ale nějak nevím, jak to mám upravit :(

s tím Timer, prostě nějak tam ten cyklus potřebuji a aji ty podmínky :-/ a přitom se to musí náhodně generovat jak jsem to měl doteď.

nebo jde nějak do této funkce dát další argumenty?? Páč když jsem to tam zkusil, tak to vyhazuje chybu
Kód: [Vybrat]
public void actionPerformed(ActionEvent e) {
         
           repaint();
   
     }
Název: Re:Java - Canvas a tlačítka
Přispěvatel: podlesh 03. 12. 2014, 15:37:21
Ale nějak nevím, jak to mám upravit :(

s tím Timer, prostě nějak tam ten cyklus potřebuji
Právě že ne - místo cyklu bude ten Timer.

V některých jazycích by se to dalo zapsat jenom jako minimální formální změna, ale v Javě zrovna ne (no, s Javou 8 už by se dalo něco podniknout, ale asi by to na přehlednosti spíš ubralo než přidalo). Takže v principu: budeš mít objekt a v něm udržovat stav, typicky proměnnou cyklu - v případě changeCups tedy y. Pak tam potřebuješ ruzné parametry (neměnné), jako Cup1X, Cup2X, cup1, cup2, diff. No a vnitřek cyklu pak prostě dáš do metody, řekněme třeba "step" a tu budeš volat z timeru. Pak samozřejmě ještě doplnit kontrolu dokončení, aby to nejelo donekonečna.
Název: Re:Java - Canvas a tlačítka
Přispěvatel: jou222 03. 12. 2014, 17:06:49
Dobře :)

takže jsem to udělal jako třídu STEP. A volám ji takže se to krásně prohodí a teď dotaz, ten druhý cyklus co jsem tam měl, resp. mám v tlačítku, je za komentovany, taky mám dělat přes Timer?

Díky :)

Kód: [Vybrat]
package game;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import graphics.Canvas;

import javax.swing.JFrame;
import javax.swing.Timer;

import objects.Background;
import objects.Ball;
import objects.Cup;
import objects.Text;

public class Game extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;

private static final String TITLE = "Skořápky";
private static final int WIDTH = 800;
private static final int HEIGHT = 600;

private static final int countCups=3 ;

private Canvas canvas;
private Background background = new Background();
private Text text;
private Ball ball;
private Cup[] cups;

private boolean isRunning = false;
private boolean isRotate = false;

private int cup1 = 0;
private int cup2 = 0;
private int cup1X = 0;
private int cup2X = 0;
private int diff = 0;
private int actual = 0;



private int start = 0;
private int end = 0;

private Timer time;

//vytvoření okna
public Game() {
this.setTitle(TITLE);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(WIDTH, HEIGHT);     
        this.setLocationRelativeTo(null);
        this.setResizable(false);
        this.setVisible(true);     
        this.add(this.canvas=new Canvas(WIDTH,HEIGHT));
        this.pack();
        gameStart(); 
        this.addKeyListener(this);
}

//Uvodní stránka
public void gameStart() {
this.canvas.addObject(this.background);
this.text=new Text("Spustit hru",800,500,true);
this.canvas.addObject(text);
}

//vytvoří objekty, nastaví pozadí hrací plochy
public void prepareGame() {
this.background.setImageString("res/table_texture.jpg");
createCupsOnTable();
this.canvas.repaint();
}

//vymaže z listu všechny objekty krom pozadí
public void removeAllObject() {
this.canvas.removeObject(ball);
for(int x=0; x<countCups; x++)
this.canvas.removeObject(this.cups[x]);
}

//nastaví velikost pole,vytvoří šálky na stůl a prostřednímu dá kuličku a všechny objekty přidá  do listu
public void createCupsOnTable() {
int pom=(countCups/2)-1;

this.cups=new Cup[countCups+1];
if(countCups>=2 && countCups<=8) {
for(int x=0; x<countCups; x++) {
this.cups[x]=new Cup(((widthCreateCup()+(40/countCups))*x)+40,(HEIGHT/2)-heightCreateCup(), widthCreateCup(),heightCreateCup(), null);
if(x==pom) {
this.cups[x].setHasBall(true);
}
this.canvas.addObject(this.cups[x]);
}
}

}

private int heightCreateCup() {
int result=widthCreateCup()*2;
if(result>200)
result=200;
return result;
}

private int widthCreateCup() {
int result=(WIDTH-100)/countCups;
if(result>100)
result=100;
return result;
}


private int generatedCountRotate() {
int x;
x=countCups+(int)(Math.random()*(5*countCups));
return x;
}

private int generatedCupForRotate(int firstIs) {
int cup;
cup=(int)(Math.random()*(countCups));
if(cup==firstIs) {
while(firstIs!=cup) {
cup=(int)(Math.random()*(countCups));
}
}
return cup;
}

private int generatedSpeed() {
int sleep;
sleep=5+(int)(Math.random()*(10));
return sleep;
}

private void showCup() {
    for(int i=0;i<50; i++) {
    cups[countCups/2-1].moveUp();
    timeSleep(10);
    this.canvas.repaint();
        } 
}

private void timeSleep(int time) {
try {
    Thread.sleep(time);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
}

@Override
     public void keyPressed(KeyEvent e) { 
if(!this.isRunning)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
this.canvas.removeObject(text);
System.out.println("ENTER");
prepareGame();
this.isRunning=true;
      }
}

if(this.isRunning && !this.isRotate)
{
if (e.getKeyCode() == KeyEvent.VK_R) {
this.isRotate=true;
System.out.println("Rotate start");


//for(int r=0; r<generatedCountRotate();r++) {

this.cup1=generatedCupForRotate(-1);
this.cup1X=cups[this.cup1].getPositionX();
this.cup2=generatedCupForRotate(this.cup1);
this.cup2X=cups[this.cup2].getPositionX();
this.time=new Timer(generatedSpeed(),new Step());

this.diff=0;
this.actual=0;

if(this.cup1X>this.cup2X)
this.diff=this.cup1X-this.cup2X;
else
this.diff=this.cup2X-this.cup1X;
this.time.start();

//System.out.println(r);

//}

System.out.println("Rotate end");
}
}

     }

     @Override
     public void keyReleased(KeyEvent e) {

     }

     @Override
     public void keyTyped(KeyEvent e) {

     }

     private class Step implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
           
        if(actual<diff) {
        if(cup1X<cup2X) {
        cups[cup1].moveRight();
        cups[cup2].moveLeft();
        if(actual<(diff/2)) {
        cups[cup1].moveUp();
        cups[cup2].moveDown();
        } else {
        cups[cup1].moveDown();
        cups[cup2].moveUp();
        }
        }
    else {
    cups[cup1].moveLeft();
    cups[cup2].moveRight();
        if(actual<(diff/2)) {
        cups[cup1].moveUp();
        cups[cup2].moveDown();
        } else {
        cups[cup1].moveDown();
        cups[cup2].moveUp();
        }
    }
          actual++;
                repaint();
        }
        }
    }

     
}
Název: Re:Java - Canvas a tlačítka
Přispěvatel: podlesh 03. 12. 2014, 17:12:38
Dobře :)

takže jsem to udělal jako třídu STEP. A volám ji takže se to krásně prohodí a teď dotaz, ten druhý cyklus co jsem tam měl, resp. mám v tlačítku, je za komentovany, taky mám dělat přes Timer?

Díky :)
Odpověď je celkem jednoduchá: ano, princip zůstává, všude kde by byl nějaký sleep tak použít Timer.

Nemám teď čas (ani moc chuť) zrovna zkoumat přímo ten kód, jenom bych upozornil že se ten časovač vlastně nikdy neukončí, jen se točí naprázdno. Což nemusí být nutně na škodu, jenom bacha pokud se pokaždé vyrábí znova tak se po čase začnou hromadit přebyteční horníci.
Ale to jsou všechno věci které si musí začínající programátor vyzkoušet a naučit tím, že si to prakticky osahá.
Název: Re:Java - Canvas a tlačítka
Přispěvatel: jou222 03. 12. 2014, 18:13:25
Jo v pohodě, já to přikládám automaticky. :) Dobře děkuji moc za pomoc snad už to dotáhnu do funkčního konce :)