Java图形图像处理【双缓冲技术与游戏】【九】
Java图形图像处理【双缓冲技术与游戏】
- 19.3 图像绘制的双缓冲技术
- 19.4 休闲游戏--射击气球游戏
- 一、需求分析与功能实现
- 二、游戏各功能模块代码的编写
19.3 图像绘制的双缓冲技术
闪烁是图形编程的一个常见问题:在屏幕上多次重复绘制的图形图像时,屏幕显示时会出现闪烁。闪烁通常是由于直接在屏幕上绘制图形图像时,图像还未完全绘制完成就被显示出来,导致视觉上的不连贯。
双缓冲技术(Double Buffering)是计算机动画处理时消除屏幕闪烁的常用优化手段。双缓冲技术通过在内存中创建一个称为虚拟屏(offscreen)的缓冲区作为画布,虚拟屏的大小与实际显示区域一样大(高度*宽度)。我们先在虚拟屏上绘制好所有图像,然后一次性将缓冲区(虚拟屏)的图像绘制到屏幕组件上。
双缓冲技术可减少屏幕图像的刷新次数,有效消除由多重绘制造成的屏幕闪烁问题。
在Java中,双缓冲技术可以通过以下步骤实现:
- 创建一个虚拟屏:使用BufferedImage或VolatileImage来创建图像缓冲区。
- 在虚拟屏的缓冲区中绘制图形:在paintComponent方法中,先将所有图形绘制到虚拟屏的缓冲区中。
- 将虚拟屏的缓冲区中的图形图像一次性绘制到屏幕上。
双缓冲技术适用于需要频繁更新屏幕内容的应用程序,如动画、游戏等。
Java的AWT组件,默认情况没有实现双缓冲。双缓冲通常是在组件的paint(Graphics g)方法或者paintComponent()方法中实现。在早期的AWT动画编程中使用双缓冲效果非常明显。
Swing组件可通过setDoubleBuffered(true)方法来启用双缓冲技术。
JComponent类(所有轻量级Swing组件的基类)默认启用了双缓冲技术。JComponent类的setDoubleBuffered(true)是默认行为。这是对AWT组件的重要改正之一,改善了GUI组件重绘时的显示效果,尤其在动画演示类程序,避免了图形刷新时闪烁现象,大大改善了用户的体验。
当你调用 repaint() 时,Swing组件会自动在虚拟屏(offscreen)的缓冲区中绘制组件的内容,然后将整个缓冲区图像一次性绘制到屏幕上,从而避免闪烁。
Swing编程,通常情况下无须手动实现双缓冲,因为 Swing 已经默认处理了。只有在以下情况下,可能需要手动实现双缓冲:
- 自定义绘制逻辑:如果你在 paintComponent(Graphics g) 中进行了复杂的绘制操作,可能需要手动优化。
- 性能优化:在某些高性能场景(如游戏开发)中,手动管理双缓冲可能会带来更好的性能。
19.4 休闲游戏–射击气球游戏
【例程19-6】“射击气球”游戏例程GameMain
本节介绍一个射击气球游戏的开发。这是一个短小精简的休闲游戏,用枪发射子弹射击从窗口上方不断下落的气球,击中气球有奖励分。
一、需求分析与功能实现
游戏实现如下功能:
(1)6种不同的气球不断从窗口界面上方徐徐落下;
(2)界面下方有一支枪,枪口跟随鼠标移动,单击鼠标开枪发射子弹射向气球;
(3)子弹击中气球可得分,一个气球5分;
(4)每次游戏时间1分钟,界面左上角显示得分和剩余时间;时间用完时,游戏结束显示得分和排名信息。
(5)在游戏开始状态单击鼠标,可进入游戏运行状态;鼠标离开游戏界面窗口,进入游戏暂停状态;鼠标再次进入游戏界面,可从暂停状态转换为运行状态;游戏时间用完时进入游戏结束状态。
(6)游戏的开始状态、暂停状态和结束状态,各自在界面中央用文字显示信息。在游戏结束状态单击鼠标可进入游戏开始状态。
经过对需求的分析,我们把射击气球游戏要实现的功能分解为五个模块:
游戏主控模块、移动对象模块、图片模块、辅助处理模块、数据库处理模块。本游戏程序全部由Java程序来实现,以下介绍各个模块功能及其实现方法。
- 游戏主控模块
主控模块只有一个游戏主程序,在程序中使用Static静态块载入所需的资源(图像)文件;利用鼠标监听器的事件处理器控制游戏的状态,即初始、运行、暂停和游戏结束状态,以及处理射击气球子弹的发射;利用Timer计划任务组件进行一些流程控制,包括创建气球、移动气球和子弹、更新游戏成绩、处理越界气球和子弹,以及检查游戏是否结束。主程序中界面图像的处理使用双缓冲技术,以优化屏幕的闪烁。源文件保存在项目的game包中。 - 移动对象模块
抽取游戏中的移动对象的一些共同特性,包括移动对象的图像、坐标、长度和宽度等,以及一些共有的方法,设计了一个抽象类。移动对象枪、子弹和气球都继承抽象类,其源文件都保存在项目的com包中。 - 图片模块
游戏中的移动对象都有图像文件相关联,其中气球共有6种图像根据一定的算法进行配置,手枪(用枪口图像)和子弹各有一个图像。所有图像文件都保存在项目的images子文件夹里。在游戏的主控模块中载入。 - 辅助处理模块
辅助处理模块由3部分构成:1,参数接口文件把游戏的一些主要参数都汇集在一起;2,绘制处理器类(DrawHandler)则把游戏中所有绘制方法都集中在该类中,包括移动对象枪、子弹和气球的绘制,游戏各状态的界面绘制,得分信息和提示信息等绘制;检查处理器类(CheckHandler)则汇集了一些检查的功能,如子弹是否击中气球、子弹和气球是否出界,以及游戏是否结束等。这些接口和类文件都保存在项目的util包中。 - 数据库处理模块
数据库处理模块只有一个数据库处理器(DBHandler)内含插入游戏记录和查询游戏排名等方法。该类文件也保存在项目的util包中。更详细的说明请参见后文的代码编写。
下面这个是射击气球程序运行效果图:
二、游戏各功能模块代码的编写
(一)、移动对象模块
移动对象模块共有四个源文件,都保存在com包里,分别介绍如下:
1,移动对象抽象类Movable.java源代码:
package com;
import java.awt.Image;
public abstract class Movable {protected Image icon; //图像protected int x; //x坐标protected int y; //y坐标protected int w; //宽protected int h; //高public Image getIcon() { return icon; }public int getX() { return x; }public void setX(int x) { this.x = x; }public int getY() { return y; }public void setY(int y) { this.y = y; }public int getW() { return w; }public void setW(int w) { this.w = w; }public int getH() { return h; }public void setH(int h) { this.h = h; }public void setIcon(Image icon) { this.icon = icon; }/***检查是否出界***/public abstract boolean beyondW();/***物体移动方法***/public abstract void move();
} //移动对象抽象类Movable.java结束。
2,移动对象气球类Balloon.java源代码:
package com;
import java.util.Date;
import java.util.Random;
import game.GameMain;
import util.Constants;
public class Balloon extends Movable implements Constants {public Balloon() {/*利用随机数发生器产生一个气球的x坐标*/w = BALLWIDTH;h = BALLHEIGHT;Random random = new Random(new Date().getTime());x = random.nextInt(GWindowWD-w);y = -h;icon = GameMain.ballIcon[x%6]; //配置气球图像}@Overridepublic boolean beyondW() { return y>GWindowHG; }@Overridepublic void move() {y += BALLSPEED;}/***气球是否被击中***/public boolean isHit(Bullet bullet) {/***计算子弹中心坐标***/int b_x = bullet.x + BULLETWIDTH/2;int b_y = bullet.y;//判断子弹是否在气球中间if ((b_x>x&&b_x<x+w) && (b_y>y&&b_y<y+h))return true;else return false;}
} //移动对象气球类Balloon.java结束。
3,移动对象子弹类Bullet.java源代码:
package com;
import game.GameMain;
import util.Constants;
public class Bullet extends Movable implements Constants {public Bullet(int x, int y) {this.x = x;this.y = y;w = BULLETWIDTH;h = BULLETHEIGHT;icon = GameMain.bulletIcon;}@Overridepublic boolean beyondW() {/***当"y < -h"时返回true;否则返回false ***/return y < -h;}@Overridepublic void move() {y -= BULLETSPEED; //子弹往上飞}
} //移动对象子弹类Bullet.java源代码结束。
4,移动对象枪类Gun.java源代码:
package com;
import game.GameMain;
import java.util.List;
import com.Bullet;
import util.Constants;
public class Gun extends Movable implements Constants { public Gun() {icon = GameMain.gunIcon;w = GUNWIDTH; //枪的宽度h = GUNHEIGHT;//枪的高度//枪的初始位置坐标x = GWindowWD/2;y = GWindowHG/5*4;}@Overridepublic boolean beyondW() {return false;}@Overridepublic void move() {} //枪的移动由鼠标控制,这个方法用不到。 /**枪移动至新位置的方法,根据鼠标指针位置来设置枪口位置。**/public void moveTo(int x, int y) {this.x = x - w; //设置枪的x值: 鼠标x值-枪的宽this.y = y - h; //设置枪的y值:鼠标的y值-枪的高}/***射击气球方法***/public void shootBall(List<Bullet> bullets) {/**根据枪口坐标初始化子弹坐标,创建子弹后加到列表**/int x1 = x + w/3; //子弹的x坐标,修正后位于枪口int y1 = y; //子弹的y坐标bullets.add( new Bullet(x1, y1) );} //把即将发射的子弹加入列表,以后子弹坐标更新由定时任务处理
} //移动对象枪类Gun.java源代码结束。
(二)、辅助处理模块
辅助处理模块共有三个源文件:常量参数接口Constants、绘图处理器DrawHandler和检查处理器CheckHandler,都保存在util包里,分别介绍如下:
1,常量参数接口Constants
参数接口Constants中定义了一些与游戏相关的一些常量数据,供其他类调用,放在一起有利于维护。
辅助处理模块中的常量参数接口Constants.java源代码:
package util;
/***常量:定义游戏中的主要常量参数***/
public interface Constants {//设定游戏窗体大小public static int GWindowWD = 1000; //游戏界面宽度public static int GWindowHG = 800; //游戏界面高度//设定游戏的状态(开始、运行、中止、结束)public static int GSTART = 0; //游戏开始状态public static int GRUN = 1; //游戏进行状态public static int GPAUSE = 2; //游戏暂停状态public static int GOVER = 3; //游戏结束状态//设定汽球大小和速度 public static int BALLWIDTH = 50; public static int BALLHEIGHT = 70; public static int BALLSPEED = 6; //气球速度//设定子弹大小和速度public static int BULLETWIDTH = 10; public static int BULLETHEIGHT = 30; public static int BULLETSPEED = 10; //子弹速度//设定枪的大小public static int GUNWIDTH = 26; public static int GUNHEIGHT = 25; public static int HITAWARD = 5; //击中气球奖励5分public static int GAMETIME = 60000; //游戏时间60秒public static int INTERVAL = 20; //刷新绘图间隔20ms
} //辅助处理模块中的常量参数接口Constants.java源代码结束。
2,检查处理器CheckHandler
检查处理器CheckHandler实现多种检查功能:检查气球是否被击中;检查气球和子弹是否出界;检查游戏是否结束。
辅助处理模块中的检查处理器CheckHandler.java源代码:
package util;
import java.util.List;
import com.Balloon;
import com.Bullet;
public class CheckHandler implements Constants {public static int checkHit(List<Balloon> balls,List<Bullet> bullets,int score) {for (Bullet b : bullets) {if (isHit(b, balls)) score += HITAWARD; //击中得分}return score;}public static boolean isHit(Bullet bullet,List<Balloon> balls) {int id = -1;//遍历所有的气球for (int i = 0; i < balls.size(); i++) {Balloon ball = balls.get(i);//调用气球的方法isHit(),判断是否击中if (ball.isHit(bullet)) {id = i; //击中的气球编号break; //跳出for循环}}if (id >= 0) { //找到击中的气球balls.remove(id); //从列表中移除击中的气球return true;}return false;}/***检查气球和子弹是否出界?若出界,则从列表中移除***/public static void checkBeyond(List<Balloon> balls,List<Bullet> bullets) {//遍历所有的气球for (int i = 0; i < balls.size(); i++) {Balloon ball = balls.get(i);if (ball.beyondW()) balls.remove(i); //出界,移除}//遍历所有的子弹for (int i = 0; i < bullets.size(); i++) {Bullet bullet = bullets.get(i);if (bullet.beyondW()) bullets.remove(i); //出界,移除}}/***检查游戏是否结束***/public static boolean checkGameOver(int usedTime) {return (usedTime<GAMETIME) ? false : true ;}
} //辅助处理模块中的检查处理器CheckHandler.java源代码结束。
3,绘图处理器DrawHandler
在绘图处理器DrawHandler中实现游戏的各个模式状态的绘制;移动对象气球、子弹和枪的绘制;各种提示信息的绘制。
辅助处理模块中的绘图处理器DrawHandler.java源代码:
package util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.List;
import game.GameMain;
import com.Balloon;
import com.Bullet;
import com.Gun;
public class DrawHandler implements Constants {/***画游戏不同的模式状态***/public static void drawMode(Graphics g, int mode) {Font fontOld = g.getFont();//创建字体实例 //"新宋体"Font font = new Font("华文彩云", Font.BOLD, 32);g.setFont(font); //设置画笔的字体switch (mode) {case GSTART:g.drawString("初始状态!",GWindowWD/2-50,GWindowHG/2-30);break;case GPAUSE:g.drawString("暂停状态!",GWindowWD/2-50,GWindowHG/2-30);break;case GOVER:g.drawString("游戏结束状态!",GWindowWD/2-50,GWindowHG/2-30);break;}g.setFont(fontOld); //设置画笔的字体,恢复原字体}/***画枪***/public static void drawGun(Graphics g, Gun gun) {g.drawImage(GameMain.gunIcon, gun.getX(), gun.getY(), null);}/***画所有的子弹***/public static void drawBullets(Graphics g, List<Bullet> bList) {for (Bullet b : bList) { g.drawImage(b.getIcon(), b.getX(), b.getY(), null);}}/***画所有气球***/public static void drawBall(Graphics g, List<Balloon> balls) {for (Balloon ball : balls) { g.drawImage(ball.getIcon(), ball.getX(), ball.getY(), null);}}/***显示得分***/public static void drawScore(Graphics g, int score, int usedTime) {int x = 10; //显示分数的x坐标int y = 25; //显示分数的y坐标Font font = new Font(Font.SANS_SERIF, Font.BOLD, 16);g.setColor(Color.blue);g.setFont(font); //设置字体g.drawString("射击得分:"+score, x, y); //显示得分y += 30; int time = (GAMETIME - usedTime)/1000;g.drawString("剩余时间:"+time+"秒", x, y); }/***显示排名或提示信息***/public static void drawTips(Graphics g, String str) {int x = GWindowWD/2-60; //显示排名的x坐标int y = GWindowHG/2; //显示排名的y坐标Font font = new Font(Font.SANS_SERIF, Font.BOLD, 12);g.setColor(Color.blue);g.setFont(font); //设置字体g.drawString(str, x, y); //显示排名}
} //辅助处理模块中的绘图处理器DrawHandler.java源代码结束。
(三)、游戏主控模块
游戏主控模块是游戏程序的入口,也是游戏的主程序,实现如下功能:
1,图像模块中的图像载入;
2,定义一个鼠标适配器MouseAdapter作为鼠标监听器,处理鼠标事件;
3,利用双缓冲技术绘制游戏窗口;
4,使用Timer计划任务管理器来控制游戏流程。其源代码如下所示:
方法drawGameWindow( Graphics g )是游戏显示界面绘制,使用了双缓冲技术以减少屏幕的闪烁。
游戏主控模块GameMain.java源代码:
package game;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import javax.swing.*;
import com.Balloon;
import com.Bullet;
import com.Gun;
import util.CheckHandler;
import util.DBHandler;
import util.DrawHandler;
import util.Constants;
public class GameMain extends JComponent implements Constants {public static Image gunIcon;public static Image bulletIcon;public static Image ballIcon[] = {null,null,null,null,null,null};//在类的静态块中加载图像文件static {try {gunIcon = ImageIO.read(new File("image\\手枪.png"));bulletIcon = ImageIO.read(new File("image\\子弹.png"));ballIcon[0] = ImageIO.read(new File("image\\气球0.png"));ballIcon[1] = ImageIO.read(new File("image\\气球1.png"));ballIcon[2] = ImageIO.read(new File("image\\气球2.png"));ballIcon[3] = ImageIO.read(new File("image\\气球p0.png"));ballIcon[4] = ImageIO.read(new File("image\\气球p1.png"));ballIcon[5] = ImageIO.read(new File("image\\气球p2.png"));} catch (Exception e) {e.printStackTrace();} }private String message = ""; //提示信息private int mode; //表示游戏运行状态private int score = 0; //游戏得分private int usedTime = 0; //记录游戏进行时间private Timer runTimer; //定时器//每隔间隙时间(20ms)检查一下游戏各对象的状态,重新绘图private int chkInterval = INTERVAL;//间隙时间private Gun gun = new Gun();private List<Balloon> balloons = new ArrayList<>();private List<Bullet> bullets = new ArrayList<>();public static void main(String[] args) {JFrame frame = new JFrame("射击气球游戏");GameMain game = new GameMain(); //游戏面板frame.add(game); //添加游戏面板到框架窗口frame.setSize(GWindowWD, GWindowHG);frame.setAlwaysOnTop(true);frame.setLocationRelativeTo(null);//窗体在屏幕中央显示frame.setVisible(true);game.launch();}public void launch() {addMouseListener(msAdapter);addMouseMotionListener(msAdapter);runTimer = new Timer();runTimer.schedule(new TimerTask() {@Override //每隔间隙时间移动、检查游戏对象的状态,并重新绘图public void run() {if (mode==GRUN) {usedTime += chkInterval;if(usedTime%300==0){ //每0.3秒加一新气球balloons.add(new Balloon());} //每隔间隙时间移动气球和子弹位置for (Balloon ball : balloons) ball.move();for (Bullet b : bullets) b.move();//检查是否击中气球,并更新成绩score = CheckHandler.checkHit(balloons, bullets, score);//检查超界的气球和子弹,并删除CheckHandler.checkBeyond(balloons, bullets);if (CheckHandler.checkGameOver(usedTime)) { DBHandler.addScore(score);//游戏结束把成绩记入数据库//与历史数据比较,获取本次游戏排名message = DBHandler.qryRank(score);mode = GOVER; //置游戏结束状态}}repaint(); //重绘组件}}, chkInterval, chkInterval);}@Overrideprotected void paintComponent(Graphics g) {drawGameWindow(g);}public void drawGameWindow( Graphics g ) {int w=getWidth();int h=getHeight();//双缓冲技术 //创建一个和画板长宽相同的虚拟屏(画布)Image imgBuf = createImage(w, h);Graphics gBuf = imgBuf.getGraphics();//获得画布GraphicsgBuf.setColor(getBackground());gBuf.fillRect(0,0,w, h); //将画布设置为背景色/***在缓冲区中绘制游戏图像***/DrawHandler.drawGun(gBuf, gun);DrawHandler.drawBullets(gBuf, bullets);DrawHandler.drawBall(gBuf, balloons);DrawHandler.drawScore(gBuf, score, usedTime);DrawHandler.drawMode(gBuf, mode);if (mode==GSTART) message = "提示信息:鼠标单击开始游戏。";DrawHandler.drawTips(gBuf, message);
gBuf.dispose(); //释放画笔资源//将虚拟屏中的图像绘制到组件面板上g.drawImage(imgBuf, 0, 0, null);}private MouseAdapter msAdapter = new MouseAdapter() {@Override public void mouseClicked(MouseEvent e) {switch (mode) {case GSTART:mode = GRUN;message = "";//消息清空break;case GRUN:gun.shootBall(bullets); //向气球射击repaint(); //重画组件break;case GOVER:balloons.clear(); //清空气球bullets.clear(); //清空子弹gun = new Gun(); //重新创建枪usedTime = 0; //重置所用时间score = 0; //重置成绩mode = GSTART; //游戏模式设置为开始。break;}}@Overridepublic void mouseEntered(MouseEvent e) {if (mode==GPAUSE) { //如果游戏处于暂停状态mode = GRUN; //继续运行游戏}}@Overridepublic void mouseExited(MouseEvent e) {if (mode==GRUN) { //如果游戏处于运行状态mode = GPAUSE; //暂停游戏}}@Override public void mouseMoved(MouseEvent e) {if (mode==GRUN) {//鼠标移动时,重新定位枪的坐标gun.moveTo(e.getX(), e.getY());}}};
} //游戏主控模块GameMain.java源代码结束。
(四)、数据库处理模块
数据库处理模块实现了游戏数据库的创建功能、数据记录插入功能和查询游戏排名功能。为了使得游戏程序环境更加简单,本系统使用了Java自带的derby数据库的嵌入式数据库模式。定义了一个GameDB数据库,在数据库中创建了score表用于记录游戏玩家每次战绩,该表有三个字段ID、date和score。ID和score都是整型数据,ID表示玩的序号,score则表示每次玩的得分;date表示该次游戏的日期时间,其类型是时间戳。数据库处理器(DBHandler)在插入和查询统计数据记录时使用的是动态SQL语句。
下面是数据库处理器(DBHandler)的源代码文件:
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.time.LocalDateTime;
public class DBHandler {private static Connection con = null;//Derby嵌入式数据库驱动private static String driver="org.apache.derby.jdbc.EmbeddedDriver";private static String dbProtocol="jdbc:derby:" ;//数据库协议/***Derby嵌入式数据库,数据库名dbName变量,其中的 ";create=true"*表示如果数据库不存在,就新建之。若用绝对路径数据库名,将在指定* 路径创建数据库;如果使用相对路径数据库名,则数据库创建在项目的*工作目录下,如本例所示。 ***/private static String dbName="GameDB;create=true";//数据库名private static String USER = "SuperMan";private static String PASSWD = "newsky@73";/***创建游戏数据库基本信息表score的SQL语句脚本***/public static final String CrtScoreTableSQL = "CREATE TABLE score ( " +"ID INT PRIMARY KEY," + //玩家ID号"date TIMESTAMP," + //记录时间"score INT )" ; //玩家得分//根据建表参数,新建数据库表public static void CrtDbTable(String sql ){ Connection con = null;String url = dbProtocol + dbName ;try {Class.forName(driver) ; /***加载数据库驱动***///建立数据库连接con = DriverManager.getConnection(url,USER,PASSWD);Statement stmt = con.createStatement();stmt.executeUpdate(sql); //执行更新stmt.close();System.out.println("创建数据库GameDB成功!");}catch (SQLException se) {se.printStackTrace();}catch(ClassNotFoundException e){System.err.println(driver+" not in CLASSPATH");}}//连接数据库方法public static Connection ConnectDB() {String url = dbProtocol + dbName;try { //加载驱动程序Class.forName(driver);} catch (ClassNotFoundException e) {e.printStackTrace();} try { //连接数据库con=DriverManager.getConnection(url, USER, PASSWD);} catch (SQLException e) {e.printStackTrace();}return con;}public static void addScore(int score) {String InsertSql = "insert into score (ID,score,date) values (?,?,?)";String QryTotalSql = "select count(*) from score";int id = 0;try {if (con==null) con = ConnectDB();//建立数据库连接Statement stmt = con.createStatement();/***执行查询,统计记录条数,返回结果集***/ResultSet rSet = stmt.executeQuery(QryTotalSql);if (rSet.next()) {int recordNo = rSet.getInt(1);id = recordNo+1; //设置新记录的ID号,实际是游戏序号。}//得到当前时间的时间戳Timestamp datetime = null;datetime = Timestamp.valueOf(LocalDateTime.now()); //把本次成绩记入score表中PreparedStatement ps=con.prepareStatement(InsertSql);ps.setInt(1, id); //设置第一个参数ps.setInt(2, score); //设置第二个参数ps.setTimestamp(3, datetime);ps.executeUpdate(); //插入记录到score表中/***释放资源***/stmt.close();ps.close();}catch (SQLException se) {se.printStackTrace();}}public static String qryRank(int score) {String QryTotalSql = "select count(*) from score";String QryRankSql = "select count(*) from score where score > ?";int n = 0;String message = "";try {if (con==null) con = ConnectDB();//建立数据库连接Statement stmt = con.createStatement();/***执行查询,统计记录条数,返回结果集***/ResultSet rSet = stmt.executeQuery(QryTotalSql);if (rSet.next()) {n = rSet.getInt(1); //记录数}/***执行查询,查询成绩排名,返回结果集***/int rank = 0; //名次变量PreparedStatement ps = null;
ps = con.prepareStatement(QryRankSql);ps.setInt(1, score);rSet = ps.executeQuery();if (rSet.next()) {/***查询到的排名结果加1,是本次游戏的实际名次***/rank = rSet.getInt(1) + 1;}/***编写message信息***/message = "本次游戏得分:"+score+ "。 在总计"+n+"次游戏中排名第"+rank; /***释放资源***/stmt.close();ps.close();}catch (SQLException se) {se.printStackTrace();}return message;}//执行时,显示“创建数据库GameDB成功!”表示数据库创建完毕。public static void main(String[] args) {CrtDbTable(CrtScoreTableSQL);/*创建数据库,只执行一次。*/}
} //数据库处理器类DBHandler.java源代码结束。
构建路径配置说明:使用嵌入式derby数据库时,在项目的构建路径中要导入derby.jar,如下图。执行一次数据库处理器(DBHandler)就可创建GameDB数据库和相关表,供游戏项目使用了。无须人工启停数据库,游戏程序直接会调用,非常方便。
数据库处理器(DBHandler)功能说明:
数据库处理器(DBHandler)有两个功能:
- 创建数据库环境:根据上文所述配置好项目的构建路径以后,编译运行数据库处理器(DBHandler)会创建射击气球游戏的数据库环境。
数据库GameDB创建后,数据库文件夹GameDB在项目文件夹下(如图)。若想重新创建数据库GameDB,只要把文件夹GameDB删除,然后重新执行一次DBHandler.java即可。
- 游戏程序会调用DBHandler中的方法,把每次游戏运行数据写入数据库和查询游戏排名功能。
思考题:
在射击气球游戏编译测试通过后,再提二个问题,有兴趣的请实现之:
1,在游戏程序中,是手动点击鼠标向气球发射子弹的,如何改成由程序自动每0.3秒发射一颗子弹?
2,在数据库处理器类DBHandler.java中,增加一个查询打印GameDB中数据记录信息的方法,并测试显示出来。
博客导航:
上一篇博客:
下一篇博客: