Java版SLG游戏开发入门[2]--数据的读取及保存-阿里云开发者社区

开发者社区> 科技小能手> 正文

Java版SLG游戏开发入门[2]--数据的读取及保存

简介:
+关注继续查看
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格式,实现了一种较为另类的表格式数据存储。
 
   CSVComma 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
    * 
    * [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
    * 
    * 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]ceponline@yahoo.com.cn[/email]
    * @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,大可以直接将当前状态序列化到本地文件中,不过是ObjectInputStreamObjectOutputStream的把戏罢了。
 
  如果没有或者你不想的话,你只要将关键数据以某种格式保存(这个真是随便,能再读出来就成),然后再反馈给游戏即可。实际上我们都知道所谓读档/保存并不是时间真的重来了,而是数据被还原罢了。

  用例代码:

  
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
    *
    * [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
    *
    * 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]ceponline@yahoo.com.cn[/email] 
    * @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(newByteArrayInputStream(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的例子。
  
   



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
OceanBase数据库漫谈
从开发视角、运维视角、数据拆分漫谈OceanBase
21 0
【mockito】单元测试之mockito简单使用
项目使用的是springmvc+mybatis 开发; mock包为 mockito-all;虽然也引用了powermock,但截至目前,还未使用到;如果使用到后续再补相关笔记。
15 0
Node + Express + MySQL 接口开发完整案例
Node + Express + MySQL 接口开发完整案例
20 0
浅谈我对DDD领域驱动设计的理解
DDD的全称为Domain-driven Design,即领域驱动设计。下面我从领域、问题域、领域模型、设计、驱动这几个词语的含义和联系的角度去阐述DDD是如何融入到我们平时的软件开发初期阶段的。要理解什么是领域驱动设计,首先要理解什么是领域,什么是设计,还有驱动是什么意思,什么驱动什么。
12 0
ACP实战特训营RDS(DAY3)
要点记录 1. PolarDB的基本概念 1.1、对比单机数据库优势有哪些:简单易用、极致性能、降低成本、海量存储、安全可靠、快速弹性 1.2 、单机数据库容量瓶颈-单机数据库扩展困难-数据库使用成本过高-分布式数据库应用开发繁琐 2. PolarDB产品系列:集群版-单节点-历史库-多主架构 2.1、集群:一个集群包含一个主节点和多个读节点,最多16个节点,即一个主节点和15个只读节点 2.2、地域:是指物理的数据中心,一般情况下,PolarDB集群应该和ECS实例位于同一地域,以实现最高的访问性能
14 0
Flutter基础笔记
目录 List里面常用的属性和方法: Set Map forEach,map, where,any,every extends抽象类 和 implements Flutter环境搭建 入口文件、入口方法 第一个 Demo Center 组件的 使用 把内容单独抽离成一个组件 给 Text 组件增加一些装饰 用MaterialApp 和 Scaffold两个组件装饰 App Text 组件 Container 组件 图片组件 引入本地图片 裁剪布局之 ClipRect、ClipRRect、ClipOval、ClipPath、CustomClipper 实现圆角以及实现圆形图片 圆形头像
4 0
高智商学生如何玩转无影云电脑
作为一名刚学习编程技术的学生来说,非常有幸体验阿里的无影云电脑,非常的荣幸,也感谢阿里云的支持与帮助,本人将写一遍无影云电脑评测的文章来记录一下这一令人感动的瞬间。
33 0
云起实验室学习报告之LAMP搭建
云起实验室学习报告之LAMP搭建
17 0
flutter开发之必须掌握的dart知识点:list,set,map
要说,List在我的开发使用中,确实是最为频繁的了,那么如何使用list,也就成了一个问题,list提供的方法又有哪些 这些都是需要掌握理解的。 首先第一个, 对于固定长度的list,如何删除添加元素呢
10 0
23706
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载