1. 程式人生 > >Java遊戲開發——開心農場

Java遊戲開發——開心農場

遊戲介紹:

“開心農場”是一款以種植為主的社交遊戲。使用者可以扮演一個農場的農場主,在自己的農場裡開墾土地,種植各種水果蔬菜。本次開發了一個“開心農場”遊戲,執行程式,效果如下圖所示。滑鼠先選定指定土地(預設選擇第一塊土地),點選“播種”按鈕,可以播種種子;點選“生長”按鈕,可以讓作物處於生長階段;點選“開花”按鈕,可以讓作物處於開花階段;點選“結果”按鈕,可以讓作物結果;點選“收穫”按鈕,可以收穫果實到倉庫。預設生長期為1分鐘,開花期為2分鐘,落果期為3分鐘,支援作物離線生長。

使用素材資料夾:

素材以及完整原始碼連結:https://pan.baidu.com/s/1p1rXX1VlB21RnLfHEAStCg 提取碼: gszw

遊戲設計的思路:

使用一個帶背景的面板作為遊戲面板,圖片裡有九塊可種植的土地,每塊土地對應的Farm物件儲存當前土地狀態和最後一次操作時間,先判斷滑鼠點選的哪塊土地,再對選定的土地進行後續操作。面板使用“播種”、“生長”、“開花”、“結果”、“收穫”五個按鈕和9個用於表示九塊土地上的作物的Crop物件。5個按鈕的點選事件,會改變土地的狀態和最後操作時間以及Crop物件的圖片,通過改變Crop物件的圖片可以達到農作物各種狀態的轉變。使用文字儲存當前遊戲資料,使用執行緒每秒自動重新整理遊戲頁面並更新檔案中的遊戲資料,在遊戲初始化時先讀取歷史資料,再做狀態的換算,用以達到作物離線生長的作用。

設計步驟:

1.1設計農作物Crop類:

農作物Crop類實現作物狀態的顯示,通過繼承JLabel元件和設定JLabel元件的Icon實現。

package farmGame;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;

public class Crop extends JLabel{

	
	Icon icon = null;
	public Crop(){
		super();
	}

	public void setIcon(String picture){
		icon = new ImageIcon(picture);//獲取圖片
		setIcon(icon);
	}
	
}

1.2設計遊戲面板BackgroundPanel類:

BackgroundPanel類主要用來顯示遊戲背景圖。

package farmGame;

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

import javax.swing.JPanel;

public class BackgroundPanel extends JPanel{

	private Image image;
	
	public BackgroundPanel(){
		super();
		setOpaque(false);//當設定為false時,元件並未不會顯示其中的某些畫素,允許控制元件下面的畫素顯現出來。
		setLayout(null);
	}
	
	public void setImage(Image image){
		this.image = image;
	}
	
	protected void paintComponent(Graphics g){//重寫繪製元件外觀方法
		if(image!=null){
			int width = getWidth();
			int height = getHeight();
			g.drawImage(image, 0, 0, width, height, this);
		}
		super.paintComponent(g);
	}
		
}

1.3農場土地Farm類:

Farm類物件用來儲存一塊土地的狀態和最後一次操作時間(例如播種),包含該塊土地相關的播種、生長、開花、結果、收穫操作;state==0時未播種,state==1時已播種,state==2時生長中,state==3時開花中,state==4已結果;每次操作如果符合條件修改state值和最後一次操作時間,否則設定提示資訊為不能播種。

package farmGame;

import java.io.Serializable;
import java.util.Date;

public class Farm implements Serializable {
	private static final long serialVersionUID = 1L;
	public int state = 0;
	public Date lastTime = null;
	
	public int getState() {
		return state;
	}

	public void setState(int state) {
		this.state = state;
	}

	public Date getLastTime() {
		return lastTime;
	}

	public void setLastTime(Date lastTime) {
		this.lastTime = lastTime;
	}

	public String seed(Crop crop,String picture){
		String returnValue = "";
		if(state == 0){
			crop.setIcon(picture);
			state = 1;
			lastTime = new Date();
		}else{
			returnValue = getMessage()+",不能播種";
		}
		
		return returnValue;
	}
	
	public String grow(Crop crop,String picture){
		String returnValue = "";
		if(state == 1){
			crop.setIcon(picture);
			state = 2;
			lastTime = new Date();
		}else{
			returnValue = getMessage()+",不能生長";
		}
		
		return returnValue;
	}
	
	public String bloom(Crop crop,String picture){
		String returnValue = "";
		if(state == 2){
			crop.setIcon(picture);
			state = 3;
			lastTime = new Date();
		}else{
			returnValue = getMessage()+",不能開花";
		}
		
		return returnValue;	
	}
	

	public String fruit(Crop crop,String picture){
		String returnValue = "";
		if(state == 3){
			crop.setIcon(picture);
			state = 4;
			lastTime = new Date();
		}else{
			returnValue = getMessage()+",不能結果";
		}
		
		return returnValue;
	}
	
	public String harvest(Crop crop,String picture){
		String returnValue="";
		if(state==4){
			crop.setIcon(picture);
			state = 0;
			lastTime = null;
		}else{
			returnValue = getMessage()+",不能收穫!";
		}
		
		return returnValue;
	}
	
	
	public String getMessage() {
	
		String message = "";
		switch(state){
		case 0:
			message = "作物還沒有播種";
			break;
		case 1:
			message = "作物剛剛播種";
			break;
		case 2:
			message = "作物正在生長";
			break;
		case 3:
			message = "作物正處於開花期";
			break;
		case 4:
			message = "作物已經結果";
			break;
		}
		
		return message;
	}
	
}

1.4設計遊戲視窗MainFrame類:

編寫一個繼承自JFrame類的MainFrame窗體類,用於完成播種、生長、開花、結果、收穫等操作。

GROWTIME常量表示1分鐘的生長期毫秒數,BLOOMTIME和FRUITTIME表示2分鐘的開花期毫秒數和3分鐘的落果期毫秒數,fruitNumber記錄倉庫果實數量,farms對應9塊土地,crops對應9塊土地上的作物圖片,isInDiamond()方法用於判斷滑鼠點選的是哪塊土地,saveNowState()方法用於儲存當前遊戲資料,readNowState()方法用於讀取歷史遊戲資料並轉換成當前時間對應的遊戲資料,執行緒每秒執行一次儲存遊戲和讀取遊戲的操作。

package farmGame;

import java.awt.EventQueue;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingConstants;

public class MainFrame extends JFrame implements MouseListener,Runnable{

	int fruitNumber = 0;
	Farm[] farms = new Farm[9];
	Crop[] crops = new Crop[9];
	Farm farm = new Farm();
	Crop crop = new Crop();
	JLabel storage;	
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	FileInputStream fis = null;
	FileOutputStream fos = null;
	DataInputStream dis = null;
	DataOutputStream dos = null;

	//生長期1小時,開花期2小時,結果期3小時
	public static final long GROWTIME = 1000*60,BLOOMTIME = 1000*60*2,FRUITTIME = 1000*60*3;

	public MainFrame(){
		super();
		setTitle("打造自己的開心農場");
		setBounds(500,200,900,600);//設定視窗位置和大小
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		addMouseListener(this);
		final BackgroundPanel backgroundPanel = new BackgroundPanel();
		Image bk = Toolkit.getDefaultToolkit().getImage("D:/Game/FarmGame/farmBackground.png");
		backgroundPanel.setImage(bk);
		backgroundPanel.setBounds(0,0,855,553);//設定遊戲面板位置和大小
		getContentPane().add(backgroundPanel);
		storage = new JLabel();
		storage.setHorizontalAlignment(SwingConstants.CENTER);
		storage.setText("您的倉庫沒有任何果實,快快播種吧!");
		storage.setBounds(200,70,253,28);//標籤位置
		backgroundPanel.add(storage);

		initlize();
		for(Crop temp:crops){
			backgroundPanel.add(temp);
		}		

		final JButton button_1 = new JButton();//播種
		button_1.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/播種1.png"));//移動到圖示上顯示的圖片
		button_1.setBorderPainted(false);
		button_1.setContentAreaFilled(false);
		button_1.setIcon(new ImageIcon("D:/Game/FarmGame/播種.png"));//正常顯示圖片
		button_1.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {
				if(farm == null)//如果沒有先選中土地
					return ;

				String message = farm.seed(crop, "D:/Game/FarmGame/seed.png");
				if(!message.equals("")){
					JOptionPane.showMessageDialog(null, message);
				}
			}			
		});		
		button_1.setBounds(140,477,56,56);//29, 185, 56, 56
		backgroundPanel.add(button_1);


		final JButton button_2 = new JButton();//生長
		button_2.setContentAreaFilled(false);
		button_2.setBorderPainted(false);
		button_2.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/生長1.png"));
		button_2.setIcon(new ImageIcon("D:/Game/FarmGame/生長.png"));
		button_2.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if(farm == null)//如果沒有先選中土地
					return ;

				String message = farm.grow(crop, "D:/Game/FarmGame/grow.png");
				if(!message.equals("")){
					JOptionPane.showMessageDialog(null, message);
				}
			}
		});
		backgroundPanel.add(button_2);
		button_2.setBounds(280,477,56,56);//114,185,56,56


		final JButton button_3 = new JButton();
		button_3.setContentAreaFilled(false);
		button_3.setBorderPainted(false);
		button_3.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/開花1.png"));
		button_3.setIcon(new ImageIcon("D:/Game/FarmGame/開花.png"));
		button_3.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {

				if(farm == null)//如果沒有先選中土地
					return ;

				String message = farm.bloom(crop, "D:/Game/FarmGame/bloom.png");
				if(!message.equals("")){
					JOptionPane.showMessageDialog(null, message);
				}
			}
		});
		backgroundPanel.add(button_3);
		button_3.setBounds(420,477,56,56);//199,185,56,56;


		final JButton button_4 = new JButton();
		button_4.setContentAreaFilled(false);
		button_4.setBorderPainted(false);
		button_4.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/結果1.png"));
		button_4.setIcon(new ImageIcon("D:/Game/FarmGame/結果.png"));
		button_4.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if(farm == null)//如果沒有先選中土地
					return ;

				String message = farm.fruit(crop,"D:/Game/FarmGame/fruit.png");
				if(!message.equals("")){
					JOptionPane.showMessageDialog(null, message);
				}
			}
		});
		backgroundPanel.add(button_4);
		button_4.setBounds(560,477,56,56);


		final JButton button_5 = new JButton();
		button_5.setContentAreaFilled(false);
		button_5.setBorderPainted(false);
		button_5.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/收穫1.png"));
		button_5.setIcon(new ImageIcon("D:/Game/FarmGame/收穫.png"));
		button_5.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if(farm == null)//如果沒有先選中土地
					return ;

				String message = farm.harvest(crop, "");
				if(!message.equals("")){
					JOptionPane.showMessageDialog(null, message);
				}else{
					fruitNumber++;
					storage.setText("您的倉庫現在有"+fruitNumber+"個果實.");
				}
			}
		});
		backgroundPanel.add(button_5);
		button_5.setBounds(700,477,56,56);//369,185,56,56

		new Thread(this).start();

	}


	public void initlize(){

		for(int i=0;i<9;i++){
			farms[i] = new Farm();
			crops[i] = new Crop();
		}

		crops[0].setBounds(269, 153, 106, 96);
		crops[1].setBounds(165, 195, 106, 96);
		crops[2].setBounds(59, 238, 106, 96);
		crops[3].setBounds(378, 199, 106, 96);
		crops[4].setBounds(278, 236, 106, 96);
		crops[5].setBounds(169, 284, 106, 96);
		crops[6].setBounds(501, 247, 106, 96);
		crops[7].setBounds(393, 281, 106, 96);
		crops[8].setBounds(286, 333, 106, 96);


		farm = farms[0];//預設選中第一塊土地
		crop = crops[0];//預設第一株植物
		readNowState();//更新現在狀態
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		int x = e.getX();
		int y = e.getY();

		System.out.println("x is "+x+", y is "+y);

		if(isInDiamond(x-324,y-258,160,65)){//第一塊土地
			System.out.println("第一塊土地");
			farm = farms[0];
			crop = crops[0];
		}else if(isInDiamond(x-220,y-300,160,65)){
			System.out.println("第二塊土地");			
			farm = farms[1];
			crop = crops[1];
		}else if(isInDiamond(x-114,y-343,160,65)){
			System.out.println("第三塊土地");
			farm = farms[2];
			crop = crops[2];
		}else if(isInDiamond(x-433,y-304,160,65)){
			System.out.println("第四塊土地");
			farm = farms[3];
			crop = crops[3];
		}else if(isInDiamond(x-333,y-341,160,65)){
			System.out.println("第五塊土地");
			farm = farms[4];
			crop = crops[4];
		}else if(isInDiamond(x-224,y-389,160,65)){
			System.out.println("第六塊土地");
			farm = farms[5];
			crop = crops[5];
		}else if(isInDiamond(x-556,y-352,160,65)){
			System.out.println("第七塊土地");
			farm = farms[6];
			crop = crops[6];
		}else if(isInDiamond(x-448,y-386,160,65)){
			System.out.println("第八塊土地");
			farm = farms[7];
			crop = crops[7];
		}else if(isInDiamond(x-341,y-438,160,65)){//第九塊土地
			System.out.println("第九塊土地");
			farm = farms[8];
			crop = crops[8];
		}else{
			farm = null;
			crop = null;
			System.out.println("沒有選中任何土地");
		}

	}

	//width菱形寬,height菱形高
	public boolean isInDiamond(int x,int y,int width,int height){
		if(Math.abs(x*height)+Math.abs(y*width)<=width*height*0.5){
			return true;
		}

		return false;
	}

	public void readNowState(){

		File file = new File("D://GameRecordAboutSwing");

		if(!file.exists()){
			file.mkdirs();
		}

		File record = new File("D://GameRecordAboutSwing/recordFarmGame.txt");

		try{
			if(!record.exists()){//如果不存在,新建文字
				record.createNewFile();
				fos = new FileOutputStream(record);
				dos = new DataOutputStream(fos);
				String s = "0,null;0,null;0,null;0,null;0,null;0,null;0,null;0,null;0,null&0";
				dos.writeBytes(s);
				System.out.println(record.isFile());;
			}

			//讀取記錄
			fis = new FileInputStream(record);
			dis = new DataInputStream(fis);
			String str = dis.readLine();
			String[] array1 = str.split("&");//作物相關
			String[] array2 = array1[0].split(";");//土地相關
			fruitNumber = Integer.parseInt(array1[1]);//倉庫果實
			if(fruitNumber == 0){
				storage.setText("您的倉庫沒有任何果實,快快播種吧!");
			}else{
				storage.setText("您的倉庫現在有"+fruitNumber+"個果實.");
			}

			for(int i=0;i<9;i++){
				String[] help = array2[i].split(",");
				int state = Integer.parseInt(help[0]);
				if(help[1].equals("null")){//未播種,直接跳過
					continue;
				}
				Date lastTime = sdf.parse(help[1]);
				Date nowTime = new Date();
				long subTime = nowTime.getTime() - lastTime.getTime();//得到相差毫秒數
				long temp = 0;//儲存farm[i]最後操作的時間到當前時間的時間差(毫秒錶示)
				if(state == 1){//已播種
					if(subTime>=GROWTIME){
						subTime -= GROWTIME;
						temp += GROWTIME;
						state = 2;
					}
				}
				if(state == 2){//已生長
					if(subTime>=BLOOMTIME){
						subTime -= BLOOMTIME;
						temp += BLOOMTIME;
						state = 3;
					}
				}
				if(state == 3){//已開花
					if(subTime>=FRUITTIME){
						subTime -= FRUITTIME;
						temp += FRUITTIME;
						state = 4;
					}
				}

				switch(state){
				case 1:
					farms[i].state = 1;
					farms[i].lastTime = lastTime;
					crops[i].setIcon("D:/Game/FarmGame/seed.png");
					break;
				case 2:
					farms[i].state = 2;
					farms[i].lastTime = new Date(lastTime.getTime()+temp);
					crops[i].setIcon("D:/Game/FarmGame/grow.png");
					break;
				case 3:
					farms[i].state = 3;
					farms[i].lastTime = new Date(lastTime.getTime()+temp);
					crops[i].setIcon("D:/Game/FarmGame/bloom.png");
					break;
				case 4:
					farms[i].state = 4;
					farms[i].lastTime = new Date(lastTime.getTime()+temp);
					crops[i].setIcon("D:/Game/FarmGame/fruit.png");
					break;
				}

			}

		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				if(fis!=null)
					fis.close();
				if(dis!=null)
					dis.close();			
				if(fos!=null)
					fos.close();
				if(dos!=null)
					dos.close();				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}



	public void saveNowState(){

		StringBuilder sb = new StringBuilder();
		String[] strs = new String[9];

		for(int i=0;i<9;i++){
			strs[i] = farms[i].lastTime == null?"null":sdf.format(farms[i].lastTime);//土地最後一次操作時間
			if(i!=8)
				sb.append(farms[i].state+","+strs[i]+";");
			else
				sb.append(farms[i].state+","+strs[i]);				
		}

		sb.append("&"+fruitNumber);
//		System.out.println(sb);	

		File record = new File("D://GameRecordAboutSwing/recordFarmGame.txt");

		try {
			//清空原有記錄
			FileWriter fileWriter =new FileWriter(record);
			fileWriter.write("");
			fileWriter.flush();
			fileWriter.close();
			//重新寫入文字
			fos = new FileOutputStream(record);
			dos = new DataOutputStream(fos);
			String s = sb.toString();
			dos.writeBytes(s);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				if(fos!=null)
					fos.close();
				if(dos!=null)
					dos.close();				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}

	@Override
	public void run() {
		try {
			while(true){
				Thread.sleep(1000);
				saveNowState();
				readNowState();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}


	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable(){
			public void run(){
				try{
					MainFrame frame = new MainFrame();
					frame.setVisible(true);
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		});
	}



}

遊戲程式碼已經貼完了,雖說遊戲功能較弱,但是基本的作物定時生長,種植資訊的儲存和讀取,已經實現了。

後續可能會通過微信小程式的形式對該遊戲進行完整的製作(如果畢設老師同意該命題的話^_^),敬請期待(#^.^#)。