仿雷电——飞机大战类游戏Ⅰ

简介

我们仿照 QQ游戏“”雷电这个风靡全球的游戏,来把所有知识点串接起来了
多线程用来实现动画效果、容器实现对于多发炮弹的存取和处理、常用类等等的应用。
但跟随本文只能做出雷电的雏形,但你可以在这个基础上改造出属于你的“雷电”
这是最后效果图:

第一阶段:游戏界面的绘画及图片加载

游戏开发中,图片加载是最常见的技术。我们在此处使用ImageIO类实现图片加载,并且为了代码的复用,将图片加载的方法封装到GameUtil工具类中,便于我们以后直接调用。

​ 我们要先将项目用到的图片拷贝到项目的src下面,我们可以建立新的文件夹images存放所有图片

1.添加背景图片

方法1:通过在JFrame中添加一个JPanel,背景图片放在JPanel上来实现

方法2:我们用JLayeredPane,JLayeredPane 为 JFC/Swing 容器添加了深度,允许组件在需要时互相重叠。Integer 对象指定容器中每个组件的深度,其中编号较高的组件位于其他组件之上。常用的几个层如下图:

之前我再添加背景图片时一贯使用绝对路径,每次添加修改极为麻烦,本次我们学习如何使用相对路径,首先通过GameUtil.class.getClaaaLoader().getResource来获得资源的根目录,从而获取相对位置

paint会被自动被调用,g相当于一只画笔

代码如下:
/**
 * 2019年9月21日
 */
package Thunder;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyListener;
import java.util.ArrayList;

import javax.swing.JFrame;

/**
 * 飞机大战雏形:可以发炮弹,但是没有解决连发炮弹的问题
 * 
 * 之后有两个方向:画面上有很多炮弹,控制飞机移动躲避炮弹
 * 或者经典的雷电游戏,击落敌机
 *
 * 2019年9月21日
 */
public class GameUI extends JFrame {
	
	
	// 将背景和飞机图片定义为成员变量
	Image background = GameUtil.getImage("images/back3.jpg");
	Image planeImg = GameUtil.getImage("images/plane1.png");
	Image shootImg = GameUtil.getImage("images/shoot1.png");
	
	public static int UIWidth = 1000;
	public static int UIHeigh = 1000;

	
	
	//ArrayList<Shell>  shellList = new ArrayList<Shell>();  
	Plane plane1 =  new Plane(planeImg,200,200,2,100,100);
	Shell shoot1 = new Shell(shootImg, 237,170,2,25,35);
	
	public void GFrame() {
		this.setTitle("雷電");
		this.setSize(UIWidth, UIHeigh);
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(3);
		this.addKeyListener(plane1);
		this.addKeyListener(shoot1);
		this.setVisible(true);
		//启动线程
		PaintThread pt =new PaintThread();
		pt.start();
	}

	// paint方法作用是:画出整个窗口及内部内容。被系统自动调用。
	 
	public void paint(Graphics g) {

		g.drawImage(background, 0, 0, 1000, 1000, null);
		plane1.drawMySelf(g);
		shoot1.drawMySelf(g);
			}

	// 定义内部类
	class PaintThread extends Thread {
		// 重写
		public void run() {
			while (true) {
				repaint();// 重画

				try {
					Thread.sleep(10);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	//双缓冲解决闪烁
	private Image offScreenImage = null;
	 
	public void update(Graphics g) {
	    if(offScreenImage == null)
	        offScreenImage = this.createImage(500,500);//这是游戏窗口的宽度和高度
	     
	    Graphics gOff = offScreenImage.getGraphics();
	    paint(gOff);
	    g.drawImage(offScreenImage, 0, 0, null);
	}  

	// 主方法
	public static void main(String[] args) {
		// 类里面调用窗体
		GameUI gu = new GameUI();
		gu.GFrame();

	}

}

【要點】:

1.继承Frame类,画出窗口
2. 了解坐标系,窗口坐标以左上角为(0,0)点
3. 物体就是矩形,物体的位置就是所在矩形左上角顶点的坐标
4. 窗口关闭,居中等我们需要自己添加功能,如下

在JFrame中我们可以这样写:
drawframe.setDefaultCloseOperation(3);//关闭时程序结束
drawframe.setLocationRelativeTo(null);
但在Frame中,我们需要
/ 增加关闭窗口监听,这样用户点击右上角关闭图标,可以关闭游戏程�?
	this.addWindowListener(new WindowAdapter() {
		@Override
		public void windowClosing(WindowEvent e) {
			System.exit(0);
		}
	});

第二阶段:使用多线程让动画动起来

第三阶段:键盘操控飞机

第四阶段:炮弹和飞机碰撞,爆炸

第五阶段:显示分数

项目结构如图:
在这里插入图片描述
所有代码:

/**
 * 2019年9月21日
 */
package Thunder;

import java.awt.Graphics;
import java.awt.Image;

import javax.swing.JFrame;

/**
 * 
 *
 * 2019年9月21日
 */
public class GameUI extends JFrame {
	// 将背景和飞机图片定义为成员变量
	Image background = GameUtil.getImage("images/back3.jpg");
	Image planeImg = GameUtil.getImage("images/plane1.png");
	
	public static int UIWidth = 1000;
	public static int UIHeigh = 1000;

	Plane plane1 =  new Plane(planeImg,200,200,2,100,100);
	
	public void GFrame() {
		this.setTitle("雷電");
		this.setSize(UIWidth, UIHeigh);
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(3);
		//添加监听器
		//Listener li = new Listener();
		//this.addKeyListener(li);
		this.addKeyListener(plane1);
		this.setVisible(true);
		//启动线程
		PaintThread pt =new PaintThread();
		pt.start();
	}

	// paint方法作用是:画出整个窗口及内部内容。被系统自动调用。
	 
	public void paint(Graphics g) {

		g.drawImage(background, 0, 0, 1000, 1000, null);
		plane1.drawMySelf(g);
	}

	// 定义内部类
	class PaintThread extends Thread {
		// 重写
		public void run() {
			while (true) {
				repaint();// 重画

				try {
					Thread.sleep(10);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	//双缓冲解决闪烁
	private Image offScreenImage = null;
	 
	public void update(Graphics g) {
	    if(offScreenImage == null)
	        offScreenImage = this.createImage(500,500);//这是游戏窗口的宽度和高度
	     
	    Graphics gOff = offScreenImage.getGraphics();
	    paint(gOff);
	    g.drawImage(offScreenImage, 0, 0, null);
	}  

	// 主方法
	public static void main(String[] args) {
		// 类里面调用窗体
		GameUI gu = new GameUI();
		gu.GFrame();

	}

}


package Thunder;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

/**
 * GameUtil类:加载图片代码
 *GameUtil获得程序运行类加载器,加载资源的根目录,
 * 2019年9月22日
 */
public class GameUtil {
	//工具类一般将构造器私有化
	public 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;
	}
	

}

/**
 * 2019年9月22日
 */
package Thunder;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;

/**
 * 游戏中所有物体的父类
 *
 * 2019年9月22日
 */
public class GameObject {
	Image img; //该物体对应的图片对象
    double x,y;    //该物体的坐标
    int speed; //该物体的运行速度
    int width,height;  //该物体所在矩形区域的宽度和高度
    public void drawMySelf(Graphics  g){
        g.drawImage(img, (int)x, (int)y, (int)width, (int)height, null);
    }
     
    public GameObject(Image img, double x, double y) {
        this.img = img;
        this.x = x;
        this.y = y;
        if(img!=null){
            this.width = img.getWidth(null);
            this.height = img.getHeight(null);
        }
    }
    public GameObject(Image img, double x, double y, int width,
            int height) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
     
    public GameObject(Image img, double x, double y, int speed, int width,
            int height) {
        this.img = img;
        this.x = x;
        //这只是构造方法初始化化时的值,并不能作为约束
        this.y = y;
        this.speed = speed;
        this.width = width;
        this.height = height;
    }
     
    public GameObject() {
    }
     
    /**
     * 返回物体对应矩形区域,便于后续在碰撞检测中使用
     * @return
     */
    public Rectangle getRect(){
        return  new Rectangle((int)x,(int) y, width, height);
    }  

}


package Thunder;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * 
 *
 * 2019年9月22日
 */
public class Plane extends GameObject implements KeyListener {

	boolean left, right, up, down,shoot;
	boolean live = true;
	

	public void drawMySelf(Graphics g) {
		super.drawMySelf(g);
		// 这里可移动,但是下面不行,应该是判断的问题
		 this.x+=2;
		 System.out.println("此时的坐标"+x);
		 //System.out.println(left);
		if (left) {
			System.out.println("此时的坐标"+x);
			x -= speed;
			if(x<=0){
	        	x=0;
	        }
		}
		if (right) {
			x += speed;
			if(x>=900){
	        	x=900;
	        }
			
		}
		if (up) {
			y -= speed;
			if(y<=10){
				y=10;
			}
			
		}
		if (down) {
			y += speed;
			if(y>=900){
				y=900;
			}
		}
	}
	public Plane(Image img, double x, double y, int speed, int width, int height) {

		super(img, x, y,width, height);
		//System.out.println("有被重画");
		//this.speed = speed;
	}

	public void keyTyped(KeyEvent e) {
	}

	public void keyPressed(KeyEvent e) {
		System.out.println(e.getKeyCode());
		int key = e.getKeyCode();
		switch (key) {
		case 37:
			left = true;
			break;
		case 38:
			up = true;
			break;
		case 39:
			right = true;
			break;
		case 40:
			down = true;
			break;
		case 32:
			shoot = true;
			break;
		default:
			break;
		}
	}

	public void keyReleased(KeyEvent e) {
		//System.out.println(e.getKeyCode());
		int key = e.getKeyCode();
		switch (key) {
		case 37:
			left = false;
			break;
		case 38:
			up = false;
			break;
		case 39:
			right = false;
			break;
		case 40:
			down = false;
			break;
		case 32:
			shoot = true;
			break;
		default:
			break;
		}
	}
}

我原来打算共享飞机坐标,这样就可以将子弹和飞机关联起来,但是这个语句导致 飞机完全不能动,因为坐标与图片没有关联了,重绘只画了一次
在这里插入图片描述
该怎么把子弹放入数组列表中呢?
现在子弹是一个新的类,而且有构造器,得随着飞机移动,但是
应该直接放入监听就可以了,点一下,延迟几秒,画一个。

现在到这个方向有两种结果可以走:
第一种:屏幕随机生成炮弹,我们控制飞机去躲避,生存时间越长,得分越高
第二钟:和游戏雷电一样,对面有敌机,我们在躲避敌机炮弹的同时击落敌机,获取分数。
在炮弹的任意角度飞行时,如果触碰到边界就反转角度,模拟碰撞效果。
同时为了效果美观,我们需要在一些细节上注意:例如碰撞边界时要减去球 的直径,碰撞标题栏时需要减去标题栏的宽度。
但是一个炮弹远远不够,我们希望增大一些挑战难度,有很多炮弹,就可以用到数组了。
分为三步:
1)定义数组
2)初始化五十个炮弹
3)画出炮弹

当使用Frame时,屏幕闪烁如图:

在这里插入图片描述

当使用JFrame解决屏幕闪烁问题时:
在这里插入图片描述
最后我们采用了frame+双缓冲的技术来解决,解决效果如图:
在这里插入图片描述

只需要把这段代码放入界面的类中(任意位置即可)

//双缓冲解决闪烁
	private Image offScreenImage = null;
	 
	public void update(Graphics g) {
	    if(offScreenImage == null)
	        offScreenImage = this.createImage(1000,1000);//这是游戏窗口的宽度和高度
	     
	    Graphics gOff = offScreenImage.getGraphics();
	    paint(gOff);
	    g.drawImage(offScreenImage, 0, 0, null);
	}  

在Jfram和frame切换中可能的报错:

当我们使用JFrame时可以调用这行语句关闭窗口
但是在Frame里面没有,所以我们需要添加监听器
在这里插入图片描述

//this.setDefaultCloseOperation(3);
		//增加关闭窗口监听,这样用户点击右上角关闭图标,可以关闭游戏程序
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

游戏中,多个元素是否碰到一起,实际上,通常是用“矩形检测”原理实现的。 我们在前面提到,游戏中所有的物体都可以抽象成“矩形”,我们只需判断两个矩形是否相交即可。对于一些复杂的多边形、不规则物体,实际上是将他分解成多个矩形,继续进行矩形检测。

Java的API中,为我们提供了Rectangle类来表示矩形相关信息,并且提供了intersects()方法,直接判断矩形是否相交。
我们使用GameObject里面的矩形检测来判断,飞机有,炮弹也有,这也是继承的隐形好处.

爆炸图片轮播:
图片加载一次很耗费资源,所以我们将他设为static,只初始化一次,之后直接用就可以,而不需要new了,
爆炸类如下

	package Thunder_two;

import java.awt.Graphics;
import java.awt.Image;
/**
 * 
 * 
 *
 */
public class Explode {
    double x,y;
    static Image[] imgs = new Image[16];
    static {
        for(int i=0;i<16;i++){
            imgs[i] = GameUtil.getImage("images/explode/e"+(i+1)+".gif");
            imgs[i].getWidth(null);
        }
    }
     
    int 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;
    }
}

到这一步,雷电游戏的雏形大概就完成了,但是我们却少了最重要的奖励机制,玩家不知道自己的得分,自然也不会有兴趣继续挑战了,所有我们加入时间类,在玩家死亡之后,在屏幕上显示获得的分数。
先定义两个成员变量
Date startTime = new Date(); //游戏起始时刻
Date endTime; //游戏结束时刻
如果飞机死亡,调用显示的方法

  if(!plane.live){
            if(endTime==null){
                endTime = new Date();
            }
            int period = (int)((endTime.getTime()-startTime.getTime())/1000);
            printInfo(g, "时间:"+period+"秒", 50, 120, 260, Color.white);
        }
//显示的方法
public void printInfo(Graphics g,String str,int size,int x,int y,Color color){
        Color c = g.getColor();
        g.setColor(color);
        Font f = new Font("宋体",Font.BOLD,size);
        g.setFont(f);
        g.drawString(str,x,y);
        g.setColor(c);
    }  

至此,雷电小游戏的第一个方向就完成了,但大家也许还有点意犹未尽呢吧!这和我们儿时玩的雷电差距太大了吧,下一篇文章中,我们就来开发自动发射+击落敌机的部分,翘首以待吧。

以下是雷电Ⅰ的全部代码
项目结构如图:
在这里插入图片描述

package Thunder_one;

import java.awt.Graphics;
import java.awt.Image;
/**
 * 
 * 
 *
 */
public class Explode {
    double x,y;
    static Image[] imgs = new Image[16];
    static {
        for(int i=0;i<16;i++){
            imgs[i] = GameUtil.getImage("images/explode/e"+(i+1)+".gif");
            imgs[i].getWidth(null);
        }
    }
     
    int 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;
    }
}

/**
 * 2019年9月22日
 */
package Thunder_one;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;

/**
 * 游戏中所有物体的父类
 *
 * 2019年9月22日
 */
public class GameObject {
	Image img; //该物体对应的图片对象
    double x,y;    //该物体的坐标
    int speed; //该物体的运行速度
    int width,height;  //该物体所在矩形区域的宽度和高度
    public void drawMySelf(Graphics  g){
        g.drawImage(img, (int)x, (int)y, (int)width, (int)height, null);
    }
     
    public GameObject(Image img, double x, double y) {
        this.img = img;
        this.x = x;
        this.y = y;
        if(img!=null){
            this.width = img.getWidth(null);
            this.height = img.getHeight(null);
        }
    }
    public GameObject(Image img, double x, double y, int width,
            int height) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
     
    public GameObject(Image img, double x, double y, int speed, int width,
            int height) {
        this.img = img;
        this.x = x;
        //这只是构造方法初始化化时的值,并不能作为约束
        this.y = y;
        this.speed = speed;
        this.width = width;
        this.height = height;
    }
     
    public GameObject() {
    }
     
    /**
     * 返回物体对应矩形区域,便于后续在碰撞检测中使用
     * @return
     */
    public Rectangle getRect(){
        return  new Rectangle((int)x,(int) y, width, height);
    }  

}

package Thunder_one;

import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.Timer;
import javax.swing.JFrame;
import javax.swing.JLabel;

/**
 * :雷电第一个版本:
 * 屏幕有很多随机生成的炮弹,我们需要控制飞机来躲避炮弹
 * 存活时间越长,得分越高
 *
 * 
 */
public class GameUI extends Frame {

	Date startTime = new Date(); // 游戏起始时刻
	Date endTime; // 游戏结束时刻
	private int score = 0;// �?获分�?
	// 界面宽和�?
	public static int UIWidth = 1000;
	public static int UIHeigh = 1000;
	// 炮弹的数量
	int ShellNum = 20;
	// 飞机的速度
	int PlSpeed = 20;

	// 将背景和飞机图片定义为成员变量
	Image background = GameUtil.getImage("images/back3.jpg");
	Image planeImg = GameUtil.getImage("images/plane1.png");
	Image shootImg = GameUtil.getImage("images/shoot1.png");
	Plane plane1 = new Plane(planeImg, 500, 800, PlSpeed, 100, 100);

	// 炮弹的数�?
	Shell[] shellArr = new Shell[ShellNum];
	// 创建爆炸对象
	Explode bao;
	// 爆炸的轮播图
	Image[] imgs = new Image[16];

	// 主界面
	public void GFrame() {
		this.setTitle("雷電");
		this.setSize(UIWidth, UIHeigh);
		this.setLocationRelativeTo(null);
		// 增加关闭窗口监听,这样用户点击右上角关闭图标,可以关闭游戏程�?
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		this.addKeyListener(plane1);
		// 初始化,生成炮弹
		for (int i = 0; i < ShellNum; i++) {
			shellArr[i] = new Shell();
		}
		this.setVisible(true);
		// 启动线程
		PaintThread pt = new PaintThread();
		pt.start();
	}

	// paint方法作用是:画出整个窗口及内部内容�被系统自动调用�
	public void paint(Graphics g) {
		g.drawImage(background, 0, 0, 1000, 1000, null);
		plane1.drawMySelf(g);
		// 画出容器中所有的子弹
		for (int i = 0; i < shellArr.length; i++) {
			shellArr[i].draw(g);
			// 检测碰撞
			boolean peng = shellArr[i].getRect().intersects(plane1.getRect());
			if (peng) {
				plane1.live = false;// 飞机死掉
				endTime = new Date();
				if (bao == null) {
					bao = new Explode(plane1.x, plane1.y);
				}
				bao.draw(g);
			}
		}
		if (!plane1.live) {
			if (endTime == null) {
				endTime = new Date();
			}
			int period = (int) ((endTime.getTime() - startTime.getTime()) / 1000);
			printInfo(g, "您的得分为:" + period + "�?", 50, 120, 260, Color.white);
		}
	}

	// �?後計�?
	public void printInfo(Graphics g, String str, int size, int x, int y, Color color) {
		Color c = g.getColor();
		g.setColor(color);
		Font f = new Font("宋体", Font.BOLD, size);
		g.setFont(f);
		g.drawString(str, x, y);
		g.setColor(c);
	}

	// 定义内部�?
	class PaintThread extends Thread {
		// 重写
		public void run() {
			while (true) {
				repaint();// 重画
				try {
					Thread.sleep(10);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	// 双缓冲解决闪�?
	private Image offScreenImage = null;

	public void update(Graphics g) {
		if (offScreenImage == null)
			offScreenImage = this.createImage(1000, 1000);
		// 这是游戏窗口的宽度和高度
		Graphics gOff = offScreenImage.getGraphics();
		paint(gOff);
		g.drawImage(offScreenImage, 0, 0, null);
	}

	// 主方�?
	public static void main(String[] args) {
		// 类里面调用窗�?
		GameUI gu = new GameUI();
		gu.GFrame();
	}
}

package Thunder_one;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

/**
 * GameUtil类:加载图片代码
 *GameUtil获得程序运行类加载器,加载资源的根目录,
 * 2019年9月22日
 */
public class GameUtil {
	
	//工具类一般将构造器私有化
	public 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;
	}
	

}

package Thunder_one;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * 
 *
 * 2019年9月22日
 */
public class Plane extends GameObject implements KeyListener {

	boolean left, right, up, down, shoot;
	boolean live = true;

	public void drawMySelf(Graphics g) {
		if(live){

			super.drawMySelf(g);
			// 这里可移动,但是下面不行,应该是判断的问题
			// this.x+=2;

			// System.out.println(left);
			if (left) {

				x -= speed;
				if (x <= 0) {
					x = 0;
				}
			}
			if (right) {
				x += speed;
				if (x >= 900) {
					x = 900;
				}
			}
			if (up) {
				y -= speed;
				if (y <= 10) {
					y = 10;
				}
			}
			if (down) {
				y += speed;
				if (y >= 900) {
					y = 900;
				}
			}
		
		}
	}

	public Plane(Image img, double x, double y, int speed, int width, int height) {

		super(img, x, y, width, height);
		this.speed = speed;
	}

	public void keyTyped(KeyEvent e) {
	}

	public void keyPressed(KeyEvent e) {
		//System.out.println(e.getKeyCode());
		int key = e.getKeyCode();
		
		switch (key) {
		case 37:
			left = true;
			break;
		case 38:
			up = true;
			break;
		case 39:
			right = true;
			break;
		case 40:
			down = true;
			break;
		default:
			break;
		}
	}

	public void keyReleased(KeyEvent e) {
		// System.out.println(e.getKeyCode());
		int key = e.getKeyCode();
		switch (key) {
		case 37:
			left = false;
			break;
		case 38:
			up = false;
			break;
		case 39:
			right = false;
			break;
		case 40:
			down = false;
			break;

		default:
			break;
		}
	}
}
/**
 * 2019年9月26日
 */
package Thunder_one;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.util.Random;

import Thunder.GameUtil;

/**
 * 
 *
 * 2019年9月26日
 */
public class Shell extends GameObject{
	double degree;
	
	Random ra = new Random();
    
    public Shell(){
        degree = Math.random()*Math.PI*2;
        x = ra.nextInt(700)+250;
        y = ra.nextInt(700)+150;
        width = ra.nextInt(15)+10;
        height = ra.nextInt(15)+10;
        speed = ra.nextInt(3)+2;
    }
     
    public void draw(Graphics g){
        //将外部传入对象g的状态保存好
        Color c = g.getColor();
        
        g.setColor(Color.yellow);
        
        Image shootImg = GameUtil.getImage("images/shoot1.png");
        g.drawImage(shootImg, (int)x, (int)y, width, height, null);
 
        //g.fillOval((int)x, (int)y, width, height);
         
        //炮弹沿着任意角度飞行
        x += speed*Math.cos(degree);
        y += speed*Math.sin(degree);
         
        //如下代码,用来实现碰到边界,炮弹反弹回来(原理和打台球游戏一样)
        if(y>GameUI.UIHeigh-height||y<40){
            degree = -degree;
        }
        if(x<0||x>GameUI.UIWidth-width){
            degree = Math.PI-degree;
        }
        //返回给外部,变回以前的颜色
        //g.setColor(c);
    }

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值