Specialeffekter och spelutveckling
i Java(TM) - Ett litet ritblock
av Anibal Wainstein
5.0.3 Ett litet ritblock (mouseDrag(),
mouseDown() och mouseUp())
En
bra tillämpning för musmeddelanden är ritapplets.
Vi skall här använda oss av testet i föregående
avsnittet och göra ett litet ritblock. Det som är
annorlunda med det här ritblocket är att vi kommer
att blanda in animation. Innan vi börjar med appleten
skall vi dock skriva en metod som kan komma väl till
pass senare. Ibland kan det vara bra att kunna skapa kopior
av Image-objekt. Det dock ingen metod för detta så
vi får göra en egen. Titta på följande
metod:
public Image createCopy(Image img)
{
//createCopy() metoden skapar en likadan
//bild som den som specifieras.
//Först tar man reda på bredden och höjden för bilden.
int width=img.getWidth(this);
int height=img.getHeight(this);
//Först skapar vi en tom bild med samma dimensioner
//som bilden vi skall ha en kopia av.
Image result=createImage(width,height);
//Slutligen skapar vi en Graphics-objekt som pekar
//på den tomma bilden och ritar ut bilden som
//vi vill kopiera.
Graphics resultg=result.getGraphics();
resultg.drawImage(img,0,0,this);
return result;
}
Metoden
createCopy() tar en bild som argument och lämnar tillbaka
en bild. Den tar reda på bildens dimensioner med getWidth()
och getHeight() metoderna, skapar en tom bild och ritar ut
bilden som skall kopieras på den tomma bilden.
Nu
är det dags att göra vår ritblock. Idén
med appleten är att användaren skall kunna rita
med svart penna på ritblocket. När man vill rensa
så klickar man på vänstra kanten så
att sidan bläddras över. Animationen för sidbläddrandet
ser ut som följande och skapades med ett 3D-ritprogram:
Animationen
av ritblocket när man rensar skärmen.
För
att kunna visa animationen måste dock varje bild laddas
in för sig och sedan lagras i en bild-array. Vi börjar
med att titta på deklarationerna och init()-metoden:
//Variabeln "backgroundimage" pekar på
//bakgrundsbilden (bilden som man ritar på).
Image backgroundimage;
Graphics backgroundg;
//Image-arrayen "blockimages" innehåller
//de nödvändiga bilderna för ritblockets
//animation.
Image blockimages[];
Image penimage;
Image bufferimage;
Graphics bufferg;
//För att inte pennan skall skriva "hackigt"
//så måste vi lagra de gamla värderna för
//pennans position. Vi lägger därför till variablerna
//"oldpenx" och "oldpeny".
int penx,oldpenx;
int peny,oldpeny;
public void init()
{
Dimension d=size();
bufferimage=createImage(d.width,d.height);
bufferg=bufferimage.getGraphics();
//Vi måste ladda in 7 bilder på blocket
//för animationens skull. Vi använder oss
//därför av en for-slinga.
blockimages=new Image[7];
MediaTracker tracker=new MediaTracker(this);
for (int i=0; i<7; i++)
{
//"block0.jpg","block1.jpg" osv kommer att laddas
//in och lagras i arrayen "blockimages" samtidigt
//som de lagras hos spåraren (i ordning).
blockimages[i]=getImage(getDocumentBase(),"block"+i+".jpg");
tracker.addImage(blockimages[i],i);
}
penimage=getImage(getDocumentBase(),"pen.gif");
//Pennbilden läggs in sist i spåraren.
tracker.addImage(penimage,7);
try {tracker.waitForAll();}
catch(InterruptedException e)
{
System.out.println("Någonting stoppade inladdningen...");
}
//Vi måste ha en kopia av block0.jpg bilden
//ty den skall vi använda för att rita på.
//Samtidigt vill vi inte förstöra bilden eftersom
//den behövs när vi skall rensa skärmen.
backgroundimage=createCopy(blockimages[0]);
//Vi behöver en Graphics-objekt för
//för senare operationer.
backgroundg=backgroundimage.getGraphics();
//Pennfärgen får vara svart.
backgroundg.setColor(Color.black);
}
När
man gör en applet av den här typen måste man
tänka på hur muspekaren fungerar och hur ofta musmetoderna
anropas. Tyvärr är det så att metoderna för
förflyttning kommer inte att anropas för varje pixel
som muspekaren förflyttar sig. Anta att vi ritar punkter
för varje position där muspekaren finns. Punkterna
kommer inte att "hänga ihop".
Skillnaden
när man använder punkter (till vänster) och
linjer (till höger) när man ritar ut muspekarens
position.
Allting
blir mycket bättre om man däremot ritar linjer mellan
muspekarens positioner. Detta kan man göra om man lagrar
föregående muspositionen i "oldpenx"
och "oldpeny" variablerna som vi har deklarerat
ovan. Bilden "backgroundimage" kommer nu att användas
till att rita på. Den här bilden är en kopia
av den första blockbilden "block0.jpg" och
vi använder oss av Graphics-objektet "backgroundg"
för att kunna rita på bilden. Metoderna paint(),
mouseMove() och update() ser exakt likadana ut som i föregående
avsnittet, effekten med pennan som följer muspekaren
bevaras. Vi lägger till funktionalitet för att rita
genom att lägga in mouseDrag() metoden som anropas när
användare klickar och drar med musen.
public boolean mouseDrag(Event e, int x, int y)
{
//När användaren draggar så uppdateras "penx"
//och "peny" som vanligt men innan dess uppdateras
//"oldpenx" och "oldpeny". Dessa kommer nu att innehålla
//det gamla värdet.
oldpenx=penx;
oldpeny=peny;
penx=x;
peny=y;
//Nu ritar vi en streck mellan punkterna
//(oldpenx,oldpeny) och (penx,peny)
//Detta gör vi med Graphics-metoden drawline.
backgroundg.drawLine(oldpenx,oldpeny,penx,peny);
//Skärmen måste nu uppdateras så att bilden syns.
repaint(70);
return true;
}
Metoden
drawLine() i Graphics-klassen är ännu en rithjälpmedel
som i det här fallet ritar ut en linje mellan två
punkter. Nu skulle appleten nästan kunna vara färdig
men eftersom "oldpen" variablerna kommer att innehålla
pennans position, där användaren släppte musknappen,
kan detta leda till problem. Detta kan vara att en oönskad
linje uppstår mellan två områden där
man har ritat och där man har släppt musknappen
en gång. Därför måste man sätta
"oldpen" variablerna till samma värde på
"pen" variablerna så att detta förhindras.
Det görs i mouseDown() metoden:
public boolean mouseDown(Event e, int x, int y)
{
//När användaren klickar på musknappen måste
//"oldpenx" och "oldpeny" peka på det nya x och
//y värdena.
penx=x;
peny=y;
oldpenx=x;
oldpeny=y;
//Om användaren klickar på kanten av skärmen
//där så skall sidan bytas och
//skärmen rensas.
if (penx<18)
{
//Vi använder oss av en for-slinga
//för att rita ut de sju bilderna.
for (int i=0; i<7; i++)
{
//Vi använder paint() metoden för att
//rita ut bilderna så därför sätter
//vi "backgroundimage" att peka
//på de animerade bilderna och anropar paint()
//för varje bild med en 70ms tidsfördröjning.
backgroundimage=blockimages[i];
update(getGraphics());
try {Thread.sleep(70);}
catch(InterruptedException ex) {}
}
//Nu initierar vi "backgroundimage" igen
//som vi gjorde på init() metoden
//Så att vi kan rita på skärmen.
backgroundimage=createCopy(blockimages[0]);
backgroundg=backgroundimage.getGraphics();
backgroundg.setColor(Color.black);
//Skärmen måste till slut uppdateras
//med den första bilden.
update(getGraphics());
}
return true;
}
public boolean mouseUp(Event e, int x, int y)
{
//När användaren släpper på musknappen så
//nollställ "oldpen" variablerna.
oldpenx=0;
oldpeny=0;
return true;
}
Som
du ser ovan är en annan funktion som måste finnas
i mouseDown() metoden funktionen för animationen och
för att rensa på skärmen. Detta aktiveras
om användaren klickar i vänstra kanten på
ritblocket. Med andra ord om muspekarens x position (penx)
är mindre än 18 pixlar.
Bilden
visar klickområdet (i rött) där skärmen
rensas och ritblocksanimationen sätter igång.
Det
lättaste sättet att göra animationen är
att använda en for-slinga, där "backgroundimage"
sätts till att peka på de lagrade bilderna i "blockimages"
arrayen. För varje bild så uppdateras skärmen
och 70 millisekunder senare så kommer nästa bild.
Efter for-slingan så förbereds "backgroundimage"
och "backgroundg" så att man kan rita igen,
precis som vi gjorde innan i init() metoden. Slutligen så
lägger vi in mouseUp() metoden som får nollställa
oldpen variablerna. Klicka
här för att rita!
Nästa sida >>
|