用java Swing做的小游戏"像素鸟"
程序员文章站
2022-06-17 10:27:59
...
最终效果
整个项目都是基于swing实现的。窗是口将图片加载到JPanel面板,然后将面板添加到到JFrame窗口实现显示。
这个类是选择几只像素鸟的类,也是main函数里执行的方法,代码有详细的注释,这里就不废话了
public class select extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
Image background;//添加到窗口的背景图片
Image title1;//添加到窗口的游戏名称图片
JFrame jf; //选择小鸟的窗口
JButton []b ;//按钮数组
client t,t1,t2;//主游戏类
public select(){
jf = new JFrame();//实例化窗口
jf.setTitle("请小心选择小鸟数量");//窗口的标题
jf.setSize(300, 300);//设置窗口大小
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口关闭方式,关闭即退出程序
jf.setLocationRelativeTo(null);//设置窗口在屏幕中间显示
jf.setResizable(true);//窗口不可更改大小
jf.add(this);//将面板加入窗口
setLayout(null);//设置面板的布局为空,从而可以任意位置放置按钮
final ImageIcon icon = new ImageIcon("Image/2.png");
background = ((ImageIcon) icon).getImage();
title1 = new ImageIcon("Image/title.png").getImage();
b = new JButton[3];
//实例化按钮
for(int i = 0;i < 3;i++)
b[i] = new JButton();
//为按钮添加照片、设置背景位置
b[0].setIcon(new ImageIcon("Image/bird0_0.png"));
b[0].setBounds(120,80,40,30);
this.add(b[0]);
//为第二个按钮添加照片、设置背景位置
b[1].setIcon(new ImageIcon("bird3_2.png"));
b[1].setBounds(95,130,100,33);
this.add(b[1]);
//外第三个按钮添加照片
b[2].setIcon(new ImageIcon("bird3_3.png"));
b[2].setBounds(70,185,146,35);
this.add(b[2]);
//为第一个按钮添加点击事件
b[0].addActionListener(new ActionListener(){
public void actionPerformed(final ActionEvent e) {
t = new client();//启动游戏类对象
jf.dispose();//撤销当前窗口
t.init1();//初始化即进入游戏
}
});
//为第二个按钮添加点击事件
b[1].addActionListener(new ActionListener(){
public void actionPerformed(final ActionEvent e) {
jf.dispose();
t = new client();
t.init2();
}
});
//为第三个按钮添加点击事件
b[2].addActionListener(new ActionListener(){
public void actionPerformed(final ActionEvent e) {
jf.dispose();
t = new client();
t.init3();
}
});
jf.setVisible(true);//设置窗口可见
}
//启动窗口后会自动调用这个画画方法,将背景图片画到界面上
public void paint(final Graphics g){
super.paint(g);
g.drawImage(background,0,0,300,300,this);
g.drawImage(title1,55,20,174,48,this);
}
}
client类,游戏主类。Main入口方法也放在了这里
public class client extends JPanel implements KeyListener{
/**
*
*/
private static final long serialVersionUID = 1L;
private Image background1,background2; //定义两张图片作为背景
private Plane pl; //定义一个线程
private pipe P[]=new pipe[300];//定义柱子数组对象
ArrayList<pipe> list=new ArrayList<pipe>();//存放柱子的集合
Random ran=new Random();
public int k=0;
public int score = 0;
private bird b1,b2,b3; //定义一个小鸟
int flag;//判断调用哪个初始化函数,即创建几个小鸟
private int x = 0; //这个变量是用来改变小鸟的飞行状态,起到切换图片的作用
public static Music m;//背景音乐
public static Music m2;//结束时的声音
JFrame jf;
public Image gameover = new ImageIcon("Image/text_game_over.png").getImage();//游戏结束的图片
//分别获取三只小鸟的散装飞行状态的图片
ImageIcon icon1 = new ImageIcon("Image/bird0_0.png");
ImageIcon icon2 = new ImageIcon("Image/bird0_1.png");
ImageIcon icon3 = new ImageIcon("Image/bird0_2.png");
ImageIcon icon4 = new ImageIcon("Image/bird1_0.png");
ImageIcon icon5 = new ImageIcon("Image/bird1_1.png");
ImageIcon icon6 = new ImageIcon("Image/bird1_2.png");
ImageIcon icon7 = new ImageIcon("Image/bird2_0.png");
ImageIcon icon8 = new ImageIcon("Image/bird2_1.png");
ImageIcon icon9 = new ImageIcon("Image/bird2_2.png");
public boolean b = true;//判断是否进入画排行榜的变量
Font font = new Font("宋体",Font.BOLD,30);
//存放从文件中获取的分数
int ScoreArray[] = new int[300];
JButton continueButton,outButton;
RandomAccessFile Score;//定义随机流变量
int nandu = 1;//游戏难度
//实例化一个游戏类
public static void main(String[] args) {
new select();
}
public int produce() {
return (int)(Math.random()*30 );
}
//主类的构造方法
public client(){
jf = new JFrame("请注意!即将开始游戏,控制三只小鸟的按钮依次时是:Q,W,E");//新建一个窗口
jf.setSize(600, 393);//设置窗口大小
jf.setLocationRelativeTo(null);//设置窗口在屏幕中间显示
jf.setResizable(false);//窗口不可更改大小
jf.add(this);//将面板加入窗口
jf.setVisible(true);//设置窗口可见
jf.addKeyListener(this); //为窗口注册按键事件
m=new Music("风中舞.WAV");//因为这个文件太大,在资源中我已经删掉了。想要背景音乐的话下载一个wav格式的音乐放到文件目录下。然后在这里输入资源名称即可
m2=new Music("小黄人2.WAV");
//通过ImageIcon类加载背景图片
ImageIcon icon = new ImageIcon("Image/3.png");
// ImageIcon icon1=new ImageIcon("Image/pipe_down.png");
background1 = icon.getImage();
background2 = icon.getImage();
//给柱子初始化
for(int i=0;i<300;i++){
P[i]=new pipe(600+k,produce(),this);
list.add(P[i]);
k+=150;
}
}
//数据初始化方法
//创建一只小鸟的时候调用
public void init1()
{
flag = 1;//设置小鸟数为1,为下面的画小鸟做准备
b1 = new bird(0,100,this);//创建一个小鸟对象
b1.setbird(icon1,icon2,icon3);
pl = new Plane(background1,background2,0,0,this);//创建一个线程对象
m.play();
pl.start(); //
}
//两只小鸟一起飞时调用
public void init2()
{
flag = 2;
b1 = new bird(0,100,this);//创建一个小鸟对象
b1.setbird(icon1,icon2,icon3);
b2 = new bird(50,100,this);//创建一个小鸟对象
b2.setbird(icon4,icon5,icon6);
pl = new Plane(background1,background2,0,0,this);//创建一个线程对象
m.play();
pl.start(); //开始线程
}
//三只小鸟一起飞时调用
public void init3()
{
flag = 3;
b1 = new bird(0,100,this);//创建一个小鸟对象
b1.setbird(icon1,icon2,icon3);
b2 = new bird(50,100,this);//创建一个小鸟对象
b2.setbird(icon4,icon5,icon6);
b3 = new bird(100,100,this);//创建一个小鸟对象
b3.setbird(icon7,icon8,icon9);
pl = new Plane(background1,background2,0,0,this);//创建一个线程对象
m.play();
pl.start(); //开始线程
}
//JPanel面板的画图方法-------这个方法需不断地被执行,我们放在自己创建的一个线程里执行
public void paint(Graphics g){
super.paint(g);
g.drawImage(pl.background1, pl.x, pl.y, 600, 393, this);//画第一张背景图
g.drawImage(pl.background2,599+pl.x, pl.y, 600, 393, this);//画第二张背景图
//通过判断flag值来确定画几个小鸟
String s = Integer.toString(score);
String n = Integer.toString(nandu);
this.drawBirdAndJump(g);
//画柱子和柱子的移动
for(int i=0;i<list.size();i++){
list.get(i).drawDown(g);
list.get(i).drawUp(g);
list.get(i).move();
}
g.setColor(Color.RED);
g.setFont(font);
g.drawString("得分:"+s, 150, 25); //画分数
g.setColor(Color.PINK);
g.drawString("难度:"+n, 380, 25);
this.isDead(g);
try {
this.showScore(g);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void isDead(Graphics g){
//柱子和1只小鸟碰撞
if(flag==1){
for(int i = 0;i < list.size();i++){
if(b1.Getrect().intersects(list.get(i).GetDOWNrect()) ||//第一只鸟与上边界柱子相碰
b1.Getrect().intersects(list.get(i).GetUPrect())//第一只鸟与下边界柱子相碰
|| b1.gety() < 0 || b1.gety() > 393)//小鸟往上飞出窗口或者往下掉出窗口
{
pl.setFlag(false);//柱子停止移动
m.stop();//游戏结束时停止背景音乐
g.drawImage(gameover,200,180,204,54,this);//画出游戏结束图片
}
}
}
//2只小鸟时,小鸟与柱子的碰撞
else if(flag==2){
for(int i = 0;i < list.size();i++){//
if(b1.Getrect().intersects(list.get(i).GetDOWNrect()) ||
b1.Getrect().intersects(list.get(i).GetUPrect())
|| b2.Getrect().intersects(list.get(i).GetDOWNrect())
|| b2.Getrect().intersects(list.get(i).GetUPrect())
|| b1.gety() < 0 || b1.gety() > 393
|| b2.gety() < 0 || b2.gety() > 393)
{
pl.setFlag(false);
m.stop();//游戏结束时停止背景音乐
g.drawImage(gameover,200,180,204,54,this);
}
}
}
//三只小鸟时候的小鸟与柱子碰撞
else if(flag==3){
for(int i = 0;i < list.size();i++){
if(b1.Getrect().intersects(list.get(i).GetDOWNrect()) ||
b1.Getrect().intersects(list.get(i).GetUPrect())
|| b2.Getrect().intersects(list.get(i).GetDOWNrect())
|| b2.Getrect().intersects(list.get(i).GetUPrect())
|| b3.Getrect().intersects(list.get(i).GetDOWNrect())
|| b3.Getrect().intersects(list.get(i).GetUPrect())
|| b1.gety() < 0 || b1.gety() > 393
|| b2.gety() < 0 || b2.gety() > 393
|| b3.gety() < 0 || b3.gety() > 393)
{
pl.setFlag(false);
m.stop(); //游戏结束时停止背景音乐
g.drawImage(gameover,200,180,204,54,this);
}
}
}
if(pl.getFlag())
{
for(int i = 0;i < list.size();i++){
if(list.get(i).getUppipex() < 0){
//每十根柱子更改移动速度
if(score % 10 == 0 && score != 0){
pipe.setMoveSp();
nandu++;
}
list.remove(i);
Music mus=new Music("小黄人1.WAV"); ;//定义多个过柱子时的笑声
mus.play();
score++ ;
}
}
}
}
/*
* 画出排行榜
* 画出当前分数的排行
*/
public void showScore(Graphics g) throws IOException{
int max,sedmax,threemax;//三个变量保存分数的前三名
if(!pl.getFlag()&&b){
b = false;//避免线程刷新画面时重新进入浪费资源,设置为false
int i;
//创建随机流访问文件
//判断是几只小鸟,然后将分数存到不同的文件中
if(flag == 1)
Score = new RandomAccessFile("scoreOneBird.dat","rw");
else if(flag == 2)
Score = new RandomAccessFile("scoreTwoBird.dat","rw");
else
Score = new RandomAccessFile("scoreThreeBird.dat","rw");
//将位置跳到文件末,存放分数
Score.seek(Score.length());
Score.writeInt(score);
//关闭文件
Score.close();
i =0;
while(true){
//判断是在玩几只小鸟,然后打开不同的存分数文件
if(flag == 1)
Score = new RandomAccessFile("scoreOneBird.dat","rw");
else if(flag == 2)
Score = new RandomAccessFile("scoreTwoBird.dat","rw");
else
Score = new RandomAccessFile("scoreThreeBird.dat","rw");
//每隔四个字节读取一个整形数
Score.seek(i*4);
//将分数保存到数组中
ScoreArray[i] = Score.readInt();
i++;
//如果到文件末尾,退出循环
if(Score.length() == i*4){
break;
}
}
//关闭文件
Score.close();
//初始化三个变量
max = sedmax = threemax = 0;
//将数组按顺序排序
Arrays.sort(ScoreArray);
//轻松获取前三大值
max = ScoreArray[ScoreArray.length-1];
sedmax = ScoreArray[ScoreArray.length-2];
threemax = ScoreArray[ScoreArray.length-3];
//记录排名,从末尾往前
int count = ScoreArray.length;
for(i = 0;i < ScoreArray.length;i++)
{
//如果分数比数组里的数大,排名往前一位
if(ScoreArray[i] < score )
count--;
//如果等0,说明是第一名
if(count == 0)
count = 1;
}
//三个整形转换成字符型
String ScMax = Integer.toString(max);
String ScSeMax = Integer.toString(sedmax);
String ScThMax = Integer.toString(threemax);
//设置字体大小、改变颜色、画出排名
g.setFont(font);
g.setColor(Color.MAGENTA);
g.drawString("当前排名:"+count,150, 90);
g.setColor(Color.ORANGE);
g.drawString("得分排名第一:"+ScMax, 150, 120);
g.setColor(Color.CYAN);
g.drawString("得分排名第二:"+ScSeMax, 150, 150);
g.setColor(Color.GREEN);
g.drawString("得分排名第三:"+ScThMax, 150, 180);
//实例两个按钮
continueButton = new JButton("重新开始");
outButton = new JButton("退出游戏");
//将按钮添加到面板上、设置背景颜色、出现的位置以及大小
this.add(continueButton);
this.add(outButton);
continueButton.setBackground(Color.green);
continueButton.setBounds(140,240,100,50);
outButton.setBackground(Color.CYAN);
outButton.setBounds(350,240,100,50);
//为继续按钮添加点击事件
continueButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
jf.dispose();
new select();
}
});
// 为退出游戏按钮添加点击事件
outButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
jf.dispose();
}
});
}
}
public void drawBirdAndJump(Graphics g){
//画一只小鸟
if(flag == 1)
{
jf.setTitle("单飞像素鸟 跳跃键:Q");
b1.draw(g,x);//调用小鸟对象的画图方法画一个小鸟
}
//flag=2 画2只小鸟
else if(flag == 2)
{
jf.setTitle("双飞像素鸟 跳跃键:Q、W");
b1.draw(g,x);
b2.draw(g,x);
}
//画三只小鸟
else
{
jf.setTitle("三飞像素鸟 跳跃键:Q、W、E");
b1.draw(g,x);//调用小鸟对象的画图方法画一个小鸟
b2.draw(g,x);
b3.draw(g,x);
}
x++;//每次跑都改变小鸟飞行的图片
if(flag == 1)
b1.move();//小鸟往下掉的方法
else if(flag == 2)
{
b1.move();
b2.move();
}
else if(flag == 3){
b1.move();//小鸟往下掉的方法
b2.move();
b3.move();
}
if(x == 3)//小鸟飞行有三张图片,循环变换
x = 0;
}
//按下键盘时自动调用的方法
public void keyPressed(KeyEvent e) {
}
//按下并且释放键盘时自动调用的方法
public void keyReleased(KeyEvent e) {
//通过flag值判断调用多少个小鸟的按键函数
if(flag == 1)
b1.keyPressed1(e);//调用小鸟类的按键方法
else if(flag == 2){
b1.keyPressed1(e);
b2.keyPressed2(e);
}
else{
b1.keyPressed1(e);//调用小鸟类的按键方法
b2.keyPressed2(e);
b3.keyPressed3(e);
}
}
线程类非常简单。只要一个死循环就行
public class Plane extends Thread {
public Image background1, background2; // 定义两张背景
public int x = 0; // 背景图片x坐标
public int y = 0;// 背景图片y坐标
public JPanel jp;// 定义一个面板
public int movesp = 5;// 背景图的移动速度
public boolean flag = true;
public Music m = new Music("风中舞.WAV");
public Music m2 = new Music("小黄人2.WAV");
// 构造方法
public Plane(Image background1, Image background2, int x, int y, JPanel jp) {
m.play();
this.background1 = background1;
this.background2 = background2;
this.x = x;
this.y = y;
this.jp = jp;// 将面板传进来主要是为了在线程中调用窗口面板的repaint
// 方法,重画游戏窗口
}
public boolean setFlag(boolean flag) {
this.flag = flag;
return flag;
}
public boolean getFlag() {
return flag;
}
public void run() {
// 在线程中循环调用背景图的move方法,并且重画面板
while (true) {
if (flag) {
//移动就是通过不断刷新更改x,y值实现的
jp.repaint();
} else {
m.stop();
m2.play();
}
try {
Thread.sleep(100);// 线程睡眠0.1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
还有一个柱子类、封装的调用播放音乐的类非常非常简单这里就不放占用资源了。下载地址
像素鸟源代码下载下载后直接导入到myeclipse中就可以运行了噢
上一篇: 理解HashMap(jdk8)
下一篇: 读源码理解jdk8 HashMap