Tenniskamp
Illustrasjon
Den viktigste bevegelsen i spillet er banen til ballen. Under ser du noen screen-shoots av hvordan to av de fire banene går. Dette er punkter som ligger i en array, og som er tegnet opp som en Bezier-kurve.
Josefine server
Josef slår tilbake
Komponentene
Komponentene jeg har brukt i dette prosjektet er bygget opp av en del
av de vanlige grafiske delene som OpenGL og gl4java har i
sitt bibliotek.
Som:
-
Kule:
glu.gluSphere( qd , 0.4f , 30 , 30 );
-
Sylinder:
glu.gluCylinder( qd , 0.1 , 0.1 , 0.7 , 20 , 20 );
-
Disk:
glu.gluDisk( qd , 0.1f , 0.4f , 20 , 20 );
-
Polygon:
gl.glBegin(GL_POLYGON); gl.glVertex3f(-0.5f, -0.0f, 0.5f); gl.glVertex3f( 0.5f, -0.0f, 0.5f); gl.glVertex3f( 0.5f, -0.0f, -0.5f); gl.glVertex3f(-0.5f, -0.0f, -0.5f); gl.glEnd();
-
Bezier:
float ctrlpoints[] = { -0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f, -0.5f }; gl.glMap2f(GL_MAP2_VERTEX_3,0.0f,1.0f,3,2,0.0f,1.0f,6,2,ctrlpoints); gl.glEnable(GL_MAP2_VERTEX_3); gl.glMapGrid2f(10, 0.0f, 1.0f, 10, 0.0f, 1.0f); gl.glEnable(GL_DEPTH_TEST); gl.glShadeModel(GL_FLAT); gl.glEvalMesh2(GL_LINE, 0, 10, 0, 10);
-
Resultat:
Som sagt så vil jeg gå ganske tett inn på hver enkelt komponent som er laget til kampen. Under følger en oversikt over dette.
[Josef, Josefine og dommeren] [Nettet] [Banen] [Racketen] [Skjørtet] [Stolen]
-
Josef, Josefine og dommeren
Josefine.javaJosef.javaReferee.javaHovedkomponentene i spillet er Josef og Josefine. Jeg har tatt utgangspunkt i den originale Josef, på sidene til Børre. Dommeren er også bygget opp etter dette prinsippet.
Personene er bygget opp med sylindere og kuler, hvor du kan rotere rundt alle vinkler i alle kuleleddene. I alle de tre person-klassene, har jeg en get-metode og en set-metode for hvert ledd. Jeg bruker på langt nær alle disse metodene, men hvis jeg senere vil utvikle bevegelsene, er det greit å ha. Alle vinklene i hvert ledd ligger i to-dimensjonale arrayer.
For eksempel:private float v_shoulder_left[] = {0.0f, 0.0f, 0.0f}; private float v_overarm_left[] = {0.0f, 0.0f, 0.0f}; private float v_underarm_left[] = {0.0f, 0.0f, 0.0f}; private float v_hand_left[] = {0.0f, 0.0f, 0.0f};
Så når jeg skal rotere på vinkler i armene bruker jeg bare set-metodene som ligger i de Josef.java, Josefine.java eller Referee.java.public void setv_overarm_right(float v[]) { v_overarm_right = v; } josef.setv_overarm_right(josef_overarm_right);
Måten personene er bygget opp på er for eksempel slik: (Her bygges det venstre benet til Josefine)//left leg gl.glPushMatrix(); gl.glRotatef(josefine.getv_hip_left()[1],0,1,0); gl.glRotatef(josefine.getv_hip_left()[0],1,0,0); gl.glRotatef(josefine.getv_hip_left()[2],0,0,1); glu.gluCylinder(qd,0.045*josefine.getsize(), 0.045*josefine.getsize(), 0.2*josefine.getsize(),20,20); gl.glTranslated(0.0f,0.0f,0.2f*josefine.getsize()); glu.gluSphere(qd,0.07f*josefine.getsize(),20,20); gl.glRotatef(josefine.getv_thigh_left()[0],1,0,0); gl.glRotatef(josefine.getv_thigh_left()[1],0,1,0); gl.glRotatef(josefine.getv_thigh_left()[2],0,0,1); glu.gluCylinder(qd,0.045*josefine.getsize(), 0.045*josefine.getsize(), 0.5*josefine.getsize(),20,20); gl.glTranslated(0.0f,0.0f,0.5f*josefine.getsize()); glu.gluSphere(qd,0.07f*josefine.getsize(),20,20); gl.glRotatef(josefine.getv_leg_left()[0],1,0,0); gl.glRotatef(josefine.getv_leg_left()[1],0,1,0); gl.glRotatef(josefine.getv_leg_left()[2],0,0,1); glu.gluCylinder(qd,0.045*josefine.getsize(), 0.045*josefine.getsize(), 0.45*josefine.getsize(),20,20); gl.glTranslated(0.0f,0.0f,0.45f*josefine.getsize()); glu.gluSphere(qd,0.07f*josefine.getsize(),20,20); gl.glRotatef(josefine.getv_foot_left()[0],1,0,0); gl.glRotatef(josefine.getv_foot_left()[1],0,1,0); gl.glRotatef(josefine.getv_foot_left()[2],0,0,1); glu.gluCylinder(qd,0.065*josefine.getsize(), 0.0*josefine.getsize(), 0.3*josefine.getsize(),20,20); gl.glPopMatrix();
-
Nettet
Nettet.java
Et nett må til på en tennisbane :)
Dette er to cylindere med en bezierflate mellom. Der de gulene punktene er tegnet ligger alle kontrollpunktene. Når ballen etterhvert treffer nettet, vil noen av kontrollpunktene forandre seg, slik at nettet går bakover. (Mer under spillet).
Her er kontrollpunktene jeg har brukt:
private float ctrlpoints[] = { -2.5f, 0.0f, 0.0f, -2.0f, 0.0f, 0.0f, -1.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.5f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 2.5f, 0.0f, 0.0f, -2.5f, 0.3f, 0.0f, -2.0f, 0.3f, 0.0f, -1.5f, 0.3f, 0.0f, -1.0f, 0.3f, 0.0f, -0.5f, 0.3f, 0.0f, 0.0f, 0.3f, 0.0f, 0.5f, 0.3f, 0.0f, 1.0f, 0.3f, 0.0f, 1.5f, 0.3f, 0.0f, 2.0f, 0.3f, 0.0f, 2.5f, 0.3f, 0.0f, -2.5f, 0.6f, 0.0f, -2.0f, 0.6f, 0.0f, -1.5f, 0.6f, 0.0f, -1.0f, 0.6f, 0.0f, -0.5f, 0.6f, 0.0f, 0.0f, 0.6f, 0.0f, 0.5f, 0.6f, 0.0f, 1.0f, 0.6f, 0.0f, 1.5f, 0.6f, 0.0f, 2.0f, 0.6f, 0.0f, 2.5f, 0.6f, 0.0f, -2.5f, 0.9f, 0.0f, -2.0f, 0.9f, 0.0f, -1.5f, 0.9f, 0.0f, -1.0f, 0.9f, 0.0f, -0.5f, 0.9f, 0.0f, 0.0f, 0.9f, 0.0f, 0.5f, 0.9f, 0.0f, 1.0f, 0.9f, 0.0f, 1.5f, 0.9f, 0.0f, 2.0f, 0.9f, 0.0f, 2.5f, 0.9f, 0.0f, };
Og her er måten jeg har tegnet det på. I gl.glEvalMesh2, har jeg brukt LINE, slik at det blir som et nett. Hvis jeg istede her hadde brukt, FILL, ville det bli en fyllt flate.
//fargen blir svart setNormalMaterial_black(); //rotere 90 grader rundt y-aksen gl.glRotatef(90, 0.0f, 1.0f, 0.0f); //maper opp alle punktene til nettet gl.glMap2f(GL_MAP2_VERTEX_3,0.0f,1.0f,3,11,0.0f,1.0f,33,4,ctrlpoints); gl.glEnable(GL_MAP2_VERTEX_3); //Maper opp rutenettet med 10*30 ruter gl.glMapGrid2f(30, 0.0f, 1.0f, 10, 0.0f, 1.0f); gl.glEnable(GL_DEPTH_TEST); gl.glShadeModel(GL_FLAT); gl.glEvalMesh2(GL_LINE, 0, 30, 0, 10);
-
Banen
Dette er banen. Den er bygget opp av mange små hvite polygoner som er lagt utover et stort grønnt polygon. Jeg kunne ha gjort dette annerledes, ved foreksempel å legge på en textur, men dette ble ikke så pent som det er nå.
Eksempel på et polygon://the green ground around gl.glBegin(GL_POLYGON); setNormalMaterial_green(); gl.glVertex3f(-6.0f, -0.01f, 4.0f); gl.glVertex3f( 6.0f, -0.01f, 4.0f); gl.glVertex3f( 6.0f, -0.01f, -4.0f); gl.glVertex3f(-6.0f, -0.01f, -4.0f); gl.glEnd();
-
Racketen
Racket.java
Racketen har jeg bygget opp av en bezierflate og en sylinder.
Denne er altså bygget opp av en Bezierflate som er bygget opp på følgende måte:
private float RACKET = 0.5f; private int UN =5; private int VN =7; float ctr1=0.3f*RACKET; float ctr2=0.7f*RACKET; float ctr3=0.4f*RACKET; float ctr4=0.15f*RACKET; float ctr5=0.1f*RACKET; float ctr6=0.05f*RACKET; float ctrz1=0.0f*RACKET; float ctrz2=0.3f*RACKET; float ctrz3=1.0f*RACKET; float ctrz4=0.85f*RACKET; float ctrz5=0.9f*RACKET; float ctrz6=RACKET*1.2f;
float M[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, ctr1, ctrz1, 0.0f, 0.0f, ctrz1, 0.0f, 0.0f, ctrz1, 0.0f, 0.0f, ctrz1, 0.0f, -ctr1, ctrz1, 0.0f, ctr2, ctrz2, 0.0f, 0.0f, ctrz2, 0.0f, 0.0f, ctrz2, 0.0f, 0.0f, ctrz2, 0.0f, -ctr2, ctrz2, 0.0f, ctr3, ctrz3, 0.0f, ctr3, ctrz3, 0.0f, 0.0f, ctrz3, 0.0f, -ctr3, ctrz3, 0.0f, -ctr3, ctrz3, 0.0f, ctr4, ctrz4, 0.0f, ctr4, ctrz4, 0.0f, 0.0f, ctrz4, 0.0f, -ctr4, ctrz4, 0.0f, -ctr4, ctrz4, 0.0f, ctr5, ctrz5, 0.0f, ctr5, ctrz5, 0.0f, 0.0f, ctrz5, 0.0f, -ctr5, ctrz5, 0.0f, -ctr5, ctrz5, 0.0f, ctr6, ctrz6, 0.0f, 0.0f, ctrz6, 0.0f, 0.0f, ctrz6, 0.0f, 0.0f, ctrz6, 0.0f, -ctr6, ctrz6, 0.0f, };
-
Skjørtet
Som en hver kvinnelig tennisspiller, må Josefine få på seg et skjørt. Dette er bygd opp at to like bezierflater, hvor den ene er rotert 180 grader. Her har jeg tatt utgangspunkt i Påskegg eksempelet til Børre.
Og her følger hvordan det er bygd opp:
//The skirt private int UN =5; private int VN =4; //size private float SKIRT = 0.4f; float ctr0=0.1f*SKIRT; float ctr1=0.5f*SKIRT; float ctr2=0.7f*SKIRT; float ctr3=0.7f*SKIRT; /** offset along z */ float ctz1=0.0f*SKIRT; float ctz2=0.3f*SKIRT; float ctz3=1.0f*SKIRT; float ctz4=SKIRT; /** factor for circlecompensation */ float rf=1.35f; /** Bezier points */ float M[] = { ctr0,0.0f,ctz1, ctr0,ctr0,ctz1, 0.0f,rf*ctr0,ctz1, -ctr0,ctr0,ctz1, -ctr0,0.0f,ctz1, ctr1,0.0f,ctz1, ctr1,ctr1,ctz1, 0.0f,rf*ctr1,ctz1, -ctr1,ctr1,ctz1, -ctr1,0.0f,ctz1, ctr2,0.0f,ctz2, ctr2,ctr2,ctz2, 0.0f,rf*ctr2,ctz2, -ctr2,ctr2,ctz2, -ctr2,0.0f,ctz2, ctr3,0.0f,ctz3, ctr3,ctr3,ctz3, 0.0f,rf*ctr3,ctz3, -ctr3,ctr3,ctz3, -ctr3,0.0f,ctz3 }; //***************************************************// gl.glMap2f(GL_MAP2_VERTEX_3,0.0f,1.0f,3,josefine.getUN(), 0.0f,1.0f,3*josefine.getUN(),josefine.getVN(), josefine.getM()); gl.glEnable(GL_MAP2_VERTEX_3); gl.glEnable(GL_AUTO_NORMAL); gl.glEnable(GL_NORMALIZE); gl.glMapGrid2f(20,0.0f,1.0f,20,0.0f, 1.0f); gl.glFrontFace(GL_CW); gl.glEvalMesh2(GL_FILL, 0, 20, 0, 20); gl.glFrontFace(GL_CCW); gl.glRotatef(180.0f,0.0f,0.0f,1.0f); gl.glEvalMesh2(GL_FILL, 0, 20, 0, 20); gl.glFrontFace(GL_CCW);
-
Stolen
Her er dommerstolen som dommeren sitter på. Den er bygget opp av sylindere og polygoner.
Spillet
Spillet er lagt opp slik at jeg har en fast bestemt bane hvor ballen
skal gå. Denne banen ligger i en array med koordinater. Under ser du
hvordan de fire forskjellige banenen går. Banene er tegnet opp ved
hjelp av bezier-kurver.
Josefine server
Josef slår tilbake
Josefine slår tilbake igjen
Josef slår ballen i nettet og taper
Jeg vet alltid i hvilket punkt racketen og ballen møter hverandre. Så da blir det enkelt
å putte dette sammen å synkronisere bevegelsene. Ballen vet jeg hvor er ved å se på
arrayen med alle koordinatene til hvor ballen skal gå.
Et eksempel er når Josef skal slå tilbake ballen fra Josefine sin serve:
//Ballen blir slått av Josef, ned i bakken for (int i = 0; i < (playing.getUN_3()*3)-2; i++) { //Josef roterer armen sin josef_overarm_right[0] = teller_1; josef.setv_overarm_right(josef_overarm_right); //dommeren vrir på hodet... referee_neck[2] = teller_2; referee.setv_neck(referee_neck); //ballen går videre ball = i; //oppdater framen display(); i = i + 2; teller_1+=2; teller_2 = teller_2 + 4.5f; }
gl.glPushMatrix(); if (kast) { gl.glTranslated(playing.getkastPoints()[ball], playing.getkastPoints()[ball+1], playing.getkastPoints()[ball+2]); } else if (hit_1) { gl.glTranslated(playing.getctrlpoints_1()[ball], playing.getctrlpoints_1()[ball+1], playing.getctrlpoints_1()[ball+2]); } else if (sprett_1) { gl.glTranslated(playing.getctrlpoints_2()[ball], playing.getctrlpoints_2()[ball+1], playing.getctrlpoints_2()[ball+2]); } else if (hit_2) { gl.glTranslated(playing.getctrlpoints_3()[ball], playing.getctrlpoints_3()[ball+1], playing.getctrlpoints_3()[ball+2]); } . . . . else { gl.glTranslated(playing.getctrlpoints_6()[(playing.getUN_6()*3)-3], playing.getctrlpoints_6()[(playing.getUN_6()*3)-2], playing.getctrlpoints_6()[(playing.getUN_6()*3)-1]); } //the ball glu.gluSphere(qd,0.07f,30,30); gl.glPopMatrix();
//The net changes when the ball hits if (i == 36) { nettet.ctrlpoints[116] = 0.0f; nettet.ctrlpoints[83] = 5.0f; } else if (i == 39) { nettet.ctrlpoints[83] = 0.0f; nettet.ctrlpoints[50] = 5.0f; } else if (i == 42) { nettet.ctrlpoints[83] = 0.0f; nettet.ctrlpoints[50] = 5.0f; } else if (i == 45) { nettet.ctrlpoints[50] = 0.0f; nettet.ctrlpoints[17] = 5.0f; finish = true; }
Videreutvikling
Det er selvfølgelig mange ting man kunne gjort annerledes, bedre og mer effektivt. Men siden dette bare er et 4-vekttallskurs har jeg valgt å begrense meg til det jeg syntes jeg hadde tid og kapasitet til.
Det minst krevende å forandre på er nok selve layouten til alle komponentene. Jeg kunne nok ha gjort mer utseendemessig, men nå var det ikke det jeg mest skulle konsentrere meg om, så det er noe som kan komme en senere anledning. Målet med komponentene var å lage det så grunnleggende som mulig, så en nybegynner, greit kan lære seg dette.
Når det gjelder hvordan selve spillet er lagt opp, er det nok dette jeg ville utviklet videre på hvis dette hadde vært et større kurs. Nå er spillet bygd opp på den måten at jeg bestemmer hvor ballen skal gå og hvor den skal ende opp. Det som hadde vært interessant hadde vært og utviklet et spill hvor ballen ble bestemt ut i fra hvor den traff på racketen, hvilken vinkel ballens bane hadde og hvor på banen ballen lander. Da måtte Josef og Josefine beveget seg ut fra hvor ballen kom. Kanskje også ta et skritt til høyre eller venstre for å få tak i ballen.
Klassene
MyGLCanvas.java | Dette er hovedklassen hvor mye av GL-kode blir kjørt. |
Tennis.java | Denne klassen startet opp hele programmet. Herfra kalles MyGLCanvas.java. |
Playing.java | Her ligger alle arrayene med koordinatene til hvor ballen skal gå. Også en del get-metoder for å hente ut verdiene. |
ColorsAndComponents.java | I denne klassen ligger alle komponentene i programmet som ikke skal bevege på seg. Feks banen og stolen til dommeren. Her ligger også alle fargene jeg bruker. Dette er en GL-klasse. |
Nettet.java | Her ligger kontrollpunktene til tennis-nettet og et par get-metoder. |
Josefine.java | Her er alle vinklene til Josefines kropp. Hodet, kroppen, armer og ben. Også mange set- og get-metoder for å forandre på bevegelser fra klassen MyGLCanvas.java. |
Josef.java | Her er alle vinklene til Josefs kropp. Hodet, kroppen, armer og ben. Også mange set- og get-metoder for å forandre på bevegelser fra klassen MyGLCanvas.java. |
Referee.java | Her er alle vinklene til dommerens kropp. Hodet, kroppen, armer og ben. Også mange set- og get-metoder for å forandre på bevegelser fra klassen MyGLCanvas.java. |
Racket.java | Her ligger kontrollpunktene til racketen og et par get-metoder. |
Game.java | Splash-screen |
Alle filene: alt.zip.
Javadoc kan du finne her
Utviklet i GL4Java på Windows 2000 plattform
Har aldri spilt tennis før :)