基于Java Swing制作一個Pong小游戲
之前呢我們用Python的Pygame做過這個Pong游戲
這一次,我們用Java的Swing來實(shí)現(xiàn)類似的效果

首先我們列出本次的項(xiàng)目結(jié)構(gòu)

這個程序分為四個部分,一個程序入口,一個模型,一個刷新幀,一個視圖,模型里面放入球和擋板的類,視圖里面放入主窗口Frame和主面板Panel
接下來是項(xiàng)目目錄

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)然后實(shí)例化GamePanel面板(待會寫),然后綁定事件并添加到container容器中,設(shè)置窗口可見
然后是GamePanel的代碼,也是整個程序的核心,在這里我們要做出對賽點(diǎn)、得分、球體碰撞等檢測,并繪制圖形
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表示沒有賽點(diǎn),1表示玩家1的賽點(diǎn),2表示玩家2的賽點(diǎn)
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("賽點(diǎn)",295,200);
break;
case 2:
g2.drawString("賽點(diǎn)",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并實(shí)現(xiàn)KeyListener事件,接下來就是聲明變量,一個主圖片img以及對應(yīng)的g2,select用于儲存按下的按鍵,這個待會講,接下來是fresh刷新幀線程,兩塊板,一個球,兩個玩家的分?jǐn)?shù),勝利所需要的分?jǐn)?shù),賽點(diǎn)歸屬于誰,是否出現(xiàn)了贏家等。接下來來到構(gòu)造函數(shù),先創(chuàng)建img主圖片850x1000還有g(shù)2,初始化一些東西,這里Board的0和1表示歸屬于哪個玩家,0則為左手邊的,1則為右手邊的。接下來創(chuàng)建球(ps.這些類待會就來寫),然后分?jǐn)?shù)初始化,賽點(diǎn)為0,表示沒有人獲得賽點(diǎn),獲勝分?jǐn)?shù)為11分,和乒乓球一樣,然后winner為0表示沒有贏家,創(chuàng)建線程,這里傳入了自己,是為了待會可以通過repaint方式不斷重繪,然后啟動線程。paint中,沒有獲勝,則移動板(move),移動球,檢查得分,檢查賽點(diǎn),保持兩塊板處于場內(nèi)(窗口內(nèi)),然后設(shè)置顏色為黑色,填充背景為黑色,繪制,并把主圖片畫在g中。paintImage中對板、球、得分、中線進(jìn)行繪制,不難理解。好,GamePanel的最后,我們來講講剛剛的那個select,因?yàn)槲覀円瑫r檢測做個鍵盤按鈕,但是這個KeyListener只支持一次性按下一個,按下多個也只會獲取到一個,所以我們用一種方法來解決,我們創(chuàng)建一個select數(shù)組,按下按鍵且按鍵不在select中則添加到select中,松開則刪除,這樣同時按下多個按鈕,這些按鈕(就像排著隊(duì)伍一樣)相繼添加到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)實(shí)現(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還有大小,因?yàn)檫@個是個圓,所以我們用半徑r,然后xspeed和yspeed表示各個方向的速度從而實(shí)現(xiàn)斜著移動,還有max_speed和min_speed用于把動態(tài)變換的速度限制于這個范圍內(nèi),待會每碰到一次墻壁或板,就會適當(dāng)增加或減少速度,所以要把速度限制在特定范圍內(nèi),max_speed2和min_speed2也一樣,前2者是用于正數(shù)速度的,后2者是用于負(fù)數(shù)速度的,然后移動的時候就對一些碰撞等情況進(jìn)行檢測就好了,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)在全部就都寫完了,最后畫上點(diǎn)睛之筆,寫上最后的程序入口就大功告成啦?。。?/p>
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項(xiàng)目中feignClient使用方式
文章介紹了在Spring Boot項(xiàng)目中配置Feign客戶端攔截器的具體步驟,包括在application.yml中添加配置、在主類上啟用組件掃描、將攔截器加入到攔截器列表中以及在接口調(diào)用時的說明,總結(jié)指出這是個人經(jīng)驗(yàn)分享,希望對大家有所幫助2024-11-11
使用springboot對linux進(jìn)行操控的方法示例
這篇文章主要介紹了使用springboot對linux進(jìn)行操控的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Java解析調(diào)用webservice服務(wù)的返回XML串詳解
這篇文章主要介紹了Java解析調(diào)用webservice服務(wù)的返回XML串詳解的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-07-07
Spring Boot 使用WebAsyncTask異步返回結(jié)果
這篇文章主要介紹了Spring Boot 使用WebAsyncTask異步返回結(jié)果的相關(guān)資料,需要的朋友可以參考下2018-02-02
Java微信二次開發(fā)(二) Java微信文本消息接口請求與發(fā)送
這篇文章主要為大家詳細(xì)介紹了Java微信二次開發(fā)第二篇,Java微信文本消息接口請求與發(fā)送功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
Java內(nèi)存溢出實(shí)現(xiàn)原因及解決方案
這篇文章主要介紹了Java內(nèi)存溢出實(shí)現(xiàn)原因及解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03

