基于Java Swing制作一个Pong小游戏

 更新时间:2023年01月05日 14:46:03   作者:Leleprogrammer  
《Pong》是美国雅达利公司(ATARI)开发的视频游戏,该作模拟了两个打乒乓球的人,就是在两条线中间有一个点在动,操纵器就是一个摇杆上有一个按钮的那种。本文就来用Java Swing制作一个Pong小游戏吧

之前呢我们用Python的Pygame做过这个Pong游戏

这一次,我们用Java的Swing来实现类似的效果

首先我们列出本次的项目结构

这个程序分为四个部分,一个程序入口,一个模型,一个刷新帧,一个视图,模型里面放入球和挡板的类,视图里面放入主窗口Frame和主面板Panel

接下来是项目目录

src资源下面,我们把东西全部写到com.mr包下,main里的Start是主入口,model里面分别是Ball和Board,service下是刷新帧的服务,view视图下分别为主窗体和主面板 

好啦,现在先来写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,导入一些东西,然后主窗体继承自JFrame,设置以下标题和窗口大小,还有关闭后退出程序的设置(setDefaultCloseOperation)然后实例化GamePanel面板(待会写),然后绑定事件并添加到container容器中,设置窗口可见

然后是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包下,导入一些东西,然后创建主类GamePanel,继承自JPanel并实现KeyListener事件,接下来就是声明变量,一个主图片img以及对应的g2,select用于储存按下的按键,这个待会讲,接下来是fresh刷新帧线程,两块板,一个球,两个玩家的分数,胜利所需要的分数,赛点归属于谁,是否出现了赢家等。接下来来到构造函数,先创建img主图片850x1000还有g2,初始化一些东西,这里Board的0和1表示归属于哪个玩家,0则为左手边的,1则为右手边的。接下来创建球(ps.这些类待会就来写),然后分数初始化,赛点为0,表示没有人获得赛点,获胜分数为11分,和乒乓球一样,然后winner为0表示没有赢家,创建线程,这里传入了自己,是为了待会可以通过repaint方式不断重绘,然后启动线程。paint中,没有获胜,则移动板(move),移动球,检查得分,检查赛点,保持两块板处于场内(窗口内),然后设置颜色为黑色,填充背景为黑色,绘制,并把主图片画在g中。paintImage中对板、球、得分、中线进行绘制,不难理解。好,GamePanel的最后,我们来讲讲刚刚的那个select,因为我们要同时检测做个键盘按钮,但是这个KeyListener只支持一次性按下一个,按下多个也只会获取到一个,所以我们用一种方法来解决,我们创建一个select数组,按下按键且按键不在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循环实现啦~~~

然后是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表示各个方向的速度从而实现斜着移动,还有max_speed和min_speed用于把动态变换的速度限制于这个范围内,待会每碰到一次墙壁或板,就会适当增加或减少速度,所以要把速度限制在特定范围内,max_speed2和min_speed2也一样,前2者是用于正数速度的,后2者是用于负数速度的,然后移动的时候就对一些碰撞等情况进行检测就好了,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也有着差不多的一些参数和类成员和方法

现在全部就都写完了,最后画上点睛之笔,写上最后的程序入口就大功告成啦!!!

Start.java:

package com.mr.main;
 
import com.mr.view.GameFrame;
 
public class Start {
    public static void main(String[] args) {
        GameFrame gameFrame=new GameFrame();
    }
}

到此这篇关于基于Java Swing制作一个Pong小游戏的文章就介绍到这了,更多相关Java Swing制作Pong游戏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java equals()方法使用详解及总结

    Java equals()方法使用详解及总结

    这篇文章主要介绍了Java equals()方法使用详解及总结的相关资料,需要的朋友可以参考下
    2017-03-03
  • java中处理json各种各样的转换方法(推荐)

    java中处理json各种各样的转换方法(推荐)

    下面小编就为大家分享一篇java中处理json各种各样的转换方法小结,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-11-11
  • java线程间通信的通俗解释及代码示例

    java线程间通信的通俗解释及代码示例

    这篇文章主要介绍了java线程间通信的通俗解释,介绍了线程通信中的几个相关概念,然后分享了线程通信的实现方式及代码示例,具有一定参考价值 ,需要的朋友可以了解下。
    2017-11-11
  • Java8实战之Stream的延迟计算

    Java8实战之Stream的延迟计算

    JDK中Stream的中间函数如 filter(Predicate super T>)是惰性求值的,filter并非对流中所有元素调用传递给它的Predicate,下面这篇文章主要给大家介绍了关于Java8实战之Stream延迟计算的相关资料,需要的朋友可以参考下
    2021-09-09
  • Java自带的加密类MessageDigest类代码示例

    Java自带的加密类MessageDigest类代码示例

    这篇文章主要介绍了Java自带的加密类MessageDigest类代码示例,分享了常见的三种加密方式代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Spring Framework远程代码执行漏洞分析(最新漏洞)

    Spring Framework远程代码执行漏洞分析(最新漏洞)

    Spring Framework 是一个开源应用框架,旨在降低应用程序开发的复杂度,它具有分层体系结构,允许用户选择组件,同时还为 J2EE 应用程序开发提供了一个有凝聚力的框架,对Spring远程代码执行漏洞相关知识感兴趣的朋友一起看看吧
    2022-04-04
  • 一文掌握Spring中循环依赖与三级缓存

    一文掌握Spring中循环依赖与三级缓存

    这篇文章主要介绍了Spring中循环依赖与三级缓存,Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池,二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories),本文结合实例代码介绍的非常详细,需要的朋友参考下吧
    2023-09-09
  • FutureTask为何单个任务仅执行一次原理解析

    FutureTask为何单个任务仅执行一次原理解析

    这篇文章主要为大家介绍了FutureTask为何单个任务仅执行一次原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Eclipse连接Mysql数据库操作总结

    Eclipse连接Mysql数据库操作总结

    这篇文章主要介绍了Eclipse连接Mysql数据库操作总结的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-08-08
  • Java实现线程的四种方式解析

    Java实现线程的四种方式解析

    这篇文章主要介绍了Java实现线程的四种方式解析,线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序,需要的朋友可以参考下
    2023-10-10

最新评论