Tag Archives: Thread

Liikkuva 2D-grafiikka Androidilla

Ensimmäinen asia, mitä lähes jokainen aloitteleva ohjelmoija haluaa tehdä on oma peli. Tämän toteuttamiseen tarvitaan lähes välttämättä liikkuvien grafiikkojen piirtämistä. Tässä artikkelissa esitetään Androidin 2D-grafiikkojen piirtäminen kankaalle. Käymme myös läpi hiukan yksinkertaista säikeiden käyttöä ja säieturvallisuutta.

Androidille 2D-grafiikkojen piirtämiseen tarvitaan näkymä, jolle voi piirtää. Toteutamme tässä esimerkissä oman versiomme SurfaceView:stä, jolle piirtäminen tapahtuu käytännössä antamalla SurfaceView:lle kankaan onDraw()-metodissa.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    starList = new ArrayList<StarFallActivity.Star>();

    setContentView(new StarSurface(this));
}

class StarSurface extends SurfaceView implements SurfaceHolder.Callback {
    private GameThread gameThread;
    private GraphicThread graphicThread;

public StarSurface(Context context) {
    super(context);
    getHolder().addCallback(this);
    gameThread = new GameThread(this);
    graphicThread = new GraphicThread(getHolder(), this);
}

@Override
public void onDraw(Canvas canvas) {
    canvas.drawColor(Color.BLACK);
    synchronized (starList) {
        for(Star star : starList) {
            canvas.drawBitmap(star.getImage(), star.getX(), star.getY(), null);
        }
    }
}
...

Pidämme kirjaa rajoittamattomasta määrästä putoavia objekteja ja piirrämme ne kankaalle niiden sisällään pitämän sijainnin muukaan onDraw()-metodissa. Resursseissa olevan kuvan piirtäminen valmiista kuvaresurssista ei vaadi, kuin resurssin bittikartaksi muuttamisen ja X- ja Y-koordinaatit, mihin kuva pintanäkymässä tulee.

BitmapFactory.decodeResource(getResources(), R.drawable.icon);

Säikeet ja säieturvallisuus

Pidämme objektien liikuttamisen ja ruudunpäivityksen erillisissä säikeissä ja laitamme säikeet odottamaan, kun niitä ei tarvita. Säikeiden käytössä täytyy myös ottaa huomioon samanaikaisten operaatioiden turvallisuus. Koska pidämme liikuvat objektit yhdessä listassa täytyy lista asettaa synkronoiduksi, niin että siihen voi kohdistua vain yksi operaatio kerrallaan. Tämä täytyy tehdä jokaisessa paikassa, missä listaa käsitellään. Esimerkiksi pelin osia liikuttelevassa säikeessä:

synchronized (starList) {
    for (Iterator<Star> iterator = starList.iterator(); iterator.hasNext();) {
        Star star = iterator.next();
        star.move();
        if(star.getY() >= maxHeight) {
            iterator.remove();
        }
    }
}

Koska poistamme listasta putoavan tähden, jos se menee ruudun ulkopuolelle joudumme käyttämään iteraattoria, koska listasta poistaminen sen läpikäynnin aikana ei suoraan ole mahdollista.

Säikeissä tarvitsee pääasiassa vain metodit säikeen käynnissä olon asettamiseen ja run()-metodin oma toteutus, missä säikeen suoritusaikainen toiminta tapahtuu. Sijettä ei tule tappaa stop()-metodilla, sillä se voi jättää ohjelmistosi outoon tilaan. Tämän sijasta tulisi käyttää run()-metodissa while lausetta joka toistuu niin kauan, kuin säikeen sisäinen while ehto muuttuu epätodeksi ja run()-metodi pääsee suorittamaan loppuun. Säikeet joiden run()-metodi on päässyt suoriutumaan kokonaan tapetaan automaattisesti järjestelmän toimesta.

Jos et halua säikeesi vievän kaikkea prosessointitehoa aseta säie jokaisen while luupin lopuksi odottamaan jonkin aikaa. Piirtopintaa ei esimerkiksi kannata päivittää useampaa, kuin 60 kertaa sekunnissa ja tämäkin on yleensä liikaa. Säikeen voi myös pakottaa käynnistymään kutsumalla sille notify()-metodia.

Lataa esimerkki tästä.
Voit nyt tehdä esimerkiksi oman versiosi kitarasankarista ja pudottaa ruudun yläpäästä kuvia ja tarkastella osuuko käyttäjä niihin ennen, kuin ne putoavat ulos ruudulta.