1. 程式人生 > 其它 >課程設計:基於Java的超級瑪麗遊戲的設計與實現(素材+原始碼+論文+學習視訊)

課程設計:基於Java的超級瑪麗遊戲的設計與實現(素材+原始碼+論文+學習視訊)

技術標籤:遊戲java

目錄

專案展示:

在這裡插入圖片描述

請新增圖片描述
在這裡插入圖片描述

本軟體是針對超級瑪麗小遊戲的JAVA程式,進入遊戲後首先按空格鍵開始,利用方向鍵來控制的馬里奧的移動,同時檢測馬里奧與場景中的障礙物和敵人的碰撞,並判斷馬里奧的可移動性和馬里奧的生命值。當馬里奧通過最後一個場景後遊戲結束。

系統模組設計:

窗體類

該類主要用於存放遊戲的場景以及其他各類,並且實現KeyListener介面,用於從鍵盤的按鍵中讀取資訊。該類中的一些屬性主要包括了用於存放所有場景的list集合 allBG,馬里奧類 mario,當前的場景 nowBG以及其他一些遊戲中需要的標記等。而且在該類中,運用雙快取的技術使得遊戲的流暢度更高,解決了遊戲中出現的閃屏問題。

將該類的名字定義為MyFrame,並且要在該類中實現KeyListener介面和Runnable介面。然後首先要在該類中定義一個List集合,集合的泛型為背景類BackGround,集合的名字定義為allBG,用於存放所有的背景。接著定義一個Mario類屬性,名字為mario,這個就是遊戲執行時候的所需要的mario。接下來還要在類中定義一個BackGround屬性,nowBG,預設值應當為空,會在構造方法中賦予該屬性初值,這個屬性主要是用來存放當前遊戲執行時馬里奧所處的遊戲場景。另外該類中還應該有一個Thread類屬性t,這個屬性主要是為了在遊戲執行的時候控制遊戲的執行緒。然後就可以在類中定義main()方法,將該類實現就可以了。值得一提的是該類的構造方法相對來說是比較複雜的。
在該類的構造方法中,應當首先繪製窗體類的標題,以及窗體類的大小,並且要對窗體類在初始化的時候的位置,也就是在螢幕中顯示的位置,最好是顯示的時候居中,這樣的話在遊戲執行時會比較美觀一些。其次還要對窗體的一個是否可拉昇屬性進行一下設定,這個設定的主要目的是因為遊戲的介面都是開發者經過深思熟慮考慮出來的比較美觀的介面,玩家隨意改變遊戲的視窗大小可能會對遊戲的體驗造成影響,所以在這裡應該設定遊戲的窗體預設不可以被拉伸。

public MyFrame(){
		this.setTitle("瑪麗奧");
		this.setSize(900, 600);
           //這裡是為了獲得電腦螢幕的整體大小,以便於下面確定窗體的位置
int width = Toolkit.getDefaultToolkit().getScreenSize().width; int height = Toolkit.getDefaultToolkit().getScreenSize().height; this.setLocation((width-900)/2, (height-600)/2); //設定窗體預設不可以被拉伸 this.setResizable(false); //初始化圖片 StaticValue.init();

當這些都設定好以後,接下來就應當在構造方法中繪製了,當然最先應當將遊戲的場景繪製到窗體類中,然後在窗體類中還應當繪製馬里奧類,這是遊戲中必不可少的。當然在繪製場景類的時候因為不知一個場景,所以可以使用迴圈,將所有的場景全部繪製。然後在將所需要的所有監視設定好以後就可以開啟該類的執行緒了。

//使用迴圈建立全部場景
		for(int i=1;i<=7;i++){
			this.allBG.add(new BackGround(i, i==7?true:false));
		}
		//將第一個場景設定為當前場景
		this.nowBG = this.allBG.get(0);
		//初始化瑪麗奧
		this.mario = new Mario(0, 480);
		//將瑪麗奧放入場景中
		this.mario.setBg(nowBG);
		this.repaint();
		this.addKeyListener(this);
		this.t = new Thread(this);
		t.start();
      //使視窗在關閉的時候,程式也同時停止。
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}

在這些最基本的東西設定完以後,還需要一個方法來解決遊戲中經常會出現的閃屏問題。這個方法就是雙快取方法,現在類中定義一個BufferedImage的圖片,然後從該圖片中獲取到圖片的Graphics g2,然後利用畫筆g2將所要繪製的東西繪製到這個空的圖片中,然後在利用窗體類中的paint方法中的畫筆g將這個已經繪製好的圖片繪製到窗體類中,這樣利用空白圖片作為程式執行中的中轉,就可以很好的解決遊戲執行過程中出現的閃屏問題。

public void paint(Graphics g) {
		//先定義一個圖片,然後利用雙快取解決閃屏問題
		BufferedImage image = new BufferedImage(900, 600, BufferedImage.TYPE_3BYTE_BGR);
		Graphics g2 = image.getGraphics();
      //利用上面圖片中得到的畫筆g2,將所需繪製到圖片中
		if(this.isStart){
			//繪製背景
			g2.drawImage(this.nowBG.getBgImage(), 0, 0, this);
			//繪製生命
			g2.drawString("生命:    "+this.mario.getLife(), 800, 50);
			//繪製怪物敵人
			Iterator<Enemy> iterEnemy = this.nowBG.getAllEnemy().iterator();
			while(iterEnemy.hasNext()){
				Enemy e = iterEnemy.next();
				g2.drawImage(e.getShowImage(), e.getX(), e.getY(), this);
			}
		//把快取圖片繪製進去
		g.drawImage(image, 0, 0, this);
	}

當然遊戲的宗旨是讓玩家和電腦之間的互動,那麼就又涉及到一個問題,就是玩家對遊戲中的馬里奧的控制。我們前面已經說過了該類中必須要實現KeyListener介面,這個介面的作用就是使該類中實現一些方法,以便於達到玩家在遊戲進行時可以對遊戲中的馬里奧進行控制。我們這裡擬定對於馬里奧的控制可以使用我們常見的四個方向鍵,即我們說的上下左右。並且通過控制檯列印,可以知道上對應的是38,右對應的是39,左對應的是37。並且遊戲的設定是開始後遊戲不會直接執行,而是要使用空格鍵以後遊戲才會真正開始,所以還要加入當按空格鍵的時候遊戲正式開始,空格鍵對應的是32。

public void keyPressed(KeyEvent e) {
		if(this.isStart){
			//瑪麗奧的移動控制
			if(e.getKeyCode()==39){
				this.mario.rightMove();
			}
			if(e.getKeyCode()==37){
				this.mario.leftMove();
			}
			//跳躍控制
			if(e.getKeyCode()==38){
				this.mario.jump();
			}
		}else if(e.getKeyCode()==32){
			this.isStart = true;
		}
	}

對於按鍵,那麼相對應的就是當抬起建的時候。因為你向右移動的時候,如果這時候突然停止,那麼很可能馬里奧會保持一個運動的狀態停下來,那麼就必須在瑪麗奧停止的時候給他一個指令,讓他的移動圖片變為靜止。相對於運動的時候是類似的,這裡不做累述。

public void keyReleased(KeyEvent e) {
		if(this.isStart){
			//控制瑪麗奧的停止
			if(e.getKeyCode()==39){
				this.mario.rightStop();;
			}
			if(e.getKeyCode()==37){
				this.mario.leftStop();;
			}
		}

當這一切都做好以後,那麼最後就應該在類中重寫一下run方法了,在這個方法中應當提一下游戲的通關和死亡後的狀態。即遊戲通關,或者馬里奧死亡時應當彈出一個視窗,說明遊戲通關或者馬里奧死亡,並且點選了這個視窗以後,遊戲應當結束,而且整個遊戲也應當關閉。

if(this.mario.isDead()){
		JOptionPane.showMessageDialog(this, "遊戲結束");
		System.exit(0);
	}
	if(this.mario.isClear()){
		JOptionPane.showMessageDialog(this, "恭喜遊戲通關!");
		System.exit(0);
		}

初始化類

用於存放遊戲所需要的所有靜態檔案,在遊戲開始的時候將所有檔案匯入,提高遊戲的執行速度。並且在該類中將所有需要用到的圖片進行分類,分為障礙物類,馬里奧類,敵人類以及背景圖片。當遊戲執行時可以直接呼叫這些集合中的圖片進行遍歷,在呼叫的時候更加方便,而且可以使馬里奧或者敵人在移動的時候產生動態效果。
首先在類中應當定義一個靜態的List,泛型為BufferedImage,屬性名字為allMarioImage,這個屬性的作用在於存放所有的馬里奧圖片,裡面包括了馬里奧的移動圖片,站立圖片以及馬里奧跳躍的圖片。這樣在程式執行的時候就可以從該類中的這個屬性裡面將所需要的馬里奧圖片直接調用出來,並且還可以在馬里奧移動時不斷遍歷裡面的圖片,這樣就可以使馬里奧產生移動的動態效果。接下來要在該類中定義開始圖片,結束圖片以及背景圖片,預設的初始值都為null。注意這些所有的屬性都是靜態的,包括下面要提到的所有的屬性,這樣做的目的是為了在程式執行時先載入這些圖片。然後應當定義存放食人花的List集合allFlowerImage,這個集合將食人花的不同形態,張嘴、閉嘴圖片存放進去,這樣在執行的時候進行遍歷就可以打到動態效果。同理存放蘑菇怪的集合allTrangleImage,以及存放所有障礙物的集合allObstructionImage。

public class StaticValue {
	public static List<BufferedImage> allMarioImage = new ArrayList<BufferedImage>();
	public static BufferedImage startImage = null;
	public static BufferedImage endImage = null;
	public static BufferedImage bgImage = null;
	public static List<BufferedImage> allFlowerImage = new ArrayList<BufferedImage>();
	public static List<BufferedImage> allTriangleImage = new ArrayList<BufferedImage>();
	public static List<BufferedImage> allObstructionImage = new ArrayList<BufferedImage>();
定義完這些屬性之後,剩下的就是初始化了,在該類中定義一個init()方法,這個方法在執行的時候會將所需的所有圖片放入到之前定義好的各個集合中。因為圖片存放的路徑都是一樣的,所以為了減少程式碼量會定義一個公共路徑ImagePath。然後就可以利用迴圈,將存放的圖片全部匯入進去。
   //介紹程式碼量,定義公共路徑
	public static String ImagePath = System.getProperty("user.dir")+"/bin/";
	//定義方法init(),將圖片初始化
	public static void init(){
		//利用迴圈將瑪麗奧圖片初始化
		for(int i=1;i<=10;i++){
			try {
				allMarioImage.add(ImageIO.read(new File(ImagePath+i+".png")));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//匯入背景圖片
		try {
			startImage = ImageIO.read(new File(ImagePath+"start.jpg"));
			bgImage = ImageIO.read(new File(ImagePath+"firststage.jpg"));
			endImage = ImageIO.read(new File(ImagePath+"firststageend.jpg"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//匯入瑪麗奧死亡圖片
		try {
			mariDeadImage = ImageIO.read(new File(ImagePath+"over.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

背景類

該類表示馬里奧及障礙物和敵人所處的場景,並且將障礙物和敵人繪製到場景中。在該類中包括用於存放敵人和障礙物的list集合,以及當敵人或者障礙物被消滅後用於存放已經消失的敵人和障礙物的集合,這樣做是為了在馬里奧死亡時重置場景所用的。其次在該類中還使用了控制敵人移動的方法,是為了在程式之初控制敵人靜止,然後在玩家點選空格以後在使得敵人開始移動。並且在第六個關卡處設定了一個隱形通關要點,只有當馬里奧頂到這個隱形磚塊時才會出現,馬里奧就可以藉助這個磚塊通過關卡。
首先背景類中肯定要有一個標記來表示現在是第幾個場景,因為不同的背景中所繪製的場景,障礙物等也不同,所以該類中要有一個int型別的場景順序sort。並且在遊戲的設定中,如果玩家玩到最有一關的時候馬里奧會失去玩家的控制,自己走向城堡。那麼這裡就要這幾一個標記,是否為最後的場景,型別為boolean型別。如果馬里奧失去所有生命值,或者遊戲通關的話,那麼遊戲就會結束,這裡還應當加一個boolean的標記isOver判斷遊戲是否結束。

public class BackGround {
	//當前場景圖片
	private BufferedImage bgImage = null;
	//場景順序
	private int sort;
	//是否為最後的場景
	private boolean flag;
	//遊戲結束標記
	private boolean isOver = false;

在最後一個關卡中,馬里奧到達旗杆的位置後就會失去控制,同時旗子將會開始下降,只有等旗子下降完畢後,馬里奧才能開始移動,所以這裡還要定義一個旗子是否下降完畢的boolean型別的屬性isDown,用於判斷馬里奧什麼時候移動。

//定義降旗結束
	private boolean isDown = false;

當馬里奧失去生命的時候,但是並沒有失去所有的生命,那麼這個時候應當重置這個場景,將所有消滅掉的障礙物和敵人全部還原。因此除了在該類中除了要定義存放敵人和障礙物的List集合以外,還應當有存放被消滅的敵人或者障礙物的List,當敵人或者障礙物被消滅的時候先放入到這個List中,這樣在充值的時候就可以直接將這個集合中的資料在還原到原先的集合裡面。

//用集合儲存敵人
	private List<Enemy> allEnemy = new ArrayList<Enemy>();
	//用集合儲存障礙物
	private List<Obstruction> allObstruction = new ArrayList<Obstruction>();
	//被消滅的敵人
	private List<Enemy> removeEnemy = new ArrayList<Enemy>();
	//被消滅的障礙物
	private List<Obstruction> removeObstruction = new ArrayList<Obstruction>();

在遊戲的設定中,應當是遊戲開始的時候,所有的敵人其實是靜止的,而且玩家也不能控制馬里奧,必須要等到玩家按空格鍵開始以後遊戲才會進行,那麼這裡就應當在定義一個方法,即當玩家空格鍵的時候會呼叫這個方法,同時遊戲中的敵人開始移動,遊戲正式開始。這個方法也就是相當於控制敵人開始移動的方法,所以命名為enemyStartMove()方法。

//敵人開始移動
	public void enemyStartMove(){
       //遍歷當前場景中的敵人,使之開始移動
		for(int i=0;i<this.allEnemy.size();i++){
			this.allEnemy.get(i).startMove();
		}
	}

接下來就應當定義背景類的構造方法了,通過獲取場景的順序,即場景的sort,來判斷是哪一個場景,同時將場景繪製好。

//構造方法
	public BackGround(int sort,boolean flag){
		//第一個場景
		if(sort==1){
			for(int i=0;i<15;i++){
				this.allObstruction.add(new Obstruction(i*60, 540, 9,this));
			}
			//繪製磚塊和問號
			this.allObstruction.add(new Obstruction(120, 360, 4,this));
			this.allObstruction.add(new Obstruction(300, 360, 0,this));
			......
	}

前面提到,如果馬里奧死亡,但是卻沒有失去所有的生命值,那麼遊戲應當重置,當前場景中的所有敵人和障礙物,也包括馬里奧都應當回到初始位置。為了達到這個效果,那麼我們的場景類中就必須要定義一個reset()方法,來呼叫障礙物和場景還有馬里奧的各自的重置方法,來使當前的場景還原。並且在這之前我們還要將消滅掉的敵人和障礙物從消滅掉的存放的List中提出來,放回到原來的List中。然後遍歷障礙物和敵人的List,使用迴圈呼叫他們的重置方法。

//重置方法,重置障礙物和敵人
	public void reset(){
		//將移除的障礙物和敵人還原
		this.allEnemy.addAll(this.removeEnemy);
		this.allObstruction.addAll(this.removeObstruction);
		//呼叫障礙物和敵人的重置方法
		for(int i=0;i<this.allEnemy.size();i++){
			this.allEnemy.get(i).reset();
		}
		for(int i=0;i<this.allObstruction.size();i++){
			this.allObstruction.get(i).reset();
		}
	}	

馬里奧類

用來控制馬里奧的行動,並且在該類中加入碰撞檢測,判斷馬里奧是否與障礙物或者敵人發生碰撞。該類中的屬性主要定義了馬里奧所在的場景,馬里奧的移動和跳躍的速度,以及馬里奧在移動過程中需要顯示的圖片。另外該類中還定義了玩家的生命值和所獲得的分數。並且在run()方法中還定義了當馬里奧到達最後一關的旗子時,玩家將失去對馬里奧的控制,剩下的由程式控制走到城堡,完整全部遊戲。
在遊戲中,瑪麗奧要在玩家的控制下完成移動、跳躍等動作,那麼這些動作首先肯定要涉及到座標,那麼我們在該類中首先要定義兩個屬性,這兩個屬性即為馬里奧的座標x和y。並且該類還要實現Runnable介面,在run()方法中寫馬里奧的移動規則。

public class Mario implements Runnable{
	//座標
	private int x;
	private int y;
	//定義瑪麗奧所在場景
	private BackGround bg;
	//加入執行緒
	private Thread t = null;

為了玩家在遊戲過程中的良好體驗,那麼對於馬里奧的移動速度和跳躍速度就必須要定義好。所以該類裡面還應當定義馬里奧的移動速度和跳躍速度,其本質就是馬里奧在移動過程中座標加減的值。當然初始值為零,必須等到馬里奧構造的時候,再將這些屬性賦予相對應的值。在本類中還要定義遊戲的分數以及馬里奧的生命數,這些都是必不可少的。

//移動速度
	private int xmove = 0;
	//跳躍速度
	private int ymove = 0;
	//狀態
	private String status;
	//顯示圖片
	private BufferedImage showImage;
	//生命和分數
	private int score;
	private int life;

在馬里奧這個類中,還要定義馬里奧的移動和跳躍方法,以便玩家在按下方向鍵後呼叫這些方法,來達到控制馬里奧的移動。下面是馬里奧向左移動的方法,其他方法同理。

	public void leftMove(){
		//移動速度
		xmove = -5;
		//改變狀態
		//如果當前已經是跳躍,應該保持原有狀態,不能再改變
		if(this.status.indexOf("jumping") != -1){
			this.status = "left-jumping";
		}else{
			this.status = "left-moving";
		}
	}
 ......

在定義馬里奧的跳躍方法的時候,不單單定義一個方法就行,而且還要判斷馬里奧的狀態。如果馬里奧是在地面或者是在障礙物的上方,那麼馬里奧可以進行跳躍,如果馬里奧處於空中,那麼馬里奧就不可以繼續跳躍。

public void jump(){
//判斷馬里奧是否可以進行跳躍
		if(this.status.indexOf("jumping") == -1){
			if(this.status.indexOf("left") != -1){
				this.status = "left-jumping";
			}else{
				this.status = "right-jumping";
			}
			ymove = -5;
			upTime = 36;
		}
	}

接下來就要寫馬里奧中的run()方法了,這個方法中的內容相對來說比較麻煩,因為要在這個方法中對馬里奧和障礙物或者敵人之間進行邏輯判斷,即所謂的碰撞檢測。首先在這個類中對馬里奧是否處於最後一個場景進行判斷,如果馬里奧處於最後一個場景,並且座標大於520,那麼說明馬里奧已經撞到的旗杆,這個時候馬里奧將不會由玩家控制。並且同時呼叫旗子的移動方法,使旗子進行下落,當旗子下落完畢後給馬里奧一個標記,馬里奧開始移動到城堡。當馬里奧的座標大於780,即馬里奧到達城堡的門口的時候,這個時候遊戲結束。

public void run() {
		while(true){
			//判斷是否與障礙物碰撞
			//定義標記
			if(this.bg.isFlag() && this.x >= 520){
				this.bg.setOver(true);
				if(this.bg.isDown()){
					//降旗後瑪麗奧開始移
					this.status = "right-moving";
					if(this.x < 580){
						//向右
						this.x += 5;
					}
						if(this.x >= 780){
							//遊戲結束
							this.setClear(true);
						}

然後對當前馬里奧所處的場景中的所有障礙物進行遍歷,獲取到所有障礙物的座標,通過障礙物的座標和馬里奧的座標的之間的關係的判斷,來決定馬里奧是否與障礙物發生了碰撞,並且通過判斷的結果來對馬里奧和障礙物的狀態進行相應的變化。

for(int i=0;i<this.bg.getAllObstruction().size();i++){
		Obstruction ob = this.bg.getAllObstruction().get(i);
		//不能向右移動
	if(ob.getX()==this.x+60&&(ob.getY()+50>this.y&&ob.getY()-50<this.y)){
			if(ob.getType() != 3){
				canRight = false;
			}
		}
		......

當馬里奧撞到障礙物的時候,那麼就要根據障礙物的型別進行接下來的判斷,如果是磚塊或者是問號的話,那麼障礙物消失,馬里奧被彈回,即馬里奧的狀態由上升狀態變為下落狀態,並且將消失掉的障礙物放入相對應的消失的List集合當中。如果障礙物的型別為其他,比如說是石頭的話,那麼障礙物不變,馬里奧直接被彈回。

//判斷瑪麗奧跳躍時是否撞到障礙物
	if(ob.getY()==this.y-60&&(ob.getX()+50>this.x && ob.getX()-50<this.x)){
		//如果是磚塊
		if(ob.getType()==0){
			//移除磚塊
			this.bg.getAllObstruction().remove(ob);
			//儲存到移除的障礙物中
			this.bg.getRemoveObstruction().add(ob);
		}

為了遊戲的可玩性,將會在遊戲中加入一個隱藏的陷阱,或者是隱藏的通關點。這個隱藏的障礙物在遊戲進行的時候不會顯示出來,當然馬里奧從他的左右兩邊過去的時候也不會觸發這個隱藏的障礙物,必須是從下方撞到這個障礙物時才會顯示出來。同時馬里奧由上升狀態變為下落狀態。而且他和磚塊障礙物相同,被頂到後會變為石頭,改變型別。

//如果是問號||隱藏的磚塊
		if((ob.getType()==4 || ob.getType()==3) && upTime > 0){
			ob.setType(2);
			ob.setImage();
		}
      //馬里奧開始下落
		upTime = 0;

在遊戲中敵人大致可以分為兩類。一類是蘑菇怪,這種敵人是可以被殺死的,當馬里奧從蘑菇怪的正上方踩到蘑菇怪時,那麼蘑菇怪就會被消滅,同時馬里奧向上跳起一小段距離。而消失掉的蘑菇怪就會被放到消失掉的敵人的List集合中,等到重置的時候在調用出來。但是如果馬里奧從蘑菇怪的左右兩邊碰到蘑菇怪的話就會失去一條生命,並且重置遊戲。第二類是食人花,這種敵人不會被馬里奧消滅掉,不論馬里奧從哪個方向去碰撞食人花,食人花都不會消失,而且如果馬里奧碰到了食人花,自身還會失去一條生命,並且遊戲重置,當然前提是馬里奧沒有失去所有的生命值,否則的話遊戲就結束。
首先馬里奧對於所有的敵人,如果從左右兩邊碰撞到敵人,那麼馬里奧死亡,失去一條生命,遊戲重置。

```java
//對敵人的判斷
	for(int i=0;i<this.bg.getAllEnemy().size();i++){
		Enemy e = this.bg.getAllEnemy().get(i);
      //對於所有的敵人都適用
		if((e.getX()+50>this.x && e.getX()-50<this.x) && (e.getY()+60>this.y && e.getY()-60<this.y)){
			//瑪麗奧死亡
			this.dead();
		}

      //這裡開始區分敵人的類別,對於不同的敵人做出不同的反應
		if(e.getY()==this.y+60 && (e.getX()+60>this.x && e.getX()-60<this.x)){
			if(e.getType() == 1){
				e.dead();
				this.upTime = 10;
				this.ymove = -5;
			}else if(e.getType() == 2){
				this.dead();
			}
		}					

障礙物類

繪製場景中所需要的障礙物,例如地面、磚塊、水管等等。該類中的屬性包括了障礙物的座標,障礙物所需要顯示的圖片等。並且在該類中也定義了障礙物類的重置方法,當馬里奧死亡時,場景類會呼叫該方法。
遊戲中的場景是由背景中的障礙物繪製而成的,不同的障礙物所在的位置肯定也不相同,那麼對於障礙物而言,就必須要有座標屬性來使繪製的時候將不同的障礙物繪製到不同的位置,所以必須要有兩個int屬性x和y來表示障礙物的座標。同時該類也必須要實現Runnable介面,實現這個介面的作用主要是為了在最有一個場景中控制旗子的運動,當然同時還要為該類加入執行緒。

public class Obstruction implements Runnable{
	//座標
	private int x;
	private int y;
	//控制旗子
	private Thread t = new Thread(this);

前面說過,當馬里奧頂到問好或者是隱藏的磚塊時,那麼這個障礙物的型別就會改變,變為石頭。那麼在障礙物這個類裡面就必須要定義一個屬性stype,這個屬性用於表示當前障礙物的型別,以便於變化形態的時候呼叫。這個型別的值就可以用初始化類中的相對應的List集合裡面的下標表示。既然有改變,就要有恢復,所以還要定義一個不變的type,命名為starttype,這個屬性是為了當遊戲重置的時候,障礙物可以通過呼叫這個屬性恢復到最初始的狀態。而且不同的狀態對應不同的顯示圖片,所以還要有showImage屬性。

//型別
	private int type;
	//初始型別
	private int starttype;
	//顯示圖片
	private BufferedImage showImage = null;
	//取得場景
	private BackGround bg;

在該類中還要寫入reset()方法,這個方法是為了當馬里奧死的時候呼叫重置方法,對已經被消滅掉的障礙物進行重置。因為有的障礙物被頂掉以後會給變型別和圖片,所有還要定義一個setImage()方法,用來改變障礙物的顯示圖片。

//重置方法
public void reset(){
	this.type = starttype;
	this.setImage();
}
//根據狀態改變顯示圖片
public void setImage(){
	showImage = StaticValue.allObstructionImage.get(type);
}

最後該類中的run方法主要是為了控制最後一個場景中的旗子的移動,並且在旗子移動完畢後要設定一個標記,並且將該標記表示給馬里奧類,這樣馬里奧就可以開始自主移動了。

if(this.bg.isOver()){
				if(this.y < 420){
					this.y += 5;
				}else{
                  //設計標記為true,即表示馬里奧可以開始移動了
					this.bg.setDown(true);
				}

敵人類

該類中主要設定了兩種敵人,一種是蘑菇怪,可以被馬里奧踩死,另一種是食人花,不能被踩死。該類中的屬性包括了敵人的座標,敵人的初始座標,需要顯示的圖片,以及敵人的移動方向和移動範圍等。敵人的初始座標主要是為了當敵人執行重置方法後將敵人的位置還原。
在該類中首先要實現Runnable介面,因為在遊戲中的敵人是可以移動的,所以一定要通過重寫run()方法來達到敵人可以移動的效果。當然還要在該類中定義一個Thread屬性,用於控制執行緒。然後說說到移動,必然少不了座標問題,那麼在該類中就要定義兩個int屬性x和y,用於控制敵人的位置以及敵人的移動。

public class Enemy implements Runnable{
	//座標
	private int x;
	private int y;
   //加入執行緒
	private Thread t = null;

當馬里奧失去一條生命值的時候,遊戲會被重置,敵人回回到初始的位置,所以還要定義另外兩個int屬性startx和starty,用來當遊戲進行重置的時候,可以根據這個初始座標回覆敵人的位置。

//初始座標
	private int startx;
	private int starty;

對於不同的敵人,所顯示的圖片肯定是不同的,所以要定義一個現實的圖片屬性showImage,並且在馬里奧中,馬里奧要通過判斷敵人的型別,來決定是馬里奧死亡,還是敵人死亡,對於不同的敵人有不同的反應,所以還要在該類中定義一個type屬性,用來表示敵人的型別。

//怪物型別
private int type;
//顯示圖片
private BufferedImage showImage;

對於敵人裡面的食人花而言,他是在水管中直上直下的,所以他的上下移動應當有一個界限,不論是向上移動還是向下移動,都不能超過這個界限,否則的話食人花就會從水管中飛出來或者是移動到MyFrame外面了。

//移動範圍
	private int upMax = 0;
	private int downMax = 0;

在這個類中應當有兩個構造方法,對於不同的敵人,所需要的屬性都是不同的。並且在兩個類中都有一個共同的程式碼,那就是要在開啟執行緒後應當先將執行緒掛起。這是為了配合遊戲在開始的時候敵人不移動,必須要等到玩家按空格鍵的時候才會開始,所以先將執行緒掛起來,當點選空格鍵以後在將執行緒開啟。

//蘑菇怪的構造方法
	public Enemy(int x,int y,boolean isLeft,int type,BackGround bg){
		... ...
		this.t = new Thread(this);
		t.start();
		t.suspend();
	}

接下來是寫敵人類中的run()方法了,該方法主要是為了控制蘑菇怪以及食人花敵人的移動的。因為不同的敵人在不同的場景中有不同的移動方法,所以對於敵人的移動而且,首先要判斷敵人的型別和敵人所處的場景。

public void run() {
	while(true){
		//判斷怪物型別
		if(type==1){
			if(this.isLeftOrUp){
				this.x -= 5;
			}else{
				this.x += 5;
			}
	    

在遊戲中,當馬里奧死亡的時候會對整個場景中的障礙物進行重置,當然敵人也不例外。當馬里奧死亡的時候,不僅要將所有被消滅的敵人全部顯示出來,即從消滅的List中還原到原來的敵人List中,並且敵人的狀態和座標也要進行重置。要將敵人的座標還原到最開始的座標,而且把圖片進行還原。並且在重置方法中也要對敵人的型別進行判斷,使得敵人的型別和他的顯示圖片相對應。

public void reset(){
	//還原座標
	this.x = this.startx;
	this.y = this.starty;
	//還原圖片
	if(this.type == 1){
		this.showImage = StaticValue.allTriangleImage.get(0);
	}else if(this.type == 2){
		this.showImage = StaticValue.allFlowerImage.get(0);
	}
}

最後在該類中定義一個死亡方法,主要是針對蘑菇怪被消滅的時候所呼叫的方法。在這個方法中要定義蘑菇怪死亡的時候的顯示圖片,也就是蘑菇怪被踩扁的圖片。並且要將這個敵人從相對應的場景的敵人集合中除去,放入別消滅的敵人的List集合。

public void dead(){
		//死亡圖片
		this.showImage = StaticValue.allTriangleImage.get(2);
		//從原來的List集合中刪除,讓入被消滅的List集合中
		this.bg.getAllEnemy().remove(this);
		this.bg.getRemoveEnemy().add(this);
	}	
}

素材+原始碼+論文+學習視訊)下載

連結:打包下載點這裡
提取碼:vypf