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 >>
|