1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > java多线程游戏设计-弹球游戏(包含全部代码)

java多线程游戏设计-弹球游戏(包含全部代码)

时间:2021-02-16 01:12:03

相关推荐

java多线程游戏设计-弹球游戏(包含全部代码)

目录

需求分析 3

1.1目的 3

1.2背景 3系统设计 3

2.1 项目结构 3

2.2游戏初始化界面设计 4

2.3游戏开始界面设计 5

2.4游戏结束界面设计 5系统实现 6

3.1 游戏初始化(UI类) 6

3.1.1 框架结构 6

3.1.2 构造函数 6

3.1.3 窗体初始化 7

3.1.4 数据初始化 7

3.1.5 Paint方法 8

3.1.6 监听 8

3.2 球线程(Thread Ball类) 9

3.2.1 框架结构 9

3.2.2 run方法 10

3.3 球板线程(Thread Paddle类) 11

3.3.1框架结构 11

3.3.2 run方法 11

3.4 绘图线程(Thread Controle类) 12

3.4.1框架结构 12

3.4.2 run方法 12

3.5主函数(Main 类) 13系统测试 13

4.1游戏状态切换测试 13

4.1.1未开始-开始 13

4.1.2开始-结束 14

4.1.3结束-重新开始 14

4.2游戏运行bug改进 15

4.2.1 小球和球板碰撞bug 15

4.2.2 游戏界面闪烁 15小结 16

1.需求分析

1.1目的

利用多线程的思想,编写一个游戏。

1.2背景

1).游戏名称:弹球游戏。

2).开发环境:IDEA。

3).游戏规则:小球实现从不同角度,不同坐标落下,与挡板接触后反弹并加分,落在地面上则游戏结束失败。

2.系统设计

2.1 项目结构

由五个类构成:

1).三个线程类分别控制球的移动,球板的移动和画面的重绘。

2).UI类控制界面的初始化和绘制。

3).Main类用来创建线程和启动线程。

2.2游戏初始化界面设计

1).游戏界面显示:点击开始游戏。

2).点击以后,游戏状态改变,显示开始游戏界面。

2.3游戏开始界面设计

1).画出两个对象:球和球板。

2).显示得分。

2.4游戏结束界面设计

1).给出提示信息:游戏结束以及得分情况。

2).给出提示信息:按下空格可以重新开始。

3.系统实现

3.1 游戏初始化(UI类)

import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.awt.image.BufferedImage;public class Ui extends JFrame implements KeyListener ,MouseListener{//判断球还活着static boolean blIsOver;//分数static int intSore;//定义游戏状态 0:游戏未开始/1:游戏开始/2:暂停/3:结束static int state=0;//双缓存解决界面闪烁Image offScreenImage=null;//构造函数public Ui() {initFrame();initData();addKeyListener(this);addMouseListener(this);setVisible(true);}public void initFrame(){setTitle("弹球游戏");setBackground(Color.WHITE);setSize(900,600);//setLocationRelativeTo(null);setLocation(300,50);setResizable(false);setFocusable(true); //获取键盘焦点,将键盘聚集在游戏界面上//setVisible(true);setDefaultCloseOperation(EXIT_ON_CLOSE);}//初始化窗体public void initData(){//球的初始化坐标ThreadBall.PositionX=(int)(Math.random()*100)+300;//300-400ThreadBall.PositionY=(int)(Math.random()*100)+100;//100-200//初始化角度ThreadBall.intDu= (int)(Math.random()*3)+1;//1-4//初始化球板ThreadPaddles.PositionA=0;ThreadPaddles.PositionB=450;//初始化intSore=0;System.out.println("state="+state);}//初始化数据public void paint(Graphics gImage) {if(offScreenImage==null){offScreenImage=createImage(900,600);}//获取画笔对象Graphics g=offScreenImage.getGraphics();//画笔对象g.fillRect(0,0,900,600); //填充一个宽900,高600的区域if(state==0){//游戏未开始g.clearRect(0, 0, 900, 600);//0,0:是相对于容器的坐标//g.drawImage(GameUtils.bgImg,0,0,null);g.setFont(new Font("仿宋",Font.BOLD,40));g.drawString("点击开始游戏",300,300);repaint();}if(state==1){g.clearRect(0, 0, 900, 600);//0,0:是相对于容器的坐标//球板:fillRect用笔刷g(红色)填充一个矩形(从左上边界开始填充)g.setColor(Color.red);g.fillRect(ThreadPaddles.PositionA,ThreadPaddles.PositionB,ThreadPaddles.RecWidth,ThreadPaddles.RecHeight);//球:设置画笔颜色为绿色,fillOval用笔刷g(绿色)填充一个圆(从左上边界开始填充)g.setColor(Color.green);g.fillOval(ThreadBall.PositionX,ThreadBall.PositionY, ThreadBall.BallWidth,ThreadBall.BallHeight);g.setFont(new Font("宋体", ALLBITS, 50));g.setColor(Color.BLUE);g.drawString(new String("分数:" + String.valueOf(intSore)), 550, 150);repaint();}if(state==3){//游戏结束//if(blIsOver) {//绘制游戏结束的界面g.clearRect(0, 0, 900, 600);//0,0:是相对于容器的坐标g.setFont(new Font("宋体", ALLBITS, 50));g.setColor(Color.RED);g.drawString(new String("游戏结束!你的得分:" +String.valueOf(intSore)) , 249, 250);g.setColor(Color.BLUE);g.drawString(new String("按下空格重新开始"), 250, 350);repaint();//}}//将绘制好的图片一次性呈现出来gImage.drawImage(offScreenImage,0,0,null);}//画笔//监听@Override//鼠标监听public void mouseClicked (MouseEvent e){if (e.getButton() == 1 && state == 0) {//按下鼠标或者状态为没开始state = 1;//更改游戏状态并且重绘repaint();System.out.println("state=" +state);}}@Override//键盘监听public void keyPressed (KeyEvent e){char b = e.getKeyChar();int a = e.getKeyCode();if (b == 'd' || b == 'D' || a == 39) {ThreadPaddles.PositionA+=10;} else if (b == 'a'||b == 'A' || a == 37) {ThreadPaddles.PositionA-=10;} else if(a==32&&state==3){state=0;initData();repaint();}/*if(a==32){//暂停switch (state){case 1:state=2;System.out.println("s"+state);break;case 2:state=1;System.out.println("s"+state);break;default:}}*/}@Overridepublic void mousePressed (MouseEvent e){}@Overridepublic void mouseReleased (MouseEvent e){}@Overridepublic void mouseEntered (MouseEvent e){}@Overridepublic void mouseExited (MouseEvent e){}@Overridepublic void keyTyped (KeyEvent e){}@Overridepublic void keyReleased (KeyEvent e){}}

3.1.1 框架结构

1).属性:

(1)游戏状态=0: 0:游戏未开始/1:游戏开始/2:暂停/3:结束。

(2)分数=0。

(3)缓存画布:解决页面闪烁。

2).方法:

(1)无参构造。

(1)初始化窗体。

(2)初始化数据。

(3)重写鼠标监听和键盘监听。

(4)画笔。

3.1.2 构造函数

构造方法中调用了两个初始化方法和添加了两个监听。

3.1.3 窗体初始化

1).Init Frame用于初始化窗体。

2).标题:弹球游戏;

背景色:白色;

窗体大小:900*600;

窗体位置:(300,50);

窗体大小不可变;

键盘焦点汇聚在界面上;

设置退出方式:按下右上角×就可以结束进程。

3.1.4 数据初始化

1).initData用于初始化数据。

2).初始化球的坐标:用random函数获得一个随机数:x(300-400),y(100-200)。

3).初始化球的角度:用random函数获得一个随机数:intDu(1-4)。

4).初始化球板的坐标:(0,450)。

5).初始化分数:0。

3.1.5 Paint方法

1).state=0:定义了游戏未开始的界面。

2).在界面上画一个字符串“点击开始游戏”。

3).添加一个鼠标监听,当按下鼠标后,游戏状态=“1”。

1).state=1:定义了游戏开始的界面。

2).在界面上画一个绿色球和一个红色的球板。

3).在界面上画一个字符串“分数:”。

1).tate=3:定义了游戏结束的界面。

2).在界面上画两个字符串“游戏结束1 分数:”和提示信息“按下空格后重新开始”。

3).添加一个键盘监听,当按下空格,state=0,游戏重新开始。

3.1.6 监听

1).鼠标监听:当游戏状态state=0游戏未开始时,检测到鼠标按下,则将游戏状态state改为1,并且重绘。

2).键盘监听:

(1)监听键盘A/D/a/d/←/→按键,用来控制球板的坐标。

(2)当游戏状态state=3游戏结束时,监听空格键,检测到空格键按下时,将状态state改为0,初始化数据,并且重绘界面。

3.2 球线程(Thread Ball类)

public class ThreadBall extends Thread{//球的坐标及大小static int BallWidth=25,BallHeight=25,PositionX,PositionY;static int intDu;static boolean blUpOrDown;//游戏状态开始?public ThreadBall(){}public void run(){while(true){if(Ui.state==1) {if (PositionY >= 600) {blUpOrDown=true;Ui.state = 3;//游戏状态为结束Ui.blIsOver = true;//游戏结束System.out.println("state="+Ui.state);} else if (PositionY < 600) {Ui.state = 1;//游戏状态为开始blUpOrDown = false;}//向上碰撞 上墙面的情况if (PositionY <= 0) {if (intDu == 3) {intDu = 1; }else if (intDu == 4) {intDu = 2;}}//左侧墙面碰撞if (PositionX <= 0) {if (intDu == 2) {intDu = 1; }else if (intDu == 4) {intDu = 3; }}//右侧墙面碰撞if (PositionX >= 900) {if (intDu == 1) {intDu = 2; }else if (intDu == 3) {intDu = 2; }}}if(!blUpOrDown){switch (intDu){//1为右下方行进,2为左下方,3 为右上方,4为左上方case 1:PositionY+=6;PositionX+=6;break;case 2:PositionY+=6;PositionX-=6;break;case 3:PositionY-=6;PositionX+=6;break;case 4:PositionY-=6;PositionX-=6;break;}}try {sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public int getBallWidth() {return BallWidth;}public void setBallWidth(int ballWidth) {BallWidth = ballWidth;}public int getBallHeight() {return BallHeight;}public void setBallHeight(int ballHeight) {BallHeight = ballHeight;}public int getPositionX() {return PositionX;}public void setPositionX(int positionX) {PositionX = positionX;}public int getPositionY() {return PositionY;}public void setPositionY(int positionY) {PositionY = positionY;}public int getIntDu() {return intDu;}public void setIntDu(int intDu) {this.intDu = intDu;}}

3.2.1 框架结构

1).属性:球的坐标,宽高和角度。

2).方法:

(1)重写Run方法。

(2)set和get方法。

3.2.2 run方法

1).首先判断游戏是否结束:

当状态为1时,并且小球坐标<600,继续,否则,将游戏状态改为3结束。

2).当小球存活时:判断与界面碰撞的情况,由入射角度将角度设置为相对应的初始角度。

3).用一个Switch语句控制小球运动的坐标。

4).sleep中的休眠时间控制小球移动速度,数值越小,小球移动速度越快。

3.3 球板线程(Thread Paddle类)

import javax.swing.*;public class ThreadPaddles extends Thread{//球板static int PositionA=0,PositionB=450,RecWidth=200,RecHeight=20,Width=900;public ThreadPaddles(){}public void run(){while(true){if(Ui.state==1&&ThreadBall.PositionX>PositionA&&ThreadBall.PositionX+25<=PositionA+RecWidth){if(ThreadBall.PositionY>=PositionB&&ThreadBall.PositionY<=PositionB+RecHeight){//ThreadBall.blUpOrDown=true;Ui.intSore+=10;System.out.println(Ui.intSore);switch (ThreadBall.intDu){case 1:ThreadBall.intDu=3;break;case 2:ThreadBall.intDu=4;break;}JOptionPane.showMessageDialog(null,ThreadBall.PositionX,"2", JOptionPane.INFORMATION_MESSAGE);}}try {sleep(100);//参数改小时,分数会错误,因为执行的次数多。} catch (InterruptedException e) {e.printStackTrace();}}}public int getPositionA() {return PositionA;}public void setPositionA(int positionA) {PositionA = positionA;}public int getPositionB() {return PositionB;}public void setPositionB(int positionB) {PositionB = positionB;}public int getRecWidth() {return RecWidth;}public int getRecHeight() {return RecHeight;}}

3.3.1框架结构

1).属性:球板的坐标,宽高。

2).方法:

(1)重写Run方法。

(2)set和get方法。

3.3.2 run方法

1).当游戏状态为1开始状态,并且小球横坐标>球板的横坐标,并且小球的横坐标+宽度<球板的横坐标+板子宽度。并且小球的纵坐标<球板纵坐标,并且小球纵坐标>球板纵坐标+球板高度时,小球与球板碰撞。分数+10。

2).碰撞后重新设置角度。

3).sleep中的休眠时间控制球板移动速度,数值越小,控制球板移动速度越快。

3.4 绘图线程(Thread Controle类)

public class ThreadControle extends Thread {Ui ui=new Ui();public void run(){if (Ui.state==1) {//还活着,就重画while (true) {ui.repaint();}}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}

3.4.1框架结构

1).实例化UI类

2).重写run方法。

3.4.2 run方法

1).当游戏状态为开始时,不断重绘界面。

3.5主函数(Main 类)

public class Main {public static void main(String[] args) {//创建三个线程ThreadControle threadControle = new ThreadControle();ThreadBall threadBall = new ThreadBall();ThreadPaddles threadPaddles = new ThreadPaddles();//启动他们threadPaddles.start();threadControle.start();threadBall.start();try {threadPaddles.join();//防止线程堵塞,相当于用了wait()threadControle.join();threadBall.join();} catch (InterruptedException e) {e.printStackTrace();}}}

1).创建三个线程类对象

2).启动三个线程对象。

3).在try/catch中调用join,防止线程堵塞。

4.系统测试

4.1游戏状态切换测试

4.1.1未开始-开始

state=0——state=1

4.1.2开始-结束

state=1——state=3

4.1.3结束-重新开始

state=3——state=0

4.2游戏运行bug改进

4.2.1 小球和球板碰撞bug

1.Bug描述:

休眠时间太长会造成:明明小球和球板已经已经碰上了,小球却穿过了球板并没有反弹。

休眠时间太短会造成:线程堵塞,分数出现错误。

2.解决方案:

1.选取合适的休眠值,并且将小球和球板重绘的休眠时间改为一样。

2.经过测试,选取100ms是比较合适的。

4.2.2 游戏界面闪烁

1.问题描述:

小球和球板实现视觉上的移动,原理就是在间隔很小的时间,将这些对象绘制在窗体上。由于是不断绘制的,会造成视觉上的闪烁。

2.解决方案:

定义一个缓冲画布,将这些对象一次性绘制在缓冲画布上,再一次性绘制出来,就能解决频闪问题。

5.小结

1)建立了三个线程类,小球线程,球板线程,重绘线程,理解了多线程思想和怎么创建多线程。

(a)建立线程类有两种方法,一种是继承Thread,另一种是继承Implement接口,我采用的是第一种方法:继承Thread类。

(b)继承Thread后要重写run方法。我在run方法中写入了小球的坐标改变,球板的坐标改变,重绘。

(c)在Main函数中实例化三个线程类,线程实例化后的对象调用run方法启动线程。

(d)线程停止,采用用标志位的方法。

(e)线程休眠:运用在了控制球板和球的移动,重绘依次休眠一会,休眠时间越短,重绘速度越快,移动速度越快。

2)UI类实现了窗体的构建,利用构造函数初始化窗体,将初始化的语句封装成函数写入构造函数。 UI类继承了监听接口,所以要重写监听函数。继承了JFrame类,重写paint方法,获取画笔,将需要的图形对象画在缓冲区,再一次性呈现出来。

3)监听:实现监听接口,重写监听函数。

4)可以优化的地方:

(a)界面不够美观。

(b)游戏规则比较简单,可以添加一个时间线程,可以随着时间的增加,掉下来的小球数目增加,或者障碍物增加。

可以添加背景音乐线程,随着游戏的开

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。