Java小游戏之《大鱼吃小鱼》

1.GameWin(主类):GameWin extends JFrame, 子类继承父类,创建窗口,设置窗口的变量参数width, heigth ,用于设置窗口大小,在主类中创建launch()方法,用组件类Component的方法设置窗口相关参数并添加窗口图标

public void launch() {

Image logo=(new ImageIcon("img/enemyFish/logo.png")).getImage();

    this.setVisible(true); //设置窗口是否可见

this.setSize( width, height);//设置窗口大小

this.setLocationRelativeTo(null);//设置窗口在屏幕居中位置

  this.setIconImage(logo);

    this.setTitle("大鱼吃小鱼");//设置窗口标题

  this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //设置关闭窗口按钮

在main()函数中创建GameWin类的对象 再调用launch()方法

public static void main(String[] args){

GameWin gameWin=new GameWin();

gameWin.launch();

}

2、创建GameUtils类:在写小游戏的根目录下新建一个文件夹(与游戏时同一级别)

在GameUtils类中为游戏添加背景图(sea.jpg);创建我方鱼的对象以及敌方鱼对象

public static Image beijing = Toolkit.getDefaultToolkit().getImage("img/enemyFish/sea.jpg");

    //敌方鱼类

public static Image enamyl_img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish4_l.png");

public static Image enamyr_img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish4_r.png");

public static Image enamyl_2img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish1_r.gif");

public static Image enamyr_2img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish1_l.gif");

public static Image enamyl_3img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish2_r.png");

public static Image enamyr_3img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish2_l.png");

public static Image enamyl_4img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish3_r.png");

public static Image enamyr_4img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish3_l.png ");

public static Image bossing =Toolkit.getDefaultToolkit().getImage("img/enemyFish/boss.gif");

//我方鱼类

public static Image MyFishimg_L =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish_left.gif");

public static Image MyFishimg_R =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish_right.gif");

public static Image MyFishimg2_L =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish1_l.png");

public static Image MyFishimg2_R =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish1_r.png");

public static Image MyFishimg3_L =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish3_left.png");

public static Image MyFishimg3_R =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish3_right.png");

在GameWin中创建GameUtils对象,重写paint()方法,画出背景图

public void paint(Graphic g){

      g.drawImage(GameUtils.bgimg,0,0,null);

}

由于游戏有5种状态:0未开始,1游戏中,2通关失败,3通关成功,4暂停

在GameWin类中定义游戏默认状态,在launch()方法中用switch()语句定义游戏的五种状态

static int state=0;

在paint方法中用switch语句来定义方法状态创建一个启动方法用来创建窗口信息将背景绘制自身的方法放到游戏状态为1的语句下,在0状态下设置启动页面的背景图,用画笔绘制开始界面文字并设置颜色字体样式大小和坐标;

创建一个背景图的实体类Bg,编写绘制背景的方法,在类中设置游戏五种状态中背景文字

import java.awt.*;

//新建一个背景图的实体类

public class Bg {

void paint(Graphics g,int fishLevel) {//编写绘制背景的方法

g.drawImage(GameUtils.beijing,0,0,null);//在绘制背景的方法中定义所需参数

switch(GameWin.state) {

case 0:

GameUtils.drawWord(g,"开始",Color.pink,80,700,500);

break;

case 1:

GameUtils.drawWord(g,"积分"+GameUtils.count,Color.ORANGE,50,200,120);

GameUtils.drawWord(g,"难度"+GameUtils.level,Color.ORANGE,50,600,120);

GameUtils.drawWord(g,"等级"+fishLevel, Color.ORANGE, 50, 1000, 120);

break;

case 2:

break;

case 3:

GameUtils.drawWord(g,"积分"+GameUtils.count,Color.ORANGE,50,200,120);

GameUtils.drawWord(g,"难度"+GameUtils.level,Color.ORANGE,50,600,120);

GameUtils.drawWord(g,"等级"+fishLevel, Color.ORANGE, 50, 1000, 120);

GameUtils.drawWord(g,"胜利", Color.red, 80, 700,500);

break;

case 4:

break;

default:

}

}

}

在launch()方法中添加鼠标监听事件,当鼠标点击开始文字时,启动页面消失游戏进入运行状态

//添加鼠标监听事件(匿名内部类:存在于方法区内,执行一瞬间,执行完之后消失)

this.addMouseListener(new MouseAdapter(){//启动页面是在游戏最开始的时候显示,此时游戏的状态码为0,当点击启动页面窗口后,进入游戏状态,然后游戏状态码为1启动页面文字消失

@Override

public void mouseClicked(MouseEvent e) {

super.mouseClicked(e);

if(e.getButton()==1&&state==0) {//用if判断鼠标左键是否点击并且进入游戏运行状态。鼠标左键

state=1;                    //在鼠标左键点击代码块中将游戏状态改为1并调用paint方法,

repaint();                //因为repaint方法会再次调用paint方法,这样才能将改变后的窗口绘制出来                                         

}

if(e.getButton()==1&&(state==2||state==3)) {

reGame();

state=1;

}

}

});

在GameWin中获取Bg类的对象,将背景绘制自身的方法放置在游戏状态为1 的语句下,由于背景需要不断地调用,在launch()方法中添加一个while循环,每间隔40毫秒重复调用repaint()方法(因为repaint()方法会再次调用paint()方法),并在GameWin类中声明一个成员变量time,用于记录repaint()调用次数。

while (true) {

repaint();

time++;

try {

Thread.sleep(40);//Thread线程类(Thread类常用方法:public static void sleep(long millis)为当前执行的线程指定睡眠时间。参数millils是线程睡眠的毫秒数。)

}catch (InterruptedException e){//sleep()方法必须写在try-catch块内

e.printStackTrace();//当try语句中出现异常是时,会执行catch 中的语句,java运行时系统会自动将catch括号中的Exception e 初始化,

                    //也就是实例化Exception类型的对象。e是此对象异常名称。然后e(引用)会自动调用Exception类中指定的方法,也就出现了e.printStackTrace();

}                      //printStackTrace()方法的意思是:在命令行打印异常信息在程序中出错的位置及原因。

}

运行程序,查看结果。发现有闪屏现象。

!!!双缓冲解决闪屏现象

使用缓存的方式解决闪屏问题,思路是重新创建一个空的图片,把所有的组件绘制到空的图片上,然后把绘制好的图片一次性绘制到主窗口上

定义一个成员变量offScreenImage,在paint函数中懒加载模式初始化对象

public void paint(Graphics g) {

//懒加载模式初始化对象

offScreenImage=createImage(width,height);

Graphics gImage=offScreenImage.getGraphics();//获取图片对应的画笔对象,把组件绘制到新的图片当中

bg.paint(gImage,myFish.level);

}

创建Enamy类用于敌方鱼的添加

import java.awt.Graphics;

import java.awt.Image;

import java.awt.Rectangle;

import javax.swing.ImageIcon;

//编写敌方鱼的父类

public class Enamy {

//定义图片

Image enamyFish;

//定义物体坐标

int x, y, width, height;

//移动速度

int speed;

//定义方向用,用于计算敌方鱼的移动方向,从左向右则是1,从右向左则是-1

int dir=1;

//敌方鱼类型

int type;

//敌方鱼分值

int count;

//编写元素绘制自身的方法

public void paintSelf(Graphics g) {

g.drawImage(enamyFish, x, y, width, height, null);

}

//和获取自身矩形的方法 用于碰撞检测

public Rectangle getRec() {

return new Rectangle(x,y,width,height);

}

}

//敌方鱼左类(创建敌方鱼左类,继承敌方鱼父类)

class Enamy_1_L extends Enamy{//小鱼左

Enamy_1_L(){

this.x=-45;

this.y=(int)(Math.random()*700+100);

this.width=45;

this.height=80;

this.speed=12;

this.count=1;

this.type=1;

this.enamyFish= GameUtils.enamyl_img;

}

}

//敌方鱼右类

class Enamy_1_R extends Enamy_1_L{//小鱼右

Enamy_1_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_img;

}

}

class Enamy_2_L extends Enamy{//海马鱼

Enamy_2_L(){

this.x=-100;

this.y=(int)(Math.random()*700+100);

this.width=45;

this.height=80;

this.speed=18;

this.count=5;

this.type=2;

this.enamyFish=GameUtils.enamyl_2img;

}

}

class Enamy_2_R extends Enamy_2_L{

Enamy_2_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_2img;

}

}

class Enamy_3_L extends Enamy{//蓝带

Enamy_3_L(){

this.x=-100;

this.y=(int)(Math.random()*700+100);

this.width=100;

this.height=150;

this.speed=18;

this.count=8;

this.type=3;

this.enamyFish=GameUtils.enamyl_3img;

}

}

class Enamy_3_R extends Enamy_3_L{

Enamy_3_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_3img;

}

}

class Enamy_4_L extends Enamy{//剑鱼

Enamy_4_L(){

this.x=-300;

this.y=(int)(Math.random()*700+100);

this.width=300;

this.height=150;

this.speed=20;

this.count=20;

this.type=4;

this.enamyFish=GameUtils.enamyl_4img;

}

public Rectangle getRec() {

return new Rectangle(x+40,y+30,width-80,height-60);

}

}


class Enamy_4_R extends Enamy_4_L{

Enamy_4_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_4img;

}

}

class Enamy_Boss extends Enamy{

Enamy_Boss(){

this.x=-1000;

this.y=(int)(Math.random()*700+100);

this.width=340;

this.height=340;

this.speed=100;

this.count=0;

this.type=10;

this.enamyFish=GameUtils.bossing;

}

}

在GameWin中获取Enamy类的对象,在paint()方法中用画笔gImage画出敌方鱼

接下来进行敌方鱼的批量添加,在GameUtils中创建敌方鱼类集合

//创建敌方鱼类集合,实现敌方小鱼的批量添加

public static List<Enamy>EnamyList=new ArrayList<>();//在GameWin类中创建logic()方法用于批量添加敌方鱼类

在GameWin类中创建logic()方法用于批量添加敌方鱼类并设置关卡难度

void logic() {

//关卡难度

if(GameUtils.count<15) {

GameUtils.level=0;

myFish.level=1;

}else if(GameUtils.count<=30) {

GameUtils.level=1;

}else if(GameUtils.count<=50) {

GameUtils.level=2;

myFish.level=2;

}else if(GameUtils.count<=80) {

GameUtils.level=3;

myFish.level=3;

}else if(GameUtils.count<=100) {

GameUtils.level=4;

myFish.level=4;

}else if(GameUtils.count>300) {

state=3;

}

random=Math.random();

//用关卡等级限制敌方鱼的生成:等级0生成敌方第一种鱼;等级1生成敌方第一种鱼和敌方第二种鱼;等级2生成敌方

switch(GameUtils.level) {//游戏难度等级

case 4:                      //由于paint()每40毫秒调用一次,生成1条鱼,生成速度过快,所以用time%来限制生成速度

if(time%60==0) {

if(random>0) {       

boss=new Enamy_Boss();

isboss=true;

}

}

case 3:

if(time%50==0) {

if(random>0.5) {  //random>0.5,让敌方左鱼和敌方右鱼都有一半的概率出现

enamy=new Enamy_4_L();

}else {

enamy=new Enamy_4_R();

}

GameUtils.EnamyList.add(enamy);

}

case 2:

if(time%30==0) {

if(random>0.5) {

enamy=new Enamy_3_L();

}else {

enamy=new Enamy_3_R();

}

GameUtils.EnamyList.add(enamy);

}

case 1:

if(time%20==0) {

if(random>0.5) {

  enamy=new Enamy_2_L();

}else {

enamy=new Enamy_2_R();

}

GameUtils.EnamyList.add(enamy);

}

case 0:

//由于敌方鱼需要不断地添加,所以将方法添加到游戏运行状态为1的代码块中

    //敌方鱼生成

if(time%10==0) {

if(random>0.5) {

enamy=new Enamy_1_L();//将敌方鱼添加到敌方鱼物体的集合中

} else {

enamy =new Enamy_1_R();

}

    GameUtils.EnamyList.add(enamy);

break;

default:

}

    //移动方向

    for(Enamy enamy:GameUtils.EnamyList) {

    enamy.x=enamy.x+enamy.dir*enamy.speed;

    if(isboss) {//boss鱼与敌方鱼的碰撞检测

    if(boss.getRec().intersects(enamy.getRec())) {

    enamy.x=-200;

    enamy.y=-200;

    }

    if(boss.getRec().intersects(myFish.getRec())) {

    state=2;

    }

    }


    //我方鱼与敌方鱼碰撞检测

    if(myFish.getRec().intersects(enamy.getRec())) {

    if(myFish.level>=enamy.type) {


    //把敌方鱼进行移除操作

    enamy.x=-200;

    enamy.y=-200;

    GameUtils.count=GameUtils.count+enamy.count;

    }else {

    state=2;

    }

    }

}

}

在GameWin中的launch()方法中添加键盘监听事件

//键盘监听事件

this.addKeyListener(new KeyAdapter()

{@Override

public void keyPressed(KeyEvent e) {

super.keyPressed(e);

//方向键控制键盘移动

if(e.getKeyCode()==KeyEvent.VK_UP) {

GameUtils.UP=true;

}

if(e.getKeyCode()==KeyEvent.VK_DOWN) {

GameUtils.DOWN=true;

}

if(e.getKeyCode()==KeyEvent.VK_LEFT) {

GameUtils.LEFT=true;

}

if(e.getKeyCode()==KeyEvent.VK_RIGHT) {

GameUtils.RIGHT=true;

}

if(e.getKeyCode()==KeyEvent.VK_SPACE) {

switch(state) {

case 1:

state=4;

GameUtils.drawWord(getGraphics(),"游戏暂停!!!",Color.red,50,600,400);

break;

case 4:

state=1;

break;

}

}

}

@Override//抬起

public void keyReleased(KeyEvent e) {

if(e.getKeyCode()==KeyEvent.VK_UP) {

GameUtils.UP=false;

}

if(e.getKeyCode()==KeyEvent.VK_DOWN) {

GameUtils.DOWN=false;

}

if(e.getKeyCode()==KeyEvent.VK_LEFT) {

GameUtils.LEFT=false;

}

if(e.getKeyCode()==KeyEvent.VK_RIGHT) {

GameUtils.RIGHT=false;

}

}

});

创建我方鱼类MyFish

import java.awt.*;

public class MyFish {

//图片

Image myfish;

//坐标

int x=700;

int y=500;

int width=60;

int height=60;

//移动速度

int speed=20;

//我方鱼等级

int level=1;

//添加我方鱼对键盘的控制

void logic() {

if((GameUtils.UP)&&(y>=30)){    //对我方鱼进行边界限制

y=y-speed;

}

if((GameUtils.DOWN)&&(y<=770)) {

y=y+speed;

}

if((GameUtils.LEFT)&&(x>=0)) {

x=x-speed;

if(GameUtils.level<=1) {      //在每一个等级中设定我方鱼的形态

myfish=GameUtils.MyFishimg2_L;

}else if(GameUtils.level<=3) {

myfish=GameUtils.MyFishimg3_L;

}else

myfish=GameUtils.MyFishimg_L;

}

    if((GameUtils.RIGHT)&&(x<=1377)) {

x=x+speed;

if(GameUtils.level<=1) {

myfish=GameUtils.MyFishimg2_R;

}else if(GameUtils.level<=3) {

myfish=GameUtils.MyFishimg3_R;

}else

myfish=GameUtils.MyFishimg_R;

    }

}

//绘制自身的方法

public void paint(Graphics g) {

  logic();

g.drawImage(myfish, x, y, width+GameUtils.count,height+GameUtils.count, null);

}

//获取自身矩形的方法,用于碰撞检测

public Rectangle getRec() {

return new Rectangle(x,y,width+GameUtils.count,height+GameUtils.count);

}

}

在GameUtils中定义积分变量和关卡,当我方鱼与敌方鱼碰撞检测后积分增加,并将积分绘制在状态为1的语句下,在GameWin中添加游戏重写方法

void reGame() {

GameUtils.EnamyList.clear();

time=0;

myFish.level=1;

GameUtils.count=0;

myFish.x=700;

myFish.y=500;

myFish.width=50;

myFish.height=50;

boss=null;

isboss=false;

}

最后为游戏添加背景音乐

创建Test类

import java.io.File;

import java.io.IOException;

import javax.sound.sampled.AudioFormat;

import javax.sound.sampled.AudioInputStream;

import javax.sound.sampled.AudioSystem;

import javax.sound.sampled.DataLine;

import javax.sound.sampled.FloatControl;

import javax.sound.sampled.LineUnavailableException;

import javax.sound.sampled.SourceDataLine;

import javax.sound.sampled.UnsupportedAudioFileException;

public class Test extends Thread {

private String fileName;

private final int EXTERNAL_BUFFER_SIZE = 524288;

public Test(String wavFile) {

this.fileName = wavFile;

}

@SuppressWarnings("unused")

public void run() {

//1.获取要播放的音乐文件名

File soundFile = new File(fileName); // 播放音乐的文件名

if (!soundFile.exists()) {          //用于检查指定的文件在目录中是否存在

System.err.println("Wave file not found:" + fileName);

return;

}

while (true) { // 设置循环播放

//2.定义  AudioInputStream 用于接收输入的音频数据

AudioInputStream audioInputStream = null; // 创建音频输入流对象

try {

audioInputStream = AudioSystem.getAudioInputStream(soundFile); //3. 创建音频对象,使用AudioSystem来获取音频的输入流(处理(抛出)异常);

} catch (UnsupportedAudioFileException e1) {

e1.printStackTrace();

return;

} catch (IOException e1) {

e1.printStackTrace();

return;

}

//4.使用AudioFormat来获取AudioInputStream的格式

AudioFormat format = audioInputStream.getFormat(); // 音频格式

SourceDataLine auline = null; // 5.源数据线

DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);//6.获取受数据行支持的音频格式

try {

auline = (SourceDataLine) AudioSystem.getLine(info);//7、获取与上面类型相匹配的行 写到源数据行里

auline.open(format);                8、打开具有指定格式的行,这样可以使行获得资源并进行操作

} catch (LineUnavailableException e) {

e.printStackTrace();

return;

} catch (Exception e) {

e.printStackTrace();

return;

}

if (auline.isControlSupported(FloatControl.Type.PAN)) {

FloatControl pan = (FloatControl) auline.getControl(FloatControl.Type.PAN);

}

auline.start();            9、允许某个数据行执行数据i/o

//10、写数据

int nBytesRead = 0;            //读取的总字节数

byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];//设置字节数组大小

try {

//11、从音频流读取指定的最大数量的数据字节,并将其放入给定的字节数组中。

while (nBytesRead != -1) {    //-1代表没有 不等于-1时就无限读取

nBytesRead = audioInputStream.read(abData, 0, abData.length);//12、读取哪个数组

if (nBytesRead >= 0)                  //13、读取了之后将数据写入混频器,开始播放

auline.write(abData, 0, nBytesRead);

}

} catch (IOException e) {

e.printStackTrace();

return;

} finally {

//关闭

auline.drain();

              auline.close();

}

}

}

}

在main()方法中调用

public static void main(String[] args) throws UnsupportedAudioFileException, IOException, LineUnavailableException {

Test audioPlayWave = new Test("img/music/11.wav");// 开音乐 音樂名

audioPlayWave.start();

@SuppressWarnings("unused")

int musicOpenLab = 1;

GameWin gameWin=new GameWin();

gameWin.launch();

}

以上是写游戏的思路仅供参考,在修改和完善之后代码顺序有些乱,下面附上源码

GameWin类

package 游戏;

import javax.sound.sampled.LineUnavailableException;

import javax.sound.sampled.UnsupportedAudioFileException;

import javax.swing.*;

import java.awt.Graphics;

import java.awt.Color;

import java.awt.Font;

import java.awt.Image;

import java.awt.Toolkit;

import java.awt.event.KeyAdapter;

import java.awt.event.KeyEvent;

import java.awt.event.MouseAdapter;

import java.awt.event.MouseEvent;

import java.awt.image.ImageObserver;

import java.io.IOException;

@SuppressWarnings("serial")

public class GameWin extends JFrame {

/**游戏状态 0未开始,1游戏中,2通关失败,3通关成功,4暂停*/

static int state=0;//定义游戏默认状态,在paint方法中用switch语句来定义方法状态

                        //创建一个启动方法用来创建窗口信息

                            //将背景绘制自身的方法放到游戏状态为1的语句下

Image offScreenImage;

int width=1440;

int height=900;

double random;

//计数器:用于记录游戏的重绘次数和paint()调用次数

int time=0;

Bg bg=new Bg();//在窗口类中获取背景类的对象,将背景绘制的方法放在游戏状态为1的语句下-case1

//敌方鱼类

Enamy enamy;

//boss类

Enamy boss;

//是否生成boss

boolean isboss=false;//定义一个布尔型用于判断boss是否生成,初始为未生成

//我方鱼类

MyFish myFish=new MyFish();

GameUtils bgimg=new GameUtils();

//double random;

//使用缓存的方式解决闪屏问题,思路是重新创建一个空的图片,把所有的组件绘制到空的图片上,然后把绘制好的图片一次性绘制到主窗口上

//定义一个成员变量offScreenImage,在paint函数中懒加载模式初始化对象

  /**

*

*/

public void launch() {

Image logo=(new ImageIcon("img/enemyFish/logo.png")).getImage();

    this.setVisible(true); //设置窗口是否可见

this.setSize( width, height);//设置窗口大小

this.setLocationRelativeTo(null);//设置窗口在屏幕居中位置

  this.setIconImage(logo);

    this.setTitle("大鱼吃小鱼");//设置窗口标题

  this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //设置关闭窗口按钮

//添加鼠标监听事件(匿名内部类:存在于方法区内,执行一瞬间,执行完之后消失)

this.addMouseListener(new MouseAdapter(){//启动页面是在游戏最开始的时候显示,此时游戏的状态码为0,当点击启动页面窗口后,进入游戏状态,然后游戏状态码为1启动页面文字消失

@Override

public void mouseClicked(MouseEvent e) {

super.mouseClicked(e);

if(e.getButton()==1&&state==0) {//用if判断鼠标左键是否点击并且进入游戏运行状态。鼠标左键

state=1;                    //在鼠标左键点击代码块中将游戏状态改为1并调用paint方法,

repaint();                //因为repaint方法会再次调用paint方法,这样才能将改变后的窗口绘制出来                                         

}

if(e.getButton()==1&&(state==2||state==3)) {// 游戏重启

reGame();

state=1;

}

}

});

//键盘监听事件

this.addKeyListener(new KeyAdapter()

{@Override

public void keyPressed(KeyEvent e) {

super.keyPressed(e);

//方向键控制键盘移动

if(e.getKeyCode()==KeyEvent.VK_UP) {

GameUtils.UP=true;

}

if(e.getKeyCode()==KeyEvent.VK_DOWN) {

GameUtils.DOWN=true;

}

if(e.getKeyCode()==KeyEvent.VK_LEFT) {

GameUtils.LEFT=true;

}

if(e.getKeyCode()==KeyEvent.VK_RIGHT) {

GameUtils.RIGHT=true;

}

if(e.getKeyCode()==KeyEvent.VK_SPACE) {

switch(state) {

case 1:

state=4;

GameUtils.drawWord(getGraphics(),"游戏暂停!!!",Color.red,50,600,400);

break;

case 4:

state=1;

break;

}

}

}

@Override//抬起

public void keyReleased(KeyEvent e) {

if(e.getKeyCode()==KeyEvent.VK_UP) {

GameUtils.UP=false;

}

if(e.getKeyCode()==KeyEvent.VK_DOWN) {

GameUtils.DOWN=false;

}

if(e.getKeyCode()==KeyEvent.VK_LEFT) {

GameUtils.LEFT=false;

}

if(e.getKeyCode()==KeyEvent.VK_RIGHT) {

GameUtils.RIGHT=false;

}

}

});

while (true) {

repaint();

time++;

try {

Thread.sleep(40);//Thread线程类(Thread类常用方法:public static void sleep(long millis)为当前执行的线程指定睡眠时间。参数millils是线程睡眠的毫秒数。)

}catch (InterruptedException e){//sleep()方法必须写在try-catch块内

e.printStackTrace();//当try语句中出现异常是时,会执行catch 中的语句,java运行时系统会自动将catch括号中的Exception e 初始化,

                    //也就是实例化Exception类型的对象。e是此对象异常名称。然后e(引用)会自动调用Exception类中指定的方法,也就出现了e.printStackTrace();

}                      //printStackTrace()方法的意思是:在命令行打印异常信息在程序中出错的位置及原因。

}

}


public void paint(Graphics g) {

//懒加载模式初始化对象

offScreenImage=createImage(width,height);

Graphics gImage=offScreenImage.getGraphics();//获取图片对应的画笔对象,把组件绘制到新的图片当中

bg.paint(gImage,myFish.level);

switch(state) {

case 0://在状态为0的语句中设置启动页面的背景图


break;

case 1:

myFish.paint(gImage);

logic();

for(Enamy enamy:GameUtils.EnamyList) {

enamy.paintSelf(gImage);

}

if(isboss) {  //boss鱼的移动方向

boss.x=boss.x+boss.dir*boss.speed;

boss.paintSelf(gImage);

if(boss.x<0) {//boss出现预警红线

gImage.setColor(Color.red);

gImage.fillRect(boss.x, boss.y, 2400,boss.height/30);

}

}

break;

case 2:

for(Enamy enamy:GameUtils.EnamyList) {

enamy.paintSelf(gImage);

}

if(isboss) {

boss.paintSelf(gImage);

}

    break;

case 3:

myFish.paint(gImage);

break;

case 4:

return;

default:

}

g.drawImage(offScreenImage, 0, 0, null);//将内存当中绘制好的新图片一次性绘制到主窗口中

//用画笔g绘制背景图片

}

//用于批量添加敌方鱼类

void logic() {

//关卡难度

if(GameUtils.count<15) {

GameUtils.level=0;

myFish.level=1;

}else if(GameUtils.count<=30) {

GameUtils.level=1;

}else if(GameUtils.count<=50) {

GameUtils.level=2;

myFish.level=2;

}else if(GameUtils.count<=80) {

GameUtils.level=3;

myFish.level=3;

}else if(GameUtils.count<=100) {

GameUtils.level=4;

myFish.level=4;

}else if(GameUtils.count>300) {

state=3;

}

random=Math.random();

//用关卡等级限制敌方鱼的生成:等级0生成敌方第一种鱼;等级1生成敌方第一种鱼和敌方第二种鱼;等级2生成敌方

switch(GameUtils.level) {//游戏难度等级

case 4:                      //由于paint()每40毫秒调用一次,生成1条鱼,生成速度过快,所以用time%来限制生成速度

if(time%60==0) {

if(random>0) {       

boss=new Enamy_Boss();

isboss=true;

}

}

case 3:

if(time%50==0) {

if(random>0.5) {  //random>0.5,让敌方左鱼和敌方右鱼都有一半的概率出现

enamy=new Enamy_4_L();

}else {

enamy=new Enamy_4_R();

}

GameUtils.EnamyList.add(enamy);

}

case 2:

if(time%30==0) {

if(random>0.5) {

enamy=new Enamy_3_L();

}else {

enamy=new Enamy_3_R();

}

GameUtils.EnamyList.add(enamy);

}

case 1:

if(time%20==0) {

if(random>0.5) {

  enamy=new Enamy_2_L();

}else {

enamy=new Enamy_2_R();

}

GameUtils.EnamyList.add(enamy);

}

case 0:

//由于敌方鱼需要不断地添加,所以将方法添加到游戏运行状态为1的代码块中

    //敌方鱼生成

if(time%10==0) {

if(random>0.5) {

enamy=new Enamy_1_L();//将敌方鱼添加到敌方鱼物体的集合中

} else {

enamy =new Enamy_1_R();

}

    GameUtils.EnamyList.add(enamy);

break;

default:

}

    //移动方向

    for(Enamy enamy:GameUtils.EnamyList) {

    enamy.x=enamy.x+enamy.dir*enamy.speed;

    if(isboss) {                                //boss鱼与敌方鱼的碰撞检测

    if(boss.getRec().intersects(enamy.getRec())) {

    enamy.x=-200;                //将敌方鱼的移除操作

    enamy.y=-200;

    }

    if(boss.getRec().intersects(myFish.getRec())) {

    state=2;

    }

    }


      //我方鱼与敌方鱼碰撞检测

    if(myFish.getRec().intersects(enamy.getRec())) {

    if(myFish.level>=enamy.type) {


    //把敌方鱼进行移除操作

    enamy.x=-200;

    enamy.y=-200;

    GameUtils.count=GameUtils.count+enamy.count;

    }else {

    state=2;

    }

    }

}

}

    void reGame() {

GameUtils.EnamyList.clear();

time=0;

myFish.level=1;

GameUtils.count=0;

myFish.x=700;

myFish.y=500;

myFish.width=50;

myFish.height=50;

boss=null;

isboss=false;

}

//创建main()方法运行launch()方法

public static void main(String[] args) throws UnsupportedAudioFileException, IOException, LineUnavailableException {

Test audioPlayWave = new Test("img/music/11.wav");// 开音乐 音樂名

audioPlayWave.start();

@SuppressWarnings("unused")

int musicOpenLab = 1;

GameWin gameWin=new GameWin();

gameWin.launch();

}

}

GameUtils类

import java.awt.*;

import java.util.ArrayList;

import java.util.List;

import javax.swing.ImageIcon;

//创建一个工具类,让工具类定义一个静态类的背景图片

public class GameUtils {

//添加方向判定,用于对我方鱼移动的控制

static boolean UP=false;

static boolean DOWN=false;

static boolean LEFT=false;

static boolean RIGHT=false;

//分数

static int count=0;//当我方鱼与敌方小鱼碰撞检测后积分加1

//关卡等级

static int level=0;

//创建敌方鱼类集合,实现敌方小鱼的批量添加

public static List<Enamy>EnamyList=new ArrayList<>();//在GameWin类中创建logic()方法用于批量添加敌方鱼类

//背景图

public static Image beijing = Toolkit.getDefaultToolkit().getImage("img/enemyFish/sea.jpg");

    //敌方鱼类

public static Image enamyl_img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish4_l.png");

public static Image enamyr_img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish4_r.png");

public static Image enamyl_2img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish1_r.gif");

public static Image enamyr_2img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish1_l.gif");

public static Image enamyl_3img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish2_r.png");

public static Image enamyr_3img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish2_l.png");

public static Image enamyl_4img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish3_r.png");

public static Image enamyr_4img =Toolkit.getDefaultToolkit().getImage("img/enemyFish/fish3_l.png ");

public static Image bossing =Toolkit.getDefaultToolkit().getImage("img/enemyFish/boss.gif");

//我方鱼类

public static Image MyFishimg_L =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish_left.gif");

public static Image MyFishimg_R =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish_right.gif");

public static Image MyFishimg2_L =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish1_l.png");

public static Image MyFishimg2_R =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish1_r.png");

public static Image MyFishimg3_L =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish3_left.png");

public static Image MyFishimg3_R =Toolkit.getDefaultToolkit().getImage("img/enemyFish/myfish3_right.png");

//绘制文字工具类

public static void drawWord(Graphics g,String str,Color color,int size,int x,int y) {

g.setColor(color);

g.setFont(new Font("仿宋",Font.BOLD,size));

g.drawString(str,x,y);

}

}

Bg类

import java.awt.*;

//新建一个背景图的实体类

public class Bg {

void paint(Graphics g,int fishLevel) {//编写绘制背景的方法

g.drawImage(GameUtils.beijing,0,0,null);//在绘制背景的方法中定义所需参数

switch(GameWin.state) {

case 0:

GameUtils.drawWord(g,"开始",Color.pink,80,700,500);

break;

case 1:

GameUtils.drawWord(g,"积分"+GameUtils.count,Color.ORANGE,50,200,120);

GameUtils.drawWord(g,"难度"+GameUtils.level,Color.ORANGE,50,600,120);

GameUtils.drawWord(g,"等级"+fishLevel, Color.ORANGE, 50, 1000, 120);

break;

case 2:

break;

case 3:

GameUtils.drawWord(g,"积分"+GameUtils.count,Color.ORANGE,50,200,120);

GameUtils.drawWord(g,"难度"+GameUtils.level,Color.ORANGE,50,600,120);

GameUtils.drawWord(g,"等级"+fishLevel, Color.ORANGE, 50, 1000, 120);

GameUtils.drawWord(g,"胜利", Color.red, 80, 700,500);

break;

case 4:

break;

default:

}

}

}

Enamy类

import java.awt.Graphics;

import java.awt.Image;

import java.awt.Rectangle;

import javax.swing.ImageIcon;

//编写敌方鱼的父类

public class Enamy {

//定义图片

Image enamyFish;

//定义物体坐标

int x, y, width, height;

//移动速度

int speed;

//定义方向用,用于计算敌方鱼的移动方向,从左向右则是1,从右向左则是-1

int dir=1;

//敌方鱼类型

int type;

//敌方鱼分值

int count;

//编写元素绘制自身的方法

public void paintSelf(Graphics g) {

g.drawImage(enamyFish, x, y, width, height, null);

}

//和获取自身矩形的方法 用于碰撞检测

public Rectangle getRec() {

return new Rectangle(x,y,width,height);

}

}

//敌方鱼左类(创建敌方鱼左类,继承敌方鱼父类)

class Enamy_1_L extends Enamy{//小鱼左

Enamy_1_L(){

this.x=-45;

this.y=(int)(Math.random()*700+100);

this.width=45;

this.height=80;

this.speed=12;

this.count=1;

this.type=1;

this.enamyFish= GameUtils.enamyl_img;

}

}

//敌方鱼右类

class Enamy_1_R extends Enamy_1_L{//小鱼右

Enamy_1_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_img;

}

}

class Enamy_2_L extends Enamy{//海马鱼

Enamy_2_L(){

this.x=-100;

this.y=(int)(Math.random()*700+100);

this.width=45;

this.height=80;

this.speed=18;

this.count=5;

this.type=2;

this.enamyFish=GameUtils.enamyl_2img;

}

}

class Enamy_2_R extends Enamy_2_L{

Enamy_2_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_2img;

}

}

class Enamy_3_L extends Enamy{//蓝带

Enamy_3_L(){

this.x=-100;

this.y=(int)(Math.random()*700+100);

this.width=100;

this.height=150;

this.speed=18;

this.count=8;

this.type=3;

this.enamyFish=GameUtils.enamyl_3img;

}

}

class Enamy_3_R extends Enamy_3_L{

Enamy_3_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_3img;

}

}

class Enamy_4_L extends Enamy{//剑鱼

Enamy_4_L(){

this.x=-300;

this.y=(int)(Math.random()*700+100);

this.width=300;

this.height=150;

this.speed=20;

this.count=20;

this.type=4;

this.enamyFish=GameUtils.enamyl_4img;

}

public Rectangle getRec() {

return new Rectangle(x+40,y+30,width-80,height-60);

}

}


class Enamy_4_R extends Enamy_4_L{

Enamy_4_R(){

this.x=1400;

dir=-1;

this.enamyFish=GameUtils.enamyr_4img;

}

}

class Enamy_Boss extends Enamy{

Enamy_Boss(){

this.x=-1000;

this.y=(int)(Math.random()*700+100);

this.width=340;

this.height=340;

this.speed=100;

this.count=0;

this.type=10;

this.enamyFish=GameUtils.bossing;

}

}

MyFish类

import java.awt.*;

public class MyFish {

//图片

Image myfish;

//坐标

int x=700;

int y=500;

int width=60;

int height=60;

//移动速度

int speed=20;

//我方鱼等级

int level=1;

//添加我方鱼对键盘的控制

void logic() {

if((GameUtils.UP)&&(y>=30)){    //对我方鱼进行边界限制

y=y-speed;

}

if((GameUtils.DOWN)&&(y<=770)) {

y=y+speed;

}

if((GameUtils.LEFT)&&(x>=0)) {

x=x-speed;

if(GameUtils.level<=1) {      //在每一个等级中设定我方鱼的形态

myfish=GameUtils.MyFishimg2_L;

}else if(GameUtils.level<=3) {

myfish=GameUtils.MyFishimg3_L;

}else

myfish=GameUtils.MyFishimg_L;

}

    if((GameUtils.RIGHT)&&(x<=1377)) {

x=x+speed;

if(GameUtils.level<=1) {

myfish=GameUtils.MyFishimg2_R;

}else if(GameUtils.level<=3) {

myfish=GameUtils.MyFishimg3_R;

}else

myfish=GameUtils.MyFishimg_R;

    }

}

//绘制自身的方法

public void paint(Graphics g) {

  logic();

g.drawImage(myfish, x, y, width+GameUtils.count,height+GameUtils.count, null);

}

//获取自身矩形的方法,用于碰撞检测

public Rectangle getRec() {

return new Rectangle(x,y,width+GameUtils.count,height+GameUtils.count);

}

}

Test类同上方Test

————————————————

版权声明:本文为CSDN博主「娄潇文」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_63719260/article/details/125420105

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容