原网址:https://gitee.com/EdsionKXXX/pinball-game-demo
源代码:
1 package AWT_GameDemo; 2 3 4 import javax.swing.*; 5 import java.awt.*; 6 import java.awt.event.*; 7 8 9 public class GameingDemo { 10 11 // 创建窗口对象 12 Frame s = new Frame("BallGame"); 13 //设置窗口高度宽度 14 private final int W_Height = 400; 15 private final int W_Wdith = 300; 16 //球拍高宽 17 private final int P_Height = 20; 18 private final int P_Wdith = 60; 19 //球拍坐标 20 private int PX = 130; 21 private final int PY = 340; 22 23 //小球高宽 24 private final int B_SIZE = 16; 25 //小球坐标 26 private int BX = 120; 27 private int BY = 20; 28 //小球速度 29 private int BXV = 10; 30 private int BYV = 5; 31 //判断游戏是否结束变量 32 private boolean isover = false; 33 34 //定义一个定时器(刷新画面用) 35 private Timer timer; 36 37 //创建绘画组件 38 public class MyCanvas extends Canvas { 39 @Override 40 public void paint(Graphics g) { 41 //绘制画面 42 //游戏结束内容 43 if (isover) { 44 g.setColor(Color.RED); 45 g.setFont(new Font("times", Font.BOLD, 30)); 46 g.drawString("GAME OVER", 20, 200); 47 48 } else { 49 50 51 //游戏中内容 52 //绘制小球 53 g.setColor(Color.RED); 54 g.fillOval(BX, BY, B_SIZE, B_SIZE); 55 //绘制球拍 56 g.setColor(Color.PINK); 57 g.fillRect(PX, PY, P_Wdith, P_Height); 58 59 } 60 } 61 } 62 63 MyCanvas myc = new MyCanvas(); 64 65 public void mix() { 66 //球拍坐标处理逻辑 67 KeyListener k = new KeyAdapter() { 68 @Override 69 public void keyPressed(KeyEvent e) { 70 //获取当前按下按钮 71 int kc = e.getKeyCode(); 72 if (kc == KeyEvent.VK_LEFT) { 73 if (PX > 0) { 74 PX -= 10; 75 } 76 } else if (kc == KeyEvent.VK_RIGHT) { 77 if (PX < (W_Wdith - P_Wdith)) { 78 PX += 10; 79 } 80 } 81 } 82 }; 83 //给frame 和画布注册监听 84 s.addKeyListener(k); 85 myc.addKeyListener(k); 86 //小球处理逻辑 87 ActionListener task = new ActionListener() { 88 @Override 89 public void actionPerformed(ActionEvent e) { 90 //修正速度 91 if (BX <= 0 || BX > W_Wdith - B_SIZE) { 92 BXV = -BXV; 93 } 94 if (BY <= 0 || (BY > PY - B_SIZE && BX > PX && BX < PX + P_Wdith)) { 95 BYV = -BYV; 96 } 97 //游戏状态修正 98 if (BY + B_SIZE > PY && (BX < PX || BX > PX + P_Wdith)) { 99 timer.stop(); 100 isover = true; 101 myc.repaint(); 102 } 103 104 //更新小球坐标重汇界面 105 BX += BXV; 106 BY += BYV; 107 myc.repaint(); 108 } 109 }; 110 timer = new Timer(100, task); 111 timer.start(); 112 myc.setPreferredSize(new Dimension(W_Wdith, W_Height)); 113 s.add(myc); 114 s.pack(); 115 s.setVisible(true); 116 s.addWindowListener(new WindowAdapter() { 117 @Override 118 public void windowClosing(WindowEvent e) { 119 System.exit(0); 120 } 121 }); 122 } 123 124 125 public static void main(String[] args) { 126 GameingDemo G = new GameingDemo(); 127 G.mix(); 128 129 } 130 }
界面演示:游戏中:
游戏结束:
功能描述:利用awt包中的Frame类创建窗口,Canvas类创建画布,利用抽象类Graphics的画笔功能实现对小球和球拍的不断重绘来实现游戏动态效果,其中swing组件中的Timer类来实现小球和球拍的定时重绘和处理逻辑,处理逻辑为:小球在触及窗口的左,右,上和球拍时反弹,越过球拍的y坐标游戏结束。该程序包含的和窗口大小,小球大小和球拍大小等长宽数值,小球运动速度等皆为该类的成员变量。
1.部分修正:
(1)窗口位置调整:
s.pack();//s为Frame类实例对象
pack()是window类中的方法,可以根据添加的组件和设置的布局来自动设置窗口的位置和大小,但该窗口大小已设置,所以将位置调整为当前屏幕正中心附近为最佳,所以将原代码改为:
s.setBounds(500,200,W_Wdith,W_Height);//后两个参数为窗口高度宽度
或者:
s.pack(); s.setLocation(500,200);
(2)判定修正:
先来阅读源代码中的游戏结束判定:
if (BY + B_SIZE > PY && (BX < PX || BX > PX + P_Wdith)) {
99 timer.stop(); 100 isover = true; 101 myc.repaint(); 102 }
if (isover) { 44 g.setColor(Color.RED); 45 g.setFont(new Font("times", Font.BOLD, 30)); 46 g.drawString("GAME OVER", 20, 200);
47 }
/*
*BY:小球Y坐标位置,BX:小球X坐标位置,B_SIZE:小球直径,PX:球拍X坐标位置,P_Wdith(也许原作者拼错了):球拍宽度。
*timer:Timer类实例化对象,myc:Canvas类的自定义子类 MyCanvas实例化对象,g:Graphics类实例化画笔对象。isover:判定游戏是否结束的布尔类型对象
*/
阅读源代码的判定条件时,可知当小球的Y坐标加上小球直径,也就是小球的下边界超过球拍的Y坐标(球拍的上边界)时,并且小球的X坐标位置脱离球拍的X坐标位置时,游戏结束。但对于我们玩过弹球游戏的玩家来说,该判定条件下结束游戏有些突兀,应该在小球出现在球拍下边界以下时,玩家知道已经失败的时候结束游戏才显得比较正常,所以将原来的代码改为:
if (BY + B_SIZE > PY + P_Height + 10 && (BX < PX || BX > PX + P_Wdith))
在小球的下边界越过球拍下边界的10个像素单位时结束游戏。
或者改为:
if (BY > PY + P_Height && (BX < PX || BX > PX + P_Wdith))
在小球的上边界越过球拍下边界时结束游戏
2.功能扩充:
(1):键位添加
源代码利用键位监听实现了球拍的左右移动,根据大部分游戏玩家的习惯,WASD移动模式已经深入玩家的心中,所以将键位A和键位D也作为左右移动的键位,源代码改为:
if (kc == KeyEvent.VK_LEFT || kc == KeyEvent.VK_A) { if (PX > 0) { PX -= 10; } } else if (kc == KeyEvent.VK_RIGHT || kc == KeyEvent.VK_D) { if (PX < (W_Wdith - P_Wdith)) { PX += 10;
//kc为获取的当前按键,键位监听在第67行
(2):积分功能
有了积分的功能才知道自己有多厉害!ヽ(´•ω•`)、,源代码改为:
if (isover) { g.setColor(Color.RED); g.setFont(new Font("times", Font.BOLD, 30)); g.drawString("GAME OVER", 60, 200); g.setColor(Color.pink); g.setFont(new Font("times", Font.BOLD, 20)); g.drawString("得分:"+Score/2,110,250); }
if (BY <= 0 || (BY > PY - B_SIZE && BX > PX && BX < PX + P_Wdith)) {
BYV = -BYV;
++Score;
}
private int Score = 0;//新增成员变量
(3)暂停功能:
单机游戏都会提供暂停游戏的功能,来为玩家提供方便,新添代码:
if(kc == KeyEvent.VK_SPACE){ if(isPause){ isPause = false; timer.restart(); }else{ isPause = true; timer.stop(); }
private boolean isPause = false;//新增成员变量
(4)菜单选项:
利用Menu相关的组件为该游戏添加菜单,可以为玩家提供部分自定义功能,新添代码如下:
MenuBar mb = new MenuBar(); Menu menu = new Menu("选项");
......
s.setMenuBar(mb);
mb.add(menu);
4.1:设置背景颜色:
玩家可自行设置背景颜色,注意不要与小球和球拍重色。
(成员变量)
Menu m1 = new Menu("背景颜色"); MenuItem m11 = new MenuItem("黑色"); MenuItem m12 = new MenuItem("蓝色"); MenuItem m13 = new MenuItem("绿色"); MenuItem m14 = new MenuItem("黄色");
(组装)
menu.add(m1);
m1.add(m11);
m1.add(m12);
m1.add(m13);
m1.add(m14);
(设置监听器)
m11.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myc.setBackground(Color.black);
}
});
m12.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myc.setBackground(Color.blue);
}
});
m13.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myc.setBackground(Color.green);
}
});
m14.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myc.setBackground(Color.YELLOW);
}
});
4.2:设置小球速度
玩家也许想挑战更快速度的小球,可以提供小球速度修改的功能。
Menu m2 = new Menu("修改速度"); ......... MenuItem m21 = new MenuItem("加快"); MenuItem m22 = new MenuItem("减慢");
.........
(组装)
menu.add(m2);
m2.add(m21);
m2.add(m22);
(设置监听器)
m21.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
BYV *= 1.5;
BXV *= 1.5;
}
});
m22.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//速度不允许低于一定值
if (BYV <= 5 || BXV <= 10) {
BYV = 5;
BXV = 10;
} else {
BYV /= 1.5;
BXV /= 1.5;
}
}
});
这里要注意的是,X方向和Y方向的速度的增幅和减幅要一致,不然会导致小球速度的轨迹改变,所以采用乘除运算符,确保速度为线性变化。
总结:
本次的程序改进和扩充应用的主要技术就是Java AWT 中的内容,通过自身的想象力和创造力,利用GUI编程还可以做出更多东西,若本文有错误欢迎指正,本人也只是小白,希望能进一步学习到更多内容。本文添加代码皆为原创,如有雷同纯属巧合。
改后源代码:
1 package AWT_GameDemo; 2 3 4 import javax.swing.*; 5 import java.awt.*; 6 import java.awt.event.*; 7 8 9 public class GameingDemo { 10 11 // 创建窗口对象 12 Frame s = new Frame("BallGame"); 13 private int Score = 0; 14 private boolean isPause = false; 15 MenuBar mb = new MenuBar(); 16 Menu menu = new Menu("选项"); 17 Menu m1 = new Menu("背景颜色"); 18 Menu m2 = new Menu("修改速度"); 19 MenuItem m11 = new MenuItem("黑色"); 20 MenuItem m12 = new MenuItem("蓝色"); 21 MenuItem m13 = new MenuItem("绿色"); 22 MenuItem m14 = new MenuItem("黄色"); 23 MenuItem m21 = new MenuItem("加快"); 24 MenuItem m22 = new MenuItem("减慢"); 25 //设置窗口高度宽度 26 private final int W_Height = 400; 27 private final int W_Wdith = 300; 28 //球拍高宽 29 private final int P_Height = 20; 30 private final int P_Wdith = 60; 31 //球拍坐标 32 private int PX = 130; 33 private final int PY = 340; 34 35 //小球高宽 36 private final int B_SIZE = 16; 37 //小球坐标 38 private int BX = 120; 39 private int BY = 20; 40 //小球速度 41 private int BXV = 10; 42 private int BYV = 5; 43 //判断游戏是否结束变量 44 private boolean isover = false; 45 46 //定义一个定时器(刷新画面用) 47 private Timer timer; 48 49 //创建绘画组件 50 public class MyCanvas extends Canvas { 51 @Override 52 public void paint(Graphics g) { 53 //绘制画面 54 //游戏结束内容 55 if (isover) { 56 g.setColor(Color.RED); 57 g.setFont(new Font("times", Font.BOLD, 30)); 58 g.drawString("GAME OVER", 60, 200); 59 g.setColor(Color.pink); 60 g.setFont(new Font("times", Font.BOLD, 20)); 61 g.drawString("得分:" + Score / 2, 110, 250); 62 } else { 63 64 65 //游戏中内容 66 //绘制小球 67 g.setColor(Color.RED); 68 g.fillOval(BX, BY, B_SIZE, B_SIZE); 69 //绘制球拍 70 g.setColor(Color.PINK); 71 g.fillRect(PX, PY, P_Wdith, P_Height); 72 73 } 74 } 75 } 76 77 MyCanvas myc = new MyCanvas(); 78 79 public void mix() { 80 //球拍坐标处理逻辑 81 KeyListener k = new KeyAdapter() { 82 @Override 83 public void keyPressed(KeyEvent e) { 84 //获取当前按下按钮 85 int kc = e.getKeyCode(); 86 if (kc == KeyEvent.VK_LEFT || kc == KeyEvent.VK_A) { 87 if (PX > 0) { 88 PX -= 10; 89 } 90 } else if (kc == KeyEvent.VK_RIGHT || kc == KeyEvent.VK_D) { 91 if (PX < (W_Wdith - P_Wdith)) { 92 PX += 10; 93 } 94 } 95 if (kc == KeyEvent.VK_SPACE) { 96 if (isPause) { 97 isPause = false; 98 timer.restart(); 99 } else { 100 isPause = true; 101 timer.stop(); 102 } 103 } 104 } 105 }; 106 //给frame 和画布注册监听 107 s.addKeyListener(k); 108 myc.addKeyListener(k); 109 //小球处理逻辑 110 ActionListener task = new ActionListener() { 111 @Override 112 public void actionPerformed(ActionEvent e) { 113 //修正速度 114 if (BX <= 0 || BX > W_Wdith - B_SIZE) { 115 BXV = -BXV; 116 } 117 if (BY <= 0 || (BY > PY - B_SIZE && BX > PX && BX < PX + P_Wdith)) { 118 BYV = -BYV; 119 ++Score; 120 } 121 //游戏状态修正 122 if (BY > PY + P_Height && (BX < PX || BX > PX + P_Wdith)) { 123 timer.stop(); 124 isover = true; 125 myc.repaint(); 126 } 127 128 //更新小球坐标重汇界面 129 BX += BXV; 130 BY += BYV; 131 myc.repaint(); 132 } 133 }; 134 timer = new Timer(100, task); 135 timer.start(); 136 myc.setPreferredSize(new Dimension(W_Wdith, W_Height)); 137 s.add(myc); 138 s.setMenuBar(mb); 139 mb.add(menu); 140 menu.add(m1); 141 m1.add(m11); 142 m1.add(m12); 143 m1.add(m13); 144 m1.add(m14); 145 menu.add(m2); 146 m2.add(m21); 147 m2.add(m22); 148 m11.addActionListener(new ActionListener() { 149 @Override 150 public void actionPerformed(ActionEvent e) { 151 myc.setBackground(Color.black); 152 } 153 }); 154 m12.addActionListener(new ActionListener() { 155 @Override 156 public void actionPerformed(ActionEvent e) { 157 myc.setBackground(Color.blue); 158 } 159 }); 160 m13.addActionListener(new ActionListener() { 161 @Override 162 public void actionPerformed(ActionEvent e) { 163 myc.setBackground(Color.green); 164 } 165 }); 166 m14.addActionListener(new ActionListener() { 167 @Override 168 public void actionPerformed(ActionEvent e) { 169 myc.setBackground(Color.YELLOW); 170 } 171 }); 172 m21.addActionListener(new ActionListener() { 173 @Override 174 public void actionPerformed(ActionEvent e) { 175 BYV *= 1.5; 176 BXV *= 1.5; 177 } 178 }); 179 m22.addActionListener(new ActionListener() { 180 @Override 181 public void actionPerformed(ActionEvent e) { 182 //速度不允许低于一定值 183 if (BYV <= 5 || BXV <= 10) { 184 BYV = 5; 185 BXV = 10; 186 } else { 187 BYV /= 1.5; 188 BXV /= 1.5; 189 } 190 } 191 }); 192 s.pack(); 193 s.setLocation(500, 200); 194 s.setVisible(true); 195 s.addWindowListener(new WindowAdapter() { 196 @Override 197 public void windowClosing(WindowEvent e) { 198 System.exit(0); 199 } 200 }); 201 } 202 203 204 public static void main(String[] args) { 205 GameingDemo G = new GameingDemo(); 206 G.mix(); 207 208 } 209 }
部分效果展示:
标签:Java,弹球,awt,小球,private,public,Wdith,new,PX From: https://www.cnblogs.com/lplpok/p/17177776.html