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();
}
}
});
}
}
遊戲程式碼已經貼完了,雖說遊戲功能較弱,但是基本的作物定時生長,種植資訊的儲存和讀取,已經實現了。
後續可能會通過微信小程式的形式對該遊戲進行完整的製作(如果畢設老師同意該命題的話^_^),敬請期待(#^.^#)。