Animacije u Javi

Lekcija 5 od 8 · Java – grafičke aplikacije

Postoji više načina da se kreiraju animacije u programskom jeziku Java. U nastavku će biti pokazano kako se kreira animacija upotrebom tajmera tj. objekta klase Timer iz paketa javax.swing. Animacija je objašnjena kroz dva primera.

Prvi je animacija slobodnog pada, dok je drugi zapravo nastavak aplikacije kružnog kretanja.

Početak primera je na strani Grafika u Javi - primer

Timer

U prethodnim primerima prikazano je crtanje oblika po panelu bez ikakve animacije. Da bi napravili animaciju da se određen oblik kreće u dvodimenzionoj ravni panela potrebno je uvesti objekat klase Timer koji se nalazi u paketu javax.swing. Objekat klase Timer proizvodi događaj koji se ponavlja na zadatom vremenskom interval. Kao reakcija na taj događaj svaki put se poziva metoda actionListener slično kao kod klika dugmeta, samo što se ovde događaj ponavlja.

Ako bi se položaj određenog geometrijskog oblika npr. kruga pomerio za određenu vrednost dx u x pravcu pri svakom pozivu metode actionPerformed tj. za zadati vremenski interval i taj krug ponovo iscrtao dobili bi smo iluziju kretanja tog kruga.

Tajmer je potrebno prvo deklarisati i to obično kao atribut za klasu:

Timer time;

Zatim je potrebno kreirati tajmer:

int interval = 100; // u milisekundama
timer = new Timer(interval, osluskivac)

Konstruktor tajmera zahteva dva parametra: vremenski interval u milisekundama na koji se ponavlja događaj tajmera i objekat koji implementira interfejs ActionListener i koji predstavlja osluškivač događaja tajmera. Ovakav jedan objekat je sličan osluškivaču kod dugmeta, a to je ranije objašnjeno.

Da bi se tajmer pokrenuo potrebno ga je pokrenuti:

time.start();

Tajmer se može po potrebi i zaustaviti:

time.stop();

Video lekcije

Video 1: Tajmer Animacija

Kreiranje jednostavne animacije upotrebom tajmera. Objašnjeno na primeru klizanja tela po ravnoj podlozi.

Video 2: Animacija slobodnog pada

Animacija slobodnog pada tela na zemlju pomoću tajmera. Aplikacija je napravljena korišćenjem NetbeansIDE na programskom jeziku java

Video tutorijal: Kreiranje animacije kosog hica kuglica od početka

Izrada animacija u JAVI - 1.deo

Izrada animacija u JAVI - 2.deo

Izrada animacija u JAVI - 3.deo

Izrada animacija u JAVI - 4.deo

Izrada animacija u JAVI - 5.deo

Kreiranje 2D igrice u javi: "Igranje GOLF-a"

Kako se kreira 2d igrica u javi zasnovana na prethodno izrađenoj animaciji kosog hica pročitajte na sledećoj web strani:

Kako se kreira 2d igrica u javi zasnovana na prethodno izrađenoj animaciji kosog hica pročitajte na sledećoj web strani:

Kreiranje 2D igrica u JAVI

PRIMER: Slobodan pad

Tekst zadatka:

Simulirati slobodan pad tela oblika kruga

Rešenje: Objekat koji predstavlja telo biće opisan klasom Telo koja nasleđuje Ellipse2D.Double:

public class Telo extends Ellipse2D.Double {
    Color boja = Color.BLUE;
    double m;
    public double v = 0;

    public Telo(double x, double y, double w, double h) {
        super(x, y, w, h);
    }

    public Telo() {
    }
}
Slika 1: Kreiranje animacija u Javi pomoću tajmera. Klasa Telo.

Konstruktor sa parametrima dobija 4 parametra koji definišu pravougaonik u koji je upisana elipsa tj. krug u našem slučaju.

Klasa Okruzenje čuva podatke o okruženju: rastojanje putanje slobodnog pada x, početni položaj H0, poluprečnik tela i tekući položaj tela po y osi.

public class Okruzenje {
    public double x = 10; // rastojanje putanje slobodnog pada
    public double H0 = 20; // početni položaj
    public double r = 50; // poluprečnik tela
    public double h; // tekuća visina

    public Okruzenje() {
        h = H0;
    }
}
Slika 2: Kreiranje animacija u Javi pomoću tajmera. Klasa Okruzenje.

Objekat po kome se iscrtava animacija pripada klasi Animacija koja nasleđuje JPanel:

public class Animacija extends JPanel {
    Rectangle2D tlo;
    Telo telo;

    public Animacija() {
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        super.paint(g);

        g2.setColor(Color.red);
        g2.fillRect(0, 0, getWidth(), getHeight());
        g2.setColor(new Color(20, 200, 20));
        if (tlo != null) {
            g2.fill(tlo);
        }
        g2.setColor(Color.BLACK);
        if (telo != null) {
            g2.fill(telo);
        }
    }

    void kreiraj() {
        tlo = new Rectangle2D.Double(0, this.getHeight() - 100,
            this.getWidth(), 100);
    }
}
Slika 3: Kreiranje animacija u Javi pomoću tajmera. Panel animacije.

Na crtežu su kreirana dva objekta telo klase Telo i tlo klase Rectangle2D.Double, a to je klasa u paketu java.awt.geom. Ovde y koordinata pravougaonika zavisi od visine prozora koja se dobija metodom getHeight() objekta tekuće klase this, a širina tla je identična sa širinom panela, gde se trenutna vrednost širine može dobiti metodom getWidth().

Metoda paint će se pozivati na određen broj milisekundi na koliko je podešen tajmer koji je kreiran i startovan u glavnoj klasi u okviru konstruktora:

public static void main(String[] args) {
    SlobodanPadFrame app = new SlobodanPadFrame();
}

public SlobodanPadFrame() throws HeadlessException {
    // glavni prozor aplikacije
    fr = new JFrame("Slobodan pad");
    fr.setSize(600, 500);
    fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // kreira i startuje tajmer
    time = new Timer(dt, this);
    time.start();

    // kreira panel kontejner i povezuje sa glavnim prozorom
    JPanel contentPane = new JPanel();
    contentPane.setLayout(new BorderLayout(2, 2));
Slika 4: Kreiranje animacija u Javi pomoću tajmera. Prozor animacije.

Na slici je prikazan deo glavne klase u kojoj se nalazi main metoda, a to je i početak aplikacije. U njoj je kreiran objekat app klase SlobodaPadFrame tj. klase u kojoj se nalazi main metoda. pri kreiranju objekta poziva se konstruktor i dalje se izvršavaju naredbe u konstruktoru. Na slici je prikazan deo gde se kreira glavni prozor i tajmer, dok se u nastavku kreiraju svi ostali objekti:

time = new Timer(dt, this);
time.start();

// kreira panel kontejner i povezuje sa glavnim prozorom
JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout(2, 2));
fr.setContentPane(contentPane);

// kreira panel za animaciju i smešta u centralni deo
// kontejnera
animacijaPan = new Animacija();
contentPane.add(animacijaPan, BorderLayout.CENTER);

// kreira kontrolni panel i smešta u istočni (desni) deo
// kontejnera
Panel controlPanel = new Panel(new BorderLayout(2, 2));
contentPane.add(controlPanel, BorderLayout.EAST);

okruzenje = new Okruzenje();
telo = new Telo(okruzenje.x, okruzenje.H0, okruzenje.r,
    okruzenje.r);
animacijaPan.telo = telo;
animacijaPan.setSize(600, 500);
animacijaPan.kreiraj();

fr.setVisible(true);
}
Slika 5: Kreiranje animacija u Javi pomoću tajmera. Glavna metoda animacije.

Objekat koji predsavlja panel za animaciju, telo i objekat koji čuva podatke o okruženju su deklarisani kao atributi za klasu. Telo logički pripada panelu za animaciju, pa se zato referenca objekta tela čuva unutar objekta animacijaPan kao njegov atribut:

animacijaPan.telo = telo;

Atributi ove klase su:

public class SlobodanPadFrame implements ActionListener {
    JFrame fr;
    Timer time;
    private Okruzenje okruzenje;
    private Telo telo;
    private final Animacija animacijaPan;
    private double g = 9.81;
    int dt = 10; // ms
    int razmera = 60;

    public static void main(String[] args) {
        SlobodanPadFrame app = new SlobodanPadFrame();
    }

    public SlobodanPadFrame() throws HeadlessException {
        // glavni prozor aplikacije
        fr = new JFrame("Slobodan pad");
        fr.setSize(600, 500);
        fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Slika 6: Kreiranje animacija u Javi pomoću tajmera.Klasa SlobodanPadFrame.

Kao atributi ove klase osim već pomenutih, tu su i g-gravitaciono ubrzanje, dt-vremenski interval na koji se poziva događaj tajmera i razmera za iscrtavanje.

Klasa SlobodanPadFrame implementira interfejs ActionListener, tako da se objekat te klase koristi i za osluškivanje događaja tajmera i klika dugmeta. Ovaj objekat(this) je prosleđen konstuktoru tajmera(vidi prethodne slike). U ovom primeru kontrolni panel nije kreiran, samo je ostavljena mogućnost njegovog kreiranja, što se čitaocu ostavlja za vežbu.

Metoda actionPerformed izgleda:

@Override
public void actionPerformed(ActionEvent e) {
    double y, dy;

    telo.v = telo.v + ((g * dt) / 1000.0); // v = v + g·Δt  (Δt u sekundama)
    dy = telo.v * dt * razmera / 1000.0; // pomeraj u pikselima
    y = telo.y + dy;

    if (y < animacijaPan.getHeight() - (telo.getHeight() / 2) - 50) { // telo još nije palo na tlo

        animacijaPan.telo.setFrame(telo.x, y,
            telo.getWidth(), telo.getHeight());
        animacijaPan.repaint();
    }
}
}
Slika 7: Kreiranje animacija u Javi pomoću tajmera. Metoda actionPerformed.

Prvo se izračunava promenjena vrednost brzine u intervalu dt, a za gravitaciono ubrzanje g. Pošto je vrme u s, a dt u milisekundama potrebno je podeliti dt sa 1000.

Zatim se računa promena koordinate y u tom intervalu dt. Treba napomenuti da je rezultat tačniji što je dt manje. Da bi se telo premestilo na novi položaj koristi se metoda setFrame gde su kao argumenti prosleđeni x,y,w i h pravougaonika(u ovom slučaju kvadrata) u koji je elipsa(krug u ovom slučaju)upisana.

Pozivom metode repaint poziva se paint metoda koja ponovo iscrtava sve objekte, pa i telo, ali sada u novom položaju.

PRIMER: Kružno kretanje animacija

Tekst zadatka:

Napraviti simulaciju kružnog kretanja u ravni. Omogućiti unos poluprečnika putanje kružnog kretanja kao i ugaone brzine. Na osnovu unetih vrednosti za r i W izračunati: V, ac i prikazati vrednosti u polju za izveštaj

Rešenje:

Ovo je primera kružnog kretnja prikazanom u prethodnom poglavlju gde je urađena grafika za kružno kretanje, kao i unos podataka o ugaonoj brzini i poluprečniku putanje klikom na dugme za unos. U nastavku treba uvesti tajmer i kao reakciju događaja tajmera prvo izračunati promenu linijske brzine i položaja čestice pri promeni vremena dt jednaku intervalu između dva događaja tajmera. Česticu treba premestiti na novi položaj koristeći metodu setFrame, a zatim ponove sve objekte iscrtati. Za promenu vektora brzine(prikazanog samo linijom) treba koristiti objekat klase affineTransform koji ima metode za rotaciju i translaciju nekog oblika. Pri pokretanju aplikacije prikazuje se inicijalna grafika:Više o kružnom kretanju i prikaz simulacije kružnog kretanja kreirane pomoću alata Easy Java Simulation(EJS) možete pogledati na sledećoj strani:

kosi-hitac.herokuapp.com/Kruzno_Kretanje_MTPicture

Animacija kružnog kretanja. Prikaz prozora.
Slika 8: Animacija kružnog kretanja. Prikaz prozora.

Sa leve strane se nalazi nacrtana putanja, čestica i vektor brzine za podrazumevane vrednosti poluprečnika i ugaone brzine. Sa desne strane je panel za unos na kome se nalaze polja za unos poluprečnika r=0.5m i ugaone brzine W=1, kao i dugmad za unos, za pokretanje animacije i za zaustavljanje animacije. Kod koji ovo izvršava već je prikazan i objašnjen u prethodnom poglavlju.

Ako se u polja za unos unesu vrednosti za r i W npr r=0.8m i W=0.3s^-1 i klikne na dugme unos vrednosti r, W i brzine v biće ažurirane, a takođe i crtež u skladu sa promenjenim vrednostima:

Animacija kružnog kretanja. Prikaz prozora-kompletno.
Slika 9: Animacija kružnog kretanja. Prikaz prozora-kompletno.

Vidi se sada povećanje poluprečnika, kao i povećanje intenziteta brzine. I ovaj deo je objašnjen u prethodnom poglavlju.

Klikom na bilo koje dugme, kao i kad se desi događaj tajmera poziva se metoda ActionListener:

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource().equals(unosBtn)) {
        // pamti vrednosti r i v pre ažuriranja
        double rStaro = crtez.r;
        double vStaro = crtez.cestica.v;

        // skida novu vrednost r sa polja za unos r na panelu za
        // unos i računa promenu poluprečnika
        String rStr = rTF.getText();
        crtez.r = Double.parseDouble(rStr);
        double dr = crtez.r - rStaro;
        /* skida novu vrednost ugaone brzine
* sa polja za unos W (ug. brz) na panelu za
* unos podataka */
        String wStr = WTF.getText();
        crtez.cestica.W = Double.parseDouble(wStr);

        crtez.promeniParametreOkruzenja();
        double dv = crtez.cestica.v / vStaro;
        crtez.promeniPutanju();
        crtez.translirajVektorBrzine(dr);
        crtez.povecajIntenzitetVektorBrzine(dv);
        // prazni polja za unos
        rTF.setText("");
        WTF.setText("");

        izvestajTA.append("Ugaona brzina: " + crtez.cestica.W + "rad/s\n");
        izvestajTA.append("ac=" + crtez.cestica.ac + "m/s^2");
    } else if (e.getSource().equals(pocetakBtn)) {
Slika 10: Animacija kružnog kretanja. Prikaz metode actionPerformed.

Klikom na dugme za unos skidaju se sa polja za tekst vrednosti poluprečnika r i ugaone brzine W koje je uneo korisnik. Prethodne vrednosti se pamte u promenljivama rStaro i vStaro. Odnos nove i stare vrednosti brzine, označen sa dv koristi se u metodi koja srazmerno povećeva liniju na grafici kojom je brzina predstavljena. Ova vrednost se prosleđuje metodi povecajIntenzitetVectoraBrzine kao parametar. Osim toga, pozivaju se 4 metode:metoda promeniPutanju, koja menja putanju na grafici(povećavaju joj radijus) u skladu sa povećanjem radijusa r, metoda promeniParametreOkruzenja koja menja položaj čestice, njenu brzinu v i centripertalno ubrzanje ac , metoda povecajIntenzitetVectoraBrzine koja povećava dužinu linije, koja na grafici predstavlja brzinu, u skladu sa povećanim intenzitetom brzine. Polja za tekst se onda prazne, a na polje za izveštaj(JTextArea) se upisuju vrednosti ugaone brzine i centripetalnog ubrzanja.

Klikom na dugme pocetakBtn startuje se tajmer koji se kreira u metodi kreirajTajmer, a koja se poziva u konstruktoru, dok se klikom na dugme zaustaviBtn tajmer zaustavlja:

}
else if (e.getSource().equals(pocetakBtn)) {
    time.start();
}
else if (e.getSource().equals(zaustaviBtn)) {
    time.stop();
}
// svaki otkucaj tajmera: novi trenutak i novi ugao
double dfi, dt;
dt = interval / 1000.0; // interval tajmera u sekundama
crtez.t = crtez.t + (dt);
dfi = -crtez.cestica.W * dt; // promena ugla za vreme dt
crtez.fi = crtez.fi + dfi;
crtez.promeniPolozaj();
crtez.promeniCrtezVektora(dfi);

repaint();
}

private void kreirajTajmer() {
    time = new Timer(100, this); // poziva actionPerformed svakih 100 ms
}
Slika 11: Animacija kružnog kretanja. Prikaz metode actionPerformed-nastavak.

Ostatak metode actionPerformed, van proširene if- else if-else naredbe se odnosi na događaj tajmera. Prvo se određuje interval vremena dt(promenljiva interval predsavlja vremenski interval u milisekundama) u sekundama na koji se ponavlja događaj tajmera i ažurira promenljiva t(vreme) koja se čuva u objektu crteža. Pri promeni vremena za dt menja se ugao čestice u odnosu na prethodni za dfi=W*dt. Znak” minus“ je zbog toga što se za grafički prikaz, kao pozitivan tretira ugao u smeru kazaljke na satu, što je suprotno smeru ugaone brzine. Zatim se izračunava nova vrednost promenljive fi, koja predstavlja ugao položaja čestice. Metoda promeniPoložaj pomera objekat za iscrtavanje , a koji predstavlja česticu u skladu sa novom vrednošću fi. Promena ovog ugla uzrokuje pomeranje vektora brzine na novi položaj, što se rešava metodom promeniCrtezVektora, kojoj se kao parametar prosleđuje promena ugla dfi. Sve ove metode pripadaju objektu crtez, pa su zato kreirane u klasi Crtez:

/*
* Menja položaj čestice za vremenski pomak dt
*/
void promeniPolozaj() {
    double novicentarCesticeX = centar.getCenterX() +
        r * razmera * Math.cos(fi);
    double novicentarCesticeY = centar.getCenterY() +
        r * razmera * Math.sin(fi);
    double novoX = novicentarCesticeX - (cestica.width / 2);
    double novoY = novicentarCesticeY - (cestica.height / 2);
    cestica.setFrame(novoX, novoY, cestica.width, cestica.height);
}

/*
* Menja vektor brzine u novi vektor premešten
* za ugao dfi
*/
void promeniCrtezVektora(double dfi) {
    transf.setToRotation(dfi, centar.getCenterX(),
        centar.getCenterY());
    brzinaVektor = transf.createTransformedShape(brzinaVektor);
}

/*
* Translacija vektora brzine za dr
*/
void translirajVektorBrzine(double dr) {
    AffineTransform aft = new AffineTransform();
    // ... (nastavak metode)
Slika 12: Animacija kružnog kretanja. Prikaz ostalih metoda.

Proveri znanje

Otvori kviz

Kviz: Animacije u Javi

1. Čemu služi javax.swing.Timer u animaciji?




2. Šta treba uraditi u svakom koraku animacije?




3. Kod slobodnog pada brzina se u svakom koraku menja kao:




4. Ako je period tajmera 20 ms, koliko je slika u sekundi?