Fråga
Jag tänkte lära mig rita grafik i java och började därför skriva ett litet program. Detta har nu kommit en bit på väg men jag har fått ett problem som jag inte förstår. Bollen hackar förutom när man flyttar plattan. Sen finns det en hel del till att göra givetvis, men skulle vilja få veta varför det blir så här. Ha gärna åsikter om den övriga koden också man är här för att lära.
Svar
Hej Mattias!
Så kul att du bestämt dig för att lära dig Java.
För att lyckas skriva ett realtids baserat program i Swing är det en bra idé att läsa på hur det är tänkt att man skall använda biblioteket eller i alla fall hur Swing fungerar.
Till att börja med kommer anrop till repaint inte utföras med detsamma, Swing väntar istället någon sekund för att se om där kommit mer än ett repaint anrop. På det här viset minskar man belastningen av Swing. Metoden repaint kan även anropas med ett argument som anger max antal sekunder repaint får vänta. Egentligen är det anrop till paintComponent(Graphics comp) som väntas in, därför kommer ditt Swing-program fortsätta på exekveringen som kommer efter repaint anropet utan att vänta.
Vid anrop till repaint kan man även ange vilken yta man vill uppdatera, vilket du använt dig av. Sådant argument kan öka programmets prestanda genom att rita på en tillfällig yta, vilket Swing sedan kopierar till riktiga ytan. (Självklart är det endast ändrade partier som kopieras.)
När jag testade ditt program så rörde sig bollen ibland extremt snabbt och ibland extremt långsamt. Anledningen till detta beteende är att operativsystemet fördelar processorns resurser till mer än ett program. Vilket resulterar i att ditt program får vänta på andra program som exekveras. När väl ditt program exekverar igen kommer ditt program få extra mycket exekveringstid, vilket leder till snabba rörelser av bollen.
Ett tillvägagångssätt som både löser ditt problem, men som även är enkelt att implementera är att basera bollens position på klockslaget. Människor har lätt för att upptäcka ”onormala” rörelser, därför kommer klockan hjälpa oss att få bollen på den plats ögat förutsagt.
Ett exempel
ball.setX((long)(ball.getX() + ((speed * Math.cos(Math.toRadians(ball.getDirection()))) * delta)));
ball.setY((long)(ball.getY() + ((speed * Math.sin(Math.toRadians(ball.getDirection()))) * delta)));
varav delta motsvarar antal microsekunder sedan förra uppdateringen.
Ett annat tips för att få bra prestanda är att slå på OpenGL eller DirectX stödet, OpenGL slår du på genom att starta java med argumentet ”-Dsun.java2d.opengl=true” du finner mer information på http://java.sun.com/j2se/1.5.0/docs/guide/2d/flags.html.
Ibland kan det hända att uppritningen av grafik anropas innan föregående avslutats. Detta är såklart ett problem, för det är inte intressant att rita ut en boll på gamla koordinater. Därför kan det vara intressant att inte påbörja exekveringen av ett anrop medan där kommer in nya. Mer information ang. det här finner du på, http://billharlan.com/pub/papers/Improving_Swing_Performance.html.
Längst upp finner du källkod som bygger på källkoden du skickat in. Självklart finns där mer att önska och jag lämnar förbättringar åt läsaren, men här är några saker jag vill visa genom källkoden:
- Använd konstanter istället för ”magiska” siffror.
- Istället för att hårdkoda brickornas position så beräknar vi dem.
- Använd klassen Vector för att lätt kunna plocka bort brickor som man studsat på. Vilket medför att uppritningen itererar färre gånger.
- Använd enum-deklaration istället för 4 st booleska variabler. Genom att använda enum minskas behovet till enbart en variabel, vilket minskar risken att få flera olika status variabler sanna.
- Försök dela upp källkoden i så kallad MVC-struktur (Model-View-Controller).
- Basera bollens position på klockslaget.
- Bryt upp metoder och ersätt dem med subrutiner.
- Källkoden använder sig av javadoc-kommentarer, vilket är ett standardiserat sätt att skriva sina kommentarer på. Genom att använda denna standard kan jag generera dokument-filer som påminner om dokumentationen till Java API’et.
Ett slutgiltigt tips som inte avspeglas i källkoden:
- Använd enbart dubbelriktad beroende mellan två paket om du är hundra procent säker på att de alltid kommer finnas tillgängliga samtidigt, annars bör du använda primitiva typer eller klasser från standard biblioteket som argument mellan dessa paket. (Denna idé går även att applicera på kommunikation till och från en ”controller” i MVC-konceptet.)
Hoppas mina tips kommit dig till nytta. Självklart är du välkommen med fler frågor, lycka till.