• Java Swing 制作一个Pong小游戏


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

    Python 项目实战 教你用Pygame写一个Pong游戏_Leleprogrammer的博客-CSDN博客_pong游戏手把手教你用Pygame写一个Pong游戏https://blog.csdn.net/leleprogrammer/article/details/124227987?spm=1001.2014.3001.5501

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

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

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

    接下来是项目目录

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

    好啦,现在先来写GameFrame的代码

    1. package com.mr.view;
    2. import javax.swing.*;
    3. import java.awt.*;
    4. public class GameFrame extends JFrame {
    5. private Container container;
    6. private GamePanel panel;
    7. public GameFrame() {
    8. setTitle("Pong");
    9. setBounds(300,300,850,1000);
    10. setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    11. container=getContentPane();
    12. panel=new GamePanel();
    13. addKeyListener(panel);
    14. container.add(panel);
    15. setVisible(true);
    16. }
    17. }

    首先啊,还是和平常一样声明package,导入一些东西,然后主窗体继承自JFrame,设置以下标题和窗口大小,还有关闭后退出程序的设置(setDefaultCloseOperation)然后实例化GamePanel面板(待会写),然后绑定事件并添加到container容器中,设置窗口可见

    然后是GamePanel的代码,也是整个程序的核心,在这里我们要做出对赛点、得分、球体碰撞等检测,并绘制图形

    1. package com.mr.view;
    2. import com.mr.model.Ball;
    3. import com.mr.model.Board;
    4. import com.mr.service.Fresh;
    5. import javax.swing.*;
    6. import java.awt.*;
    7. import java.awt.event.KeyEvent;
    8. import java.awt.event.KeyListener;
    9. import java.awt.image.BufferedImage;
    10. import java.util.ArrayList;
    11. public class GamePanel extends JPanel implements KeyListener {
    12. private BufferedImage img;
    13. private Graphics2D g2;
    14. public ArrayList select;
    15. private Fresh fresh;
    16. private Board b1;
    17. private Board b2;
    18. private Ball ball;
    19. private int score1;
    20. private int score2;
    21. private int winPoint;
    22. private int matchPoint; // 0表示没有赛点,1表示玩家1的赛点,2表示玩家2的赛点
    23. private int winner;
    24. public GamePanel() {
    25. img=new BufferedImage(850,1000,BufferedImage.TYPE_INT_BGR);
    26. g2=img.createGraphics();
    27. select=new ArrayList<>();
    28. b1=new Board(0);
    29. b2=new Board(1);
    30. ball=new Ball();
    31. score1=0;
    32. score2=0;
    33. matchPoint=0;
    34. winPoint=11;
    35. winner=0;
    36. fresh=new Fresh(this);
    37. fresh.start();
    38. }
    39. private void paintImage() {
    40. g2.setColor(Color.WHITE);
    41. if (winner==0) {
    42. int width=10;
    43. g2.fillRect(425-width/2,0,width,1000);
    44. g2.fillRect(b1.x,b1.y,b1.width,b1.height);
    45. g2.fillRect(b2.x,b2.y,b2.width,b2.height);
    46. g2.fillOval(ball.x-ball.r,ball.y-ball.r,ball.r*2,ball.r*2);
    47. }
    48. g2.setFont(new Font("黑体",Font.BOLD,56));
    49. g2.drawString(String.valueOf(score1),295,150);
    50. g2.drawString(String.valueOf(score2),525,150);
    51. g2.setFont(new Font("黑体",Font.PLAIN,22));
    52. if (winner==0) {
    53. switch (matchPoint) {
    54. case 1:
    55. g2.drawString("赛点",295,200);
    56. break;
    57. case 2:
    58. g2.drawString("赛点",525,200);
    59. break;
    60. default:
    61. break;
    62. }
    63. } else {
    64. g2.drawString("玩家"+String.valueOf(winner)+"获胜",winner==1?295:525,200);
    65. }
    66. }
    67. public void paint(Graphics g) {
    68. if (winner==0) {
    69. move();
    70. ball.move(b1.getBound(),b2.getBound());
    71. checkPoint();
    72. checkMatchPoint();
    73. b1.checkBound();
    74. b2.checkBound();
    75. }
    76. g2.setColor(Color.BLACK);
    77. g2.fillRect(0,0,850,1000);
    78. paintImage();
    79. g.drawImage(img,0,0,this);
    80. }
    81. private void checkPoint() {
    82. if (ball.x<=0) {
    83. score2+=1;
    84. } else if (ball.x+ball.r*2>=850) {
    85. score1+=1;
    86. } else {
    87. return;
    88. }
    89. ball=new Ball();
    90. }
    91. private void checkMatchPoint() {
    92. if (score1==winPoint) {
    93. winner=1;
    94. return;
    95. } else if (score2==winPoint) {
    96. winner=2;
    97. return;
    98. }
    99. if (score1+1==winPoint&&score2+1!=winPoint) {
    100. matchPoint=1;
    101. } else if (score1+1!=winPoint&&score2+1==winPoint) {
    102. matchPoint=2;
    103. } else if (score1+1==winPoint&&score2+1==winPoint) {
    104. matchPoint=0;
    105. winPoint++;
    106. } else if (score1+1!=winPoint&&score2+1!=winPoint) {
    107. matchPoint=0;
    108. }
    109. }
    110. private void move() {
    111. for (Object code:select) {
    112. if (code==(Object)KeyEvent.VK_W) {
    113. b1.y-=b1.speed;
    114. } else if (code==(Object)KeyEvent.VK_S) {
    115. b1.y+=b1.speed;
    116. } else if (code==(Object)KeyEvent.VK_UP) {
    117. b2.y-=b2.speed;
    118. } else if (code==(Object)KeyEvent.VK_DOWN) {
    119. b2.y+=b2.speed;
    120. }
    121. }
    122. }
    123. @Override
    124. public void keyPressed(KeyEvent event) {
    125. if (select.indexOf(event.getKeyCode())==-1) {
    126. select.add(event.getKeyCode());
    127. }
    128. }
    129. @Override
    130. public void keyReleased(KeyEvent event) {
    131. select.remove((Object)event.getKeyCode());
    132. }
    133. @Override
    134. public void keyTyped(KeyEvent event) {
    135. ;
    136. }
    137. }
    138. 声明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刷新帧线程

      1. package com.mr.service;
      2. import com.mr.view.GamePanel;
      3. public class Fresh extends Thread {
      4. public final int INTERVAL=20;
      5. private GamePanel panel;
      6. public Fresh(GamePanel panel) {
      7. this.panel=panel;
      8. }
      9. public void run() {
      10. while (true) {
      11. panel.repaint();
      12. try {
      13. Thread.sleep(INTERVAL);
      14. } catch (InterruptedException e) {
      15. e.printStackTrace();
      16. }
      17. }
      18. }
      19. }

      这个就while循环实现啦~~~

      然后是Ball

      1. package com.mr.model;
      2. import java.awt.*;
      3. import java.util.Random;
      4. public class Ball {
      5. public int x;
      6. public int y;
      7. public int r;
      8. private int xspeed;
      9. private int yspeed;
      10. private int max_speed;
      11. private int min_speed;
      12. private int max_speed2;
      13. private int min_speed2;
      14. public Ball() {
      15. x=425;
      16. y=500;
      17. r=15;
      18. Random rd=new Random();
      19. xspeed=rd.nextInt(1,3)==1?rd.nextInt(4,7):rd.nextInt(-6,-3);
      20. yspeed=rd.nextInt(1,3)==1?rd.nextInt(4,7):rd.nextInt(-6,-3);
      21. max_speed=12;
      22. min_speed=3;
      23. max_speed2=-3;
      24. min_speed2=-12;
      25. }
      26. public void move(Rectangle b1,Rectangle b2) {
      27. x+=xspeed;
      28. y+=yspeed;
      29. Random rd=new Random();
      30. if (getBound().intersects(b1)||getBound().intersects(b2)) {
      31. xspeed=-xspeed;
      32. yspeed+=rd.nextInt(-2,3);
      33. }
      34. else if (y-r<=0||y+r>=965) {
      35. yspeed=-yspeed;
      36. xspeed+=rd.nextInt(-2,3);
      37. }
      38. if (xspeed>max_speed&&xspeed>0) {
      39. xspeed=max_speed;
      40. }
      41. if (yspeed>max_speed&&yspeed>0) {
      42. yspeed=max_speed;
      43. }
      44. if (xspeed0) {
      45. xspeed=min_speed;
      46. }
      47. if (yspeed0) {
      48. yspeed=min_speed;
      49. }
      50. if (xspeed>max_speed2&&xspeed<0) {
      51. xspeed=max_speed2;
      52. }
      53. if (yspeed>max_speed&&yspeed<0) {
      54. yspeed=max_speed2;
      55. }
      56. if (xspeed0) {
      57. xspeed=min_speed2;
      58. }
      59. if (yspeed0) {
      60. yspeed=min_speed2;
      61. }
      62. }
      63. private Rectangle getBound() {
      64. return new Rectangle(x-r,y-r,r*2,r*2);
      65. }
      66. }

      这个模型嘛,一般情况下都有x和y还有大小,因为这个是个圆,所以我们用半径r,然后xspeed和yspeed表示各个方向的速度从而实现斜着移动,还有max_speed和min_speed用于把动态变换的速度限制于这个范围内,待会每碰到一次墙壁或板,就会适当增加或减少速度,所以要把速度限制在特定范围内,max_speed2和min_speed2也一样,前2者是用于正数速度的,后2者是用于负数速度的,然后移动的时候就对一些碰撞等情况进行检测就好了,getBound用于返回对象的Rect长方形对象,用于检测碰撞。

      最后是Board

      1. package com.mr.model;
      2. import java.awt.*;
      3. public class Board {
      4. public int x;
      5. public int y;
      6. public int width;
      7. public int height;
      8. public int speed;
      9. public Board(int type) {
      10. width=8;
      11. height=120;
      12. speed=15;
      13. y=500-height/2;
      14. if (type==0) {
      15. x=0;
      16. } else {
      17. x=850-15-width;
      18. }
      19. }
      20. public void checkBound() {
      21. if (y+height>=985) {
      22. y=985-height;
      23. }
      24. if (y<=0) {
      25. y=0;
      26. }
      27. }
      28. public Rectangle getBound() {
      29. return new Rectangle(x,y,width,height);
      30. }
      31. }

      Board和Ball也有着差不多的一些参数和类成员和方法

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

      Start.java:

      1. package com.mr.main;
      2. import com.mr.view.GameFrame;
      3. public class Start {
      4. public static void main(String[] args) {
      5. GameFrame gameFrame=new GameFrame();
      6. }
      7. }

      好啦,这样就实现了Pong游戏的效果,喜欢的话就点赞收藏关注吧!!!

    139. 相关阅读:
      00-linux pwn环境搭建
      关于新版的Maven创建Maven项目的时候只有Maven Archetype,而找不到Maven的这个问题
      Hexagon_V65_Programmers_Reference_Manual(18)
      docker安装nginx
      【HMS Core】定位服务无法获取街道信息问题
      Java项目-网页聊天程序
      【Spring源码】10. 递归调用的processConfigurationClass()方法
      jquery 复制新增Dom对象
      mysql 8.0.34 安装
      【面试题】马上金九银十了,简历该准备起来了,面试题你准备好了吗 ?浅谈 JS 浅拷贝和深拷贝
    140. 原文地址:https://blog.csdn.net/leleprogrammer/article/details/126690356