Javaspel programmering handledning - Flappy fågeln Redux (8 / 12 steg)
Steg 8: Konkretiserade spelet: II
Nu är det dags att skapa rörliga bakgrunden som vi ser på välkomstskärmen. Detta kräver att lägga till metoden gameScreen TopClass som flera setter-metoder i PlayGameScreen.
Vi börja med att diskutera tillägg av metoden gameScreen i TopClass - det är där spelet klockan finns. Först skapa två instanser av BottomPipe och TopPipe. Så snart en uppsättning rören går ut på skärmen, kommer de positioneras så att de kommer tillbaka på skärmen. Du kan ange pipe bredden och höjden variabler till vad du vill, men jag utifrån dem skärmstorleken för att optimera spelet för olika skärmstorlekar.
XLoc1, xLoc2, yLoc1 och yLoc2 variabler referera koordinaterna av de två BottomPipe; TopPipe objektens platser kommer att vara i förhållande till de BottomPipe platserna. Jag skapade en helper metod som kallas bottomPipeLoc() som genererar ett slumpmässigt heltal som ska användas för BottomPipe y-koordinaten. Detta nummer måste tillåta både TopPipe och BottomPipe objekten tillåts vara kvar på skärmen.
På nästa rad i koden skapar vi en variabel av typen länge för att hålla aktuell tid för systemet - det ger oss ett värde för den tiden spelet klockan startar. Denna variabel uppdateras i slutet av varje iteration av spelet klockan i om-uttrycket att lagra följande starttider för varje spelklocka iteration. Principen om spelet klockan är att hålla iterera genom while loop tills skillnaden mellan aktuell tid för systemet och iterationen starttid är större än ett förutbestämt värde (i millisekunder). Spelet klockan kommer att hålla iteration så länge loopVar är sant; Detta kommer endast att ändras till false när någon form av kollision upptäcks.
Spelklocka koden innehåller kommentarer för att förklara vad som händer där. Ordningen är: uppdatera element platser, då tolka de uppdaterade delarna till klassen som drar dem, och slutligen faktiskt uppdatera på panelen för att se ändringarna.
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Image;
import java.awt.Color;
import java.awt.LayoutManager;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
allmän klass TopClass implementerar ActionListener {
globala konstant variabler
privata statisk sista int skärmbredd = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
privata statisk sista int SCREEN_HEIGHT = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
privata statisk sista int PIPE_GAP = SCREEN_HEIGHT/5; avståndet i bildpunkter mellan rören
privata statisk sista int PIPE_WIDTH = skärmbredd/8, PIPE_HEIGHT = 4 * PIPE_WIDTH;
privata statisk sista int UPDATE_DIFFERENCE = 25; tid i ms mellan uppdateringar
privata statisk sista int X_MOVEMENT_DIFFERENCE = 5; distansera det rör flytten varje uppdatering
privata statisk sista int SCREEN_DELAY = 300; behövs på grund av långa laddningstider tvingar rör dyka upp halva skärmen
globala variabler
privata boolean loopVar = sant; falskt -> Kör inte slinga; -True > springa slinga för rör
globala swing objekt
privat JFrame f = nya JFrame ("Flappy fågeln Redux");
privat JButton startGame;
privat JPanel topPanel; förklarat globalt att rymma måla verksamheten och möjliggöra removeAll(), etc.
andra globala objekt
privata statisk TopClass tc = nya TopClass();
privata statisk PlayGameScreen pgs; panel som har rörliga bakgrunden i början av spelet
/**
* Standardkonstruktör
*/
offentliga TopClass() {
}
/**
* Huvudsakliga körbara metoden anropas när du kör hittar.jar-filen
* args
*/
offentliga statisk void main (String [] args) {
bygga GUI på en ny tråd
javax.swing.SwingUtilities.invokeLater (nya Runnable() {
public void run() {
tc.buildFrame();
skapa en ny tråd för att hålla det grafiska Gränssnittet lyhörd medan spelet körs
Gänga t = nya Thread() {
public void run() {
tc.gameScreen(true);
}
};
t.start();
}
});
}
/**
-Metoden att bygga JFrame och lägga till programmet innehåll
*/
privata void buildFrame() {
Bildikon = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("resources/blue_bird.png"));
f.setContentPane(createContentPane());
f.setResizable(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setAlwaysOnTop(false);
f.setVisible(true);
f.setMinimumSize (ny Dimension (skärmbredd * 1/4, SCREEN_HEIGHT * 1/4));
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setIconImage(icon);
f.addKeyListener(this);
}
privat JPanel createContentPane() {
topPanel = nya JPanel(); översta JPanel i layout hierarki
topPanel.setBackground(Color.BLACK);
Tillåt oss till lagret panelerna
LayoutManager overlay = nya OverlayLayout(topPanel);
topPanel.setLayout(overlay);
Starta spelet JButton
startGame = ny JButton ("börja spela!");
startGame.setBackground(Color.BLUE);
startGame.setForeground(Color.WHITE);
startGame.setFocusable(false); snarare än bara setFocusabled(false)
startGame.setFont (nya typsnitt ("kaliber", Font.BOLD, 42));
startGame.setAlignmentX(0.5f); Centrera vågrätt på skärmen
startGame.setAlignmentY(0.5f); Centrera lodrätt på skärmen
startGame.addActionListener(this);
topPanel.add(startGame);
måste lägga till sist för att säkerställa knappens synlighet
PGS = nya PlayGameScreen (skärmbredd, SCREEN_HEIGHT, true); sant -> vi vill pgs vara startbilden
topPanel.add(pgs);
returnera topPanel;
}
/**
* Genomförandet för åtgärder händelser
*/
public void actionPerformed (ActionEvent e) {
IF(e.GetSource() == startGame) {
göra något
}
}
/**
* Metod som utför plaska skärm grafik rörelser
*/
privata void gameScreen (boolean isSplash) {
BottomPipe bp1 = ny BottomPipe (PIPE_WIDTH, PIPE_HEIGHT);
BottomPipe bp2 = ny BottomPipe (PIPE_WIDTH, PIPE_HEIGHT);
TopPipe tp1 = ny TopPipe (PIPE_WIDTH, PIPE_HEIGHT);
TopPipe tp2 = ny TopPipe (PIPE_WIDTH, PIPE_HEIGHT);
variabler att spåra x och y bild platser för nedre röret
int xLoc1 = skärmbredd + SCREEN_DELAY, xLoc2 = (int) ((dubbel) 3.0/2.0*SCREEN_WIDTH+PIPE_WIDTH/2.0)+SCREEN_DELAY;
int yLoc1 = bottomPipeLoc(), yLoc2 = bottomPipeLoc();
variabel att hålla slingan starttid
långa startTime = System.currentTimeMillis();
While(loopVar) {
IF((system.currentTimeMillis() - startTime) > UPDATE_DIFFERENCE) {
Kontrollera om en uppsättning rör har skärmen
om så är fallet, Återställ rörets X läge och tilldela en ny Y plats
om (xLoc1 < (0-PIPE_WIDTH)) {
xLoc1 = skärmbredd;
yLoc1 = bottomPipeLoc();
}
annars om (xLoc2 < (0-PIPE_WIDTH)) {
xLoc2 = skärmbredd;
yLoc2 = bottomPipeLoc();
}
minska på röret platser med förutbestämda belopp
xLoc1-= X_MOVEMENT_DIFFERENCE;
xLoc2-= X_MOVEMENT_DIFFERENCE;
uppdatera BottomPipe och TopPipe platserna
BP1.setX(xLoc1);
BP1.setY(yLoc1);
bp2.setX(xLoc2);
bp2.setY(yLoc2);
TP1.setX(xLoc1);
TP1.setY(yLoc1-PIPE_GAP-PIPE_HEIGHT); säkerställa tp1 på rätt plats
TP2.setX(xLoc2);
TP2.setY(yLoc2-PIPE_GAP-PIPE_HEIGHT); säkerställa tp2 på rätt plats
som de BottomPipe och TopPipe lokala variablerna i PlayGameScreen genom att analysera de lokala variablerna
pgs.setBottomPipe (bp1, bp2);
pgs.setTopPipe (tp1, tp2);
uppdatera pgs's JPanel
topPanel.revalidate();
topPanel.repaint();
uppdatera variabeln tid-spårning efter alla som har slutförts
startTime = System.currentTimeMillis();
}
}
}
/**
* Beräknar ett slumpmässigt heltal för botten rörets placering
* int
*/
privata int bottomPipeLoc() {
int temp = 0;
iterera tills temp är ett värde som gör att båda rören vara på skärmen
medan (temp < = PIPE_GAP + 50 || temp > = SCREEN_HEIGHT-PIPE_GAP) {
Temp = (int) ((dubbel) Math.random()*((double)SCREEN_HEIGHT));
}
returnera härda;
}
}
Nedan ser du den uppdaterade PlayGameScreen klass som återspeglar tillägg av set-metoderna används ovan. Vi kommer att lägga till lite kod för att konkretisera metoden paintComponent. Först vi sekventiellt Ange grafik färg och skapa rektangeln för himlen och marken och sedan dra den svarta linjen mellan dem. Därefter drar vi posterna i BottomPipe och TopPipe. Dessa element får inte vara null (dvs. de måste ha skapats) för att dra dem.
Efter detta vill vi uppmärksamma "Flappy fågeln" med stora bokstäver längst upp på skärmen. Först inrättat vi ett try-catch-sats att ange teckensnittet. Det första teckensnittet finns inte på datorer, och om inte, vi avancera till programsatsen catch där teckensnittet blir inställd på ett mer universellt teckensnitt. Vi vill också se till att meddelandet är centrerad på skärmen, så att vi får bredden på det meddelande som vi kommer att dra med hjälp av den FontMetrics linjen. Slutligen drar vi meddelandet på skärmen efter try-catch-block.
Nästa förändring gjorde var tillägget av några enkla setter-metoder. Dessa bör vara självförklarande. Den sista ändringen är tillägg av metoden sendText. Vi kommer att använda detta när spelet slutar skicka "Game Over" dras. Denna text kommer att ersätta den befintliga meddelande variabel text.
import javax.swing.*;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Color;
allmän klass PlayGameScreen utökar JPanel {
standard referens-ID
privata statisk sista långa serialVersionUID = 1L;
globala variabler
privata int screenWidth, screenHeight;
privata boolean isSplash = sant;
privata sträng meddelande = "Flappy fågeln";
privat Font primaryFont = nya teckensnitt ("Goudy Stout", Font.BOLD, 56), failFont = nya teckensnitt ("kaliber", Font.BOLD, 56);
privata int messageWidth = 0;
privat BottomPipe bp1, bp2;
privat TopPipe tp1, tp2;
/**
* Standardkonstruktor för klassen PlayGameScreen
*/
offentliga PlayGameScreen (int screenWidth, int screenHeight, boolean isSplash) {
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.isSplash = isSplash;
}
/**
* Manuellt styra vad dras på denna JPanel genom att anropa metoden paintComponent
* med graphics-objektet och målning med objektet
*/
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.setColor (ny färg (89, 81, 247)); färg för den blå himlen
g.fillRect (0, 0, screenWidth, screenHeight * 7/8); skapa den sky rektangeln
g.setColor (ny färg (147, 136, 9)); brun färg för marken
g.fillRect (0, screenHeight * 7/8, screenWidth, screenHeight/8); skapa den mark rektangeln
g.setColor(Color.BLACK); dividera Linjefärg
g.drawLine (0, screenHeight * 7/8, screenWidth, screenHeight * 7/8); dra skiljelinjen
objekt måste instantieras innan de är dras!
om (bp1! = null & & bp2! = null & & tp1! = null & & tp2! = null) {
g.drawImage(bp1.getPipe(), bp1.getX(), bp1.getY(), null);
g.drawImage(bp2.getPipe(), bp2.getX(), bp2.getY(), null);
g.drawImage(tp1.getPipe(), tp1.getX(), tp1.getY(), null);
g.drawImage(tp2.getPipe(), tp2.getX(), tp2.getY(), null);
}
behövs om primära teckensnittet inte finns
försök {
g.setFont(primaryFont);
FontMetrics mått = g.getFontMetrics(primaryFont);
messageWidth = metric.stringWidth(message);
}
fånga (undantag e) {
g.setFont(failFont);
FontMetrics mått = g.getFontMetrics(failFont);
messageWidth = metric.stringWidth(message);
}
g.drawString (meddelande, screenWidth/2-messageWidth/2, screenHeight/4);
}
/**
* När metoden för Playgamescreens globala BottomPipe variabler
* bp1 den första BottomPipe
* bp2 den andra BottomPipe
*/
public void setBottomPipe (BottomPipe bp1, BottomPipe bp2) {
This.BP1 = bp1;
This.bp2 = bp2;
}
/**
* När metoden för Playgamescreens globala TopPipe variabler
* tp1 den första TopPipe
* tp2 den andra TopPipe
*/
public void setTopPipe (TopPipe tp1, TopPipe tp2) {
This.TP1 = tp1;
This.TP2 = tp2;
}
/**
* Metod som kallas för att tolka ett meddelande på skärmen
* meddelande meddelandet att tolka
*/
public void sendText (String meddelande) {
This.Message = meddelande;
}
}