Java开发的小球躲避小游戏。通过上下左右按键控制小球移动,小球移动的方向包括:横向、纵向、斜向,小球具有初始生命值。小球通过移动来躲避炮弹,当小球碰到炮弹时,产生爆炸的动画效果。
使用双缓冲方式解决闪烁问题。
注释很全面,速度、初始子弹数量都可以通过全局变量修改。适合入门学习使用。
全部代码都在博客里,整个项目文件已经上传至:CSDN下载
MyGameFrame.java
package cn.sxt.game;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;
import javax.swing.JFrame;
public class MyGameFrame extends Frame
{
// 双缓冲 解决闪烁问题
private Image offScreenImage = null;
public void update(Graphics g)
{
if (offScreenImage == null)
offScreenImage = this.createImage(Constant.GAME_WIDTH, Constant.GAME_HEIGHT);// 这是游戏窗口的宽度和高度
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage, 0, 0, null);
}
Image bg = GameUtil.getImage("image/bg.jpg");
Image planeImg = GameUtil.getImage("image/plane.png");
Plane plane = new Plane(planeImg, 250, 250, Constant.PLANE_SPEED);
Shell shell[] = new Shell[Constant.SHELL_NUM];
{
for (int i = 0; i < Constant.SHELL_NUM; i++)
{
shell[i] = new Shell();
}
}
Explode explode = new Explode(0, 0);
Date startTime = new Date();
Date endTime;
int period;// 持续时间
@Override
public void paint(Graphics g)// 自动被调用,g相当于一支画笔
{
super.paint(g);
g.drawImage(bg, 0, 0, null);// 画背景
plane.drawItSelf(g);// 画飞机
// 画炮弹
for (int i = 0; i < Constant.SHELL_NUM; i++)
{
shell[i].draw(g);
// 检测是否飞机和炮弹相接触
boolean touch = shell[i].getRect().intersects(plane.getRect());
if (touch)
{
Constant.LIFE--;
plane.live = false;
explode.x = plane.x;
explode.y = plane.y;
System.out.println("碰到了!");
explode.count %= 16;
explode.Draw(g);
endTime = new Date();
period = (int) (endTime.getTime() - startTime.getTime()) / 1000;// 计算时间差
Font f = new Font("宋体", Font.BOLD, 20);
g.setFont(f);
g.setColor(Color.WHITE);
g.drawString("(" + period + "秒)剩余生命值:" + Constant.LIFE, (int) plane.x, (int) plane.y);
}
}
}
// 键盘监听
class KeyMonitor extends KeyAdapter
{
@Override
public void keyPressed(KeyEvent e)// 按下键
{
// TODO Auto-generated method stub
super.keyPressed(e);// 38按向上 40按向下 37按向左 39按向右
plane.KeyDown(e);
}
@Override
public void keyReleased(KeyEvent e)// 抬起键
{
// TODO Auto-generated method stub
super.keyReleased(e);
plane.KeyUp(e);
}
}
// 多线程的使用 反复重画窗口,当经常使用一个类时,可以把调用它放入类作为被调用类的内部类,这样方便调用
class PaintThread extends Thread
{
@Override
public void run()
{
// TODO Auto-generated method stub
super.run();
while (true)
{
repaint();
try
{
Thread.sleep(Constant.SLEEP);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
// 初始化窗口函数
public void launchFrame()
{
this.setTitle("魔鬼鱼");
this.setVisible(true);
this.setSize(Constant.GAME_WIDTH, Constant.GAME_HEIGHT);// 窗口大小 这里有何神奇的bug 垮外界屏幕的时候窗口无法缩小
this.setLocation(500, 500);// 窗口位置
// 匿名内部类 安卓编程到处都是 用来在关闭窗口之后结束程序
this.addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
System.exit(0);// 0表示正常结束 负数表示异常结束
}
});
new PaintThread().start();// 启动重画窗口的线程
addKeyListener(new KeyMonitor());// 添加键盘监听
}
public static void main(String[] args)// 使用alt / 组合件可以快速补全
{
MyGameFrame fr = new MyGameFrame();
fr.launchFrame();
}
}
Explode.java
package cn.sxt.game;
import java.awt.Graphics;
import java.awt.Image;
public class Explode
{
double x, y;// 爆炸的坐标
static Image[] imgs = new Image[16];// 使用static避免图像被反复加载
static// 静态初始化块
{
for (int i = 0; i < 16; i++)
{
imgs[i] = GameUtil.getImage("image/explode/e" + (i + 1) + ".gif");
imgs[i].getWidth(null);
}
}
int count;// 当前显示的图片序号
public void Draw(Graphics g)
{
if (count <= 15)//轮播16张图片
{
System.out.println("轮播16张图片");
g.drawImage(imgs[count], (int) x, (int) y, null);
count++;
}
}
//构造器
public Explode(double x, double y)
{
this.x = x;
this.y = y;
}
}
GameObject.java
package cn.sxt.game;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
//包含游戏中物体的常用属性,物体继承于这个类即可
public class GameObject
{
Image img; //图像路径名称
double x,y; //位置坐标
int speed; //速度
int width,height; //宽高
//画出自己
public void drawItSelf(Graphics g)
{
g.drawImage(img, (int)x, (int)y, null);
}
//矩形用于做碰撞检测 返回物体所在矩形
public Rectangle getRect()
{
return new Rectangle((int)x,(int)y,width,height);
}
//构造器
public GameObject(Image img, double x, double y, int speed, int width, int height)
{
super();
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
this.width = width;
this.height = height;
}
//重载的构造器 方便调用
public GameObject(Image img, double x, double y)
{
super();
this.img = img;
this.x = x;
this.y = y;
}
//无参构造器
public GameObject()
{
super();
}
}
GameUtil.java
package cn.sxt.game;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
public class GameUtil
{
private GameUtil()// 工具类最好将构造器私有化
{
}
// 返回指定路径文件的图片对象
public static Image getImage(String path)
{
BufferedImage bi = null;
try
{
URL u = GameUtil.class.getClassLoader().getResource(path);
bi = ImageIO.read(u);
} catch (IOException e)
{
e.printStackTrace();
}
return bi;
}
}
Constant.java(类似于全局变量)
package cn.sxt.game;
//用于存储常量
public class Constant
{
public static final int GAME_WIDTH = 500;
public static final int GAME_HEIGHT = 500;
public static final int SHELL_NUM = 40;
public static final int PLANE_SPEED = 5;
public static final int SHELL_SPEED = 3;
public static final int SLEEP = 50;
public static int LIFE = 1000;
}
Plane.java (plane就是斜眼笑的图片)
package cn.sxt.game;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
public class Plane extends GameObject // Plane类继承 GameObject类
{
public boolean up, down, left, right;
boolean live = true;
public void drawItSelf(Graphics g)
{
//if (live)
//{
if (up)
y -= speed;
if (down)
y += speed;
if (left)
x -= speed;
if (right)
x += speed;
g.drawImage(img, (int) x, (int) y, null);
//} else
//{
// System.out.println("碰到了额");
//}
}
public void KeyDown(KeyEvent e)
{
switch (e.getKeyCode())
{
case 38:
up = true;
System.out.println(e.getKeyCode() + "按向上");
break;
case 40:
down = true;
System.out.println(e.getKeyCode() + "按向下");
break;
case 37:
left = true;
System.out.println(e.getKeyCode() + "按向左");
break;
case 39:
right = true;
System.out.println(e.getKeyCode() + "按向右");
break;
default:
System.out.println(e.getKeyCode() + "按无效键");
}
}
public void KeyUp(KeyEvent e)
{
up = down = left = right = false;
System.out.println(e.getKeyCode() + "按键抬起");
}
public Plane(Image img, double x, double y, int speed)
{
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
this.width = img.getWidth(null);// 获得图片宽度
this.height = img.getHeight(null);// 获得图片高度
}
}
Shell.java(子弹)
package cn.sxt.game;
import java.awt.Color;
import java.awt.Graphics;
//子弹
public class Shell extends GameObject
{
double degree;// 角度
int xflag = 1;
int yflag = 1;
public Shell()
{
// 坐标
x = 100;
y = 100;
// 长宽
width = 10;
height = 10;
// 速度
speed = Constant.SHELL_SPEED;
// 角度生成
degree = Math.random() * Math.PI * 2;// 产生一个0~2π之间的随机数
}
// 画炮弹
public void draw(Graphics g)
{
Color c = g.getColor();// 保存之前的颜色
g.setColor(Color.yellow);
g.fillOval((int) x, (int) y, width, height);// 填充炮弹
if (x <= 20 || x >= Constant.GAME_WIDTH - 20)
{
xflag *= -1;
}
if (y <= 20 || y >= Constant.GAME_HEIGHT - 20)
{
yflag *= -1;
}
x += speed * Math.cos(degree) * xflag;
y += speed * Math.sin(degree) * yflag;
g.setColor(c);// 用完之后把颜色还原
}
}