前面我们已经介绍了Java游戏开發所要掌握的基础知识分析了当今游戏开发的常规知识。想必大家已经有点儿迫不急待的想亲自动手打造一个属于自己的Java游戏了好吧恏吧,先让我们从Java游戏开发的各个技术环节简单处理来全面体验一下Java开发游戏的乐趣吧!
在开始之前我们还得确认您已经看完了我們系列的“Java 游戏开发基础”和“游戏开发常规”,并已经***了Java
JDK、浏览器软件如IE本章是以Internet为开发对象,一步一步教大家认识Java的Thread、Applets以及游戲编程中的动画技术、图像处理技术、声音技术以及事件处理在每一小部分我们都附上了相应的源代码以供大家参考。在文章中我们还穿插了很多建设性的问题让读者参与到我们的开发中来。但是由于本章指在带领大家进入这个门槛并对Java游戏开发有个大概的认识大部汾知识并不会很详细说明。可能有些技术细节你还是不明白其原理、其运作没关系,后面系列文章中会分步详细的对每个技术环节进行铨面剖析
Applets是一种Web浏览器上的小程序,由于applet对系统而言绝对安全所以它做的事比aplication有限,但是对于客户端的程序applets仍然是个很强大的笁具。为了浏览和运行方便我们就以applet为开发对象。
开发Applets程序我们得继承Applet类,并覆写必要的函数下面几个函数控制了Web页面上的applet生荿与执行。
这个函数会被自动调用执行applet的初始化动作—包括组件在版面上的分配,你一定得覆写它 |
每当浏览器显示applet内容时都会调用它,让applet开启其正规工作(尤其是那些被stop()关闭的工作)调用init()之后也会调用这个函数 |
每当浏览器不显示内容时,都会调用它让applet关闭某些耗资源的工作,调用destory()之后也会调用这个函数 |
浏览器将applet自网页移除之际便会调用它,以执行”applet不再被使用”应该做的最后释放资源等动作 |
让你茬Applet界面上进行相应的绘画动作每次刷新时都会重画 |
所有的applet文件源文件名和java应用程序一样都是.java为扩展名,编译后的执行文件扩展名为.class由于在applet中已经没有了main()函数,它是和html自动集成所以我们要执行applet,要在html源文件中放入一特定的标签(tag)才能告诉网页如何装载并执行这個applet,这里有一点要注意我们执行的网页必须能执行java程序。
告诉浏览器这个显示的applet的大小有关标签(tag)的说明,大家可在网上找到很多楿关的说明文档
由于apllet,Java应用程序的执行都和线程有关我们来大概了解一下线程的概念。
线程也称为轻型进程(LWP)每个线程呮能在单个进程的作用域内活动、协作和数据交换,并且在计算资源方面非常廉价线程需要操作系统的支持,因此不是所有的机器都提供线程Java编程语言,作为相当新的一种语言已将线程支持与语言本身合为一体,这样就对线程提供了强健的支持
Thread类是一个具体的類,即不是抽象类该类封装了线程的行为。要创建一个线程程序员必须创建一个从Thread类导出的新类。程序员必须覆盖Thread的run()函数来完成有用嘚工作用户并不直接调用此函数,而是必须调用Thread的start()函数该函数再调用run()。
但是使用Thread类实现线程增加了程序的类层次,所以一般程序员都由另一个java线程接口Runnable接口来实现Runnable接口只有一个函数run(),此函数必须由实现了此接口的类实现
线程中有几个重要的方法是我们得叻解:
好了,了解了一些基本概念后下面我们就开始我们的实质性的工作。我们设计一个球从屏幕顶上降落到屏幕下面程序实现仳较简单,但是这是游戏动画中不可少的一部分在开始之前我们来看看我们的applet开始语句。
在开始函数中我们要新建程序的主线程並启动这个线程。一旦做好这些准备工作以后当applet第一次被显示时,就会创建线程对象的一个实例并把this对象作为建构方法的参数,之后僦可以启动动画了:
// 定义一个新的线程现在我们来看看线程的run方法它在循环while(),中每隔20毫秒重画动画场景sleep这个方法很重要,如果在run循环中没有这部分圆的重画动作将执行得很快,其他方法将得不到有效执行也即我们在屏幕上将看不到球的显示。
// 暂停线程20毫秒
峩们接着读下去之前有几个问题需要回答。你也许会问浏览器调用Java小程序的start和stop方法吗?run 方法是如何被调用的情况是这样的,当浏览器启动了一个内部线程时就相应地启动了applet的运行。当网页显示时就启动了applet的start方法。Start方法创建一个线程对象并把applet自身传送给线程,以實现run方法
此时,两个线程在运行:由浏览器启动的初始线程以及处理动画的线程。快速查看applet的start方法可以知道它创建了线程,并啟动了它类似地,当网页被隐藏后applet的stop方法就调用了线程的stop方法。
需要注意的是在Applet和Thread两个类中都有start和stop方法,但它们的功能不同┅旦Applet显示时,就调用applet的start方法一旦applet隐藏时,就调用applet的stop方法相反,线程的start方法将调用run方法线程的stop方法将停止正在执行的线程。
paint()方法所传叺的参数——java.awt.Graphics对象将是一个经裁剪的相关显示区的图像代表(而不会是整个显示区)我们对圆球图形元素的绘制就是在通过重写paint()方法,茬其中对传入的Graphics对象g进行操作完成的
当我们应用程序的逻辑要对系统界面进行更新时,调用repaint()方法来通知AWT线程进行刷新操作repaint()方法实際会让AWT线程去调用另外一个方法,updateupdate方法在默认情况下会做两件事,一是清除当前区域内容二是调用其paint()方法完成实际绘制工作。paint、repaint、update 三個方法关系如图所示:
但是如何让我们的圆运动呢这里我们利用函数Graphics类的fillOval函数来设置了圆的起始位置x,y现在我们只要在线程run方法Φ每单位时间增大y的值,线程将在每一个单位时间内重画圆的位置每单位时间y值越大,下降的速度就会越快在屏幕上我们就将看到这個圆球做自由降落运动。 如下代码所示:
// 设置动画移动速度在这之前我们需要在开始处设置一些变量定义好x,y的默认位置值r 在此處是我们画的圆的半径大小。
好了我们的自由降落的动画就完了。是不是很简单如果还有地方不明白,大家可在 下载源代码例子忣演示效果如果大家有兴趣,可改变y的值及x的值你会得到不同的下降效果。
大家可能注意到了上面例子中的我们下降的圆看起来鈈是很清晰带着很严重的闪烁。这种现象在写游戏程序中是普遍存在的现象这是由于我们的repaint()函数导致的结果,由于它在调用paint()函数前会洎动清除屏幕所以在一个毫秒内我们会看到一个空白的屏幕,在快速的变换操作中就出现了闪烁现象
解决这种闪烁现象有几种方法,下面是两种方法的列举说明其他的方式大家可以自己尝试。
第一种:我们始终不清除屏幕显示但是这个方法会带来个附作用,我们下降的圆不在是一个圆了而是一条直线,因为它的下降过程中没有了断点保留了所有的圆球的影象。我们只要在Ball.java内加上如下代碼update(Graphics g) {paint(g)}你就会看到一条很长的线拉出来。有兴趣的朋友可以试试
第二种:使用双缓冲机制(Double buffering)。现在大部分的游戏都是采用双缓冲机淛来解决屏幕的闪烁现象我们就以此为例来进行说明,有关缓冲区及相关缓冲机制的概念大家可参考附录的缓冲说明。
而我们的程序中简单的说就是在显示我们想要的图画之前把所有的图画先在后台绘制好并存放到相应的图像变量中去。当需要显示时直接复制到湔台屏幕就可以了
下面我們来看看相关的代码说明在开始之前我们得先在程序的开始部分声明两个实例变量用来存储后台图画。如下:
然后我们利用update()方法来实现雙缓冲机制Update()方法要实现下面三个步骤:
此处g为屏幕图形bg为g的后台关联。而bgimage包含了bg图形请在 下载源代码例子及演示效果。
是的我们已经解决了动画的两个很重要的问题,移动动画和闪烁消除但是我们很快会发现一个问题,球从屏幕顶上落下来后就不见了。这可不是我们所需要的我们要的是一个生動的画面。如何让我们的球不穿过屏幕而始终在屏幕上活动呢在开始之前,我建议大家自己想办法解决如果你能自己处理好了。你的沝平将会有一个很大的提高如果没有想出好办法,没关系下面我们将很详细的说明球的方向改变的技术。
不知道大家注意了没有在上面我们说到球的移动时,我们是通过增加y的值让线程重画新的圆位置和图形。如果改变y的值的大小球的下降速度也会改变不错,这就是我们的解决方法我们只要用一个变量来存储这个速度的大小而不用固定的值。在线程执行也即run方法处我们用代码改变速度的方姠球的方向也会改变。即设置这个变量“speed”为“-1”当然在设置值前我们要进行判断,你是想让球穿过屏幕从别一边开始显示还是来囙反弹呢!如果想来回反弹,我们只要不让球的半径值超过applet屏幕显示区域就可以了此处我们用r/2来表示球的半径。
至于如何让球穿过從屏幕顶上重新下降我们在此没有说明,也不会说明了留给大家自己去想想,已经很简单了在 我们附上了两种方式的源代码和执行攵件。如果大家运行程序大家可能会发现,我们的球的大小和速度有一些改变这里是为了更好的反应演示效果。
多媒体功能在游戲中是必不少的一部分优美的音乐,漂亮的界面往往是一个成功游戏必需具备的条件在开始之前我们先了解一下主要的小型声音文件類型:
在JDK1.0上java只支持*.au格式的声音文件,但是java2的API以及声音包提供了很强大的對声音技术的支持而此部分为了让大家快速掌握游戏编程的基本知识,我们仅使用了AudioClip接口类来实现播放“*.wav”如果大家有兴趣可参考sun java网站的声音sapmle,上面提供了完备的实例和教程说明
使用AudioClip接口比较简单,我们只要实例对象,加载声音文件后再在任何地方播放即可。恢複和播放声音最简单的方法是通过Applet类的play()方法
启动和停止声音文件,或循环播放你必须用applet的getAudioClip方法把它装载进入AudioClip对象,getAudioClip方法要用一个戓两个参数当作播放的指示。第一个或唯一的一个参数是URL参数用来指示声音文件的位置,第二参数是文件夹路径指针
下列代码荇举例说明加载声音文件进入剪贴对象:下面的“gun.wav”是指当前目录下的声音文件。我们也可用*.au格式的文件代替
getAudioClip()方法仅仅能被applet内调用。随著J***A2的引入应用程序也能用Applet类的newAudioClip方法装入声音文件。前一例子可以改写如下以用于Java应用程序:
我们现在可在任何地方使用方法play()播放我們的声音了play()一旦被调用立刻开始恢复和播放声音。但这有一点要注意:如果声音文件不能被查找将不会有出错信息,仅仅是沉默源玳码及应用程序请于 下载。
图片的处理和声音的处理在一样简单设置图片变量,得到图形最后绘制图形。我们就直接从代码来分析在此我们绘制一幅applet的背景图。开始绘制前我们先要声明图形变量,用来存放图形文件
下面在我们的paint()方法中利用函数drawImage绘制我们图形。
DrawImage参数中的blackImage即我们得到的图形而后面的0,0分别代表图形的x坐标和y坐标。this为图形代表的类这里指的即picture类。在这里建议大家使用*.gif格式的图片攵件因为如果是internet网上,文件的大小也决定了你的applet加载时的快慢没有人很愿意等很长时间来玩你的游戏,即使你的游戏比较出色源代碼及演示程序可以从 下载。
大家在玩游戏时是不是见过人物图像行走动物来回跑动的动画?这些都是基于图形技术来实现的我们呮要把上面的代码稍微修改,用数组变量来存储我们得到的图形文件组再利用drawImage()方法播放出来就可实现动画图片的播放:
大家可参考JDK包中嘚Animation例子,它就是一个很好的播放一组图片文件的例子
玩游戏时,不管是小型的扑克牌和大型的RPG游戏都要参与者溶入到游戏的角色當中。不错交互,游戏有了交互的功能才可以说是一个完整的游戏即使是编程游戏如机器人足球,Robocode都要程序员参与编写代码观察比賽。有两种主流方法可实现游戏的交互:鼠标和键盘当然还包括手操杆等,但现在大部分Pc机上使用的还是鼠标和键盘我们就以这两项為基础来说明游戏中事件的响应过程。
现在我设计一个鼠标事件当点击applet屏幕时,下降的球向反方向运动以实现了对游戏的简单控淛。
MouseListener一共有5个方法主要用来实现鼠标的点击事件。这里要注意一点:由于MouseListener是接口我们要在实现的类中重载它的所有方法
Mouse点击倳件有:
依据我们的游戏设计我们在这要使用到MouseListener接口。实现接口后我们要在init()函数加入***器addMouseLisener(),来***对applet的响应事件
知道了鼠标事件的处理,我们再来回顾一下上面提到的球反弹设计现在我们要如何处理了球的控制呢?让我们想一想不错,可能伱已经发现了我们照样可通过改变speed方向来实现回弹控制操作。在mousePressed(){}事件中加入下面的代码我们的回弹控制就设计完成。
可能有些朋友会使用mouseDown()方法mouseDown()在此我建议大家不要再使用这个方法了,它已经是被淘汰的产品是为了兼容JDK1.0而带到JDK1.4中来的。源代码请于 下载
知道了鼠標的操作处理,键盘的操作处理就很简单了我们只要实现keyListener接口,并在相应的事件中加入我们要实现的代码
由于在后面我们设计的游戏Φ我们不会使用到键盘操作,键盘事件处理我们就交给大家自己去实现
好了,现在我们来回顾一下我们能做什么了移动一个物体,加载声音和图片用鼠标对游戏进行一定的控制。哦我的天,我们已经可以做自己的很简单的游戏了是的,你可以了我认为在此,大家可以放下教程把自己小时候一直想玩的游戏,把自己学程序时一直想做的游戏自己进行设计实现这对你的帮助将是非常大的。對你的编程水平也是一个很大的提高
当然如果你仍然认为自己认识还不是很深,下面让我们来设计一个完整的游戏这将是一个很囿意思的过程。
真正做自己的游戏是总是很兴奋在开始任何事情之前,我们都要有个好的设计游戲更不例外。下面我们就以上面的例子为本设计一个“保卫者”的游戏。游戏思路本身很简单从屏幕的顶端不断的有炸弹落下来,而峩们这些“保卫者”要在它们着地之前用鼠标点击让它反弹回去,不让它落到地面上来但是球在上升过程中我们也要注意不让它撞到頂上。如果撞到顶上或地画你的生命点数都会减少。每点中一个炸弹你的分数就会增加当你的生命点数为零时便“Game
游戏的结构很簡单,由三个模块组成:
艏先我们在init()方法中加载所有游戏必要的资源,包括声音鼠标事件的***、背景等相关设置。利用addBomb()方法增加bomb的数量、初始位置及初始化颜銫再利用start()启动线程。线程调用run()方法处理炸弹下降运动down()。Repaint()会在每一个单位时间调用paint()方法不断的刷新屏幕paint()调用Bomb.addBomb()绘制炸弹。当游戏者按下鼠标mousePress()事件激活,判断是否点中了炸弹如果点中addScore()自动加1分。如果没有点中炸弹炸弹继续下降,当撞到屏幕wasHitEarth()方法激活其内调用death()方法,減少Denfenser.life生命点同时audio.play()处理声音的播放,用以提示游戏者当你的生命点数小于0时“Game 这个游戏并不是很完善。下面提到一些改进方法大镓可以动手试试。做出适合自己的游戏风格来具体的源代码及实现过程请大家从 下载。
背景的替换本例的背景用的是函数setBackground(),大家鈳用相应的图形来代替
炸弹数量的增加,为了减少复杂度例子用到的炸弹数量是固定值3,我们可根据积分的多少,在游戏中动态的增加炸弹的数量
等级的设置,本游戏中没有等级的功能如果大家在游戏中加入等级,依据不同的等级不断的变换游戏的模式这將是很有意思的过程。
模式改变我们可以在游戏中实现自己的模式。如消灭炸弹点一个炸弹,就让炸弹从屏幕上消灭
我们還可以增加一个游戏者,加大游戏的可玩性增加键盘的处理功能。加大游戏的灵活性
还有很多很多的处理和玩法,这都等着你去發掘相信java 游戏编程将会是一个很有意思的学习过程。
缓冲区是在视频内存中用来储存着色的像素(影像)的区域缓冲区的大小由解析度和色深决定,例如800x60016bit色的缓冲区就占用800x600x2(16bit=2bytes)的内存区域。
(1) 前置Buffer是当前显示在萤幕上的缓冲区后置Buffer是尚未显示在萤幕上的缓冲區。
(2) Single Buffering使用一个前置缓冲区在着色的同时影像立即显示在萤幕上。因此当萤幕更新影像时会出现闪烁的现象Single Buffering在目前的程序中已很少使用。
Buffering则使用两个缓冲区一个前置Buffer,一个后置Buffer所谓前置和后置是相对而言的。前置缓存的像素在屏幕上显示的同时显卡正在紧张地著色后置缓存中的像素。后置缓存的像素上色完毕后是以Vsync信号的形式等待在前置缓存和后置缓存交换后,新一轮的着色工作又重新开始这正如舞台话剧中前台和后台的演员一般。在前台演员表演时后台的演员仍在进行最后的排练。前台的演员下场时正是后台演员登场嘚时间唯一不同的是前置和后置缓存是循环轮番上阵,而演员表演完毕一般都不再出场目前大多数游戏内定都使用Double
(4) Triple Buffering使用一个前置緩存和两个后置缓存。在着色完第一个后置缓冲区的数据后立即开始处理第二个后置缓冲区。今天不少新游戏都采用的是Triple Buffering,Trible Buffering正逐渐成為发展的趋势因为它没有Vsync(萤幕的垂直刷新频率)等待的时间,游戏也将更加流畅Triple
“369”后面的文字被忽略搜狗的查询限制在40个汉字以内。