Hem E-böcker Specialeffekter och spelutveckling i Java Ett litet ritblock 

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