Minecraft(我的世界)中文论坛

 找回密码
 注册(register)

!header_login!

只需一步,立刻登录

查看: 4730326|回复: 33

[Mod开发讨论] [mod开发高级教程]维度生成

[复制链接]
xiweihai 当前离线
积分
2028
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-5-7
查看详细资料
发表于 2015-12-3 17:17:32 | 显示全部楼层 |阅读模式

您尚未登录,立即登录享受更好的浏览体验!

您需要 登录 才可以下载或查看,没有帐号?注册(register)

x
本帖最后由 xiweihai 于 2015-12-5 01:52 编辑

先说明一下mc的创建新世界是靠一个叫WorldProvider的类,然后每个维度(也就是世界)都有一个独立的id,比如说mc的三个世界id分别是-1,0,1当然这个世界也不列外,你只需要提供一个世界供应者和chunk供应者就能生成一个独立的维度,很幸运的是,保存地图数据玩家数据等等,mc都已经帮完成了,当然其中也有毕竟困难的地方也就是地形生成,这个我后面会说道。

首先我们先写一个世界供应者稍后注册世界会用到(不要在意中二的类命),代码如下
  1. /**
  2. * 生成世界
  3. * @author xwh
  4. *
  5. */
  6. public class WorldProviderPollute extends WorldProvider{
  7.             /**
  8.              * 注册世界和维度id和设置管理者
  9.              *
  10.              */
  11.             public void registerWorldChunkManager()
  12.             {
  13.                this.worldChunkMgr = new WorldChunkManager(worldObj);
  14.                 this.dimensionId = 21;
  15.             }
  16.             /**
  17.              * 设置世界时间的函数,你设置world时间会调用这个函数,我设置是平常时间两倍速度
  18.              */
  19.             public void setWorldTime(long time)
  20.             {
  21.                 worldObj.getWorldInfo().setWorldTime(time/2);
  22.             }
  23.             /**
  24.              * 返回世界时间,同理
  25.              */
  26.             @Override
  27.             public long getWorldTime() {
  28.                     return worldObj.getWorldInfo().getWorldTime()*2;
  29.             }
  30.             /**
  31.              * 返回天空颜色,三个值是rgb值我也不知道为啥要用Vec3表示...,mc源码就是这样
  32.              */
  33.             @Override
  34.             @SideOnly(Side.CLIENT)
  35.             public Vec3 getSkyColor(Entity cameraEntity, float partialTicks) {
  36.                     return Vec3.createVectorHelper(0.20000000298023224D, 0.029999999329447746D, 0.029999999329447746D);
  37.             }
  38.             /**
  39.              * 返回chunk供应者后面会写到
  40.              */
  41.             public IChunkProvider createChunkGenerator()
  42.             {
  43.                 return new ChunkProviderPollute(this.worldObj, this.worldObj.getSeed();
  44.             }
  45.             /**
  46.              * 是否是平常世界
  47.              */
  48.             public boolean isSurfaceWorld()
  49.             {
  50.                 return true;
  51.             }
  52.             /**
  53.              * 世界存档文件夹名称
  54.              */
  55.             public String getDimensionName()
  56.             {
  57.                 return "pulluteWorld-"+dimensionId;
  58.             }
  59. }
复制代码
说明:
    这个就是所谓的世界供应者,待会需要在mod入口注册,每个方法都有说明不过多解释
    当然这个类里面有很多返回各种参数的方法,比如说云朵高度,雾的颜色你可以重新那个方法自己定义
然后我们需要写一个WorldTeleporter类,这是因为mc一个坑爹的机制后面会讲到
  1. public class WorldTeleporterPollute extends Teleporter{

  2.         public WorldTeleporterPollute(WorldServer p_i1963_1_) {
  3.                 super(p_i1963_1_);
  4.         }
  5.         @Override
  6.         public boolean placeInExistingPortal(Entity p_77184_1_, double p_77184_2_,
  7.                         double p_77184_4_, double p_77184_6_, float p_77184_8_) {
  8.                 return false;
  9.         }
  10.         
  11.         @Override
  12.         public void placeInPortal(Entity p_77185_1_, double p_77185_2_,
  13.                         double p_77185_4_, double p_77185_6_, float p_77185_8_) {
  14.         }
  15.         
  16.         @Override
  17.         public boolean makePortal(Entity p_85188_1_) {
  18.                 return false;
  19.         }
  20.         
  21.         @Override
  22.         public void removeStalePortalLocations(long p_85189_1_) {
  23.         }
  24. }
复制代码
说明:
这个类主要的作用就是传送门的管理以及传送时需要用到,当然现在我们忽略不计后面的章节会讲
但是搞这样一个空白的类有什么意义呢,别急意义一定是有的,为什么不能用travelToDimension这个方法直接传送呢,
因为如果你调用这个传送的话,是可以传送到这个世界,但是传送到新维度后,会生成一个传送门有把你传送会以前的世界了,
这样的结果就是你永远传不过去2333,所以我们要写一个这样的类

然后需要写一个chunk供应者,作用就是提供每个chunk里面的方块,代码如下
  1. /**
  2. * @author xwh
  3. *chunk供应
  4. */
  5. public class ChunkProviderPollute implements IChunkProvider{
  6.         /** RNG. */
  7.     private Random rand;
  8.     private NoiseGeneratorOctaves noiseGeneratorOctaves;
  9.     /** Reference to the World object. */
  10.     private World worldObj;
  11.     private WorldType field_147435_p;
  12.     private MapGenScatteredFeature scatteredFeatureGenerator = new MapGenScatteredFeature();
  13.     private BiomeGenBase[] biomesForGeneration;
  14.     double[] genBuff;
  15.     /**
  16.      *
  17.      * @param worldObj 世界
  18.      * @param seed 地图种子
  19.      * @param mapFeaturesEnabled 是否生成基本结构
  20.      */
  21.     public ChunkProviderPollute(World worldObj, long seed)
  22.     {
  23.         this.worldObj = worldObj;
  24.         this.field_147435_p = worldObj.getWorldInfo().getTerrainType();
  25.         this.rand = new Random(seed);
  26.         this.noiseGeneratorOctaves = new NoiseGeneratorOctaves(this.rand, 16);
  27.         this.genBuff = new double[16*16];
  28.     }
  29.     /**
  30.      * 生成地
  31.      * @param x
  32.      * @param z
  33.      * @param blocks
  34.      */
  35.     public void generation(int x, int z, Block[] blocks)
  36.     {
  37.             this.genBuff = this.noiseGeneratorOctaves.generateNoiseOctaves(this.genBuff,x*16,0,z*16, 16, 1,16, 1000, 8000, 1000);
  38.         int i=0;
  39.         //遍历每个生成的高度
  40.         for(double d:genBuff){
  41.                 //根据幅度的大小取一个合适的高度,太高了会导致数组溢出太低了效果不好,自己看着办,在高度60米左右的地方生成基本地形
  42.                 int by=(int) (60+d/8000);
  43.                 //生成地面以及地下的方块
  44.                 for(int in=0;in<by;in++){
  45.                         int bi =(i * 256)| in;
  46.                         //在最下面两层生成基岩
  47.                         if(in<2){
  48.                                 blocks[bi]=Blocks.bedrock;
  49.                         }else if(in>=by-2){//最上面两层生成泥土
  50.                                 blocks[bi]=Blocks.dirt;
  51.                         }else{//中间生成原石
  52.                                 blocks[bi]=Blocks.stone;
  53.                         }
  54.                 }
  55.                         int bi =(i * 256)| by;
  56.                         //顶层生成草地
  57.                         blocks[bi]=blocks[bi]=Blocks.grass;
  58.                 i++;
  59.         }

  60.     }
  61.     public Chunk loadChunk(int p_73158_1_, int p_73158_2_)
  62.     {
  63.         return this.provideChunk(p_73158_1_, p_73158_2_);
  64.     }

  65.     /**
  66.      * 返回chunk,你可以在里面生成基本地形
  67.      *
  68.      */
  69.     public Chunk provideChunk(int x, int z)
  70.     {
  71.         this.rand.setSeed((long)x * 341873128712L + (long)z * 132897987541L);
  72.         Block[] ablock = new Block[65536];
  73.         byte[] abyte = new byte[65536];
  74.         this.generation(x, z, ablock);
  75.         this.biomesForGeneration = this.worldObj.getWorldChunkManager().loadBlockGeneratorData(this.biomesForGeneration, x * 16, z * 16, 16, 16);
  76.         Chunk chunk = new Chunk(this.worldObj, ablock, abyte, x, z);
  77.         byte[] abyte1 = chunk.getBiomeArray();
  78.         for (int k = 0; k < abyte1.length; ++k)
  79.         {
  80.             abyte1[k] = (byte)this.biomesForGeneration[k].biomeID;
  81.         }
  82.         chunk.generateSkylightMap();
  83.         return chunk;
  84.     }
  85.    

  86.     public boolean chunkExists(int p_73149_1_, int p_73149_2_)
  87.     {
  88.         return true;
  89.     }

  90.     /**
  91.      * 这个是添加结构的时候要用到的回调函数
  92.      */
  93.     public void populate(IChunkProvider provider, int x, int z)
  94.     {
  95.       
  96.     }
  97.     public boolean saveChunks(boolean p_73151_1_, IProgressUpdate p_73151_2_)
  98.     {
  99.         return true;
  100.     }
  101.     public void saveExtraData() {}
  102.     public boolean unloadQueuedChunks()
  103.     {
  104.         return false;
  105.     }
  106.     public boolean canSave()
  107.     {
  108.         return true;
  109.     }
  110.     public String makeString()
  111.     {
  112.         return "RandomLevelSource";
  113.     }
  114.     /**
  115.      * 返回某个位置的实体列表
  116.      */
  117.     public List getPossibleCreatures(EnumCreatureType p_73155_1_, int p_73155_2_, int p_73155_3_, int p_73155_4_)
  118.     {
  119.         BiomeGenBase biomegenbase = this.worldObj.getBiomeGenForCoords(p_73155_2_, p_73155_4_);
  120.         return p_73155_1_ == EnumCreatureType.monster && this.scatteredFeatureGenerator.func_143030_a(p_73155_2_, p_73155_3_, p_73155_4_) ? this.scatteredFeatureGenerator.getScatteredFeatureSpawnList() : biomegenbase.getSpawnableList(p_73155_1_);
  121.     }
  122.     public ChunkPosition func_147416_a(World p_147416_1_, String p_147416_2_, int p_147416_3_, int p_147416_4_, int p_147416_5_)
  123.     {
  124.         return null;
  125.     }
  126.     public int getLoadedChunkCount()
  127.     {
  128.         return 0;
  129.     }
  130.     /**
  131.      * 多重结构生成回调
  132.      */
  133.         @Override
  134.         public void recreateStructures(int p_82695_1_, int p_82695_2_) {
  135.                
  136.         }
复制代码



说明:
       这个类是重点部分,他将提供这个世界每个chunk中的方法,也就是基本地形生成都是在这里面进行的
      provideChunk当玩家在这个世界移动到一个没到过的chunk时,会加载这个函数,返回chunk加载到世界上,而一个chunk的大小是多少呢,16*16*256,也就是65536需要注意的是数组储存的顺序是xzy,所以头一位储存的是x,第二位是z,第三和第四是z,故一个在chunk里面x坐标为5z坐标为11y坐标为156可以用16进制这样表达0x5b9c,至于那个abyte是该方块所处于的生物群系id
noiseGeneratorOctaves介绍:这个是实现自然地形主要的算法,柏林噪音,,大致就是生成一段起伏不定的波浪状频谱,然后利用这个模拟地形第一个参数是储存起伏不定的噪音坐标的数组,然后就是噪音xyz开始的位子,xyz生成的大小,xyz生成的幅度,越大上下起伏的落差就越大
一个噪声函数基本上是一个种子随机发生器。它需要一个整数作为参数,然后根据这个参数返回一个随机数。如果你两次都传同一个参数进来,它就会产生两次相同的数。这条规律非常重要,否则柏林函数只是生成一堆垃圾。
这里的一张图展现了噪声函数的一个例子。X轴上每个点被赋予一个0到1之间的随机数。
perlin_02.gif
通过在值之间平滑的插值,我们定义了一个带有一个非整参数的连续函数。我们将会在后面的内容中讨论多种插值方式






下面就是最后的注册世界了
  1. DimensionManager.registerProviderType(21, WorldProviderPollute.class, true);
  2.                 DimensionManager.registerDimension(21,21);
复制代码
在mod初始化的时候加上这样的代码就行了,注意这3个21是世界的id

然后如何传送到这个世界呢
  1. if(entity instanceof EntityPlayerMP){
  2.                                         ServerConfigurationManager scm = MinecraftServer.getServer().getConfigurationManager();
  3.                                         WorldTeleporterPollute teleporter = new WorldTeleporterPollute(MinecraftServer.getServer().worldServerForDimension(21));
  4.                                         scm.transferPlayerToDimension((EntityPlayerMP) entity, 21, teleporter);
  5.                                 }else {
  6.                                         ServerConfigurationManager scm = MinecraftServer.getServer().getConfigurationManager();
  7.                                         WorldTeleporterPollute teleporter = new WorldTeleporterPollute(MinecraftServer.getServer().worldServerForDimension(21));
  8.                                         scm.transferEntityToWorld(entity, 21,(WorldServer) world,MinecraftServer.getServer().worldServerForDimension(21),teleporter);
  9.                                 }
复制代码
实体传送和玩家传送的方法
需要注意的是传送前判断一下world instanceof WorldServer道理应该都懂吧,这样的操作只能在
Server进行

运行截图



















评分

参与人数 2人气 +2 金粒 +25 收起 理由
jin2003210 + 1 + 15 可惜了,不更了,刚研究完ChunkProviderFla.
RothBlink + 1 + 10 很棒的作品!

查看全部评分

xiweihai 当前离线
积分
2028
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-5-7
查看详细资料
 楼主| 发表于 2015-12-5 00:04:20 | 显示全部楼层
本帖最后由 xiweihai 于 2015-12-5 01:44 编辑

已更新代码
回复

使用道具 举报

RothBlink 当前离线
积分
196
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2015-8-14
查看详细资料
发表于 2015-12-12 17:05:29 | 显示全部楼层
请问这个具体应该写在哪里?
DimensionManager.registerProviderType(21, WorldProviderPollute.class, true);
                DimensionManager.registerDimension(21,21);
回复

使用道具 举报

RothBlink 当前离线
积分
196
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2015-8-14
查看详细资料
发表于 2015-12-14 11:44:26 | 显示全部楼层
RothBlink 发表于 2015-12-12 17:05
请问这个具体应该写在哪里?
DimensionManager.registerProviderType(21, WorldProviderPollute.class, tru ...

我找到了应该mod的load里面但是下面的代码怎么添加。求解释啊求解释啊。
回复

使用道具 举报

xiweihai 当前离线
积分
2028
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-5-7
查看详细资料
 楼主| 发表于 2015-12-14 14:20:21 | 显示全部楼层
RothBlink 发表于 2015-12-14 11:44
我找到了应该mod的load里面但是下面的代码怎么添加。求解释啊求解释啊。 ...

醉了,在mod初始化函数里面加上啊
回复

使用道具 举报

RothBlink 当前离线
积分
196
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2015-8-14
查看详细资料
发表于 2015-12-14 15:39:49 | 显示全部楼层
大神回我了好开心!试了下可以了,但是
if(entity instanceof EntityPlayerMP){
                                        ServerConfigurationManager scm = MinecraftServer.getServer().getConfigurationManager();
                                        WorldTeleporterPollute teleporter = new WorldTeleporterPollute(MinecraftServer.getServer().worldServerForDimension(21));
                                        scm.transferPlayerToDimension((EntityPlayerMP) entity, 21, teleporter);
这一段还没有添加成功在load里面总是会失败。现在继承地狱门这个类然后勉强可以进入到这个世界里面去。
回复

使用道具 举报

xiweihai 当前离线
积分
2028
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-5-7
查看详细资料
 楼主| 发表于 2015-12-14 15:40:40 | 显示全部楼层
RothBlink 发表于 2015-12-14 15:39
大神回我了好开心!试了下可以了,但是
if(entity instanceof EntityPlayerMP){
                   ...

这是传送玩家的方法,这个你加到初始化函数里面肯定报错,你传送玩家时在调用,另外那个实体传送有问题,我看mc源码传送实体是这样子的,
要在这个世界删除掉实体,然后复制实体在另外一个世界生成来实现的,不过传送玩家这样是可以的
回复

使用道具 举报

RothBlink 当前离线
积分
196
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2015-8-14
查看详细资料
发表于 2015-12-14 18:31:44 | 显示全部楼层
所以只能传送玩家么。。。
回复

使用道具 举报

xiweihai 当前离线
积分
2028
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-5-7
查看详细资料
 楼主| 发表于 2015-12-14 18:58:10 | 显示全部楼层
RothBlink 发表于 2015-12-14 18:31
所以只能传送玩家么。。。

655G)550J6W9S}_Q3]4YZ)V.png 当然可以传送实体,其实实现原理就是把实体从这个世界删除,然后复制实体,再另外一个世界生成来实现实体传送。另外,不再回答此贴任何问题

回复

使用道具 举报

1606140786 当前离线
积分
1344
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-10-20
查看详细资料
发表于 2015-12-17 00:14:02 | 显示全部楼层
好高级哇,完全看不懂{:10_492:}
回复

使用道具 举报

xxj2003 当前离线
积分
511
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2013-11-3
查看详细资料
发表于 2016-1-14 16:28:58 | 显示全部楼层
我新建了一个世界把地狱里岩浆都改成了水。
不过有点暗
回复

使用道具 举报

xxj2003 当前离线
积分
511
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2013-11-3
查看详细资料
发表于 2016-1-14 16:34:49 | 显示全部楼层
还有请问如何添加生物群系?
地狱里全是僵尸和小白
回复

使用道具 举报

xxj2003 当前离线
积分
511
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2013-11-3
查看详细资料
发表于 2016-1-14 16:51:16 | 显示全部楼层
一个无聊的世界

一个用调试模式作为Chunk的世界

一个用调试模式作为Chunk的世界
回复

使用道具 举报

xiweihai 当前离线
积分
2028
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-5-7
查看详细资料
 楼主| 发表于 2016-1-14 17:45:18 | 显示全部楼层
xxj2003 发表于 2016-1-14 16:34
还有请问如何添加生物群系?
地狱里全是僵尸和小白

new 世界供应者的时候加上一个自定义生物群系,然后可以在生物群系里定义刷怪
回复

使用道具 举报

xiweihai 当前离线
积分
2028
帖子
主题
精华
贡献
爱心
钻石
人气
下界之星
最后登录
1970-1-1
注册时间
2014-5-7
查看详细资料
 楼主| 发表于 2016-1-14 17:44:02 | 显示全部楼层
xxj2003 发表于 2016-1-14 16:51
一个无聊的世界

噗呲,这真的好简单,建议里深入了解下地形生成算法,以及结构生成算法
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册(register)

本版积分规则

Archiver|小黑屋|Mcbbs.net ( 京ICP备15023768号-1 ) | 京公网安备 11010502037624号 | 手机版

GMT+8, 2020-8-14 10:04 , Processed in 0.067769 second(s), Total 26, Slave 25 queries, Release: Build.2020.08.12 1730, Gzip On, Redis On.

"Minecraft"以及"我的世界"为Mojang Synergies AB的商标 本站与Mojang以及微软公司没有从属关系

© 2010-2020 我的世界中文论坛 版权所有 本站原创图文内容版权属于原创作者,未经许可不得转载

快速回复 返回顶部 返回列表