小白笔记
目录
前言
学了点java基础语法后想自己动手做个碰撞检测并有计时等小游戏,在思考了一些设计步骤后,来分享一下。
需要有背景图,飞机爆炸的图片,每一帧一帧的放上去就好了,然后碰撞检测,子弹飞行速度,自身飞机的飞行速度和上下左右可以控制飞机。
设置窗口
首先在这个主文件中定义为MyGameFrame,其次还需要继承Frame类,然后在main方法里需要进行new MyGameFrame,然后对其进行操作。
我们需要固定好窗口,用我们的背景图片的分辨率来进行设置就可以了。但是首先得导入图片的路径。我们需要继承Frame类,然后在该方法里面进行操作,然后对窗口进行初始化,首先要设置好窗口的标题,然后设置窗口是否可见(窗口默认不可见),设置窗口大小,设置窗口出现的初始位置。最后需要增加窗口的关闭动作。于此同时还需要创建一个新的线程,来进行对窗口的描绘。我们还要控制飞机,就还需要设置键盘监听,然后炮弹还需要进行初始化。
键盘监听需要继承KeyAdapter类,然后需要进行按下和松开的判定。重画线程因为人眼最多60hz,所以就让线程Thread.Sleep(16)就可以了。为了让我们能够改变字体大小及颜色等,我们可以增添printInfo函数来进行修改。但是这样屏幕会有所闪烁,所以我们需要添加一个更新的方法来解决窗口闪烁的问题。
我们其次要的是绘画窗口的时候,对自身飞机进行判定,然后有碰撞检测intersects来进行判定。如果碰撞则飞机爆炸,就会告诉explode为真,立即绘画出爆炸画面,然后打印持续时间。
package com.bjsxt;
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;
import static com.bjsxt.GameUtil.*;
public class MyGameFrame extends Frame {
Image bgimg = GameUtil.getImage("images/bg.jpg");
Image planeimg = GameUtil.getImage("images/plane.png");
Shell[] shells = new Shell[50];
Explode explode;
Plane plane = new Plane(planeimg,200,200,3);
Date starttime = new Date();
Date endtime;
double period;
//初始化窗口
public void launchFrame(){
this.setTitle("飞机大战");
this.setVisible(true); // 窗口默认不可见,要设置为可见
this.setSize(500,500); // 设置窗口大小
this.setLocation(300,300); // 设置窗口起始坐标
// 增加关闭窗口的动作
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//启动窗口绘制线程
new PaintThread().start();
// 启动键盘监听
this.addKeyListener(new KeyMonitor());
// 初始化炮弹
for( int i = 0 ; i < shells.length ; i ++ ) shells[i] = new Shell();
}
// 画图片
@Override
public void paint(Graphics g){
g.drawImage(bgimg,0,0,FRAME_WIDTH,FRAME_HEIGH,null);
plane.drawMySelf(g);
for( int i = 0 ; i < shells.length ; i ++ ) {
shells[i].drawMySelf(g);
boolean crash = shells[i].getRec().intersects(plane.getRec());
if( crash && plane.live ) {
plane.live = false;
endtime = new Date();
period = (endtime.getTime() - starttime.getTime())/1000;
}
}
if( !plane.live ) {
if( explode == null )
explode = new Explode(plane.x,plane.y);
explode.draw(g);
}
if( !plane.live ) {
printInfo(g,"PlayTime:"+period+"秒",40,100,100,Color.WHITE);
}
}
public void printInfo(Graphics g,String str,int size,int x,int y,Color color){
Font oldFont = g.getFont();
Color oldcolor = g.getColor();
Font f = new Font("宋体",Font.BOLD,size);
g.setFont(f);
g.setColor(color);
g.drawString(str,x,y);
g.setColor(oldcolor);
g.setFont(oldFont);
}
// 键盘监听
class KeyMonitor extends KeyAdapter {
// 按下的键
@Override
public void keyPressed(KeyEvent e) {
plane.addDirection(e);
}
// 松开的键
@Override
public void keyReleased(KeyEvent e) {
plane.minusDirection(e);
}
}
// 重画线程
class PaintThread extends Thread {
@Override
public void run() {
while( true )
{
repaint();
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
MyGameFrame frame = new MyGameFrame();
frame.launchFrame();
}
// 缓冲解决闪烁问题
private Image offScreenImage = null;
public void update( Graphics g ) {
if( offScreenImage == null )
offScreenImage = this.createImage(FRAME_WIDTH,FRAME_HEIGH); // 这是游戏窗口的宽度和高度
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
}
工具类
我们只需要进行获得图像路径然后减去闪烁
package com.bjsxt;
import javax.imageio.ImageIO;
import java.awt.*;
import java.io.IOException;
import java.net.URL;
// 工具类
public class GameUtil {
public static final int FRAME_WIDTH = 500;
public static final int FRAME_HEIGH = 500;
// 构造器私有。防止别人创建对象
private GameUtil(){}
public static Image getImage(String pathname ){
Image img = null;
URL url = GameUtil.class.getClassLoader().getResource(pathname);
try {
img = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
return img;
}
}
游戏物体根类
我们需要定义好对应图片和物体的移动速度还有宽高。为了画物体比如背景,飞机,炮弹,我们需要创建很多GameObject方法,因为有的时候需要知道物体速度,有的时候需要知道物体的宽高,有的时候只需要知道物体的位置。然后我们需要创建一个方法来描绘物体,然后还有一个方法可以返回自己对应的矩形。
package com.bjsxt;
import java.awt.*;
// 游戏物体根类
public class GameObject {
Image img; // 对应的图片
int x, y;
int speed; // 物体移动速度
int width, height; // 物体的宽度高度
// 画自己
public void drawMySelf(Graphics g){
g.drawImage(img,x,y,width,height,null);
}
// 返回对应的矩形
public Rectangle getRec(){
return new Rectangle(x,y,width,height);
}
public GameObject(){}
public GameObject(Image img, int x, int 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(Image img, int x, int y, int speed) {
this(img,x,y);
this.speed = speed;
}
public GameObject(Image img, int x, int y) {
this(img);
this.x = x;
this.y = y;
}
public GameObject(Image img) {
this.img = img;
if( img != null ) {
this.width = img.getWidth(null);
this.height = img.getHeight(null);
}
}
}
Plane类
Plane类是需要继承GameObject类的,然后需要使用super(img,x,y,speed)需要知道图片和位置还有速度。自身飞机需要定义自己的方向,位置,速度,然后还需要进行判读飞机是否存活。重写一个画自己的drawMysel函数。并且需要监听键盘上下左右,按住则继续移动,松开则那个方向停止。
package com.bjsxt;
import java.awt.*;
import java.awt.event.KeyEvent;
// 飞机类
public class Plane extends GameObject {
// 方向
boolean left, right, up, down;
// 是否存活
boolean live = true;
// 画飞机
@Override
public void drawMySelf(Graphics g) {
if( !live ) return;
super.drawMySelf(g);
if( left && x >= 10 ) x-=speed;
if( right && x <= GameUtil.FRAME_WIDTH - width ) x += speed;
if( up && y >= 30 ) y -= speed;
if( down && y <= GameUtil.FRAME_HEIGH - height ) y += speed;
}
public void addDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT: left = true;break;
case KeyEvent.VK_RIGHT: right = true;break;
case KeyEvent.VK_UP: up = true;break;
case KeyEvent.VK_DOWN: down = true;break;
default:break;
}
}
public void minusDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT: left = false;break;
case KeyEvent.VK_RIGHT: right = false;break;
case KeyEvent.VK_UP: up = false;break;
case KeyEvent.VK_DOWN: down = false;break;
default:break;
}
}
public Plane(Image img, int x, int y, int speed) {
super(img, x, y, speed);
}
}
爆炸类
我只弄了16张逐次爆炸的图片,所以我定义了Image数组,然后用static对数组进行存入图片路径。在判断飞机死亡后,进行遍历描绘,其只需要知道描绘的位置就可以了。
package com.bjsxt;
import java.awt.*;
// 爆炸
public class Explode {
double x, y;
static Image[] imgs = new Image[16];
static {
for( int i = 0 ; i < imgs.length ; i ++ ) {
imgs[i] = GameUtil.getImage("images/explode/e"+(i+1)+".gif");
// 懒加载
imgs[i].getWidth(null);
}
}
int count = 0;
boolean live = true;
public void draw(Graphics g)
{
if( !live ) return;
if( count < 16 ) g.drawImage(imgs[count++],(int)x,(int)y,null);
else live = false;
}
public Explode(double x, double y) {
this.x = x;
this.y = y;
}
}
Shell类
炮弹类,因为炮弹最初是随机移动的,然后进行窗口碰撞的时候需要用到角度,所以首先需要随机一个角度出来然后判断是否与窗口碰撞后计算接下来的运行角度。然后该也需要重写drawMyself函数。
package com.bjsxt;
import java.awt.*;
public class Shell extends GameObject {
double degree;
@Override
public void drawMySelf(Graphics g) {
Color old = g.getColor();
g.setColor(Color.yellow);
g.fillOval(x,y,width,height);
x += (int)(speed*Math.cos(degree));
y += (int)(speed*Math.sin(degree));
if( y >= GameUtil.FRAME_HEIGH - 2*width || y <= 30 ) degree = -degree;
if( x >= GameUtil.FRAME_WIDTH - 2*height || x <= 10 ) degree = Math.PI - degree;
g.setColor(old);
}
public Shell() {
degree = Math.random()*Math.PI*2;
x = 100;
y = 100;
width = 10;
height = 10;
speed = 3;
}
}
初样
最后完成这些代码工作后,得到的出样大致如图所示
可能会存在一些BUG,但后续会继续修改。
飞机大战