基于Java Swing制作一個Pong小游戲
之前呢我們用Python的Pygame做過這個Pong游戲
這一次,我們用Java的Swing來實現(xiàn)類似的效果
首先我們列出本次的項目結(jié)構(gòu)
這個程序分為四個部分,一個程序入口,一個模型,一個刷新幀,一個視圖,模型里面放入球和擋板的類,視圖里面放入主窗口Frame和主面板Panel
接下來是項目目錄
src資源下面,我們把東西全部寫到com.mr包下,main里的Start是主入口,model里面分別是Ball和Board,service下是刷新幀的服務(wù),view視圖下分別為主窗體和主面板
好啦,現(xiàn)在先來寫GameFrame的代碼
package com.mr.view; import javax.swing.*; import java.awt.*; public class GameFrame extends JFrame { private Container container; private GamePanel panel; public GameFrame() { setTitle("Pong"); setBounds(300,300,850,1000); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); container=getContentPane(); panel=new GamePanel(); addKeyListener(panel); container.add(panel); setVisible(true); } }
首先啊,還是和平常一樣聲明package,導(dǎo)入一些東西,然后主窗體繼承自JFrame,設(shè)置以下標(biāo)題和窗口大小,還有關(guān)閉后退出程序的設(shè)置(setDefaultCloseOperation)然后實例化GamePanel面板(待會寫),然后綁定事件并添加到container容器中,設(shè)置窗口可見
然后是GamePanel的代碼,也是整個程序的核心,在這里我們要做出對賽點、得分、球體碰撞等檢測,并繪制圖形
package com.mr.view; import com.mr.model.Ball; import com.mr.model.Board; import com.mr.service.Fresh; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.util.ArrayList; public class GamePanel extends JPanel implements KeyListener { private BufferedImage img; private Graphics2D g2; public ArrayList<Object> select; private Fresh fresh; private Board b1; private Board b2; private Ball ball; private int score1; private int score2; private int winPoint; private int matchPoint; // 0表示沒有賽點,1表示玩家1的賽點,2表示玩家2的賽點 private int winner; public GamePanel() { img=new BufferedImage(850,1000,BufferedImage.TYPE_INT_BGR); g2=img.createGraphics(); select=new ArrayList<>(); b1=new Board(0); b2=new Board(1); ball=new Ball(); score1=0; score2=0; matchPoint=0; winPoint=11; winner=0; fresh=new Fresh(this); fresh.start(); } private void paintImage() { g2.setColor(Color.WHITE); if (winner==0) { int width=10; g2.fillRect(425-width/2,0,width,1000); g2.fillRect(b1.x,b1.y,b1.width,b1.height); g2.fillRect(b2.x,b2.y,b2.width,b2.height); g2.fillOval(ball.x-ball.r,ball.y-ball.r,ball.r*2,ball.r*2); } g2.setFont(new Font("黑體",Font.BOLD,56)); g2.drawString(String.valueOf(score1),295,150); g2.drawString(String.valueOf(score2),525,150); g2.setFont(new Font("黑體",Font.PLAIN,22)); if (winner==0) { switch (matchPoint) { case 1: g2.drawString("賽點",295,200); break; case 2: g2.drawString("賽點",525,200); break; default: break; } } else { g2.drawString("玩家"+String.valueOf(winner)+"獲勝",winner==1?295:525,200); } } public void paint(Graphics g) { if (winner==0) { move(); ball.move(b1.getBound(),b2.getBound()); checkPoint(); checkMatchPoint(); b1.checkBound(); b2.checkBound(); } g2.setColor(Color.BLACK); g2.fillRect(0,0,850,1000); paintImage(); g.drawImage(img,0,0,this); } private void checkPoint() { if (ball.x<=0) { score2+=1; } else if (ball.x+ball.r*2>=850) { score1+=1; } else { return; } ball=new Ball(); } private void checkMatchPoint() { if (score1==winPoint) { winner=1; return; } else if (score2==winPoint) { winner=2; return; } if (score1+1==winPoint&&score2+1!=winPoint) { matchPoint=1; } else if (score1+1!=winPoint&&score2+1==winPoint) { matchPoint=2; } else if (score1+1==winPoint&&score2+1==winPoint) { matchPoint=0; winPoint++; } else if (score1+1!=winPoint&&score2+1!=winPoint) { matchPoint=0; } } private void move() { for (Object code:select) { if (code==(Object)KeyEvent.VK_W) { b1.y-=b1.speed; } else if (code==(Object)KeyEvent.VK_S) { b1.y+=b1.speed; } else if (code==(Object)KeyEvent.VK_UP) { b2.y-=b2.speed; } else if (code==(Object)KeyEvent.VK_DOWN) { b2.y+=b2.speed; } } } @Override public void keyPressed(KeyEvent event) { if (select.indexOf(event.getKeyCode())==-1) { select.add(event.getKeyCode()); } } @Override public void keyReleased(KeyEvent event) { select.remove((Object)event.getKeyCode()); } @Override public void keyTyped(KeyEvent event) { ; } }
聲明com.mr.view包下,導(dǎo)入一些東西,然后創(chuàng)建主類GamePanel,繼承自JPanel并實現(xiàn)KeyListener事件,接下來就是聲明變量,一個主圖片img以及對應(yīng)的g2,select用于儲存按下的按鍵,這個待會講,接下來是fresh刷新幀線程,兩塊板,一個球,兩個玩家的分?jǐn)?shù),勝利所需要的分?jǐn)?shù),賽點歸屬于誰,是否出現(xiàn)了贏家等。接下來來到構(gòu)造函數(shù),先創(chuàng)建img主圖片850x1000還有g(shù)2,初始化一些東西,這里Board的0和1表示歸屬于哪個玩家,0則為左手邊的,1則為右手邊的。接下來創(chuàng)建球(ps.這些類待會就來寫),然后分?jǐn)?shù)初始化,賽點為0,表示沒有人獲得賽點,獲勝分?jǐn)?shù)為11分,和乒乓球一樣,然后winner為0表示沒有贏家,創(chuàng)建線程,這里傳入了自己,是為了待會可以通過repaint方式不斷重繪,然后啟動線程。paint中,沒有獲勝,則移動板(move),移動球,檢查得分,檢查賽點,保持兩塊板處于場內(nèi)(窗口內(nèi)),然后設(shè)置顏色為黑色,填充背景為黑色,繪制,并把主圖片畫在g中。paintImage中對板、球、得分、中線進行繪制,不難理解。好,GamePanel的最后,我們來講講剛剛的那個select,因為我們要同時檢測做個鍵盤按鈕,但是這個KeyListener只支持一次性按下一個,按下多個也只會獲取到一個,所以我們用一種方法來解決,我們創(chuàng)建一個select數(shù)組,按下按鍵且按鍵不在select中則添加到select中,松開則刪除,這樣同時按下多個按鈕,這些按鈕(就像排著隊伍一樣)相繼添加到select中,這樣也便于了我們移動板的if判斷。
接下來我們來看看一個同樣非常重要的Fresh刷新幀線程
package com.mr.service; import com.mr.view.GamePanel; public class Fresh extends Thread { public final int INTERVAL=20; private GamePanel panel; public Fresh(GamePanel panel) { this.panel=panel; } public void run() { while (true) { panel.repaint(); try { Thread.sleep(INTERVAL); } catch (InterruptedException e) { e.printStackTrace(); } } } }
這個就while循環(huán)實現(xiàn)啦~~~
然后是Ball
package com.mr.model; import java.awt.*; import java.util.Random; public class Ball { public int x; public int y; public int r; private int xspeed; private int yspeed; private int max_speed; private int min_speed; private int max_speed2; private int min_speed2; public Ball() { x=425; y=500; r=15; Random rd=new Random(); xspeed=rd.nextInt(1,3)==1?rd.nextInt(4,7):rd.nextInt(-6,-3); yspeed=rd.nextInt(1,3)==1?rd.nextInt(4,7):rd.nextInt(-6,-3); max_speed=12; min_speed=3; max_speed2=-3; min_speed2=-12; } public void move(Rectangle b1,Rectangle b2) { x+=xspeed; y+=yspeed; Random rd=new Random(); if (getBound().intersects(b1)||getBound().intersects(b2)) { xspeed=-xspeed; yspeed+=rd.nextInt(-2,3); } else if (y-r<=0||y+r>=965) { yspeed=-yspeed; xspeed+=rd.nextInt(-2,3); } if (xspeed>max_speed&&xspeed>0) { xspeed=max_speed; } if (yspeed>max_speed&&yspeed>0) { yspeed=max_speed; } if (xspeed<min_speed&&xspeed>0) { xspeed=min_speed; } if (yspeed<min_speed&&yspeed>0) { yspeed=min_speed; } if (xspeed>max_speed2&&xspeed<0) { xspeed=max_speed2; } if (yspeed>max_speed&&yspeed<0) { yspeed=max_speed2; } if (xspeed<min_speed&&xspeed<0) { xspeed=min_speed2; } if (yspeed<min_speed&&yspeed<0) { yspeed=min_speed2; } } private Rectangle getBound() { return new Rectangle(x-r,y-r,r*2,r*2); } }
這個模型嘛,一般情況下都有x和y還有大小,因為這個是個圓,所以我們用半徑r,然后xspeed和yspeed表示各個方向的速度從而實現(xiàn)斜著移動,還有max_speed和min_speed用于把動態(tài)變換的速度限制于這個范圍內(nèi),待會每碰到一次墻壁或板,就會適當(dāng)增加或減少速度,所以要把速度限制在特定范圍內(nèi),max_speed2和min_speed2也一樣,前2者是用于正數(shù)速度的,后2者是用于負(fù)數(shù)速度的,然后移動的時候就對一些碰撞等情況進行檢測就好了,getBound用于返回對象的Rect長方形對象,用于檢測碰撞。
最后是Board
package com.mr.model; import java.awt.*; public class Board { public int x; public int y; public int width; public int height; public int speed; public Board(int type) { width=8; height=120; speed=15; y=500-height/2; if (type==0) { x=0; } else { x=850-15-width; } } public void checkBound() { if (y+height>=985) { y=985-height; } if (y<=0) { y=0; } } public Rectangle getBound() { return new Rectangle(x,y,width,height); } }
Board和Ball也有著差不多的一些參數(shù)和類成員和方法
現(xiàn)在全部就都寫完了,最后畫上點睛之筆,寫上最后的程序入口就大功告成啦!??!
Start.java:
package com.mr.main; import com.mr.view.GameFrame; public class Start { public static void main(String[] args) { GameFrame gameFrame=new GameFrame(); } }
到此這篇關(guān)于基于Java Swing制作一個Pong小游戲的文章就介紹到這了,更多相關(guān)Java Swing制作Pong游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot+RabbitMQ方式收發(fā)消息的實現(xiàn)示例
這篇文章主要介紹了SpringBoot+RabbitMQ方式收發(fā)消息的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09spring cloud升級到spring boot 2.x/Finchley.RELEASE遇到的坑
這篇文章主要介紹了spring cloud升級到spring boot 2.x/Finchley.RELEASE遇到的坑,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08java數(shù)學(xué)歸納法非遞歸求斐波那契數(shù)列的方法
這篇文章主要介紹了java數(shù)學(xué)歸納法非遞歸求斐波那契數(shù)列的方法,涉及java非遞歸算法的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07java編程調(diào)用存儲過程中得到新增記錄id號的實現(xiàn)方法
這篇文章主要介紹了java編程調(diào)用存儲過程中得到新增記錄id號的實現(xiàn)方法,涉及Java數(shù)據(jù)庫操作中存儲過程的相關(guān)使用技巧,需要的朋友可以參考下2015-10-10java poi設(shè)置生成的word的圖片為上下型環(huán)繞以及其位置的實現(xiàn)
這篇文章主要介紹了java poi設(shè)置生成的word的圖片為上下型環(huán)繞以及其位置的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09