1. 程式人生 > >使用swing實現飛行射擊遊戲(帶素材連結)

使用swing實現飛行射擊遊戲(帶素材連結)

使用到的素材資料夾:

素材說明: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;}
	
}

至此,遊戲開發已經完畢,執行效果如下: