程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java版SLG游戲開發--數據的讀取及保存

Java版SLG游戲開發--數據的讀取及保存

編輯:關於JAVA

說到SLG游戲開發,無論其如何運轉,裡面都離不開各種數據的處理,一般來說,游戲越專業,需要處理的數據量將相對越大,類別也分得越細。

游戲離不開美工,也離不開策劃,各項參數的專業劃分同樣是評價一款SLG游戲是否優秀的必要指標之一。所謂的好游戲僅僅畫面出彩,配樂一流是絕對不夠的,做“靓”很容易,做“專”則很難。

比如日本的超級機器人大戰系列,自90年代初開始出現以來,截止到今天為止其中涉及的動漫超過60部,出場知名人物多達600名以上,幾乎涵蓋了日本所有知名機器人動畫的機體(當然也有遺憾,比如機動警察)。這些角色無分善惡是各有各的fans,各有各的崇拜者。請這些“天王”出場,如果還做不好,過強或者過弱某一方,或者該出現的“華麗”技能沒有出現,日本這些“ACG宅”的憤怒可是很恐怖的,正所謂“秋葉原前一聲吼,東京也要抖三抖”。

對動漫人物的把握程度,對角色參數的設定,起了尤為重要的作用。

在這裡鄙人不由得想起某位大神,就是SRC (Simulation RPG Construction)的作者鬼子Kei。這家伙從90年代末就用VB5.0開始制作SRC這個機戰的同人制作工具,而我這輩子讀的第一段程序代碼,也正是某雜志隨盤附錄的SRC0.6版及其源碼,當時我連VB是什麼都不知道,徹底的讀天書,於是才買書鑽研VB……一晃10年,VB6.0我都已放下很久,他居然還在更新SRC,而且還是使用VB5開發,我不由驚歎鬼子的勤奮還有專注。10年工夫,我是無論如何也不信Kei不會用更簡便的工具來制作SRC的,但是他卻沒有,硬是把VB5這個現在很多人用都沒用過的古董級工具(實際上我也沒用過|||)做出一款亞洲知名的機戰同人開發工具來,10年來此人網站流量累計超過1690萬,而且我也真的見過很多同人愛好者的SLG游戲是采用SRC開發。日本人真是恐怖,居然有人能甘心鑽研VB5如此之久,如果把這種勁頭用在工作上,想想我都不寒而栗,有這樣的恆心這樣棄而不捨的精神,在亞洲中國最大的潛在對手始終非日本莫屬……咳咳,扯遠了。

SRC運行畫面,運行需要VB5運行庫,並日文Windows環境。(或者先用AppLocale轉內碼,再轉日文腳本亂碼後載入)

通常來講,我們不太可能將各種游戲數據硬編碼到程序中,這樣既不利於測試,也不方便重用,總需要一個外部文件作為存儲介質。這時的選擇其實很多,在Java游戲開發中我們即可以使用xml這類現有的規范格式,也可以干脆如SRC般自己定義腳本,或者將少量數據利用properties存儲。

就我個人認為,自己訂制游戲腳本格式從長遠看是最可取的,以後的同類項目方便重用,也不容易被他人盜取數據。而xml雖然有很多現成的組件可用,但是處理復雜業務時就沒有自己的腳本用著方便,而且當數據很少時也有些殺雞用牛刀的感覺。至於properties,存取單鍵值的數據固然很方便,但是對於表格類的數據,即使很簡單也不適用,至少不直觀了。

在本例中我所采用的,是一種更為偷懶的方式,並不歸屬於以上所說,而是另辟蹊徑的利用csv格式,實現了一種較為另類的表格式數據存儲。

CSV(Comma Separated value),也叫逗號分隔值文件,是一種用來存儲數據的純文本文件格式,通常用於電子表格或數據庫軟件。我們打開windows記事本,隨便打幾個字母用“,”分割,再用excel查看,這時excel就會自動以表格方式顯示這些數據。

同樣對於Java中的表格數據存儲,也可以采用了這種方式保存,並且利用reflect機制映射到類,看上去即直觀,也比xml省心,比自己寫腳本省力。

核心代碼如下:

package org.loon.simple.slg.utils;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
/**
  * Copyright 2008
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
  *
  * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  * License for the specific language governing permissions and limitations under
  * the License.
  *
  * @project loonframework
  * @author chenpeng
  * @email:[email protected]
  * @version 0.1
  */
public class CSVConfigure {
     /**
      * 載入CSV數據後轉化為指定類的Object[]
      *
      * @param fileName
      * @param clazz
      * @return
      */
     final static public Object[] loadPropertys(final String fileName,
             final Class clazz) {
         Object[] results = null;
         try {
             List properts = CSVConfigure.loadPropertys(fileName);
             int size = properts.size();
             results = (Object[]) Array.newInstance(clazz, size);
             for (int i = 0; i < size; i++) {
                 Map property = (Map) properts.get(i);
                 Set set = property.entrySet();
                 results[i] = clazz.newInstance();
                 for (Iterator it = set.iterator(); it.hasNext();) {
                     Entry entry = (Entry) it.next();
                     ClassUtils.beanRegister(results[i],
                             (String) entry.getKey(), (String) entry.getValue());
                 }
             }
         } catch (Exception ex) {
             throw new RuntimeException(ex+" "+fileName);
         }
         return results;
     }
     /**
      * 載入CSV數據到List
      *
      * @param fileName
      * @return
      */
     final static public List loadPropertys(final String fileName) {
         List result = new ArrayList();
         try {
             CSVReader csv = new CSVReader(fileName);
             List names = csv.readLineAsList();
             int length = names.size();
             for (; csv.ready();) {
                 Map propertys = new HashMap(length);
                 String[] csvItem = csv.readLineAsArray();
                 for (int i = 0; i < length; i++) {
                     propertys.put((String) names.get(i), csvItem[i]);
                 }
                 result.add(propertys);
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
         return result;
     }
}

使用方法:

1. friends = (Friend[]) CSVConfigure.loadPropertys(FRIEND_FILE_NAME,
2.                 Friend.class);

只是一個很簡單的反射,就可以將CSV表格數據注射到類上。

再說一下游戲數據的存儲,一般來講就是指游戲記錄,這在Java中是最好辦的。因為只要你的關鍵數據對象實現serializable,大可以直接將當前狀態序列化到本地文件中,不過是ObjectInputStream和ObjectOutputStream的把戲罷了。

如果沒有或者你不想的話,你只要將關鍵數據以某種格式保存(這個真是隨便,能再讀出來就成),然後再反饋給游戲即可。實際上我們都知道所謂讀檔/保存並不是時間真的重來了,而是數據被還原罷了。

用例代碼:

package org.loon.simple.slg.utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
   *
   * Copyright 2008
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   * either express or implied. See the License for the specific language
   * governing permissions and limitations under the License.
   *
   * @project loonframework
   * @author chenpeng  
   * @email:[email protected]
   * @version 0.1
   */
public class Keep {
     private static final long serialVersionUID = -1982090153295778606L;
     final static public String LS = System.getProperty("line.separator", "\n");
     final static public String FS = System.getProperty("file.separator", "\\");

     /**
       * 增位變更指定字符串(混淆記錄用)
       *
       * @param s
       * @param x
       * @return
       */
     final static public String addChange(final String s, final int x) {
         String result = null;
         StringBuilder sbr = new StringBuilder();
         for (int i = 0; i < s.length(); i++) {
             char p = (char) (s.charAt(i) + x);
             sbr.append(p);
         }
         try {
             result = URLEncoder.encode(sbr.toString(),"UTF-8");
         } catch (UnsupportedEncodingException e) {
             throw new RuntimeException(e);
         }
         return result;
     }
     /**
       * 減位變更指定字符串(混淆記錄用)
       *
       * @param s
       * @param x
       * @return
       */
     final static public String backChange(final String s, final int x) {
         String result = null;
         try {
             result = URLDecoder.decode(s,"UTF-8");
         } catch (UnsupportedEncodingException e) {
             throw new RuntimeException(e);
         }
         StringBuilder sbr = new StringBuilder();
         for (int i = 0; i < result.length(); i++) {
             char p = (char) (result.charAt(i) - x);
             sbr.append(p);
         }
         return sbr.toString();
     }

     /**
       * 壓縮byte[]
       *
       * @param buffer
       * @return
       */
     public static byte[] compress(final byte[] buffer) {
         try {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             GZIPOutputStream gzipos = new GZIPOutputStream(baos);
             gzipos.write(buffer);
             gzipos.flush();
             gzipos.close();
             return baos.toByteArray();
         } catch (IOException e) {
             return null;
         }
     }
     /**
       * 壓縮byte[]
       *
       * @param buffer
       * @return
       */
     public static byte[] uncompress(final byte[] buffer) {
         try {
             GZIPInputStream gzipis = new GZIPInputStream(
                     new ByteArrayInputStream(buffer));
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             byte[] tmp = new byte[8192];
             int len;
             while ((len = gzipis.read(tmp)) > 0) {
                 baos.write(tmp, 0, len);
             }
             baos.flush();
             return baos.toByteArray();
         } catch (IOException e) {
             return null;
         }
     }
     /**
       * 保存游戲記錄
       *
       * @param file
       * @param bytes
       * @throws IOException
       */
     public static void save(final String fileName,final String message) throws IOException {
         save(new File(fileName), new ByteArrayInputStream(Keep.compress(message.getBytes())));
     }
     /**
       * 保存記錄到文件
       *
       * @param file
       * @param input
       * @throws IOException
       */
     private static void save(final File file,final InputStream input)
             throws IOException {
         mkdirs(file);
         BufferedOutputStream output = null;
         try {
             int contentLength = input.available();
             output = new BufferedOutputStream(
                     new FileOutputStream(file, false));
             while (contentLength-- > 0) {
                 output.write(input.read());
             }
         } finally {
             close(input, file);
             close(output, file);
         }
     }

     final private static byte[] read(final InputStream inputStream) {
         byte[] arrayByte = null;
         BufferedInputStream buffer = new BufferedInputStream(inputStream);
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         byte[] bytes = new byte[8192];
         try {
             int read;
             while ((read = buffer.read(bytes)) >= 0) {
                 byteArrayOutputStream.write(bytes, 0, read);
             }
             arrayByte = byteArrayOutputStream.toByteArray();
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {
             try {
                 if (buffer != null) {
                     buffer.close();
                     buffer = null;
                 }
             } catch (IOException e) {
                 throw new RuntimeException(e);
             }
         }
         return Keep.uncompress(arrayByte);
     }
     /**
       * 讀取記錄到list
       *
       * @param fileName
       * @return
       * @throws IOException
       */
     public static List load(final String fileName) throws IOException {
         File file = new File(fileName);
         BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(Keep.read(new FileInputStream(file)))));
         List records = new ArrayList();
         String record = null;
         try {
             while ((record = reader.readLine()) != null) {
                 records.add(record);
             }
         } finally {
             close(reader, file);
         }
         return records;
     }
     /**
       * 創建文件夾
       *
       * @param file
       * @throws IOException
       */
     private static void mkdirs(final File file) throws IOException {
         checkFile(file);
         File parentFile = file.getParentFile();
         if (parentFile != null) {
             if (!parentFile.exists() && !parentFile.mkdirs()) {
                 throw new IOException("Creating directories "
                         + parentFile.getPath() + " failed.");
             }
         }
     }
     private static void checkFile(final File file) throws IOException {
         if (file.exists() && !file.isFile()) {
             throw new IOException("File " + file.getPath()
                     + " is actually not a file.");
         }
     }
     private static void close(final InputStream input,final File file) {
         if (input != null) {
             try {
                 input.close();
             } catch (IOException e) {
                 closingFailed(file, e);
             }
         }
     }
     private static void close(final OutputStream output,final File file) {
         if (output != null) {
             try {
                 output.close();
             } catch (IOException e) {
                 closingFailed(file, e);
             }
         }
     }
     private static void close(final Reader reader,final File file) {
         if (reader != null) {
             try {
                 reader.close();
             } catch (IOException e) {
                 closingFailed(file, e);
             }
         }
     }
     private static void closingFailed(final File file,final IOException e) {
         String message = "Closing file " + file.getPath() + " failed.";
         throw new RuntimeException(message + ":" + e.getMessage());
     }
}

導出的結果:

比如在這個示例游戲中,我將保存的數據先遞增字符位數,再經過URL編碼,最後gzip壓縮,出來的一組鬼畫符般記錄文檔,唯一的作用就是不讓人輕易讀出並修改罷了。當然這只是最簡單的例子,我們完全加密的更復雜(比如玩玩DES),驗證的更變態,讓玩家絞盡腦汁也無法修改游戲分毫,畢竟讓玩家快樂且痛苦的游戲,就是游戲制作者最大的樂趣及興奮點啊,哈哈哈。(驚見板磚+臭雞蛋,我閃~~~)

主菜單界面:

游戲基本界面,背景設定主角在一座城鎮中,有五項基本命令可供選擇。

隊友雇用界面:

即酒店界面,用於尋找戰友加入

物品購入界面:

商店,用於補充游戲中物品

物品裝備界面:

道具裝備,用於裝備購入的物品

關卡選擇界面:

任務選擇,本示例由於沒有考慮做大,所以直接將關卡做成賞金模式供玩家選擇

戰斗畫面:

最初有過在此例復刻夢幻模擬戰2的打算,但由於沒有找到整套的素材,所以作罷。誰有興趣幫兄弟找到整套素材(關鍵是各兵種戰斗圖)的話,我可以再作一個復刻夢幻2的例子。

SLG游戲入門示例及源碼下載地址:http://download.csdn.net/source/809105

本文出自 “Java究竟怎麼玩” 博客,請務必保留此出處http://cping1982.blog.51cto.com/601635/116596

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved