Hem E-böcker Specialeffekter och spelutveckling i Java Äntligen lite ACTION!

Specialeffekter och spelutveckling i Java(TM) - Äntligen lite ACTION!

av Anibal Wainstein

5.1.4 Äntligen lite ACTION!

Det sista steget är att lägga in laserstrålar och explosioner. Som explosion använder vi oss av en animation som lagras i Image-arrayen "explosion":

explosion[0] explosion[1] explosion[2]
explosion[3] explosion[4] explosion[5]

Jag vet att det inte ser så imponerande ut men när bilderna animeras så är det verklighetstroget. För att ge intrycket att skeppet exploderar så överlappar man de första explosionsbilderna med skeppet. När man kommer till den största bilden (index nr 3) så gömmer man bilden på skeppet. Samma explosion skall användas för alla skepp. För att hålla reda på explosionerna och skjutandet så måste vi lägga in två nya typer av variabler för varje skepp. Dessa variabler skall användas som räknare. Lägg in följande globala variabler efter deklarationen av "maxpirates".

//Arrayen "explosion" får hålla animationen
//på explosionen.
Image explosion[];

//Nya variabler måste läggas in för skeppen
//som indikerar om dom har exploderat och om
//dom skjuter eller inte.
//Följande är specifika för polisskeppet.
int exploding=0;
int shooting=0;

//Dessa är specifika för piratskeppen.
int pirateexploding[];
int pirateshooting[];

För att ladda in explosionsanimationen lägger vi in dessa rader precis innan "try" uttrycket och efter for-slingan som laddar in piratskeppen:

//Explosionsbilderna laddas in och läggs
//på position 11 i spåraren.
explosion=new Image[6];
for (int i=0; i<6; i++)
{
    explosion[i]=getImage(getDocumentBase(),"explosion"+i+".gif");
    tracker.addImage(explosion[i],i+11);
}


Ett tips! Kopiera koden för piratanimationen och ändra "pirateship" till "explosion". Glöm inte att också initiera "pirateexploding" och "pirateshooting". Man kan lägga in raderna efter initieringen av de andra pirate-arrayerna:

pirateexploding=new int[maxpirates];
pirateshooting=new int[maxpirates];

Nu ska ditt skepp kunna skjuta på piraterna varje gång du klickar på musknappen. För att genomföra detta måste man sätta "shooting" variabeln i mouseDown() metoden. Den här variabeln kommer att användas som räknare och den reglerar hur länge laserstrålen skjuter. För att inte göra det för lätt sätter vi den till 5:

public boolean mouseDown(Event e, int x, int y)
{
    //Om spelaren klickar på musknappen
    //så ska skeppet skjuta under 5 bildrutor.
    shooting=5;
    return true;
}


Laserstrålen hos polisskeppet ritas ut med följande metod:

public void drawPoliceLaser()
{
    //Använd blå färg för
    //lasern.
    bufferg.setColor(Color.blue);

    //Beroende på hur skeppet svänger så
    //måste vi rita laserstrålarna med
    //olika avstånd från centrum.
    //Dom ritas tvärs över skärmen.
    if (v==0)
    {
        bufferg.drawLine(50,py-26,400,py-26);
        bufferg.drawLine(50,py+26,400,py+26);
    }
    if (v/2==1 || v/2==-1)
    {
        bufferg.drawLine(50,py-24,400,py-24);
        bufferg.drawLine(50,py+24,400,py+24);
    }
    if (v/2==2 || v/2==-2)
    {
        bufferg.drawLine(50,py-15,400,py-15);
        bufferg.drawLine(50,py+15,400,py+15);
    }
    shooting--;
}

Det stora problemet med att rita ut laserstrålen är att lasern måste se ut som om den kommer från skeppets kanoner. När skeppet svänger så måste ju strålarna "svänga med". Detta kan man simulera genom att helt enkelt minska avståndet mellan de båda laserstrålarna. Eftersom vi har tre "svänglägen" för skeppet, så använder vi tre if-satser som ritar ut strålarna med rätt avstånd. Laserstrålarna ritas ut från kanonernas mynning och tvärs över appletskärmen. När drawPoliceLaser() anropas dekrementeras också räknaren "shooting". Det finns en drawPirateLaser() metod också:

public void drawPirateLaser(int n)
{
    //Använd röd färg för
    //lasern.
    bufferg.setColor(Color.red);

    //Beroende på hur skeppet svänger så
    //måste vi rita laserstrålarna med
    //olika avstånd från centrum.
    //Strålarna ritas från piratskeppets
    //mynning piratepx[n] mot vänstra
    //kanten på appletskärmen.
    if (v==0 || v/2==1 || v/2==-1)
    {
        bufferg.drawLine(0,piratepy[n]-8,piratepx[n],piratepy[n]-8);
        bufferg.drawLine(0,piratepy[n]+8,piratepx[n],piratepy[n]+8);
    }
    if (v/2==2 || v/2==-2)
    {
        bufferg.drawLine(0,piratepy[n]-3,piratepx[n],piratepy[n]-3);
        bufferg.drawLine(0,piratepy[n]+3,piratepx[n],piratepy[n]+3);
    }
    pirateshooting[n]--;
}


Eftersom piratskeppens kanoner ligger nära varandra så är det bara när skeppet svänger som mest som laserstrålarna måste ändras. Här används "pirateshooting[n]" som räknare för hur länge lasern skall skjuta. Lyckligtvis skjuter inte piratskeppen lika länge som ditt skepp. Raderna som ger eldkraft åt polisskeppets laser ser ut så här:

//Om polisskeppet skjuter så rita ut laserstrålarna.
if (shooting>0)
{
    drawPoliceLaser();

    //Kolla om lasern träffade några pirater
    //och kontrollera också att piratskeppet
    //inte redan har exploderat
    //(pirateexploding[i]==0).
    for (int i=0; i<maxpirates;  i++)
    {
        if (pirateexploding[i]==0 && piratepy[i]-py>-20 && piratepy[i]-py<20)
            pirateexploding[i]=6;
    }
}

If-satsen kollar om "shooting" är satt. Om den är det så anropas drawPoliceLaser() så att lasern ritas ut. Nu måste man kolla om skeppet har träffat eller inte. For-slingan går igenom piratskeppens y-position och tittar om de är i samma vertikala område som polisskeppet. Om ett piratskepp är inom 20 pixlar från polisskeppets vertikala position och inte redan har exploderat så är det träffat och då sätts explosionsräknaren för piratskeppet. Räknaren sätts till 6 eftersom det finns 6 bildrutor i animationen för explosionen. Raderna för piratskeppen nästan identiska förutom att man här måste försäkra sig om att piratskeppet är synligt på skärmen:

for (int i=0; i<maxpirates; i++)
{
   //Rita ut lasern om piraten skjuter och om
   //han syns på skärmen.
   if (pirateshooting[i]>0 && piratepx[i]<360)
   {
        drawPirateLaser(i);

        //Kolla om piraten träffade, dvs om han
        //är på ungefär samma höjd som polisskeppet
        //när han skjuter. Träffade han så spräng
        //polisskeppet. Kontrollera också att skeppet
        //inte redan har exploderat (exploding==0).
        if (exploding==0 && piratepy[i]-py>-20 && piratepy[i]-py<20)
            exploding=6;
    }
}

Återvänder vi till polisskeppet så ser vi att nästa if-sats ritar ut explosionen:

//Om skeppet exploderar så rita ut
//explosionsanimationen. Centrera explosionen kring
//skeppet.
if (exploding>0)
{
    //Rita ut polisskeppet om man ligger under
    //tredje explosionsrutan
    if (exploding>3) bufferg.drawImage(policeship[2+v/2],0,-55+py,this);

    //Rita explosionen ovanför skeppet.
    bufferg.drawImage(explosion[6-exploding],-18,py-50,this);

    //Byt till nästa explosionsbildruta.
    exploding--;

    //Om explosionen är slut så rensa
    //hela skärmen på piratskepp och starta om.
    if (exploding==0)
    {
        for (int i=0; i<maxpirates; i++)
            initPirateShip(i);
    }
}
//Polisskeppet får bara ritas ut här om
//den inte håller på att explodera.
if (exploding==0) bufferg.drawImage(policeship[2+v/2],0,-55+py,this);

Som jag sa tidigare måste vi rita ut skeppet under halva explosionen så att det ger ett realistiskt intryck. Eftersom "exploding" räknaren räknar nedåt så skriver vi "6-exploding" som index för arrayen så att animationen börjar på första bildrutan. När "exploding" har räknat klart, rensas skärmen och alla piratskepp initieras om genom att anropa initPirateShip() för varje skepp. Detta görs för att markera att polisskeppet sprängdes. Till sist används den sista raden för att som vanligt rita ut piratskeppet när explosionsanimationen inte är igång. För piratskeppen ser raderna ut så här:

for (int i=0; i<maxpirates; i++)
{
    //Om skeppet exploderar så rita ut
    //explosionsanimationen. Centrera explosionen
    //kring skeppet.
    if (pirateexploding[i]>0)
    {
        //Rita ut bilden på skeppet om
        //explosionen inte har kommit
        //längre än bildruta 3.
        if (pirateexploding[i]>3) bufferg.drawImage(pirateship[2+piratev[i]/2]
            ,piratepx[i],-25+piratepy[i],this);

        //Rita explosionen.
        bufferg.drawImage(explosion[6-pirateexploding[i]]
            ,piratepx[i]-18,piratepy[i]-50,this);

        //Byt till nästa explosionsbildruta.
        pirateexploding[i]--;

        //Om explosionen är slut så
        //"skapa ett nytt skepp".
        if (pirateexploding[i]==0) initPirateShip(i);
    }

    //Piratskeppet skall inte ritas HÄR om
    //den håller på att explodera.
    if (pirateexploding[i]==0) bufferg.drawImage(pirateship[2+piratev[i]/2]
        ,piratepx[i],-25+piratepy[i],this);
}

Det som skiljer här är att när piratskeppet exploderar så anropas initPirateShip() för skeppet så att det flyttas ut och därmed ger intrycket att det är ett nytt skepp som kommer tillbaka mot spelaren. Raderna som hanterar skeppens explosioner och skjutande samlas i metoderna drawPolice() och drawPirates():

public void drawPolice()
{
    if (shooting>0)
    {
        drawPoliceLaser();

        for (int i=0; i<maxpirates;  i++)
        {
            if (pirateexploding[i]==0 && piratepy[i]-py>-20 && piratepy[i]-py<20)
                pirateexploding[i]=6;
        }
    }
    if (exploding>0)
    {
        if (exploding>3) bufferg.drawImage(policeship[2+v/2],0,-55+py,this);
        bufferg.drawImage(explosion[6-exploding],-18,py-50,this);
        exploding--;
        if (exploding==0)
        {
            for (int i=0; i<maxpirates; i++)
                initPirateShip(i);
        }
    }
    if (exploding==0) bufferg.drawImage(policeship[2+v/2],0,-55+py,this);
}

public void drawPirates()
{
    for (int i=0; i<maxpirates; i++)
    {
        if (pirateshooting[i]>0 && piratepx[i]<360)
        {
            drawPirateLaser(i);
            if (exploding==0 && piratepy[i]-py>-20 && piratepy[i]-py<20)
                exploding=6;
        }
        if (pirateexploding[i]>0)
        {
            if (pirateexploding[i]>3) bufferg.drawImage(pirateship[2+piratev[i]/2]
                ,piratepx[i],-25+piratepy[i],this);
            bufferg.drawImage(explosion[6-pirateexploding[i]]
                ,piratepx[i]-18,piratepy[i]-50,this);
            pirateexploding[i]--;
            if (pirateexploding[i]==0) initPirateShip(i);
        }
        if (pirateexploding[i]==0) bufferg.drawImage(pirateship[2+piratev[i]/2]
            ,piratepx[i],-25+piratepy[i],this);
    }
}

Observera att for-slingorna för "pirateshooting" och "pirateexploding" har slagits ihop. Det som fattas nu är en rad som ser till att "pirateshooting" variablerna sätts. Dvs raden som får piratskeppen att skjuta. För polisskeppet använde vi mouseDown(). Men piratskeppen styrs ju slumpmässigt från movePirateShips() metoden, därför lägger vi in en rad som slumpmässigt med 1 % chans per bildruta aktiverar "pirateshooting". Lägg in följade rad efter den sista if-satsen i for-slingan som finns i movePirateShips() metoden:

//Piratskeppen kommer att skjuta med
//1% chans för varje bildruta.
if (Math.random()<0.01) pirateshooting[i]=3;

Nu ska vi bara ändra i run() metoden så att de nya rutinerna anropas:

public void run()
{
    while (true)
    {
        movePoliceShip();
        movePirateShips();

        groundposition-=2;
        if (groundposition<-600) groundposition=0;
        bufferg.drawImage(ground,groundposition,0,this);
        if (groundposition<-200)
            bufferg.drawImage(ground,(groundposition+600),0,this);

        //När marken är utritat så är det dags
        //att rita ut skeppen och laserstrålarna.
        drawPolice();
        drawPirates();

        update(getGraphics());
        try {Thread.sleep(sleeptime);}
        catch(InterruptedException e) {}
    }
}



Nu ser det lite prydligare ut i run(). Vad som händer är att först flyttas skeppen och marken ritas ut. Till sist ritas laserstrålarna, explosionerna och skeppen ut med drawPolice() och drawPirates() metoderna.

Efter mycket möda är nu spelet spelbart så det är dags att ta en titt på shoot'em up spelet SpacePirates!

Det här räcker nog för den här gången. I senare kapitel kommer vi att snygga upp spelet ännu mer genom att lägga in poängfunktioner, texter och ljudeffekter. Men nästa kapitel handlar om något annat, för då går vi igenom länkar och menyer.

 

 

Nästa sida >>