游戏简介:通过键盘来控制飞机移动,躲避炮弹,碰到炮弹则游戏结束!
游戏项目共有七个类,包含了一些java面向对象基础知识。
首先是MygameFrame游戏窗口类:
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;
public class MygameFrame extends Frame {//游戏窗口类继承JFrame类
boolean flag=true;//在这里设置了一个标志,来控制窗口的刷新
Image planeImg=GameUtil.getImage("images/plane.png");
Image bg=GameUtil.getImage("images/bg.jpg");
Image over=GameUtil.getImage("images/over.jpg");//调用GameUtil内重写的函数得到图片
Plane plane=new Plane(planeImg,250,250);//飞机类对象
Shell[] shells=new Shell[10];//炮弹数组,用来存放多个炮弹
Explode ep;//声明爆炸类对象,不需要new,在发生接触再new
Date startTime=new Date();//程序开始时new
Date endTime;//等游戏结束再new
int period;//计算游戏持续时间
@Override
public void paint(Graphics g) {//piant会自动被系统调用一次
super.paint(g);
g.drawImage(bg,1,1, this.getWidth(),this.getHeight(),null);//设置背景图片
plane.drawSelf(g);
for (Shell shell : shells) {//循环遍历炮弹类数组
shell.draw(g);
boolean p = shell.getRect().intersects(plane.getRect());//飞机与炮弹接触检测,用矩形类的intersects方法,测试对象是否接触
if (p) {
System.out.println("碰了");
plane.gameover = true;
if(ep==null){ep=new Explode(plane.x,plane.y); }
ep.newimgs();
ep.draw(g);
flag=false;//线程执行的标志
g.clearRect(0,0,500,500);//清空画布
endTime=new Date();
g.drawImage(over,0,25,this.getWidth(),this.getHeight(),null);//结束的背景图
period=(int)((endTime.getTime()-startTime.getTime())/1000);
g.setColor(Color.blue);
g.drawString("记录"+period+"秒",10,100);
}
}
}
class PaintThread extends Thread{
//内部类,可以调用外部类方法,多线程用于反复重画窗口
@Override
public void run() {
while(flag){
//System.out.println("窗口刷新");
repaint();
try {
Thread.sleep(40);//40ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void launchFrame(){//初始化窗口
this.setTitle("肖杰航作品");//设置标题
this.setVisible(true);//窗口可视化
this.setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); //设置窗口大小与坐标
this.setLocation(300,300);
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);//重写该方法,使关闭窗口后程序结束运行!
}
});
new PaintThread().start();//启动重画窗口线程,因为paint()方法只会自动调用一次
addKeyListener(new KeyMonitor());//给窗口增加键盘监听
for (int i = 0; i <shells.length ; i++) {//初始化炮弹数组内每个炮弹对象
shells[i]=new Shell();
}
}
class KeyMonitor extends KeyAdapter{//键盘监听内部类,继承 KeyAdapter适配器,重写其中两个方法,按下与释放键
@Override
public void keyPressed(KeyEvent e) {
plane.addDirection(e);
}
@Override
public void keyReleased(KeyEvent e) {
plane.minusDirection(e);
}
}
public static void main(String[] args) {//主函数调用游戏窗口类
MygameFrame f=new MygameFrame();
f.launchFrame();
}
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);
}
}/*如果窗口类继承自:java.swing.JFrame,则本身已经不完美的解决了闪烁问题,双缓冲是用于Frame,能更好的解决闪烁问题*/
先定义一个常量类,用于存放项目常量
public class Constant{
public static final int GAME_WIDTH=500;
public static final int GAME_HEIGHT=500;
}
ImageIO类来实现图片加载,并且为了代码的复用性,将图片加载的方法封装到GameUtil工具类中
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
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;
}
}
我们知道,在游戏里的物体例如飞机,炮弹有相同的共性,初始位置,长度宽度,速度等。所以我们给这些类先创建一个父类叫做游戏物品类,以便于飞机类继承。
import java.awt.*;
/**游戏内所有物体父类,因为游戏里的物体属性类似
*
*/
public class GameObject {
Image img;//图片
double x,y;//坐标
int speed;//移动速度
int width,height;
public GameObject() {//无参构造
}
public GameObject(Image img, double x, double y) {//构造器
this.img = img;
this.x = x;
this.y = y;
}
public void drawSelf(Graphics g){//定义一个画图函数
g.drawImage(img,(int)x,(int)y,null);
}
public Rectangle getRect(){//返回图片所在矩形,用于检测碰撞
return new Rectangle((int)x,(int)y,width,height);//java的矩形类
}
}
在父类里特地写了getRect()方法,得到物体的位置矩形框,以便后面判断飞机与炮弹的接触
然后写完父类就可以创建子类飞机类
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
ublic class Plane extends GameObject {
boolean left,right,up,down;//设置标志,用于键盘监听
boolean gameover=false;//设置标志,用于判断何时结束绘图
public void drawSelf(Graphics g){//重写父类中的方法
if(gameover==false){
g.drawImage(img,(int)x,(int)y,null);
if(left){//当键盘左键,横坐标左移一个单位
x=x-speed;
}
if(right){
x=x+speed;
}
if(up){
y=y-speed;
}
if(down){
y=y+speed;
}
}}
public Plane(Image img,double x,double y){
this.img=img;
this.x=x;
this.y=y;
this.speed=10;
this.width=img.getWidth(null);//宽度与你导入的飞机图片一致
this.height=img.getHeight(null);
}
public void addDirection(KeyEvent e){//方法实现按下某个键,加方向
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:left=true;break;
case KeyEvent.VK_UP:up=true;break;
case KeyEvent.VK_RIGHT:right=true;break;
case KeyEvent.VK_DOWN:down=true;break;
}
}
public void minusDirection(KeyEvent e){//方法实现松开某个键,不再加方向
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:left=false;break;
case KeyEvent.VK_UP:up=false;break;
case KeyEvent.VK_RIGHT:right=false;break;
case KeyEvent.VK_DOWN:down=false;break;
}
}
}
接下来是炮弹类,一样继承了物品类
import java.awt.*;
/*炮弹类
*/
public class Shell extends GameObject{
double degree;//角度,炮弹多了一个属性,可以各个方向移动
public Shell(){
x=200;//炮弹初速左边,可以设置为随机数
y=200;
width=10;
height=10;
speed=3;
degree=Math.random()*Math.PI*2;//炮弹发射角度随机产生
}
public void draw(Graphics g){//自己新增的方法,因为画炮弹不用图片,与飞机类不太一样。
Color c=g.getColor();//记录当前颜色以便还原
g.setColor(Color.orange);//修改炮弹颜色
g.fillOval((int)x,(int)y,width,height);//填充椭圆
x=x+speed*Math.cos(degree);//炮弹移动轨迹
y=y+speed*Math.sin(degree);
if(x<=0||x>=Constant.GAME_WIDTH-width){//碰左右边界,角度对称变化
degree=Math.PI-degree;
}
if(y<=30||y>=Constant.GAME_HEIGHT-height){//碰上下边界,角度反向变化
degree=-degree;
}
g.setColor(c);}
}
最后为了实现飞机与炮弹接触产生动画效果,写了一个爆炸类
类里使用图片数组轮播实现动态效果,使用数组保存图片信息
import java.awt.Graphics;
import java.awt.Image;
/*
* 爆炸类
*/
public class Explode {
double x,y;//设置效果显示位置
Image[] imgs = new Image[16];
public void newimgs() {
for(int i=0;i<16;i++){
imgs[i]=GameUtil.getImage("images/exp/e"+(i+1)+".gif");
}//图片命名规范,以e1~e16
}
int count;
//计数器count来控制到底画哪张图片
public void draw(Graphics g){
if(count<=15){
g.drawImage(imgs[count], (int)x, (int)y, null);
count++;
}
}
public Explode(double x,double y){
this.x = x;
this.y = y;
}
}