Java游戏开发——飞行射击类游戏

使用到的素材文件夹:

素材说明:bg0.jpg是背景图片。bomb_enemy是敌机爆炸时按顺序播放的四帧图片,bullet_0.png是子弹图片,enemy_alive.png是敌机图片,player.png是主角图片;

素材及完整源码链接:https://pan.baidu.com/s/1mwFNPrnNed5jbVZSSrtaUg 提取码: db6h

场景分析:玩家可以通过上下左右控制主角飞机的移动,主角每隔一段时间发射子弹,后台随机位置生成敌机,敌机如果碰到子弹,敌机死亡并播放爆炸效果,同时击中敌机的子弹失效;场景画布滚动播放。

设计子弹Bullet类:

package 雷电飞机射击游戏;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.ImageObserver;

import javax.swing.JPanel;

public class Bullet {

	static final int BULLET_STEP_X = 3;
	static final int BULLET_STEP_Y = 15;
	static final int BULLET_WIDTH = 30;
	public int m_posX = 0;
	public int m_posY = -20;
	boolean mFacus = true;
	private Image pic[] = null;
	private int mPlayID = 0;
	
	public Bullet(){
		pic = new Image[1];
//		System.out.println(pic.length);
		for(int i=0;i<pic.length;i++){
			pic[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/bullet_"+i+".png");
		}
	}
	
	public void init(int x,int y){
		m_posX = x;
		m_posY = y;
		mFacus = true;
	}
	
	public void DrawBullet(Graphics g,JPanel i){
		g.drawImage(pic[mPlayID++], m_posX, m_posY,30,20, (ImageObserver)i);
	//	System.out.println("当前mPlayID是 "+mPlayID+",当前pic.length是"+pic.length);
		if(mPlayID>=pic.length)
			mPlayID  = 0;
	}
	
	public void UpdateBullet(){
			m_posY -= BULLET_STEP_Y;
	}
	
}

 

子弹类分析:pic数组用于存放子弹的帧数,由于这里子弹帧数只有一张,所以数组长度设为1;UpdateBullet()用于更新子弹位置;m_posX,m_posY存放子弹坐标;

设计敌机Enemy类:

package 雷电飞机射击游戏;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.ImageObserver;

import javax.swing.JPanel;

public class Enemy {
	public static final int ENEMY_ALIVE_STATE = 0;
	public static final int ENEMY_DEATH_STATE = 1;
	static final int ENEMY_STEP_Y = 5;
	public int m_posX = 0;
	public int m_posY = 0;
	public int mAnimState = ENEMY_ALIVE_STATE;
	private Image enemyExplorePic[] = new Image[4];
	public int mPlayID = 0;
	
	public Enemy(){
		for(int i=0;i<enemyExplorePic.length;i++){
			enemyExplorePic[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/bomb_enemy_"+i+".png");
		}
	}
	
	public void init(int x,int y){
		m_posX = x;
		m_posY = y;
		mAnimState = ENEMY_ALIVE_STATE;
		mPlayID = 0;
	}
	
	public void DrawEnemy(Graphics g,JPanel i){
		if(mAnimState==ENEMY_DEATH_STATE&&mPlayID<enemyExplorePic.length){
			g.drawImage(enemyExplorePic[mPlayID], m_posX, m_posY,30,30, (ImageObserver)i);
			mPlayID++;
			return;
		}
		
		Image pic = Toolkit.getDefaultToolkit().getImage("D:/Game/enemy_alive.png");
		g.drawImage(pic, m_posX, m_posY, 30,30,(ImageObserver)i);
	}
	
	public void UpdateEnemy(){
		m_posY += ENEMY_STEP_Y;
	}
	
	
}

敌机类分析:m_posX,m_posY存放敌机坐标;enemyExplorePic数组存放敌机爆炸时顺序播放的帧数图片;mAnimState存放敌机状态;UpdateEnemy()控制敌机位置的移动;

设计游戏面板GamePanel类:

package 雷电飞机射击游戏;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;

import javax.swing.JPanel;


public class GamePanel extends JPanel implements Runnable,KeyListener{

	private int mScreenWidth = 320;//屏宽
	private int mScreenHeight = 480;//屏高
	private static final int STATE_GAME = 0;
	private int mState = STATE_GAME;
	private Image mBitMenuBG0 = null;//背景图1
	private Image mBitMenuBG1 = null;//背景图2
	private int mBitposY0 = 0;//背景1的y轴起点
	private int mBitposY1 = 0;//背景2的y轴起点
	private Sound sound = new Sound();
	private long createEnemyTime = System.currentTimeMillis();//用来监控敌机创建时间
	final static int BULLET_POOL_COUNT = 15;//最多同时存在的子弹数
	final static int PLAN_STEP = 10;//主角移动步长
	final static int PLAN_TIME = 500;//隔500ms发射子弹
	final static int ENEMY_POOL_COUNT = 5;//敌机最多同时存在个数
	final static int ENEMY_POS_OFF = 65;//敌机偏移量
	final static int BULLET_LEFT_OFFSET = 0;//子弹左偏移量
	final static int BULLET_UP_OFFSET = 10;//子弹上偏移量
	
	private Thread mThread = null;//主线程
	private boolean mIsRunning = false;//初始游戏状态未运行
	public int mAirPosX = 0;//主角x位置
	public int mAirPosY = 0;//主角y位置
	Vector<Enemy> mEnemy = new Vector<Enemy>();//存储敌机
	Vector<Bullet> mBullet = new Vector<Bullet>();//存储子弹
	
	public int mSendId = 0;
	public long mSendTime = 0L;
	Image myPlanePic[];
	public int myPlaneID = 0;
	
	public GamePanel(){
		setPreferredSize(new Dimension(mScreenWidth,mScreenHeight));
		setFocusable(true);
		addKeyListener(this);
		sound.loadSound();
		init();
		setGameState(STATE_GAME);
		mIsRunning = true;
		mThread = new Thread(this);
		mThread.start();
		setVisible(true);
	}
	
	public void init(){
		try{
			mBitMenuBG0 = Toolkit.getDefaultToolkit().getImage("D:/Game/bg0.jpg");
			mBitMenuBG1 = Toolkit.getDefaultToolkit().getImage("D:/Game/bg0.jpg");			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		mBitposY0 = 0;
		mBitposY1 = -mScreenHeight;
		mAirPosX = 150;
		mAirPosY = 400;
		myPlanePic = new Image[1];
		for(int i=0;i<myPlanePic.length;i++){
			myPlanePic[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/player.png");
		}
		
		for(int i=0;i<ENEMY_POOL_COUNT;i++){
			Enemy tempEnemy = new Enemy();
			tempEnemy.init(i*ENEMY_POS_OFF, i*ENEMY_POS_OFF - 300);
			mEnemy.add(tempEnemy);
		}
		
			mBullet.add(new Bullet());
		
		mSendTime = System.currentTimeMillis();
		
	}
	
	public void draw(){
		switch(mState){
		case STATE_GAME:
			renderBg();//更新主角飞机
			updateBg();//更新场景画布
			break;
		}
	}
	
	private void setGameState(int newState){
		mState = newState;
	}
	
	public void renderBg(){
		myPlaneID++;
		if(myPlaneID == myPlanePic.length){
			myPlaneID = 0;
		}
		repaint();
	}
	
	public void paint(Graphics g){
		g.drawImage(mBitMenuBG0, 0, mBitposY0, this);
		g.drawImage(mBitMenuBG1, 0, mBitposY1, this);
		g.drawImage(myPlanePic[myPlaneID], mAirPosX, mAirPosY, 30,30,this);
		for(int i=0;i<mBullet.size();i++){
			if(mBullet.get(i).mFacus==true)
				mBullet.get(i).DrawBullet(g, this);
			
		}
		for(int i=0;i<mEnemy.size();i++){
			mEnemy.get(i).DrawEnemy(g, this);
		}
	}
	
	public void updateBg(){
		mBitposY0 += 10;
		mBitposY1 += 10;
		if(mBitposY0 == mScreenHeight){
			mBitposY0 = -mScreenHeight;
		}
		if(mBitposY1 == mScreenHeight){
			mBitposY1 = -mScreenHeight;
		}
		
		//检测子弹是否超出屏幕以及更新子弹位置
		for(int i=0;i<mBullet.size();i++){
			if(mBullet.get(i).m_posY<=0)
				mBullet.get(i).mFacus = false;
			
			mBullet.get(i).UpdateBullet();
			
		}
		
		for(int i=0;i<mEnemy.size();i++){
			mEnemy.get(i).UpdateEnemy();
			if(mEnemy.get(i).mAnimState==Enemy.ENEMY_DEATH_STATE&&mEnemy.get(i).mPlayID==4||mEnemy.get(i).m_posY>=mScreenHeight){
				mEnemy.remove(i);								
			}	
		}
		
		long nowTime = System.currentTimeMillis();
		if(mEnemy.size()<5&&nowTime-createEnemyTime>=1000)//至少要间隔1秒才生成飞机
		{
			createEnemyTime = nowTime;
			Enemy tempEnemy = new Enemy();					
			tempEnemy.init(UtilRandom(0,mScreenWidth-30),0);
			mEnemy.add(tempEnemy);
		}
		
		
		if(mSendId<BULLET_POOL_COUNT){
			long now = System.currentTimeMillis();
			if(now-mSendTime>=PLAN_TIME){
				Bullet tempBullet = new Bullet();
				tempBullet.init(mAirPosX-BULLET_LEFT_OFFSET, mAirPosY-BULLET_UP_OFFSET);
				mBullet.add(tempBullet);
				mSendTime = now;
				mSendId++;
			}
		}else{
			mSendId= 0;
		}
		
		Collision();
	}
	
	public void Collision(){
		for(int i=0;i<mBullet.size();i++)
		{
			for(int j=0;j<mEnemy.size();j++)
			{
				if(mEnemy.get(j).mAnimState==Enemy.ENEMY_ALIVE_STATE&&mBullet.get(i).mFacus==true&&mBullet.get(i).m_posX>mEnemy.get(j).m_posX-30&&mBullet.get(i).m_posX<mEnemy.get(j).m_posX+30
					&&mBullet.get(i).m_posY>=mEnemy.get(j).m_posY&&mBullet.get(i).m_posY<=mEnemy.get(j).m_posY+30){
					mEnemy.get(j).mAnimState = Enemy.ENEMY_DEATH_STATE;
					mBullet.get(i).mFacus = false;//如果子弹撞上敌机,敌机状态修改为死亡,子弹状态修改为失效					
				}
			}
		}
	
	}
	
	//返回的敌机x坐标
	private int UtilRandom(int bottom,int top){
		
		return ((Math.abs(new Random().nextInt()) % (top - bottom)) + bottom);
	}
	
	
	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyPressed(KeyEvent e) {
		
		int key = e.getKeyCode();
		//System.out.println(key);
		if(key == KeyEvent.VK_UP){
			mAirPosY -= PLAN_STEP;
		}
		if(key == KeyEvent.VK_DOWN){
			mAirPosY += PLAN_STEP;
		}
		if(key == KeyEvent.VK_LEFT){
			mAirPosX -= PLAN_STEP;
			if(mAirPosX<0){
				mAirPosX = 0;
			}
		}
		
		if(key == KeyEvent.VK_RIGHT){
			mAirPosX += PLAN_STEP;
			if(mAirPosX > mScreenWidth-30){
				mAirPosX = mScreenWidth - 30;
			}
		}
		
//		System.out.println("飞机当前坐标是("+mAirPosX+","+mAirPosY+")");
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void run() {
		while(mIsRunning){
			draw();
			try{
				Thread.sleep(100);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}

}

游戏面板类分析:

①mThread线程用于定时生成子弹,如果当前生效的子弹数没有超过15个,每隔500ms生成子弹,子弹位于主角飞机上方一个子弹上偏移量的位置;

②image0和image1是背景图片,初始化背景图片2位于背景图片1上方,随着游戏的进行,背景图片逐渐下移,当背景图片1到屏幕外时,将背景图片1放到背景图片2上方;

③碰撞检测:循环子弹数组和敌机数组,如果当前子弹生效,当前敌机状态存活,子弹的x值>敌机的x值-子弹宽度&&子弹的x值<敌机的x值+敌机宽度&&子弹的y值>=敌机的y值&&子弹的y值<=敌机的y值+敌机高度,那么修改当前敌机的状态为死亡并播放爆炸动画,修改当前子弹状态为失效;

④如果敌机死亡并且爆炸动画播放完毕,或者敌机超出屏幕显示范围,删除敌机;

⑤如果子弹失效或者超过屏幕范围,删除子弹;

⑥敌机后台随机生成,位于屏幕上方,控制至少每隔1秒才能生成新的敌机,并且当前屏幕上的敌机数量最多不能超过五个;

 

最后还剩游戏窗口类和背景音乐类代码:

package 雷电飞机射击游戏;

import java.awt.Container;

import javax.swing.JFrame;

public class planeFrame extends JFrame{

	public planeFrame(){
		setTitle("飞行射击类游戏");
		GamePanel panel = new GamePanel();
		Container contentPane = getContentPane();
		contentPane.add(panel);
		pack();
	}

	
	public static void main(String[] args) {
		planeFrame e1 = new planeFrame();
		e1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		e1.setVisible(true);
	}

}
package 雷电飞机射击游戏;

import javax.sound.midi.*;

import java.io.File;
import java.io.IOException;

public class Sound {

	String path = new String("D:/Music/");
	String file = new String("nor.mid");
	Sequence seq;
	Sequencer midi;
	boolean sign;
	void loadSound(){
		try {
			seq = MidiSystem.getSequence(new File(path+file));
			midi = MidiSystem.getSequencer();
			midi.open();
			midi.setSequence(seq);
			midi.start();
			midi.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
			
		} catch (InvalidMidiDataException | IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MidiUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		sign = true;
	}
	
	
	public Sound(String file,String path){
		this.file = file;
		this.path = path;
	}
	
	public Sound() {
		// TODO Auto-generated constructor stub
	}


	void mystop(){midi.stop();midi.close();sign=false;}
	boolean isplay(){return sign;}
	void setMusic(String e){file=e;}
	
}

至此,游戏开发已经完毕,运行效果如下:

  • 83
    点赞
  • 284
    收藏
    觉得还不错? 一键收藏
  • 19
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值