栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

修改植物大战僵尸游戏存档(Java实现版)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

修改植物大战僵尸游戏存档(Java实现版)

目录

一、实现思路

二、实现过程

1. 创建Java类

2. 实现读取数据方法

3. 实现修改数据方法

3.1. 修改关卡数

         3.2. 修改金币数

3.3. 解锁所有模式

3.4 解决输入错误

4. 实现写入数据方法

 三、实验测试

 四、完整代码


上次任务已经使用Hex Editor Neo工具成功修改游戏存档,具体实现探索和步骤请参考:修改植物大战僵尸游戏存档——跳关并快速实现财富自由,本次任务目的将使用java代码替换工具,实现同样的修改功能。

一、实现思路
  1. 读取数据:修改的user1.dat文件的本地路径已知,使用IO流知识读取数据并存放在程序中。
  2. 修改数据:利用控制台输入,获取用户修改意向,并根据任务的不同采取不同的修改策略。
  3. 写入数据:将修改后的数据重新写入到原文件中。

二、实现过程

1. 创建Java类

按图示创建Java工程。

 之后在创建好的Java工程src目录下创建Java类。

输入类名称就可以进行敲代码啦。如下图,定义两个全局变量,分别是可变数组userIndex,用来存放.dat文件下读取出来的数据、.dat所在的本地路径。并设为static类型,可以不用创建类对象就能使用。

2. 实现读取数据方法

有两种读取文件方式,读取文本形式使用Reader,读取二进制数据使用InputStream。FileInputStream是InputStream的一个子类。顾名思义,FileInputStream就是从文件流中读取数据。BufferedInputStream继承于FilterInputStream,提供缓冲输入流功能。缓冲输入流相对于普通输入流的优势是,它提供了一个缓冲数组,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容部分或全部返回给用户。由于从缓冲区里读取数据远比直接从物理数据源读取速度快。代码如下:

//    从二进制文件中读取数据(读取到的数据从十六进制数据自动转成十进制)
    public static void readData(){
        try
        {
            FileInputStream fis= new FileInputStream(fileName);
            BufferedInputStream br = new BufferedInputStream(fis);
            int record = -1;
            while((record = br.read()) != -1)
            {
                userIndex.add(record);  //将读到的数据添加到可变数组中(全局变量)
            }
            System.out.println("从文档中读取第一行数据显示(十进制):");
            for(int i=0; i<16; i++){
                System.out.print(userIndex.get(i) + " ");
            }
            System.out.println();
        }
        catch(Exception e)
        {
            System.out.print(e.toString());
        }
    }

 3. 实现修改数据方法

修改数据思路是:主方法中先用Scanner获取用户输入的任务类型,将用户输入数据以参数方式传给changeData方法,在方法内部判断,根据类型采取不同的修改方式。具体措施如下:

3.1. 修改关卡数

3.1.1 用Scanner获取用户输入的关卡数,得到类型为字符串,例"5-1"。

3.1.2 计算修改的十进制数(因为从文件中读取时十六进制自动转换成十进制):先用substring分别获取“-”前面和后面的数字,用变量存取(此时为字符串类型)。用valueof()方法将字符串转为数字,并用前数字*10加后数字。

3.1.3 将1.2的结果替换到表示关卡数的位置:用Hex Editor Neo表示第一行04位置(下标为04)。

 public static void changeData(String type){
        if (type.equals("1")){
            System.out.println("请输入您要改的关卡:");
            Scanner input=new Scanner(System.in);
            String level=input.nextLine();
            int firstLevel=Integer.valueOf(level.substring(0,1));
            int lastLevel=Integer.valueOf(level.substring(2,3));
            //因为从1-1开始的,5-1只需加41关即可
            int addLevel=(firstLevel-1)*10+lastLevel;
            //修改数据:关卡数在第一行04位置,即数组中下标为4
            userIndex.set(4,addLevel);
        }

 3.2. 修改金币数

 3.2.1 用Scanner获取用户输入的关卡数,得到类型为字符串,例"100000000"。

 3.2.2 计算:因为植物大战僵尸的内部机制,二进制表示是真实金币数的十分之一,因此需要将2.1结果转换为数字并除以10。文件中以十六进制存储,将除以10后的十进制数据转换成十六进制。例如"100,000,000/10d=989680h"。

 3.2.3 存储:在.dat文件中,金币数量是由08,09,0a,0b是存储位,包含8个十六进制位,因此需要将不够位数的在前面补齐。此思路用for循环,循环次数是(8-十六进制数的length)。例如"989680->00989680"。补齐之后以两位为单位,分别按低位->高位的顺序替换08->0b的值。例如"80,96,98,00"分别替换"08,09,0a,0b"位的值。代码如下:

else if (type.equals("2")){
            System.out.println("请输入您要改的金币数量:");
            Scanner input=new Scanner(System.in);
            String money=input.nextLine();
            //需要加到的金钱
            int needMoney=Integer.valueOf(money)/10;
            String HexMoney=Integer.toHexString(needMoney);
            //在十六进制数前面补0到8位
            String zero=new String();
            for (int i=0;i<8-HexMoney.length();i++){
                zero+="0";
            }
            HexMoney=zero+HexMoney;//补全的十六进制
            //最后两位,对应第一行08位置,进行替换
            int data08=Integer.valueOf(HexMoney.substring(HexMoney.length()-2));
            userIndex.set(8, Integer.parseInt(String.valueOf(data08),16));
            //倒数第3,4位,对应第一行09位置,进行替换
            int data09=Integer.valueOf(HexMoney.substring(4,6));
            userIndex.set(9, Integer.parseInt(String.valueOf(data09),16));
            //正数第3,4位,对应第一行0a位置,进行替换
            int data0a=Integer.valueOf(HexMoney.substring(2,4));
            userIndex.set(10, Integer.parseInt(String.valueOf(data0a),16));
            //正数第1,2位,对应第一行0a位置,进行替换
            int data0b=Integer.valueOf(HexMoney.substring(0,2));
            userIndex.set(11, Integer.parseInt(String.valueOf(data0b),16));
        }

3.3. 解锁所有模式

按照上次的实验,发现只需将0c位的数改为01即可达到目的。代码如下:

else if(type.equals("3")){
            //修改数据:关卡数在第一行0c位置,即数组中下标为12
            userIndex.set(12,1);
        }

3.4 解决输入错误

当输入非1-3,则提示并重新获取信息,利用递归重新进行判断。

else{
//          输入有误,重新接收数字,用递归方法修改数据
            System.out.println("输入有误,请输入1-3:");
            Scanner input=new Scanner(System.in);
            String level=input.nextLine();
            changeData(level);
            return;
        }

4. 实现写入数据方法

 利用FileOutStream写入数据,并且为完全替换,append设为false。

//   将数组以二进制写入文件中
    public static void writeData() throws IOException {
        FileOutputStream fileWriter=new FileOutputStream(fileName,false);
        for (int singledata:userIndex) {
            fileWriter.write(singleData);
        }
        //写入文件
        fileWriter.close();
    }

 三、实验测试

 1. 任务一修改关卡数为5-1:

修改关卡数为6-1:

 

 2. 任务二修改金币数量为1024:

 修改金币数量为100,000,000:

3. 任务三解锁所有模式:

 4. 其他出错检查:

 四、完整代码
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

public class modifyPlantsVsZombies {
    static ArrayList userIndex=new ArrayList<>();
    static String fileName="C:\ProgramData\PopCap Games\PlantsVsZombies\userdata\user1.dat";
    public static void main(String[] args) {
        readData();
        System.out.println("请输入任务标号(1/2/3):");
        Scanner input=new Scanner(System.in);
        String level=input.nextLine();
        changeData(level);
    }
//    从二进制文件中读取数据(在输出到控制台时十六进制数据自动转成十进制)
    public static void readData(){
        try
        {
            FileInputStream fis= new FileInputStream(fileName);
            BufferedInputStream br = new BufferedInputStream(fis);
            int record = -1;
            while((record = br.read()) != -1)
            {
                userIndex.add(record);
            }
            System.out.println("从文档中读取第一行数据显示(十进制):");
            for(int i=0; i<16; i++){
                System.out.print(userIndex.get(i) + " ");
            }
            System.out.println();
        }
        catch(Exception e)
        {
            System.out.print(e.toString());
        }
    }
//    判断任务,并根据用户输入意向进行修改
    public static void changeData(String type){
        if (type.equals("1")){
            System.out.println("请输入您要改的关卡:");
            Scanner input=new Scanner(System.in);
            String level=input.nextLine();
            int firstLevel=Integer.valueOf(level.substring(0,1));
            int lastLevel=Integer.valueOf(level.substring(2,3));
            //因为从1-1开始的,5-1只需加41关即可
            int addLevel=(firstLevel-1)*10+lastLevel;
            //修改数据:关卡数在第一行04位置,即数组中下标为4
            userIndex.set(4,addLevel);
        }else if (type.equals("2")){
            System.out.println("请输入您要改的金币数量:");
            Scanner input=new Scanner(System.in);
            String money=input.nextLine();
            //需要加到的金钱
            int needMoney=Integer.valueOf(money)/10;
            String HexMoney=Integer.toHexString(needMoney);
            //在十六进制数前面补0到8位
            String zero=new String();
            for (int i=0;i<8-HexMoney.length();i++){
                zero+="0";
            }
            HexMoney=zero+HexMoney;//补全的十六进制
            //最后两位,对应第一行08位置,进行替换
            int data08=Integer.valueOf(HexMoney.substring(HexMoney.length()-2));
            userIndex.set(8, Integer.parseInt(String.valueOf(data08),16));
            //倒数第3,4位,对应第一行09位置,进行替换
            int data09=Integer.valueOf(HexMoney.substring(4,6));
            userIndex.set(9, Integer.parseInt(String.valueOf(data09),16));
            //正数第3,4位,对应第一行0a位置,进行替换
            int data0a=Integer.valueOf(HexMoney.substring(2,4));
            userIndex.set(10, Integer.parseInt(String.valueOf(data0a),16));
            //正数第1,2位,对应第一行0a位置,进行替换
            int data0b=Integer.valueOf(HexMoney.substring(0,2));
            userIndex.set(11, Integer.parseInt(String.valueOf(data0b),16));
        }else if(type.equals("3")){
            //修改数据:关卡数在第一行0c位置,即数组中下标为12
            userIndex.set(12,1);
        }else{
//          输入有误,重新接收数字,用递归方法修改数据
            System.out.println("输入有误,请输入1-3:");
            Scanner input=new Scanner(System.in);
            String level=input.nextLine();
            changeData(level);
            return;
        }
        //修改之后输出第一行
        System.out.println("修改后第一行数据显示(十进制):");
        for(int i=0; i<16; i++){
            System.out.print(userIndex.get(i) + " ");
        }
        //将数组以二进制写入文件中
        try {
            writeData();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//   将数组以二进制写入文件中
    public static void writeData() throws IOException {
        FileOutputStream fileWriter=new FileOutputStream(fileName,false);
        for (int singledata:userIndex) {
            fileWriter.write(singleData);
        }
        //写入文件
        fileWriter.close();
    }
}

心得

上次的手动修改使我了解到游戏中二进制的储存机制,并接触到了曾经认为遥不可及的事情。这次换代码实现,计算效率一下子提高了好多,只要编译成功,下次不需要再进行手动计算,就可以达到"一次编程,随时运行"的效果。另外,实现的过程也是让人感到乐在其中,首先将整体思路写出来,按照每步想实现的功能去查资料并实现。从IO流的应用到二进制的处理,再到字符串与数字的相互转换,这些知识点都在我心中有了更深的印记。感谢CSDN给我这次实训的机会,让我对Java又多了些热爱。劳伦斯曾说过“成功的秘诀,是在养成迅速去做的习惯,要趁着潮水涨得最高的一刹那,不但没有阻力,而且能帮助你迅速地成功”,对语言的真正掌握也只有不断地探索和实践。加油吧,少年!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/353223.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号