在开发此项目之前,我们需要去先下载一些关于飞翔的小鸟素材图片
小鸟的飞翔图片
地面背景图片
柱子图片
以及开始结束图片
本项目主要针对JAVA刚学完想巩固练习,却没有项目着手的同学,用的都是一些基础性的知识
运用到的知识点:
1.面向对象:封装继承多态
2.数组
3.常用类Math
4.swing组件:图形界面工具
5.鼠标监听
6.IO流
7.多线程
1.先实现窗口的制作,并且将背景图放入窗口面板
2.设计地面类,实现游戏背景地面移动
3.设计柱子类,实现游戏背景柱子移动
4.设计小鸟类,在类中添加小鸟相关方法
初始化小鸟相关参数, 判断小鸟是否碰撞柱子与地面
5.把小鸟放在面板中
6.鼠标监听控制小鸟飞行的轨迹
7.添加计分和游戏结束游戏开始的画面
代码分类:
由上述的分析,我们可以大致分析计划创建4个类
1.小鸟类:设置小鸟参数,添加小鸟判断方法
package game;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
class Bird {
BufferedImage image; // 图片
int x, y; // 位置
int width, height; // 宽高
int size; // 大小(用于碰撞检测)
double g; // 重力加速度
double t; // 位移的间隔时间
double v0; // 最初上抛速度
double speed; // 当前上抛速度
double s; // 经过t时间之后的位移
double alpha; // 小鸟的倾角(弧度)
BufferedImage[] images; // 一组图片,记录小鸟的动画帧
int index; // 动画帧数组的下标
// 初始化小鸟
public Bird() throws IOException {
image = ImageIO.read(getClass().getResource("/resources/0.png"));
width = image.getWidth();
height = image.getHeight();
x = 132;
y = 280;
size = 40;
// 初始化位移参数
g = 4;
v0 = 20;
t = 0.25;
speed = v0;
s = 0;
alpha = 0;
// 初始化动画帧参数
images = new BufferedImage[8];
for(int i=0; i<8; i++){
images[i] = ImageIO.read(getClass().getResource("/resources/"+ i +".png"));
}
index = 0;
}
// 飞行动作 (变化一帧)
public void fly(){
index ++;
image = images[(index / 12) % 8];
}
// 移动一步
public void step(){
double v0 = speed;
s = v0 * t + g * t * t /2; // 计算上抛运动位移
y = y - (int)s; // 计算鸟的坐标位置
double v = v0 -g * t; // 计算下次移动速度
speed = v;
alpha = Math.atan(s / 8); //计算倾角(反正切函数)
}
// 向上飞行
public void flappy(){
speed = v0; // 重置速度
}
// 检测小鸟是否碰撞到地面
public boolean hit(Ground ground){
boolean hit = y + size / 2 > ground.y;
if(hit){
y = ground.y - size / 2;
alpha = -3.14159265358979323 / 2;
}
return hit;
}
// 检测小鸟是否撞到柱子
public boolean hit(Column column){
// 先检查是否在柱子的范围内
if (x > column.x - column.width / 2 - size / 2
&& x < column.x + column.width / 2 + size / 2){
// 再检查是否在柱子的缝隙中
if (y > column.y - column.gap / 2 + size / 2
&& y < column.y + column.gap /2 - size / 2){
return false;
}
return true;
}
return false;
}
}
2.柱子类:设置柱子参数,添加柱子移动方法
package game;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
class Column {
BufferedImage image; // 图片
int x, y; // 位置
int width, height; // 宽高
int gap; // 柱子之间的缝隙
int distance; // 柱子之间的距离
Random random = new Random(); // 随机数工具
// 初始化第N个柱子
public Column(int n) throws IOException {
image = ImageIO.read(getClass().getResource("/resources/column.png"));
width = image.getWidth();
height = image.getHeight();
gap = 144;
distance = 245;
x = 550 + (n-1) * distance;
y = random.nextInt(218) + 132;
}
// 柱子向左移动一步
public void step(){
x--;
if (x == -width/2){
x = distance * 2 - width / 2;
y = random.nextInt(218) + 132;
}
}
}
3.地面类:设置地面参数,添加地面移动方法
package game;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
class Ground {
BufferedImage image; // 图片
int x, y; // 位置
int width, height; // 宽和高
// 初始化地面
public Ground() throws IOException {
image = ImageIO.read(getClass().getResource("/resources/ground.png"));
width = image.getWidth();
height = image.getHeight();
x = 0;
y = 500;
}
// 地面向左移动
public void step(){
x--;
if(x == -109){
x = 0;
}
}
}
4.测试类:添加开始结束、鼠标监听等方法
public class Test {
public static void main(String[] args){
Test.output();
}
public static void output(){
int[] nums=new int[100];
nums[0]=1;
for(int i=1;i<100;i++){
nums[i]=nums[i-1]+i;
}
for(int i=0;i<100;i++){
if(nums[i]%4==0&&nums[i]%14==0){
System.out.println("这是a"+i+":"+nums[i]);
}
}
}
}
在⼯程下的src⽬录下新建game和resources包最后,将该⼯程所需的图⽚拷贝⾄resources包下,将代码拷贝进game包中。
⾸先,新建BirdGame类,然后新建Groud类,表⽰游戏中的地⾯;新建Column表⽰游戏中的柱⼦;新建Bird类表⽰游戏中的鸟,这些类继承Jpanel类,都继承⾃Jpanel类。
Jpanel是Java图形界⾯化(GUI)⼯具包swing中的⾯板容器类,可以理解为画板,包含在javax.swing包中,是⼀种轻量级容器,可以加⼊到JFrame窗体中。
其中,background属性是游戏背景贴图。
其中,image属性为地⾯的贴图。
其中,image属性表⽰柱⼦贴图。
image属性表⽰Bird类的贴图,x,y表⽰鸟的中⼼位置,width和height表⽰鸟图⽚的宽度和⾼度。在本实训项⽬中,⼩鸟的⼤⼩⽤⼀个正⽅
形区域来表⽰,size即表⽰该正⽅形的边长,⽤于后续的碰撞检测。
通过程序代码⼤家可以看出实例化Column类的时候,出现了编译错误,这是因为还没有为Column类编写⼀个带参数的构造器。
下⾯介绍⼀下x、y坐标的计算⽅式,第⼀个柱⼦的x坐标为x= 432+118=550,第⼆根主⼦的x坐标为x=550+245,因此,柱⼦的x轴坐
标可以总结为x = 550+245*(n-1),其中n表⽰第⼏根柱⼦。
可以看出,柱⼦y的坐标范围在132⾄350之间随机分布,使⽤Random类的nextInt⽅法⽣成[0-218)之间的随机数,然后再加上132,这
样就能得到132⾄350之间的随机数了。
在main⽅法中,设置窗⼝的⼤⼩、居中、点击窗⼝的右上⾓“X”关闭窗⼝以及设置窗⼝可见,代码如图20所⽰。
在BirdGame类中重写paint⽅法,绘制界⾯。⾸先绘制地⾯。使⽤Graphics的drawImage⽅法,绘制背景界⾯的时候,绘制的起点坐标是(0,0)。然后,绘制2个柱⼦。
绘制两个柱⼦的时候,绘制的起点坐标与Column类的属性x,y的值不同,需要做⼀些更改。
运⾏BirdFrame类。
实现地⾯的运动
实现柱⼦的移动
实现鸟的运动
经过时间t的位移s的计算公式:
s = v0 * t + g * t * t / 2;
经过时间t后⼩鸟的纵坐标位置y1的计算公式为:
y1 = y - s;
经过t时间后,⼩鸟的运⾏速度v为:
v = v0 - g * t ;
使⽤数组,存储要显⽰的图⽚,共计8张,鸟每运动⼀次换⼀张图⽚,以达到鸟的动画效果。fly⽅法实现了鸟的飞翔。⼀个数对8取余,能够得到0到7之间的数字,正好是images数组中的下标,index/12的⽬的是 使鸟的挥动翅膀次数变慢,即切换照⽚的频率变慢。接着,在BirdGame类的action⽅法中,调⽤Bird类的fly⽅法和step⽅法。运⾏后,会发现鸟可以动起来了。最后,计算倾⾓。假设⽔平移动的位移固定为8,那么倾⾓ = 反正切s/8。调⽤Java API提供的反正切⽅法atam,计算倾⾓。
接着,在BirdGame类的paint中,为达到鸟的飞翔效果,使⽤Java API,使⼩鸟旋转。
要实现⿏标的按下事件,步骤如下:
1. 使⽤“匿名内部类”声明事件
2. 处理⽅法。即在BirdGame类的action⽅法声明事件的处理⽅法;
2、⾸先,在Bird类中添加flappy以实现鸟的飞翔。然后,在处理⽅法中,调⽤Bird类的flappy⽅法。
3、注册事件监听。即在BirdGame类的action⽅法中,注册监听事件。
要实现计分,需要进⾏以下操作:
1、在BirdGame类中增加属性score,⽤于计分,score初始化为0。
2、在BirdGame类的action⽅法的主循环中,添加逻辑判断。当柱⼦的x坐标与鸟的x坐标重合时,则加1分。
3、在BirdGame类的paint⽅法中将分数画出来。
1、实现鸟碰撞地⾯。
1)为BirdGame类增加boolean变量gameOver,该变量⽤于标识游戏是否结束,如果gameOver的值为true,则表⽰游戏结束了;如果
gameOver的值为false,则表⽰游戏没有结束。gameOver的默认值设置为fasle。
2)当检测到碰到地⾯的时候,即gameOver的值为TRUE。在循环中检测是否碰到地⾯。在Bird类中增加⽅法hit(Ground)检测鸟是否
碰地⾯。当鸟的y坐标减去size/2⼤于等于地⾯的y坐标时,则与地⾯发⽣碰撞。
3)修改BirdGame类的action⽅法中的主循环,当游戏没有结束的时,执⾏鸟和柱⼦的移动。也就是说,如果游戏结束了,鸟和柱⼦就不
移动了。
4)在BirdGame类的paint⽅法添加gameOver的值为true时,显⽰游戏结束界⾯
⼩鸟与柱⼦发⽣碰撞的x的坐标计算⼊如下:
x1 = column.x - width/2 - size/2;
x2 = column.x + width/2 + size/2;
当鸟的x坐标⼤于x1(x > x1)并且⼩于x2(x < x2)时与柱⼦发⽣碰撞。但是如果在缝隙中,则不发⽣碰撞。
⼩鸟在柱⼦的缝隙中时y坐标计算如下:
y1 = column.y - gap/2 + size/2;
y2 = column.y + gap/2 - size/2;
当鸟的y坐标⼤于y1(y>y1)并且⼩于y2(y
在Bird类添加hit(column)⽅法,⽤于判断鸟是否碰撞柱⼦
修改BirdGame类的action⽅法完成上述代码,即完成了判断鸟的碰撞功能。
1.代码运行后游戏的具体实现:
2.界面能够基本达到原先预期,,界面会出现GeT Ready!的字样。本游戏通过鼠标进行控制。点击鼠标游戏就会开始。
3.点击鼠标左键游戏开始后,每点击鼠标一次鼠标小鸟就会上升一次,每经过一根柱子左上角的数字就会加一,碰到柱子后游戏就会结束,结束界面就会出现GAMEOVER字样提醒你此次游戏已经结束,再次点击中间的符号游戏就会再次开始。自此本次的程序设计基本完成。