游戏共用到了三个包app包,放入游戏运行的入口类;main包,存放实现游戏各功能类的包;util包,存放游戏工具类以及一些常量的包;13个类,每个类都有各自的不同功能,方便进行查找和修改。
具体分析:
app包:
GameApp类:实现游戏的启动运行
GameApp类作为游戏的入口,进行初始化主窗口类的对象,来加载主窗口类的内容
代码为:
import main.GameFrame;
public class GameApp {
public static void main(String[] args){
//实例化GameFrame类
new GameFrame();
}
}
main包:
GameFrame类:游戏主窗口类
所有游戏中绘制的内容都在这个窗口中完成,进行编写的功能具体有:实例化各类;
初始化游戏中一些内容的对象;
添加监听事件;
重写update方法实现双缓冲技术,避免屏幕闪烁问题,同时添加上游戏暂停结束的画面;
代码具体的实现
package main;
import static util.Constant.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
//游戏的主窗口类,所有在游戏中绘制的内容都在此窗口中完成
//GameFrame继承Frame,使GameFrame类有创建窗口的功能
public class GameFrame extends Frame {
//实例化GameBackGround中的类
private GameBackGround gameBackGround;
//实例化小鸟的类
private Bird bird;
//实例化障碍物层类
private GameBarrierLayer gameBarrierLayer;
//实例化前景类
private GameFrontGround gameFrontGround;
//实例化云彩类
private Cloud cloud;
//存放图片的 图片
//设置一张图片
//传进图片的长度和高度
private BufferedImage buffimg = new BufferedImage(FRAM_WIDTH, FRAM_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
//定义一个构造方法初始化一些参数
public GameFrame(){
//窗口是否可见
setVisible(true);
//窗口大小(没有定义静态之前,需要使用类名进行调用)
// setSize(Constant.FRAM_WIDTH, Constant.FRAM_HEIGHT);
//如果把Constant=包定义成静态的,后面加上.*,则可以省略Constant的调用
setSize(FRAM_WIDTH, FRAM_HEIGHT);
//窗口标题
setTitle(FRAM_TITLE);
//窗口的初始化位置
setLocation(FRAM_X, FRAM_Y);
//窗口的大小不可改变
setResizable(false);
//给窗口添加关闭事件
/*该代码为在 Swing 程序中添加一个窗口监听器,
并为窗口关闭事件添加一个监听函数。当用户关闭窗口时,
代码会调用 System 类的 exit() 方法,将程序正常结束。
由于传入的窗口适配器是匿名内部类,
这意味着我们可以简单地在构建对象时指定事件,
而不用创建新的自定义窗口适配器类。
*/
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);//结束程序
}
});
//在构造器中调用下面初始化的对象
initGame();
//启动线程
new run().start();
//添加监听
/*KeyAdapter是一个类
KeyAdapter中已经实现了对键盘事件的监听方法,
但这些方法的实现是空的,我们需要在使用时重写这些方法,
根据需求处理键盘事件。*/
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//调用add方法,实现相应的键盘监听
add(e);
}
@Override
public void keyReleased(KeyEvent e) {
minu(e);
}
});
}
//初始化对象
/*在GameFrame类中,
实例化了GameBackGround类的对象gameBackGround,
即在GameFrame对象初始化的同时也初始化了gameBackGround对象。
这是因为在游戏的主窗口中,需要在窗口中绘制游戏的背景。
在GameBackGround类中封装了绘制背景的方法,
因此需要创建一个GameBackGround对象,通过调用其方法来绘制背景。
同时,在GameBackGround类中还包含了游戏中玩家、敌人、子弹等对象的初始化,
这些对象同样需要在GameFrame中使用,因此需要实例化GameBackGround类的对象。*/
public void initGame(){
//分别实例化GameBackGround类,Bird类,GameFrontGround类,GameBarrierLayer类的对象,
gameBackGround = new GameBackGround();
bird = new Bird();
gameFrontGround = new GameFrontGround();
gameBarrierLayer = new GameBarrierLayer();
}
//写一个线程
/*
* 这是一个继承自Thread类的run类。
* 它重写了Thread类中的run方法。
* 在这个run方法中,它执行了repaint()方法,
* 这个方法会调用paint()方法重新绘制组件。
* 在多线程环境下,这个类的实例可以与其他线程并发执行。
* 当这个线程启动时,它会自动调用run方法。
* 所以在这个run方法中,它会反复调用repaint方法来不断刷新界面。
*/
class run extends Thread{
@Override
public void run(){
while (true) {
repaint();
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//定义初始化是否开始的状态为true,在后面方便实现游戏暂停
public static boolean isStarted = true;
//写一个update方法,所有需要绘制的内容都在这里面绘制
/*这段代码是Java中的重写方法,通常用于在每次图形界面需要更新时,
自动调用该方法。
具体而言,该方法首先调用了gameBackGround对象的draw方法,绘制了背景图像。
然后调用了bird对象的draw方法,绘制了小鸟图像。
这里需要注意的是,Graphics对象是一个类似于画布的实例,
draw方法则是用来在画布上绘制图像的方法。因此,在调用draw方法时,
需要将该对象作为参数传递进去,使得绘制的图像可以正确显示在画布上。
实现了游戏界面的绘制
需要注意的是,该方法实现了双缓冲技术,即先将需要绘制的所有元素绘制在缓存图片上,
然后再一次性绘制到屏幕上,避免了图像闪烁等问题。*/
@Override
public void update(Graphics g){
//进行判断,如果小鸟的生命值大于0,并且处于游戏开始状态,执行代码
if (bird.life > 0 && isStarted == true){
//播放音乐
Music.playMusic();
//得到图片的画笔
Graphics graphics = buffimg.getGraphics();
//把所有要绘制的东西全部放在这张图片上,绘制背景,小鸟,前景,障碍物
gameBackGround.draw(graphics);
bird.draw(graphics);
gameFrontGround.draw(graphics);
gameBarrierLayer.draw(graphics,bird);
//然后一次性把这张图片绘制在屏幕中
g.drawImage(buffimg, 0, 0, null);
}//如果小鸟的生命值大于0,游戏处于暂停状态,执行下列代码
else if(bird.life > 0 && isStarted == false){
//停止播放音乐
Music.stopMusic();
// 设置字体颜色为白色
g.setColor(Color.WHITE);
// 设置字体,风格,字号
g.setFont(new Font("微软雅黑", 1, 40));
//打印游戏暂停时的提示语)
g.drawString("点击空格键开始", 120, 180);
}
else{//小鸟死亡时执行下列语句
// 停止播放音乐
Music.stopMusic();
// 定义游戏结束的提示
String over = "游戏结束";
// 设置字体的颜色为红色
g.setColor(Color.red);
// 设置字体,风格,字号
g.setFont(new Font("微软雅黑", 1,60));
// 字体打印的语句,xy对应的位置
g.drawString(over, 180, 200);
// 重新开始的提示语
String reset = "Space Reset Game";
// 打印的内容,对应xy轴的位置
g.drawString(reset, 25, 300);
}
}
//调整up与down的true与false情况
//fly等于1,up为true,为5,up为false
//按键
public void add(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_UP:
// 如果点击的是上建,up定义为true,Bird类中会根据up的值定义小鸟的飞行状态
bird.fly(1);
break;
// 点击的是空格键
case KeyEvent.VK_SPACE:
// 如果小鸟生命值为0,小鸟死亡时
if (bird.life == 0){
// 重新开始游戏
restart();
}else{
// 小鸟没有死亡,isStarted取相反值,使游戏停止
isStarted = !isStarted;
}
break;
}
}
//松键方法
public void minu(KeyEvent e){
switch (e.getKeyCode()){
// 松开向上按键,调用小鸟类中的fly方法,up定义为false,小鸟飞翔状态改变
case KeyEvent.VK_UP:
bird.fly(5);
break;
}
}
public void restart(){
//清空障碍物
gameBarrierLayer.restant();
//小鸟回原来位置
bird.restarDraw();
//重新定义是否开始的状态为true
isStarted = true;
}
}
定义小鸟的初始状态;给小鸟添加矩形框,方便进行碰撞检测;定义控制小鸟移动,改变小鸟移动,重新定义小鸟位置与生命值的方法
package main;
import static util.Constant.*;
import util.GameUtil;
import java.awt.*;
import java.awt.image.BufferedImage;
public class Bird {
//定义一个加速度acceleration
private int acceleration =0;
//给小鸟添加矩形框
private Rectangle rect;
//小鸟设定生命值
public int life = 3;
//定义一个数组,存放小鸟图片,三张
private BufferedImage[] images;
public static final int BIRD_IMAGE_COUNT = 3;
//小鸟移动的方向
private boolean up = false, down = false;
//小鸟初始化速度
private int speed = 4;
//小鸟状态,小鸟的状态等于对应的数字时,把小鸟的图片换成对应的图片
private int state;
public static final int STATE_NORMAL = 0;//平着飞
public static final int STATE_UP = 1;//向上飞
public static final int STATE_DOWN = 2;//向下飞
//小鸟位置
private int x = 150, y = 200;
//构造方法中对资源初始化
public Bird(){
//初始化集合用来存储小鸟的个数
/*这段代码是用来初始化小鸟对象的构造方法,
其中主要包含了对小鸟图片资源的初始化。
具体来说,
该构造方法通过定义一个名为images的实例变量来存储小鸟的图片资源,
其长度为BIRD_IMAGE_COUNT,即小鸟图片的数量。
接下来使用了一个for循环,循环次数为BIRD_IMAGE_COUNT,
即将每张小鸟图片的路径传进GameUtil.loadBufferedImage方法中,
获得对应的BufferedImage对象后存入images数组中,
使得小鸟对象的images变量存储了所有小鸟图片资源。
其中loadBufferedImage是一种自定义的方法,
用于将指定路径的图片文件读入内存并返回对应的BufferedImage对象。
在游戏中,小鸟的各个动作需要对应不同的图片资源,
因此此步骤对所有小鸟图片资源的读取和存储是十分必要的。
*/
images = new BufferedImage[BIRD_IMAGE_COUNT];
for (int i = 0; i < BIRD_IMAGE_COUNT; i++) {
//把路径传进来,把每张小鸟图片的路径传入GameUtil.loadBufferedImage方法中
images[i] = GameUtil.loadBufferedImage(BIRD_IMG[i]);
}
//定义矩形框的宽高,宽高为小鸟图片的宽高
int w = images[0].getWidth();
int h = images[0].getHeight();
// 实例化矩形框
rect = new Rectangle(w,h);
}
//绘制小鸟
public void draw(Graphics g){
// 调用控制小鸟移动的方法
flyLogic();
//画出对应的小鸟图片
//小鸟图片放到数组中。直接使用数组即可
g.drawImage(images[state], x, y, null);
//绘制小鸟矩形
// g.drawRect(x, y, (int)rect.getWidth(), rect.height);
//给矩形的宽高进行赋值
rect.x = this.x;
rect.y = this.y;
}
//控制小鸟移动的方向
public void flyLogic(){
//点击上建,up的值为true
if (up == true){
// y -= speed;
// 小鸟的加速度自减
acceleration--;
// 设置小鸟y坐标的移动
y+=acceleration;
// 如果加速度小于10,令加速度等于-10
if (acceleration < -10){
acceleration = -10;
}
// 如果y坐标小于20,令y坐标不变,加速度为0
if (y < 20){
y = 20;
acceleration = 0;
}
}
//松开向上箭头键,up为false,情况与上面类似
if (up != true){
// y += speed;
acceleration++;
y+=acceleration;
if (acceleration > 10){
acceleration = 10;
}
if (y > 475){
y = 475;
acceleration = 0;
}
}
}
//改变小鸟状态的方法
public void fly(int fly){
switch(fly){
case 1:
state = 1;
up = true;
break;
case 5:
state = 2;
up = false;
break;
}
}
//定义矩形框的get方法,方便障碍物画矩形框
public Rectangle getRect() {
return rect;
}
//重新绘制小鸟位置与小鸟生命值
public void restarDraw(){
life = 3;
x = 200;
y = 200;
}
}
详细有云彩速度,位置的定义,画出云彩的方法,判断云彩是否飞出屏幕以外的方法
package main;
import java.awt.*;
import java.awt.image.BufferedImage;
//云彩类
public class Cloud {
//云彩图片
private BufferedImage img;
//云彩速度
private int speed;
//云彩位置
private int x, y;
//构造函数 空参实参
// 在调用实参构造方法时进行赋值
public Cloud(){}
public Cloud(BufferedImage img, int speed, int x, int y){
this.img = img;
this.speed = speed;
this.x = x;
this.y = y;
}
//写一个draw方法画出云彩
public void draw(Graphics g){
// 实现云的移动,云在x轴上以speed的速度不断移动
x -= speed;
// 画出云的位置,参数分别为云的图片,xy轴的坐标,转换了更多图像时要通知的对象
g.drawImage(img, x, y, null);
}
//判断云彩是否飞出屏幕以外,根据返回值的类型在GameFrontGround类中判断是否需要移除云
public boolean isOutFrame(){
// 如果云的x坐标小于-50
if(x < -50){
// 返回true
return true;
}else{
return false;
}
}
}
GameBarrierLayer类:障碍物层类
定义一个数据记录小鸟飞行的最大时间;
实例化时间类,获取随机数,定义List集合存放障碍物,定义构造器初始化数据;
编写drw方法绘制障碍物;
编写logic方法定义游戏开始,暂停,结束状态;
编写insert方法获得障碍物对象,添加障碍物;
ran方法产生随机数;
判断小鸟发生碰撞的方法;
清空障碍物池;
储存和得到数据方法;
package main;
import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
//障碍物层类
public class GameBarrierLayer {
//定义一个数据
private int txt;
//实例化时间类
private GameTime gameTime;
private Random random = new Random();
/*这是一个Java类定义,主要作用是创建一个障碍物图层,
并且使用ArrayList来存储障碍物对象。
代码中的 "Listbarriers" 是一个成员变量,
表示一个障碍物对象的列表,其中每个障碍物对象的类型是 "Barrier"。
在构造函数中,使用 "ArrayList<>()" 初始化了 "barriers" 列表,
该列表将用于存储障碍物对象。
由于 "ArrayList" 类实现了 "List" 接口,
因此它提供了一些方便的方法来管理和访问列表中的元素。
因此,在创建 "GameBarrierLayer" 对象时,
可以使用构造函数来初始化 "barriers" 列表,
然后将其用于存储障碍物对象。
*/
//定义一个数组存放障碍物
private Listbarriers;
//定义构造器初始化数组
public GameBarrierLayer(){
barriers = new ArrayList<>();
gameTime = new GameTime();
}
//绘制障碍物
/*该代码是一个游戏中生成障碍物的功能实现。
在draw方法中,首先通过for循环遍历所有已存在的障碍物对象并进行绘制。
接着调用logic方法,该方法用于检测是否需要生成新的障碍物。
在logic方法中,首先判断已存在的障碍物数量是否为0,
若为0则调用ran方法生成一个随机数作为障碍物的高度,
并创建上下两个障碍物对象。这两个对象的x坐标都为600,
即在屏幕最右边生成,并分别设置其高度为numberTop和500-numberDown。
若已存在障碍物,
则检测最后一个障碍物是否已经完全进入屏幕内(即其x坐标是否小于0),
若已经进入则和上述过程相同再生成一个新的障碍物对。
总的来说,这段代码的作用是实现游戏中不断生成障碍物的效果,
并且保证生成的障碍物数量始终能够满足游戏的需求。
*/
public void draw(Graphics g,Bird bird){
// 如果不使用循环进行编写
//new一个障碍物类
// Barrier barrier = new Barrier(200, 0, 200, 0);
// barrier.draw(g);
// barriers.add(barrier);
// barriers.get(0).draw(g);
// Barrier barrier1 = new Barrier(300, 0, 300, 2);
// barriers.add(barrier1);
// barriers.get(1).draw(g);
// 定义了一个循环变量i,初始值为0,每次循环自增1,
// 直到i小于barriers的大小(即barriers列表中元素的数量)时循环结束。
for(int i = 0; i < barriers.size(); i++){
// 通过get(i)方法从barriers列表中获取当前元素
// 并给barrier变量赋值,用于之后的操作。
Barrier barrier = barriers.get(i);
// 判断barrier元素的可见属性是否为true,
// 如果是,就执行下一行的代码;否则执行else语句块中的代码。
if (barrier.getVisible() == true){
//调用barrier元素的draw()方法来绘制该元素的外观,
// 即在屏幕上显示。
barrier.draw(g);
}else{
// 从barriers列表中移除当前元素并将其赋给remove变量。
Barrier remove =barriers.remove(i);
// 将remove元素放回对象池(可以理解为回收该对象,
//以便之后重复利用)。
Barrierpool.setPool(remove);
//由于remove元素被移除后,
//后面的元素都往前移了一位,
//所以需要将循环变量i减去1以便下一次循环能正确地处理所有元素。
i--;
}
}
// 判断小鸟是否发生碰撞
collideBird(bird);
//
logic(g,bird);
}
/*游戏的主要逻辑部分。首先判断是否已经生成了障碍物,如果没有则调用 `ran()` 方法生成随机数来决定生成障碍物的种类和数量,并开始计时。然后根据障碍物种类和数量调用 `insert()` 方法创建相应数量的障碍物对象,添加到 `barriers` 集合中。
如果已经存在障碍物,则显示已经坚持的时间和当前小鸟的生命值,并根据上一次的最高记录调用 `getTxt()` 方法获取最高纪录,如果当前时间比最高纪录长,则更新最高纪录并调用 `setTxt()` 方法保存到文件中。接着判断最后一个障碍物是否已经完全进入屏幕内,如果是则再次随机生成障碍物种类和数量,并调用 `insert()` 方法创建新的障碍物对象后添加到 `barriers` 集合中。最后刷新游戏窗口,并继续下一次循环*/
public void logic(Graphics g,Bird bird){
// 如果障碍物集合为空
if(barriers.size() == 0){
// 随机数方法,得到两个随机数
ran();
//添加计时
gameTime.begin();
//障碍物从屏幕最右边生成
//上方
// Barrier top = new Barrier(600, 0, numberTop, 0);
// barriers.add(top);
//下方
// Barrier down = new Barrier(600, 500-numberDown, numberDown, 2);
// barriers.add(down);
// 获取到一个障碍物并进行赋值,上方的
insert(600, 0, numberTop, 0);
// 获取到一个下方的障碍物并进行赋值,同时把障碍物存入集合中
insert(600, 500-numberDown, numberDown, 2);
}else{
// 获得游戏时间,并把游戏时间打印出来
long differ = gameTime.differ();
//设置字体,格式,大小
g.setFont(new Font("微软雅黑", 1, 20));
//设置字体颜色
g.setColor(Color.black);
//画出经历的时间
g.drawString("坚持了"+differ+"秒", 30, 50);
//画出小鸟生命值剩余情况
g.drawString("小鸟生命:"+bird.life,450,50);
//获得文本内储存的数据
txt = getTxt();
if (differ <= txt){
//如果经历的时间小于文本内储存的数据,打印最高成绩为文本内数据
g.drawString("最高成绩 :"+txt, 200, 50);
}else{
//否则,储存最高记录
setTxt(String.valueOf(differ));
// 打印最高记录
g.drawString("最高成绩:"+differ, 200, 50);
}
//判断最后一个障碍物是否完全进入屏幕内
Barrier last = barriers.get(barriers.size() - 1);
//如果进入屏幕内
if (last.isInFrame()){
//重新生成随机数
ran();
// 生成的随机数小于50
if(number < 50){
// 画
insert(600, 50, 350, 4);
// System.out.println("440的障碍物");
}else if(number > 450){
insert(600, 125, 200, 6);
// System.out.println("220的一个障碍物");
}else{
insert(600, 0, numberTop, 0);
insert(600, 500-numberDown, numberDown, 2);
}
// //障碍物从屏幕最右边生成.创建上下两个障碍物对象
// //上方
// Barrier top = new Barrier(600, 0, numberTop, 0);
// barriers.add(top);
// //下方
// Barrier down = new Barrier(600, 500-numberDown, numberDown, 2);
// barriers.add(down);
}
}
}
//从池中获取对象,把参数封装成barrier存入barriers数组中
/*这段代码的作用是往一个存储障碍物的列表中添加一个新的障碍物,
总体来说,这段代码是实现了从对象池中获取一个Barrier对象,
并通过给其属性赋值的方式来创建一个新的障碍物,
最后将其添加到存储障碍物的列表中。
这种方式可以避免频繁地创建和销毁对象带来的性能问题。
*/
//定义了一个公共方法insert,传入参数包括障碍物的位置和类型信息
public void insert(int x, int y, int num, int type){
// 从一个对象池中获取一个Barrier对象,并赋值给top变量
Barrier top = Barrierpool.getPool();
// 给top对象设置x坐标
top.setX(x);
// 给top对象设置y坐标。
top.setY(y);
// //给top对象设置长度
top.setHeight(num);
// 给top对象设置类型。
top.setType(type);
//设置visiable的值为true
top.setVisible(true);
// 将top对象添加到一个存储障碍物的列表中。
barriers.add(top);
}
//上方障碍物高度
private int numberTop;
//下方障碍物高度
private int numberDown;
//中间悬浮的障碍物
private int number;
//产生三个100~500的随机数的方法
public void ran(){
numberTop = random.nextInt(400)+100;
numberDown = random.nextInt(400)+100;
number = random.nextInt(500);
//如果管道重合(两个管道的高度和大于450,小鸟无法飞过),重新生成随机数
if (numberTop + numberDown >= 450){
ran();
}
}
//判断障碍物与小鸟发生碰撞
public boolean collideBird(Bird bird){
for (int i = 0; i < barriers.size(); i++) {
// 获得障碍池中的障碍物对象
Barrier barrier = barriers.get(i);
// 矩形框的碰撞方法,判断矩形框是否有重合
if (barrier.getRect().intersects(bird.getRect())){
System.out.println("撞上了");
// 撞上后小鸟的生命值减一
bird.life--;
// 移除与小鸟相撞的障碍物对象
barriers.remove(barrier);
}
}
return false;
}
//清空障碍物池的方法
public void restant(){
barriers.clear();
}
//创建一个File,传入记录最高分数文件的路径,创建的文件内传入一个数字,否则会报出异常
static File file = new File("传入txt文件的路径");
//储存数据了,写入一个字符串到文件中
public static void setTxt(String str){
// 创建一个FileWrite的对象,该对象是FileWriter的实现类,置为null,方便异常处理
FileWriter fileWriter = null;
try {//利用传入的文件对象,创建FileWriter对象,指定写入的文件
fileWriter = new FileWriter(file);
} catch (IOException e) {
e.printStackTrace();
}
try {//调用write方法,把参数str写入到文件中
fileWriter.write(str);
} catch (IOException e) {
e.printStackTrace();
}
try {//关闭文件
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//得到文件中的数据
public int getTxt(){
//创建一个名为in的BufferedReader对象,该对象取至file文件,null方便进行异常处理
BufferedReader in = null;
// 通过readLine方法逐行读取文件内容,并将其解析为整数值,赋值给read
try {//利用传入的文件file,创建BufferedReader的对象
in = new BufferedReader(new FileReader(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// String s = in.readLine();
int read = 0;
try {//从文件中读取一行字符串,并将其转换为整型值
read = Integer.parseInt(in.readLine());
} catch (IOException e) {
e.printStackTrace();
}
try {//关闭文件流
in.close();
} catch (IOException e) {
e.printStackTrace();
}//返回读到的整型值
return read;
}
}
创建用于管理池中所有对象的容器
定义池中初始化对象的个数和池中对象的最大个数
使用静态代码块初始化池中的对象
分别定义从从池中获取对象和把对象归还容器的方法
package main;
import java.util.List;
import java.util.ArrayList;
/*每次需要障碍物的时候都会创建一个对象,占用大量内存,需要创建一个对象池类
为了避免反复创建和销毁对象,使用对象池来创建好一些对象,
使用的时候从池中获得,使用完毕后,归还*/
public class Barrierpool {
/*这段代码是一个使用对象池技术实现的管理器。
对象池是一种重复利用已经创建的对象来避免重复创建和
销毁对象的技术,从而提高程序性能。
在这个示例中,对象池中存储的是Barrier对象。
这个对象池使用一个静态的List来存储对象,
而不是每次需要一个Barrier对象时都重新创建。
对象池的大小是之前定义的initCount。*/
//用于管理池中所有对象的容器
private static Listpool = new ArrayList<>();
//池中初始化对象的个数
public static final int initCount = 16;
//对象池中最大个数
public static final int maxCount = 20;
/*初始化对象池的过程在static块中进行,
这个块是在类加载时运行的,并且只会执行一次。
在这个示例中,
static块创建了initCount个Barrier对象,
并将它们添加到对象池中。
这样,在需要Barrier对象的时候,
程序可以从对象池中获取可用的对象,
而不是重新创建一个新的对象。
*/
//初始化池中的对象
static{
for (int i = 0; i < initCount; i++) {
pool.add(new Barrier());
}
}
//从池中获取一个对像
public static Barrier getPool(){
//获得池中对象的个数
int size = pool.size();
//如果池中有对象才可以拿对象
if (size > 0){
//移除并返回对象
System.out.println("拿走一个");
return pool.remove(size - 1);
}else{
//池中没有对象,只能new
System.out.println("新的对象");
//新定义一个对象,返回给方法
return new Barrier();
}
}
//把对象归还容器
public static void setPool(Barrier barrier){
//如果池中障碍物的数量小于最大数量
if (pool.size() < maxCount){
//向池中添加障碍物
pool.add(barrier);
System.out.println("容器归还了");
}
}
}
定义云彩的个数,存放云彩的集合,云彩的飞行速度;
得到资源图片,制造随机数的函数;
构造器初始化数据;
绘制云彩的方法;
控制云彩个数的方法;
package main;
import util.GameUtil;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
//背景类
public class GameFrontGround {
//云彩的个数
private static final int CLOUND_COUNT = 2;
//存放云彩的容器
/*这行代码是定义了一个名为clouds的List对象,
其中存储了一组Cloud对象。
这个List是Java中的一种集合,
使用它可以把若干个元素放在一起,
进行批量操作,比如添加元素、遍历元素、
删除元素等等。
具体的使用方式可能需要根据具体的场景和业务需求来进行调整。
*/
private Listclouds;
//云彩飞行的速度
private static final int CLOUND_SPEED = 1;
/*这行代码定义了一个类型
为 BufferedImage 数组的变量 img。
BufferedImage 是 Java 中表示图像的类,
它是一个实现了 RenderedImage 接口的缓存图像类,
可以用于操作和修改图像数据。
数组表示可以存储多张图片数据的数组,
每个元素代表一张图片,可以对每张图片单独进行操作。
因此,private BufferedImage[] img;
定义了一个变量 img,
它可以存储多张 BufferedImage 图像,
可以对每张图片进行单独处理。*/
//使用到的图片资源
private BufferedImage[] img;
//制造随机数的函数
private Random random;
//构造器初始化数据
public GameFrontGround(){
/*这行代码声明了一个名
为 "clouds" 的 ArrayList(动态数组)对象,
并对其进行初始化。在这个例子中,
"<>" 符号表示 ArrayList 存储的是未知类型的元素,
这样声明和初始化 ArrayList 对象后,
就可以在其中存储和操作元素。通常,
开发人员使用 add() 方法将元素添加到 ArrayList中,
使用 get() 方法访问 ArrayList 中的特定元素,
使用 size() 方法获取 ArrayList 的大小等。*/
//实例化存放云彩的容器
clouds = new ArrayList<>();
//定义数组中储存元素的最大数量
img = new BufferedImage[CLOUND_COUNT];
//容器中添加云彩图片
for (int i = 0; i < CLOUND_COUNT; i++) {
//调用GameUtil方法中的loadBufferedImage方法来获取图片添加到
//img数组中
img[i] = GameUtil.loadBufferedImage("newBird\\img\\cloud"+i+".png");//括号内填写图片的路径
}
//创建一个随机数的对象
random = new Random();
}
//绘制云彩
public void draw(Graphics g){
// //传入图片(创建对象)
//没有使用循环
// Cloud cloud = new Cloud(img[1], CLOUND_SPEED, 300, 300);
// //把云彩添加到容器中
// //clouds是一个集合
// clouds.add(cloud);
//调用方法控制云彩的个数
logic();
for (int i = 0; i < clouds.size(); i++) {
//画出云彩
clouds.get(i).draw(g);
}
}
//控制云彩个数的方法
private void logic(){
//产生一个0~500的随机数
//Math.random()方法产生一个0~1的随机数
//如果生成的随机数小于8
if((int)(500*Math.random())< 8){
///生成一个新的云的图像,使用了随机的图片速度,位移参数
Cloud cloud = new Cloud(img[random.nextInt(CLOUND_COUNT)], CLOUND_SPEED, 600, random.nextInt(180));
//将新生成的云添加到云列表中
clouds.add(cloud);
}
//去除云彩
for (int i = 0; i < clouds.size(); i++) {
//得到云
Cloud cloud = clouds.get(i);
//如果当前的云对象已经移出了场景
if(cloud.isOutFrame()){
//将当前云对象从云列表删除
clouds.remove(i);
//将当前遍历到的云对象的下标减一,以便下一次循环能够正确遍历到所有的云对象
i--;
System.out.println("云被移除了"+cloud);
}
}
}
}
定义变量开始时间,游戏结束时的时间,时间差;
计算时间差的方法
计时开始的方法;
package main;
public class GameTime {
//开始
private long beginTime;
//结束
private long endTime;
//时间差
private long differ;
public GameTime(){}
//计时开始方法,System调用方法,计算从起始时间开始到现在的时间
public void begin(){
beginTime = System.currentTimeMillis();
}
//时间差方法
public long differ(){
//获得从起始时间开始到游戏结束时的时间
endTime = System.currentTimeMillis();
// 得到时间差,除以1000是以秒为单位
return differ = (endTime - beginTime)/1000;
}
}
声明背景音乐文件
静态代码块加载背景音乐文件
循环播放音乐的方法
停止播放音乐的方法
package main;
import javax.sound.sampled.*;
import java.io.File;
public class Music {
//声明了名为music的private static Clip变量,表示音乐文件
private static Clip music;
//加载背景音乐文件,并将其储存在music变量中
static{
File bgMusicFile = new File("newBird\\\\img\\\\bgm2.wav");
try{
AudioInputStream ais = AudioSystem.getAudioInputStream(bgMusicFile);
music = AudioSystem.getClip();
music.open(ais);
}catch (Exception e){
e.printStackTrace();
}
}
public static void playMusic(){
//循环播放
music.loop(Clip.LOOP_CONTINUOUSLY);
}
//停止播放音乐
public static void stopMusic(){
music.stop();
}
}
定义窗口大小的变量;
定义窗口标题,窗口初始化位置;
定义背景颜色;
定义数组存放小鸟图片的路径;
定义数组存放障碍物图片资源的路径;
package util;
import java.awt.*;
//初始化需要用到的参数
public class Constant {
//窗口的大小
//定义静态的组最终变量作为宽高
public static final int FRAM_WIDTH = 600;
public static final int FRAM_HEIGHT = 500;
//窗口的标题
//静态的最终变量作为窗口的标题
public static final String FRAM_TITLE = "飞翔的小鸟";
//窗口初始化的位置
//静态最终变量
public static final int FRAM_X = 200;
public static final int FRAM_Y = 200;
//图片的路径
public static final String BK_IMG_OATH = "newBird\\img\\bird_bk.png";
//定义背景的颜色
public static final Color BK_COLOR = new Color(0X4B4CF);
//存放三张小鸟图片的路径
public static final String[] BIRD_IMG = {
"newBird\\img\\bird_normal.png",
"newBIrd\\img\\bird_up.png",
"newBird\\img\\bird_down.png"
};
//定义障碍物图片资源(路径)
public static final String[] BARRIER_IMG_PATH = {
"newBird\\img\\barrier.png",
"newBird\\img\\barrier_up.png",
"newBird\\img\\barrier_down.png"
};
}
装载图片
package util;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.IOException;
//加载图片工具类
public class GameUtil {
//装载图片
public static BufferedImage loadBufferedImage(String imgPath){
try{
/*这里是创建一个输入流,
用来读取指定路径下的文件,
imgPath是文件路径。在该方法中,
我们使用FileInputStream将图片文件读入内存中,
返回BufferedImage对象。
*/
return ImageIO.read(new FileInputStream(imgPath));
}catch(IOException e){
e.printStackTrace();
}
return null;
}
}