用户界面如下 :
玩法:双方玩家可以分别控制A-D键和←-→键来进行各自球拍的位置,如果某方没有接住小球,则对方获胜,游戏结束。
代码如下:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class PinBall {
//创建窗口对象
private Frame frame = new Frame("弹球游戏");
//桌面宽度
private final int TABLE_WIDTH = 300;
//桌面高度
private final int TABLE_HEIGHT = 400;
//球拍的高度和宽度
private final int RACKET_WIDTH = 60;
private final int RACKET_HEIGHT = 20;
//小球的大小
private final int BALL_SIZE = 16;
//定义变量记录小球的坐标
private int ballX = 120;
private int ballY = 50;
//定义变量记录小球在X和Y方向上分别移动的速度
private int speedX = 8;
private int speedY = 12;
//定义变量记录球拍的坐标
private int racketX = 120;
private final int racketY = 370;//球拍位置在纵坐标不发生改变,只能横向移动
//TODO 定义对手球拍的坐标
private int OppracketX = 120;
private final int OppracketY = 20;
//定义变量标识当前游戏是否已经结束
private boolean isOver = false;
//TODO 定义上方失误或者下方事物标记
private boolean upside = true;
//声明一个定时器(重绘小球)
private Timer timer;
//自定义一个类,继承canvas,充当画布
private class MyCanvas extends Canvas{
@Override
public void paint(Graphics g){
//TODO 在这里绘制内容
if(isOver){
//游戏结束
g.setColor(Color.BLUE);//设置颜色
g.setFont(new Font("Times",Font.BOLD,30));//设置一个字体
if(upside){
g.drawString("下方玩家获胜!",50,150);//设置字体位置
//g.drawString("但台智聪依旧美丽!",20,230);//设置字体位置
}else {
g.drawString("上方玩家获胜!",50,150);//设置字体位置
}
}else {
//还在游戏中
//绘制小球
g.setColor(Color.GREEN);//设置小球颜色
g.fillOval(ballX,ballY,BALL_SIZE,BALL_SIZE);//fillOval-填充圆(椭圆)形,填充一个小球,四变量分变为x,y,高,宽;
//绘制球拍
g.setColor(Color.pink);
g.fillRect(racketX,racketY,RACKET_WIDTH,RACKET_HEIGHT);//fillRect-绘制填充矩形
//TODO 绘制对手球拍
g.setColor(Color.orange);
g.fillRect(OppracketX,OppracketY,RACKET_WIDTH,RACKET_HEIGHT);//fillRect-绘制填充矩形
}
}
}
//创建绘画区域
MyCanvas drawArea = new MyCanvas();
public void init(){
//组装视图,游戏逻辑的控制
//创建监听器,完成球拍坐标的变化
KeyListener listener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//获取当前按下的键
int keyCode = e.getKeyCode();
//物体和窗口的坐标都是左上角
if(keyCode == KeyEvent.VK_LEFT){
//接收到←箭头,应该向左移动
if(racketX>0){
racketX -= 10;
}
}
if(keyCode == KeyEvent.VK_RIGHT){
//接收到→箭头,应该向右移动
if(racketX < (TABLE_WIDTH - RACKET_WIDTH)){
racketX += 10;
}
}
}
};
//TODO 创建对手监听器,完成球拍坐标的变化
KeyListener Opplistener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//获取当前按下的键
int keyCode = e.getKeyCode();
//物体和窗口的坐标都是左上角
if(keyCode == KeyEvent.VK_A){
//接收到a,应该向左移动
if(OppracketX>0){
OppracketX -= 10;
}
}
if(keyCode == KeyEvent.VK_D){
//接收到d,应该向右移动
if(OppracketX < (TABLE_WIDTH - RACKET_WIDTH)){
OppracketX += 10;
}
}
}
};
//给Frame和drawArea注册监听器
frame.addKeyListener(listener);
drawArea.addKeyListener(listener);
//TODO 给Frame和drawArea注册对手监听器
frame.addKeyListener(Opplistener);
drawArea.addKeyListener(Opplistener);
//小球坐标的控制
ActionListener task = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//根据边界范围,修正速度(即考虑到碰到墙壁以及球拍的反弹情况)
if(ballX <= 0 || ballX >= (TABLE_WIDTH-BALL_SIZE)){
speedX = -speedX;
}
/*if(ballY <0 || (ballY>=racketY-BALL_SIZE && ballX>racketX && ballX<racketX+RACKET_WIDTH) ){
speedY = -speedY;
}*/
if(ballY>=racketY-BALL_SIZE && ballX>racketX && ballX<racketX+RACKET_WIDTH){
speedY = -speedY;
}
if(ballY<=OppracketY+RACKET_HEIGHT && ballX>OppracketX && ballX<OppracketX+RACKET_WIDTH){
speedY = -speedY;
}
if(ballY > racketY-BALL_SIZE && (ballX<racketX || ballX>racketX+RACKET_WIDTH)){
//证明当前小球没有被下方球拍接住,掉到下方
//停止定时器
timer.stop();
//修改游戏是否结束标记
isOver = true;
//标记下方失误
upside = false;
//重绘界面
drawArea.repaint();
}
if(ballY<OppracketY+RACKET_HEIGHT && (ballX<OppracketX || ballX>OppracketX+RACKET_WIDTH)){
//证明当前小球没有被上方球拍接住,掉到上方
//停止定时器
timer.stop();
//修改游戏是否结束标记
isOver = true;
//标记上方失误
upside = true;
//重绘界面
drawArea.repaint();
}
//更新小球的坐标,重绘界面
ballX += speedX;
ballY += speedY;
//重绘界面
drawArea.repaint();
}
};
timer = new Timer(100,task);//每隔100毫秒更新task
timer.start();
drawArea.setPreferredSize(new Dimension(TABLE_WIDTH,TABLE_HEIGHT));
frame.add(drawArea);
//设置frame最佳大小,并且可视
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new PinBall().init();
}
}
备注:此代码是对哔站“黑马程序员java进阶教程深入浅出讲解java的图形化界面编程p32”的改编。