うまげーむさん

Fortniteなどのゲームの情報を主に投稿します。Twitterでブログの更新をツイートしてます。(@umageemu)

【マインクラフト Modding】1.15対応 自作MODの作り方 #12 ディメンション

このシリーズのまとめはこちら:

umagame.hatenablog.jp

こんばんは。

記事のストックがやばいのでもうすぐ毎日投稿が終わります。多分。

はじめに

今回はディメンションを作ります。

前回:

umagame.hatenablog.jp

ディメンションを作る

ディメンションのクラス

まずはディメンションのクラスをworld/dimensionに作ります。

f:id:Umagame:20200328232116p:plain

Dimensionを継承して、メソッド、コンストラクタを追加します。

package com.umagame.dirtmod.world.dimension;

import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.ChunkGenerator;

public class DimensionDirt extends Dimension{

    public DimensionDirt(World p_i225788_1_, DimensionType p_i225788_2_, float p_i225788_3_) {
        super(p_i225788_1_, p_i225788_2_, p_i225788_3_);
        // TODO 自動生成されたコンストラクター・スタブ
    }

    @Override
    public ChunkGenerator<?> createChunkGenerator() {
        // TODO 自動生成されたメソッド・スタブ
        return null;
    }

    @Override
    public BlockPos findSpawn(ChunkPos chunkPosIn, boolean checkValid) {
        // TODO 自動生成されたメソッド・スタブ
        return null;
    }

    @Override
    public BlockPos findSpawn(int posX, int posZ, boolean checkValid) {
        // TODO 自動生成されたメソッド・スタブ
        return null;
    }

    @Override
    public float calculateCelestialAngle(long worldTime, float partialTicks) {
        // TODO 自動生成されたメソッド・スタブ
        return 0;
    }

    @Override
    public boolean isSurfaceWorld() {
        // TODO 自動生成されたメソッド・スタブ
        return false;
    }

    @Override
    public Vec3d getFogColor(float celestialAngle, float partialTicks) {
        // TODO 自動生成されたメソッド・スタブ
        return null;
    }

    @Override
    public boolean canRespawnHere() {
        // TODO 自動生成されたメソッド・スタブ
        return false;
    }

    @Override
    public boolean doesXZShowFog(int x, int z) {
        // TODO 自動生成されたメソッド・スタブ
        return false;
    }

}

 とりあえずコンストラクタは3つ目のfloatを0にしておきます。

(この数字についてはよくわかりませんが、通常世界が0、ネザーが0.1、エンドが0になっているので0にしておきます。)

メソッドは上から順に

  • チャンクジェネレータ(後ほど作ります)
  • スポーン地点(リスポーンできないようにするので、今回はnull)
  • CelestialAngle(これはまじでわからない。太陽と月の角度らしいけど、今回は他からコピペします)
  • 地上の世界かどうか(今回はtrue)
  • 霧の色(今回は通常世界と同じ)
  • リスポーンできるかどうか(今回はfalse)
  • 霧の有無(ネザーのみtrueです。今回はfalse)

を設定できます。

コードはこんな感じ。

package com.umagame.dirtmod.world.dimension;

import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.ChunkGenerator;

public class DimensionDirt extends Dimension{

    public DimensionDirt(World p_i225788_1_, DimensionType p_i225788_2_) {
        super(p_i225788_1_, p_i225788_2_,0.0F);
    }

    @Override
    public ChunkGenerator<?> createChunkGenerator() {
        return new DirtChunkGenerator(world, new DirtBiomeProvider(), new DirtGenSettings()); //後で追加
    }

    @Override
    public BlockPos findSpawn(ChunkPos chunkPosIn, boolean checkValid) {
        return null;
    }

    @Override
    public BlockPos findSpawn(int posX, int posZ, boolean checkValid) {
        return null;
    }

    @Override
    public float calculateCelestialAngle(long worldTime, float partialTicks) {
        double d0 = MathHelper.frac((double)worldTime / 24000.0D - 0.25D);
          double d1 = 0.5D - Math.cos(d0 * Math.PI) / 2.0D;
          return (float)(d0 * 2.0D + d1) / 3.0F;
    }

    @Override
    public boolean isSurfaceWorld() {
        return true;
    }

    @Override
    public Vec3d getFogColor(float celestialAngle, float partialTicks) {
        float f = MathHelper.cos(celestialAngle * ((float)Math.PI * 2F)) * 2.0F + 0.5F;
          f = MathHelper.clamp(f, 0.0F, 1.0F);
          float f1 = 0.7529412F;
          float f2 = 0.84705883F;
          float f3 = 1.0F;
          f1 = f1 * (f * 0.94F + 0.06F);
          f2 = f2 * (f * 0.94F + 0.06F);
          f3 = f3 * (f * 0.91F + 0.09F);
          return new Vec3d((double)f1, (double)f2, (double)f3);
    }

    @Override
    public boolean canRespawnHere() {
        return false;
    }

    @Override
    public boolean doesXZShowFog(int x, int z) {
        return false;
    }

}

とりあえずこれでディメンション自体のクラスはOKです。

GenSettingsのクラス

生成の設定のクラスをworld/dimensionに作ります。

f:id:Umagame:20200329000346p:plain

このクラスではディメンションの生成の設定をできます。

設定は色々ありますが、とりあえず今回は通常世界でいう石ブロックを圧縮された土ブロックに変えておきます。

詳しくはGenerationSettingsクラスを見てください。

package com.umagame.dirtmod.world.dimension;

import com.umagame.dirtmod.init.DirtModBlocks;

import net.minecraft.block.BlockState;
import net.minecraft.world.gen.GenerationSettings;

public class DirtGenSettings extends GenerationSettings{
    @Override
    public BlockState getDefaultBlock() {
          return DirtModBlocks.SUPER_COMPRESSED_DIRT.getDefaultState();
    }
}

チャンクジェネレータのクラス

チャンクジェネレータのクラスをworld/dimensionに作ります。

f:id:Umagame:20200328235747p:plain

コードは超難解なので他からコピペします。

package com.umagame.dirtmod.world.dimension;

import net.minecraft.util.Util;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.IWorld;
import net.minecraft.world.WorldType;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.provider.BiomeProvider;
import net.minecraft.world.gen.NoiseChunkGenerator;
import net.minecraft.world.gen.OctavesNoiseGenerator;

public class DirtChunkGenerator extends NoiseChunkGenerator<DirtGenSettings>{
    
    private static final float[] field_222576_h = Util.make(new float[25], (p_222575_0_) -> {
        for (int i = -2; i <= 2; ++i) {
            for (int j = -2; j <= 2; ++j) {
                float f = 10.0F / MathHelper.sqrt((float) (i * i + j * j) + 0.2F);
                p_222575_0_[i + 2 + (j + 2) * 5] = f;
            }
        }

    });

    private final OctavesNoiseGenerator depthNoise;
    private final boolean isAmplified;
    
    public DirtChunkGenerator(IWorld worldIn, BiomeProvider provider, DirtGenSettings settingsIn) {
        super(worldIn, provider, 4, 8, 256, settingsIn, true);
        this.randomSeed.skip(2620);
        this.depthNoise = new OctavesNoiseGenerator(this.randomSeed, 15, 0);
        this.isAmplified = worldIn.getWorldInfo().getGenerator() == WorldType.AMPLIFIED;
    }

    @Override
    protected double[] func_222549_a(int p_222549_1_, int p_222549_2_) {
        double[] adouble = new double[2];
        float f = 0.0F;
        float f1 = 0.0F;
        float f2 = 0.0F;
        int j = this.getSeaLevel();
        float f3 = this.biomeProvider.func_225526_b_(p_222549_1_, j, p_222549_2_).getDepth();

        for (int k = -2; k <= 2; ++k) {
            for (int l = -2; l <= 2; ++l) {
                Biome biome = this.biomeProvider.func_225526_b_(p_222549_1_ + k, j, p_222549_2_ + l);
                float f4 = biome.getDepth();
                float f5 = biome.getScale();
                if (this.isAmplified && f4 > 0.0F) {
                    f4 = 1.0F + f4 * 2.0F;
                    f5 = 1.0F + f5 * 4.0F;
                }

                float f6 = field_222576_h[k + 2 + (l + 2) * 5] / (f4 + 2.0F);
                if (biome.getDepth() > f3) {
                    f6 /= 2.0F;
                }

                f += f5 * f6;
                f1 += f4 * f6;
                f2 += f6;
            }
        }

        f = f / f2;
        f1 = f1 / f2;
        f = f * 0.9F + 0.1F;
        f1 = (f1 * 4.0F - 1.0F) / 8.0F;
        adouble[0] = (double) f1 + this.getNoiseDepthAt(p_222549_1_, p_222549_2_);
        adouble[1] = (double) f;
        return adouble;
    }

    @Override
    protected double func_222545_a(double p_222545_1_, double p_222545_3_, int p_222545_5_) {
        double d1 = ((double) p_222545_5_ - (8.5D + p_222545_1_ * 8.5D / 8.0D * 4.0D)) * 12.0D * 128.0D / 256.0D
                / p_222545_3_;
        if (d1 < 0.0D) {
            d1 *= 4.0D;
        }

        return d1;
    }

    @Override
    protected void func_222548_a(double[] p_222548_1_, int p_222548_2_, int p_222548_3_) {
        this.func_222546_a(p_222548_1_, p_222548_2_, p_222548_3_, (double) 684.412F, (double) 684.412F, 8.555149841308594D,
                4.277574920654297D, 3, -10);
    }
    
    private double getNoiseDepthAt(int noiseX, int noiseZ) {
        double d0 = this.depthNoise.func_215462_a((double) (noiseX * 200), 10.0D, (double) (noiseZ * 200), 1.0D, 0.0D, true)
                * 65535.0D / 8000.0D;
        if (d0 < 0.0D) {
            d0 = -d0 * 0.3D;
        }

        d0 = d0 * 3.0D - 2.0D;
        if (d0 < 0.0D) {
            d0 = d0 / 28.0D;
        } else {
            if (d0 > 1.0D) {
                d0 = 1.0D;
            }

            d0 = d0 / 40.0D;
        }

        return d0;
    }

    @Override
    public int getGroundHeight() {
        return 64;
    }
    @Override
    public int getSeaLevel() {
        return 63;
    }
}

一番下のgetGroundHeightとgetSeaLevelで地面と海の高さを設定できます。

バイオームプロバイダのクラス

バイオームプロバイダのクラスをworld/dimensionに作ります。

f:id:Umagame:20200329001927p:plain

このクラスで、ディメンション内のバイオームを指定します。

(今回は一つだけですが、biomeListに複数入れたらディメンションに複数のバイオームが出るようになると思います。多分。)

package com.umagame.dirtmod.world.dimension; import java.util.Set; import com.google.common.collect.ImmutableSet; import com.umagame.dirtmod.init.DirtModBiomes; import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.provider.BiomeProvider; public class DirtBiomeProvider extends BiomeProvider{ private static final Set<Biome> biomeList = ImmutableSet.of(DirtModBiomes.DIRT); protected DirtBiomeProvider() { super(biomeList); } @Override public Biome func_225526_b_(int p_225526_1_, int p_225526_2_, int p_225526_3_) { return DirtModBiomes.DIRT; } }

ModDimensionのクラス

最後に、登録をするときに使うModDimensionのクラスを作ります。

f:id:Umagame:20200329002433p:plain

ModDimensionを継承して、メソッドの戻り値をディメンションのクラスにします。

コンストラクタにsetRegistryNameを追加して、IDも設定しておきます。

package com.umagame.dirtmod.world.dimension;

import java.util.function.BiFunction;

import net.minecraft.world.World;
import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.ModDimension;

public class DirtModDimension extends ModDimension{
    public DirtModDimension() {
        this.setRegistryName("dirt");
    }
    @Override
    public BiFunction<World, DimensionType, ? extends Dimension> getFactory() {
        return DimensionDirt::new;
    }
}

ディメンションの登録

initに登録用クラスを作ります。

f:id:Umagame:20200329002820p:plain

アイテムなどとは少し違った感じです。

package com.umagame.dirtmod.init;

import com.umagame.dirtmod.main.DirtMod;
import com.umagame.dirtmod.world.dimension.DirtModDimension;

import net.minecraftforge.common.ModDimension;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.registries.ObjectHolder;

@ObjectHolder(DirtMod.MOD_ID)
@EventBusSubscriber(modid = DirtMod.MOD_ID, bus = Bus.MOD)
public class DirtModDimensions {
    public static final ModDimension DIRT_DIMENSION = new DirtModDimension();

    @SubscribeEvent
    public static void onDimensionRegistryEvent(RegistryEvent.Register<ModDimension> event) {
        event.getRegistry().register(new DirtModDimension());
    }
}

ディメンションの登録はこれだけではだめで、もう一つクラスが必要です。

これから使うかもしれないのでmainに作ります。

f:id:Umagame:20200329003600p:plain

正直名前はなんでもいいです。

DIMENSION_TYPE_RLのところにディメンションのIDを入力する必要があるので、忘れないように。

package com.umagame.dirtmod.main;

import com.umagame.dirtmod.init.DirtModDimensions;

import net.minecraft.util.ResourceLocation;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.event.world.RegisterDimensionsEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;

@EventBusSubscriber(modid = DirtMod.MOD_ID, bus=Bus.FORGE)
public class CommonForgeEventHandler {

  public static final ResourceLocation DIMENSION_TYPE_RL = new ResourceLocation(DirtMod.MOD_ID, "dirt");

    @SubscribeEvent
    public static void onRegisterDimensionsEvent(RegisterDimensionsEvent event) {
        if (DimensionType.byName(DIMENSION_TYPE_RL) == null){
            DimensionManager.registerDimension(DIMENSION_TYPE_RL, DirtModDimensions.DIRT_DIMENSION, null, true);
        }
    }
}

これでディメンションの追加は完了です。

起動して確認しましょう。

「/forge setdimension @p ディメンションID」でディメンションに移動できます。

f:id:Umagame:20200329010013p:plain

石の部分もちゃんと変わっています。

f:id:Umagame:20200329010024p:plain

 さいごに

今回はここまでです。

今回は書いてる途中に結構バグが出たので、もしかしたらコードに誤りがあるかもしれません。

ディメンションはバイオームと同様に設定が多いので、今回紹介できなかった設定は結構あります。

ではでは。

次回:

umagame.hatenablog.jp