ImageVerifierCode 换一换
格式:DOCX , 页数:37 ,大小:210.08KB ,
资源ID:27263081      下载积分:10 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/27263081.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(桌面弹球java开发分析及源码.docx)为本站会员(b****4)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

桌面弹球java开发分析及源码.docx

1、桌面弹球java开发分析及源码桌面弹球(java开发)分析及源码桌面弹球1 桌面弹球概述桌面弹球是游戏中常见的游戏,从以前的掌上游戏机到如今的手机游戏,都是一个十分经典的游戏。玩家控制一个可以左右移动的挡板去改变运动中小球的移动方向,目的是用小球消除游戏屏幕中的所有障碍物到达下一关,在障碍物被消除的过程中,可能会产生一些能改变挡板或者小球状态的物品,例如:挡板变长、变短,小球威力加强等等。本章主要介绍如何实现一个简单的弹球游戏,让读者了解“动画”的实现原理。在本章中,将介绍与使用Java的绘图功能,使用到JPanel的paint(Graphics g)方法去绘图,绘图主要是依靠这个方法中的Gr

2、aphics类型的参数,将使用Java中的Timer去重复绘图,产生动画效果,桌面弹球游戏的效果如图4.1所示。图 1.1 桌面弹球1.1 动画原理简单地来说,动画是利用人的视觉暂留的生理特性,实现出来的一种假象,只要每隔一段时间(这个时间少于人的视频暂留时间)就重新绘制一幅状态改变的图片,就能造成这种“动”的假象。我们在程序中不断的进行绘画(使用repaint方法),对程序来讲,只需要在短时间内进行多次的绘画,并且每次绘画都需要改变绘画的相关值,就可以达到“动画”的效果。1.2 小球反弹的方向在本章实现的过程中,我们会设置小球于对称的方式,并出现少许偏移的方式反弹,如图4.2所示。让小球反弹

3、出现少许编移是为了让游戏增加点不确定性,增加游戏的趣味性。我们需要在编写游戏前确定这些小细节,这样在开发的过程中,我们就可以按照这些小细节去逐步实现我们的程序。图 1.2 小球的反弹2 流程描述玩家使用左右方向键开始游戏与控制挡板,在未消除完所有的障碍物或者挡板没有档住向下移动的小球之前,会一直处于游戏状态,在这个状态中,小球会一直处于直线运动或者改变方向,当小球消除掉障碍物的时候,有机率产生一些物品,产生的物品会直线向下移动,用挡板接住物品后,物品的特殊效果会生效。如果消除了所有的障碍物,就判断玩家为赢,如果挡板没有接住向下移动的小球,就判断玩家为输。具体的游戏流程如图2.1所示。图 2.1

4、 游戏流程游戏中并不涉及复杂的流程,只需要处理游戏的输赢即可,因此在实现的过程中,关键是如何确定游戏输赢的标准(挡栏没有挡住小球)。3 创建游戏对象在这个游戏中,有挡板,小球,砖块(障碍物),道具等物品,这些物品都有共同的特性,有属于自己的x与y坐标属性,有图片属性,有速度属性,所以,在这时在,设计一个基类BallComponent包含这些属性与相关的方法,让其子类继承。继承此类的子类有Stick类(用于定义挡板的行为于属性),Ball类(控制小球的移动与其它动作),Brick类(砖块类),Magic类(道具抽像类,此类中有一个用于使道具功能实现的抽象方法,供其子类实现)。道具类的子类有Lon

5、gMagic与ShortMagic,作用是使Stick的长度变长或者变短。在平时的开发中,如果发现多个对象之间有一些共同的特性或者行为,并且觉得可以使用这些特性或者行为构成一个对象,那么可以建立一个新的对象作为这些对象的父类。如果该父类中某些方法并不需要由父类实现,我们可以将父类做成抽象类,并将这些方法变成抽象的。确定了我们游戏中的所涉及的对象后,我们还需要一个BallFrame类去创建一个画板,用于绘制图片,此类还完成界面的初始化,监听用户的键盘,而与游戏相关的业务逻辑(判断输赢或者球的运动),我们放到BallService类中去处理,本章类的关系如图3.1所示。图 3.1 桌面弹球类图笔者

6、在这里提供了本章的类图,是为了让读者可以更清晰的了解本章程序的结构,但在实现开发的过程中,我们可以根据实际情况,加入或者改变各个类的关系或者程序的结构,但最终都是为降低程序的耦合、提高内聚、编写出优秀的代码。3.1 基类BallComponentBallComponent,做为Brick(砖块)类、Magic(道具)类、Stick(挡板)类、Ball(小球)类的父类,定义了这些子类共有的属性与方法,属性有:x坐标,初始值为-1;y坐标,初始值为-1;图片image,初始值为null;速度speed,初始值为5。根据不同的需要,提供以下三个构造方法: BallComponent( String

7、path ),path是图片的路径,用图片的路径来构造一个BallComponent,在此构造方法中,将根据路径去读取图片,再设置对象中image属性。 BallComponent( int panelWidth , int panelHeight, String path ),以panelWidth,panelHeight,与path去构造一个Component。 BallComponent( String path , int x , int y ),以x坐标,y坐标和path去构造一个BallComponet。除去这些构造方法,此类提供了这些属性的setter与getter方法,用于获取

8、对象的坐标与图片,或者改变对象的坐标位置与图片属性。如果我们在编码的过程中发现有一些共同的属性或者方法,我们可以将这些放到这个基类中。创建BallComponent的时候,我们可以将这个类变成抽象类,即使它没有任何的抽象方法,这样做的目的是,在我们的桌面弹球游戏中,该类并不是具体存在的某一个对象,而是我们将一些公用的属性或者方法存放到该类中,因此它在游戏中并不代表某个具体的对象。将该类创建为抽象类,我们就可以提供(如果需要的话)一些抽象方法让子类去实现,并且可以在父类中调用这些抽象方法。3.2 砖块类(Brick)此类是BallComponet的一个子类,提供一个Brick(String pa

9、th, int type, int x, int y )构造器,其中pah、x与y参数用于调用父类的构造器,type是代表砖块的类型:1代表此砖块里面有LongMagic类型 的道具;2代表此砖块里面有ShortMagic类型的道具;其它代表此砖块里面没有道具。另外,本类增加了magic与disable属性,magic代表此砖块中所包含的道具,初始值为null,disable是用来标志Brick的状态,如果diable为true,则表明此砖块已经不可用,不会再显示。并提供这两个属性相关的以下方法: void setMagic( Magic magic ),设置道具。 Magic getMagi

10、c(),获取道具。 boolean isDisable(),用来判断此类是否有效。 void setDisable( boolean disable ),停用或者启用此类,disable的值为true或者false。确定了一个砖块由一个Brick对象来表示后,在界面中,我们可以提供一个Brick的二维数组,来表示界面中所有的砖块,实现原理与控制台五子棋中的棋盘一样,但是在本章中,二维数组的每一个元素并不是字符串,而是具体的某个Brick对象,在以后的章节中,当遇到需要在界面中绘画某些图片的时候,我们都可以建立一个二维数组,将相应的对象放置到该数组中,当界面进行绘画的时候,就可以将这个二维数组“

11、画”出来。3.3 道具类及其子类(Magic)Magic类是一个道具类,在游戏中表现包含在砖块中,是BallComponet的一个抽象子类,此类提供一个Magic( String path, int x , int y )构造器去调用父类的构造器,并提供一个抽象的方法magicDo( Stick stick ),此抽象方法是实现道具的效果功能,用于给其子类实现,现在实现的子类的LongMagic类和ShortMagic类,两个子类的magicDo方法中分别实现使挡板变长与变短的功能。 abstract void magicDo( Stick stick ),道具的功能,给其子类实现。在本例中,

12、挡板是可以变长或者变短的,而使挡板变长或者变短的方式是通过道具来实现,因此可以将道具抽象成变长的道具或者变短的道具,而它们都需要做同一件是,就是改变挡板的展现形式。为了程序的可扩展性,我们在这里将一个道具变为一个抽象类(Magic),当我们需要有其他形式的道具的时候,就可以为该类添加子类,并提供不同的实现。当然,这里只提供一个Stick的参数可能并不够,如果以后游戏中出现另外一种道具,会改变球的速度(变快或者变慢),那么我们就需要为该抽象类提供更多的参数。3.4 挡板类(Stick)同样,Stick 类也是BallComponet的子类,用来代表游戏中的挡板,由于挡板只有左右移动的,所以,此类

13、中只定义了挡板x方向的移动速度SPEED ,还有定义挡板的初始长度preWidth ,并提供此方法的setter与getter方法,如下: void setPreWidth( int preWidth ),设置初始长度。 int getPreWidth(),获取初始长度。由于该类继承于BallComponet类,因此只需要提供一个构造器即可。在本例中,挡板是可以变长或者变短的,并且在建立道具抽象类的时候,已经定义了一个magicDo的方法,该方法的参数就是一个挡板对象,所以挡板类必须包括长度的属性,这样,在实现道具类的时候,就可以通过改变挡板类的长度来实现本例中所需要实现的长短挡板功能。在St

14、ick类中并不需要关心挡板的图片、位置与大小,这些属性已经在BallComponet中体现。3.5 小球类(Ball)Ball类也是BallComponet的子类,由于小球在游戏面板中运动的时候除了横竖方向,还有各种角度的斜方向,所以我们把小球的速度分解成横向速度与竖向速度(speedX与speedY),游戏未开始前,小球是处于静止状态,所以用一个started属性来标志小球是否已经开始运动。游戏结束后,小球也是处于静止状态,但不能再移动,同样,用一个stop属性来标志小球是否能再移动。除了定义这些属性,还为这些属性提供相应的setter与getter方法,如下: setSpeedX( int

15、 speed ),设置小球的横向速度。 setSpeedY( int speed ),设置小球的竖向速度。 boolean isStarted(),小球是否已经在运动。 void setStarted( boolean b ),把小球状态设置为运动或者静止。 int getSpeedX(),获取小球的横向速度。 int getSpeedY(),获取小球的竖向速度。在本例中,小球对象只保存一些相关的属性,例如横向速度与纵向速度(图片、位置与大小在父类中体现),如果需要改变小球的速度,可以调用相关的setter方法来进行,但是我们需要知道由哪些对象来改变小球的相关属性,我们在前面的章节中提到,提供

16、一个业务类进负责处理游戏的相关逻辑,因此,业务类就需要维护一个小球的对象,来控制小球的运动或者其他行为。在这里,小球对象可以单纯的看作一个简单的对象,并不负责处理任何的行为,这可以看作我们一般所说的贫血模式,对象并不负责处理任何的业务逻辑。如果需要将该小球对象编写成为充血模式,可以为小球对象提供一些与之相关的行为,例如小球会运动,我们可以为Ball类加入一个run的方法,表示球的运动,例如小球会停止运动(在游戏结束或者开始时),我们就可以为Ball类添加一个stopRun的方法,总之,如果需要做到充血模式,可以将所有与小球相关的方法加入到Ball中。3.6 业务处理类(BallService)

17、BallService 处理了这个游戏中的大部分业务功能,包括开始游戏、小球移动、道具移动、挡板移动、测试小球与挡板是否有碰撞或者挡板和其它元素有碰撞、设置挡板的长度、判断用户是否通关、初始化砖块的排列与道具、画图等功能。这些功能的实现都有对应的方法,如下: void run(),小球进行运动。 void setStickPos( KeyEvent ke ),改变挡板的坐标位置。 setBallPos(),改变小球的坐标位置。 boolean isHitBrick( Brick brick ),测试小球与砖块是否有碰撞,参数brikc是指砖块。 isHitStick( BallComponen

18、t image ),测试某元素与挡板是否有碰撞。 void setMagicPos(),改变道具的坐标位置。 void setStickWidth( Magic magic ),根据道具(magic)的类型去设置改变挡板的长度。 boolean isWon(),判断玩家是否已经过关。 Brick createBrickArr( String path, int xSize, int ySize ),创建砖块,返回一个Brick类型的数组,参数path是指砖块的图片,xSize与ySize是数组的长度。 void draw( Graphics g ),画图,方法中是使用Graphics对象g去画

19、图。当游戏开始时,程序中需要不停的调用run方法,让小球进行运动,当然,小球进行运动的前提是Ball的isStarted方法返回true,即游戏已经开始,run方法的主要功能就是调小球的位置。我们需要在游戏中通过上、下、左、右的键来控制挡板的位置,因此就需要提供一个setStickPos的方法来改变挡板的位置。在本章的程序中,BallService处理所有的相关逻辑,例如判断小球在运动的过程中是否越界、游戏是否胜利等。在例中BallService处理了大部分的游戏逻辑,当然,我们也可将这些逻辑放到相关的类中(即前面提到的充血模式),例如道具的下落、挡板的移动等。3.7 主界面类(BallFra

20、me)BallFrame是创建一个JFrame主界面,设置主界面的标题、长与宽、画板等属性,并且为增加键盘事件监听器以及创立一个Timer每隔一小段时间去刷新画板,主要有初始化界面与或者画板两个方法,如下: void initialize() throws IOException,此方法抛出IO异常,初始化界面。 BallPanel getBallPanel(),获取一个BallPanel类型的JPanel去充当画板,BallPanel是这个类中的一个内部类。我们使用了BallService类来处理大部分的游戏逻辑,主界面类中几乎不包括任何的逻辑处理,该类维护一个BallService的对象,

21、得到界面中相关对象的信息后,可以调用BallService中的方法进行处理,并根据返回的信息来改变界面。例如小球的运动,我们可以调用BallService的run方法,再调用BallSerivce的draw方法将小球的图片“画”到界面中。到此,本章中所有的对象都已经创建并确定了它们的行为,在建立道具类(Magic)的时候,我们将一个道具抽象为一个Magic对象,该类可以有多个实现,在使用Magic对象的时候,我们可以利用面向对象的多态特性,使用Magic的magicDo方法来进行“道具的使用”,在这个过程中,我们并不需要去关心道具具体的实现。在创建游戏各个对象的过程中,我们将处理逻辑的方法放置

22、到一个业务类中,从一定程度上讲,减少了代码之间的耦合,并遵循了单一职责的原则。4 主界面实现在这个桌面弹球游戏中,游戏中的所有元素都是用Graphics对象画出来的,所以,我们的主界面应该是一个只设置了窗口标题还有颜色等基本属性的JFrame,在这个JFrame中,我们只需要提供一个JPanel对象即可,因为游戏的界面并没有多复杂的布局与界面交互。当我们实现游戏的一些相关逻辑的时候(球的运动、道具的下落等),我们可以调用JPanel的repaint方法将JPanel进行重绘。4.1 初始化界面(initialize()方法)首先,设置JFrame窗口的标题、背景颜色与是否可以改变大小,然后获取

23、JPanel对象,最后把JPanel画板加到JFrame中,见以下代码。代码清单:codeballsrcorgcrazyitballBallFrame.javapublic void initialize() throws IOException /设置窗口的标题 this.setTitle(弹球); /设置为不可改变大小 this.setResizable( false ); /设置背景为黑色 this.setBackground( Color.BLACK ); /获取画板 ballPanel = getBallPanel(); /把画板加到JFrame this.add( ballPane

24、l );看加粗的一行代码ballPanel = getBallPanel()是调用本类中的getBallPanel()方法去获取一个BallPanle对象,BallPanel是本类的一个内部类,并且继承JPanel,见以下代码。代码清单:codeballsrcorgcrazyitballBallFrame.java/定义一个JPanel内部类来完成画图功能public class BallPanel extends JPanel /* * 重写void paint( Graphics g )方法 * * param g Graphics * return void */ public void

25、 paint( Graphics g ) /可以调用BallService的draw方法进行绘制 而获取这个BallPanel实现是在BallPanel getBallPanel方法中,此类保证这个Panel是单态的,每次只有一个BallPanle对象,见以下代码。代码清单:codeballsrcorgcrazyitballBallFrame.javapublic BallPanel getBallPanel() if ( ballPanel = null ) /新建一个画板 ballPanel = new BallPanel(); /设置画板的大小 ballPanel.setPreferre

26、dSize( new Dimension( BALLPANEL_WIDTH, BALLPANEL_HEIGHT ) ); return ballPanel;在这里需要注意的是,我们需要在BallFrame中维护一个BallPanel的对象,然后通过getBallPanel的方法来获得BallPanel的实例,由于BallPanel并不需要每次去创建,所以我们可以将BallPane变成单态的。在众多的设计模式中,有一种叫做单态模式。如果遇到一些对象并不需要多次创建或者创建这些对象将会严重消耗系统资源,那么我们可以考虑将该对象写成单态的。4.2 单态模式简介单态模式也可以叫单例模式,该模式保证一个

27、类有且仅有一个实例,并为外界提供一个访问,让外界可以通过这个访问点来访问该类的唯一实例。在我们平时开发的过程中,会遇到一些不需要多次创建的对象,例如JDBC的Connection对象,那么我们就可以利用单态模式来创建这些对象。例如单态模式,系统可以不必多次创建该对象的实例,外界使用的时候可以使用同一个实例,因此在一定程序上减低了系统在创建对象时的开销。为一个类实现单态模式,需要为该类提供一个私有的构造器,再提供一个可以获取该类实现的方法(为外界提供唯一的访问点),私有构造器是为了不让外界去使用new关键字来创建该类的实现,如果外键可以使用new关键字来创建该类的实例,那么就意味着该类将不会是单

28、态,有可能外界多次通过new关键字来创建,这就无法保证该对象的实例的唯一性。4.3 运行效果编写了BallFrame的初始化代码后,我们可以运行具体查看相关的游戏效果。编写创建BallFrame的代码: BallFrame ballFrame = new BallFrame(); ballFrame.pack(); ballFrame.setVisible(true); ballFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);当前程序的效果如图4.1所示。图4.1 初始化游戏时的界面注:我们当前并没有对BallService中的draw

29、方法作任何的实现,我们实现了BallService的draw方法后,就可以将BallPanel中的paint方法加入BallService.draw。4.4 监听器与Timerjavax.swing.Timer可以设定每隔一个时间周期就重复执行某个task,类似于Window系统的计划任务或者Linux系统的crobtab,并用start()方法去启用Timer。在这个弹球游戏中,我们只有键盘操作,所以只监听键盘的操作,用一个KeyListener去监听键盘的动作,请看以下代码。代码清单:codeballsrcorgcrazyitballBallFrame.java/定义每0.1秒执行一次监听

30、器ActionListener task = new ActionListener() public void actionPerformed( ActionEvent e ) /开始改变位置 service.run(); /刷新画板 ballPanel.repaint(); ;/如果timer不为空,调用timer的restart方法if( timer != null ) /重新开始timer timer.restart(); else /新建一个timer timer = new Timer( 100, task ); /开始timer timer.start();/增加事件监听器KeyL

31、istener klarr = this.getKeyListeners();if( klarr.length = 0 ) /定义键盘监听适配器 KeyListener keyAdapter = new KeyAdapter() public void keyPressed( KeyEvent ke ) /改变挡板的坐标 service.setStickPos( ke ); ; this.addKeyListener( keyAdapter );首先,建立一个ActionListener对象做为Timer的task,这个task主要是处理游戏中各个组件位置的改变以及reapint画板,这个task每100毫秒执行一次,即每隔一百毫秒小球(或者其他组件)会执行一次运动。如果此类的属性timer为空,就以ActionListern对象为参数去创建一个每100毫秒执行一次的Timer,并用调用start()方法启动Timer,如果timer不为空,直接调用restart()方法启动timer。在这里我们需要明白的是,第一次进行游戏时,timer为null,就需要进行创建,当进行第二次游戏的时候,timer非空,由于

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1